diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 1f37de499a19b..15d82afe82746 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -270,14 +270,27 @@ InFlightDiagnostic &InFlightDiagnostic::fixItRemove(SourceRange R) { // the token we want to remove. auto &SM = Engine->SourceMgr; auto charRange = toCharSourceRange(SM, R); - - // If we're removing something (e.g. a keyword), do a bit of extra work to - // make sure that we leave the code in a good place, without extraneous white - // space around its hole. Specifically, check to see there is whitespace - // before and after the end of range. If so, nuke the space afterward to keep - // things consistent. - if (extractCharAfter(SM, charRange.getEnd()) == ' ' && - isspace(extractCharBefore(SM, charRange.getStart()))) { + auto charAfter = extractCharAfter(SM, charRange.getEnd()); + + // If we're removing something (e.g. a keyword or an attribute), do a bit of + // extra work to make sure that we leave the code in a good place, without + // extraneous white space around its hole: + // + // - If there's a newline immediately following the range, check whether the + // content of the line leading up to the range is all whitespace. Remove + // the entire line if so. + // - If there is a space before and after the end of range, nuke the + // space afterward to keep things consistent. + if (charAfter == '\n' || charAfter == '\r') { + auto lineStartLoc = Lexer::getLocForStartOfLine(SM, charRange.getStart()); + auto lineStart = SM.extractText( + toCharSourceRange(SM, lineStartLoc, charRange.getStart())); + if (lineStart.ltrim().empty()) { + charRange = CharSourceRange( + lineStartLoc, lineStart.size() + charRange.getByteLength() + 1); + } + } else if (charAfter == ' ' && + isspace(extractCharBefore(SM, charRange.getStart()))) { charRange = CharSourceRange(charRange.getStart(), charRange.getByteLength()+1); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index e3c3056a36019..8560ae403ddc8 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -5326,6 +5326,12 @@ void suggestAnyAppleOSAvailability(const Decl *D, if (!sf) return; + // Skip implicit declarations. Availability for these declarations is + // typically derived from other declarations and those other declarations are + // the ones that should adopt anyAppleOS. + if (D->isImplicit()) + return; + if (!diags.isDiagnosticGroupEnabled(sf, DiagGroupID::UseAnyAppleOSAvailability)) return; @@ -5336,11 +5342,24 @@ void suggestAnyAppleOSAvailability(const Decl *D, attrsByIntroVersion; for (const AvailableAttr *attr : attrs) { - auto semAttr = D->getSemanticAvailableAttr(attr); - if (!semAttr || !semAttr->isPlatformSpecific()) + // Only consider @available attributes that could indicate an introduction. + if (attr->getKind() != AvailableAttr::Kind::Default) continue; - if (attr->getKind() != AvailableAttr::Kind::Default) + // Check whether the attributes have any fields besides 'introduced:' and + // skip if they do since we don't attempt to judge whether those fields + // match and could be consolidated. + // FIXME: This is conservative; attrs with matching fields could be merged. + if (attr->getRawDeprecated() || attr->getRawObsoleted() || + !attr->getMessage().empty() || !attr->getRename().empty()) + continue; + + // @_spi_available attributes should not be considered. + if (attr->isSPI()) + continue; + + auto semAttr = D->getSemanticAvailableAttr(attr); + if (!semAttr || !semAttr->isPlatformSpecific()) continue; auto platform = semAttr->getPlatform(); @@ -5367,35 +5386,27 @@ void suggestAnyAppleOSAvailability(const Decl *D, return; llvm::VersionTuple commonVersion; - llvm::SmallVector attrsToReplace; - for (auto &[version, attrList] : attrsByIntroVersion) { + llvm::SmallVector attrsForCommonVersion; + for (auto &[version, attrsForVersion] : attrsByIntroVersion) { // Prefer replacing the longest list of attributes. - if (attrList.size() < attrsToReplace.size()) + if (attrsForVersion.size() < attrsForCommonVersion.size()) continue; // If the lists have the same length, prefer the earlier version. - if (attrList.size() == attrsToReplace.size()) { + if (attrsForVersion.size() == attrsForCommonVersion.size()) { if (version > commonVersion) continue; } commonVersion = version; - attrsToReplace = attrList; + attrsForCommonVersion = attrsForVersion; } + auto attrsToReplace = llvm::SmallSetVector( + attrsForCommonVersion.begin(), attrsForCommonVersion.end()); if (attrsToReplace.size() < 2) return; - // Check whether the attributes have any fields besides 'introduced:' and skip - // if they do since we don't attempt to judge whether those fields match and - // could be consolidated. - // FIXME: This is conservative; attrs with matching fields could be merged. - for (auto *attr : attrsToReplace) { - if (attr->getRawDeprecated() || attr->getRawObsoleted() || - !attr->getMessage().empty() || !attr->getRename().empty()) - return; - } - auto diag = diags.diagnose(D->getLoc(), diag::availability_use_any_apple_os, commonVersion); @@ -5411,64 +5422,148 @@ void suggestAnyAppleOSAvailability(const Decl *D, } auto attrGroups = getAvailableAttrGroups(attrs); - if (attrGroups.empty()) { - // The attributes are all written in long-form. - // FIXME: Generate fix-its for this pattern if it is common enough. - return; - } - if (attrGroups.size() > 1) { - // The attributes are written in multiple short-form groups. - return; - } + // When all replaceable attrs live in a single short-form group, use an + // inner-replacement fix-it (that just replaces the specs inside the parens) + // so the source range is as narrow as possible. + if (attrGroups.size() == 1) { + llvm::SmallDenseSet remainingAttrsToReplace( + attrsToReplace.begin(), attrsToReplace.end()); + + llvm::SmallString<128> replacement; + llvm::raw_svector_ostream os(replacement); + os << "anyAppleOS " << commonVersion; + + auto groupHead = attrGroups.front(); + + // Get the location of the @available attr's right paren. + SourceLoc groupEndLoc = groupHead->getEndLoc(); + + // Build up the fix-it contents and find the starting source loc for the + // availability specs. + SourceLoc groupStartLoc = groupHead->getDomainLoc(); + for (auto *member = groupHead; member != nullptr; + member = member->getNextGroupedAvailableAttr()) { + // The attributes are enumerated in reverse, so the group's starting loc + // is the last domain loc we see. + groupStartLoc = member->getDomainLoc(); + if (remainingAttrsToReplace.erase(member)) + continue; + if (auto semAttr = D->getSemanticAvailableAttr(member)) { + os << ", " << platformString(semAttr->getPlatform()); + if (auto v = member->getRawIntroduced()) + os << " " << *v; + } + } + os << ", *"; - // The attributes include a single short-form. If they all belong - // to the same group we can emit a fix-it to update it. - llvm::SmallSet remainingAttrsToSkip; - for (auto *attr : attrsToReplace) - remainingAttrsToSkip.insert(attr); + if (remainingAttrsToReplace.empty()) { + // Make sure we have a valid source range in a single buffer. We might + // not if some attributes were expanded from an availability macro. + if (groupStartLoc.isInvalid() || groupEndLoc.isInvalid()) + return; + + auto startBuffer = ctx.SourceMgr.findBufferContainingLoc(groupStartLoc); + auto endBuffer = ctx.SourceMgr.findBufferContainingLoc(groupEndLoc); + if (startBuffer != endBuffer) + return; - llvm::SmallString<128> replacement; - llvm::raw_svector_ostream os(replacement); - os << "anyAppleOS " << commonVersion; + diag.fixItReplaceChars(groupStartLoc, groupEndLoc, replacement); + return; + } - auto groupHead = attrGroups.front(); + // Some replaceable attrs are outside the single group (e.g. long-form + // attrs mixed with a short-form group). Fall through to the general + // multi-attr fix-it below. + } - // Get the location of the @available attr's right paren. - SourceLoc groupEndLoc = groupHead->getEndLoc(); + // The replaceable attrs span multiple separate @available + // attributes which could be long-form (`@available(iOS, introduced: 26)`), + // separate single-platform short-form (`@available(iOS 26, *)`), multiple + // groups, or a mix of those patterns. + // + // Replace the first attr in source with the consolidated anyAppleOS + // attribute and then remove the remaining attrs. Any non-replaced members of + // a partially- covered group are folded into the replacement text. + + // Map each group member to its group head (the first attr in the chain). + llvm::SmallDenseMap + memberToGroupHead; + for (auto *head : attrGroups) + for (auto *m = head; m; m = m->getNextGroupedAvailableAttr()) + memberToGroupHead[m] = head; + + // For each replaceable attr, find its group and collect any non-replaced + // members of the same group that must survive. + llvm::SmallDenseSet groupHeadsToReplace; + llvm::SmallVector groupMembersToKeep; - // Build up the fix-it contents and find the starting source loc for the - // availability specs. - SourceLoc groupStartLoc = groupHead->getDomainLoc(); - for (auto *member = groupHead; member != nullptr; - member = member->getNextGroupedAvailableAttr()) { - // The attributes are enumerated in reverse, so the group's starting loc - // is the last domain loc we see. - groupStartLoc = member->getDomainLoc(); - if (remainingAttrsToSkip.erase(member)) + for (auto *attr : attrsToReplace) { + const AvailableAttr *head = + attr->isGroupMember() ? memberToGroupHead.lookup(attr) : attr; + if (!head) + return; + if (!groupHeadsToReplace.insert(head).second) continue; - if (auto semAttr = D->getSemanticAvailableAttr(member)) { - os << ", " << platformString(semAttr->getPlatform()); - if (auto v = member->getRawIntroduced()) - os << " " << *v; + + if (head->isGroupMember()) { + for (auto *m = head; m; m = m->getNextGroupedAvailableAttr()) { + if (!attrsToReplace.contains(m)) + groupMembersToKeep.push_back(m); + } } } - os << ", *"; - // Only emit the fix-it if all of the attributes to replace were found in - // the group. - if (remainingAttrsToSkip.empty()) { - // Make sure we have a valid source range in a single buffer. We might not - // if some attributes were expanded from an availability macro. - if (groupStartLoc.isInvalid() || groupEndLoc.isInvalid()) + if (groupHeadsToReplace.empty()) + return; + + // Validate that all source attrs have valid locations in a single buffer + // before sorting (isBeforeInBuffer requires valid locs in the same buffer). + // An invalid or cross-buffer location indicates an availability macro + // expansion. + std::optional primaryBuf; + for (auto *head : groupHeadsToReplace) { + auto range = head->getRangeWithAt(); + if (range.Start.isInvalid() || range.End.isInvalid()) return; - auto startBuffer = ctx.SourceMgr.findBufferContainingLoc(groupStartLoc); - auto endBuffer = ctx.SourceMgr.findBufferContainingLoc(groupEndLoc); - if (startBuffer != endBuffer) + auto buf = ctx.SourceMgr.findBufferContainingLoc(range.Start); + if (!primaryBuf) { + primaryBuf = buf; + } else if (buf != *primaryBuf) { return; + } + } + + // Sort by source location so the primary attr is first. + llvm::SmallVector sortedGroupHeadsToReplace( + groupHeadsToReplace.begin(), groupHeadsToReplace.end()); + llvm::sort(sortedGroupHeadsToReplace, + [&](const AvailableAttr *a, const AvailableAttr *b) { + return ctx.SourceMgr.isBeforeInBuffer(a->getRangeWithAt().Start, + b->getRangeWithAt().Start); + }); + + // Build: @available(anyAppleOS N[, kept members from all groups...], *) + llvm::SmallString<128> fullReplacement; + llvm::raw_svector_ostream fos(fullReplacement); + fos << "@available(anyAppleOS " << commonVersion; + for (auto *kept : groupMembersToKeep) { + auto semAttr = D->getSemanticAvailableAttr(kept); + if (!semAttr) + continue; + fos << ", " << semAttr->getDomain().getNameForAttributePrinting(); + if (auto v = kept->getRawIntroduced()) + fos << " " << *v; + } + fos << ", *)"; + + // Replace the primary attr and remove the rest. + diag.fixItReplace(sortedGroupHeadsToReplace.front()->getRangeWithAt(), + fullReplacement); - diag.fixItReplaceChars(groupStartLoc, groupEndLoc, replacement); + for (auto *head : llvm::drop_begin(sortedGroupHeadsToReplace)) { + diag.fixItRemove(head->getRangeWithAt()); } } diff --git a/test/Availability/availability_suggest_any_apple_os.swift b/test/Availability/availability_suggest_any_apple_os.swift index 30bf53b880b70..52a4ef3a04709 100644 --- a/test/Availability/availability_suggest_any_apple_os.swift +++ b/test/Availability/availability_suggest_any_apple_os.swift @@ -1,6 +1,6 @@ -// RUN: %target-typecheck-verify-swift -Wwarning UseAnyAppleOSAvailability \ -// RUN: -define-availability 'FourOSesAligned 26:macOS 26, iOS 26, tvOS 26, watchOS 26' \ -// RUN: -verify-ignore-unrelated +// RUN: %target-typecheck-verify-swift -Wwarning UseAnyAppleOSAvailability + +// NOTE: Do not add -verify-ignore-unknown to frontend invocations in this test. @available(macOS 26, iOS 26, tvOS 26, watchOS 26, *) // expected-note@-1 {{'allFourOSesAligned()' is available in macOS 26 or newer}} @@ -30,9 +30,6 @@ func allFourOSesAlignedReversed() { } // expected-warning {{use '@available(anyA // expected-note@-4 {{'allFourOSesAlignedSomeTrailingZeroes()' is available in watchOS 26.0 or newer}} func allFourOSesAlignedSomeTrailingZeroes() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-5:12-56=anyAppleOS 26, *}} -@available(FourOSesAligned 26, *) -func allFourOSesAlignedMacro() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}} - @available(macOS 26, iOS 26, tvOS 26, watchOS 26, visionOS 26, macCatalyst 26, *) // expected-note@-1 {{'allSixOSesAligned()' is available in macOS 26 or newer}} // expected-note@-2 {{'allSixOSesAligned()' is available in iOS 26 or newer}} @@ -100,58 +97,105 @@ func twoAlignedVersions() { } // expected-warning {{use '@available(anyAppleOS 2 // expected-note@-2 {{'twoAlignedVersionsSwapped()' is available in watchOS 26 or newer}} func twoAlignedVersionsSwapped() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-3:12-56=anyAppleOS 26, iOS 26.4, macOS 26.4, *}} +@available(macOS 26, *) +// expected-note@-1 {{'separateSinglePlatformGroups()' is available in macOS 26 or newer}} +@available(iOS 26, *) +// expected-note@-1 {{'separateSinglePlatformGroups()' is available in iOS 26 or newer}} +@available(visionOS 26, *) +// expected-note@-1 {{'separateSinglePlatformGroups()' is available in visionOS 26 or newer}} +@available(watchOS, unavailable) +@available(tvOS, unavailable) +func separateSinglePlatformGroups() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-8:1-24=@available(anyAppleOS 26, *)}}{{-6:1--5:1=}}{{-4:1--3:1=}} + +struct Outer { + @available(macOS 26, *) + // expected-note@-1 {{'separateSinglePlatformGroupsIndented()' is available in macOS 26 or newer}} + @available(iOS 26, *) + // expected-note@-1 {{'separateSinglePlatformGroupsIndented()' is available in iOS 26 or newer}} + @available(visionOS 26, *) + // expected-note@-1 {{'separateSinglePlatformGroupsIndented()' is available in visionOS 26 or newer}} + @available(watchOS, unavailable) + @available(tvOS, unavailable) + func separateSinglePlatformGroupsIndented() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-8:3-26=@available(anyAppleOS 26, *)}}{{-6:1--5:1=}}{{-4:1--3:1=}} +} + +@available(macOS 26, *) @available(iOS 26, *) @available(visionOS 26, *) @available(watchOS, unavailable) @available(tvOS, unavailable) +// expected-note@-1 {{'separateSinglePlatformGroupsSameLine()' is available in macOS 26 or newer}} +// expected-note@-2 {{'separateSinglePlatformGroupsSameLine()' is available in iOS 26 or newer}} +// expected-note@-3 {{'separateSinglePlatformGroupsSameLine()' is available in visionOS 26 or newer}} +func separateSinglePlatformGroupsSameLine() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-4:1-24=@available(anyAppleOS 26, *)}}{{-4:25-47=}}{{-4:47-74=}} + +@available(macOS 26, *) +// expected-note@-1 {{'separateSinglePlatformGroupsAlternateOrder()' is available in macOS 26 or newer}} +@available(iOS 26, *) +// expected-note@-1 {{'separateSinglePlatformGroupsAlternateOrder()' is available in iOS 26 or newer}} +@available(watchOS, unavailable) +@available(tvOS, unavailable) +@available(visionOS 26, *) +// expected-note@-1 {{'separateSinglePlatformGroupsAlternateOrder()' is available in visionOS 26 or newer}} +func separateSinglePlatformGroupsAlternateOrder() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-8:1-24=@available(anyAppleOS 26, *)}}{{-6:1--5:1=}}{{-2:1--1:1=}} + +// Separate single-platform short-form groups with a minor version. +@available(iOS 26.4, *) +// expected-note@-1 {{'separateSinglePlatformGroupsMinorVersion()' is available in iOS 26.4 or newer}} +@available(macOS 26.4, *) +// expected-note@-1 {{'separateSinglePlatformGroupsMinorVersion()' is available in macOS 26.4 or newer}} +@available(watchOS 26.4, *) +// expected-note@-1 {{'separateSinglePlatformGroupsMinorVersion()' is available in watchOS 26.4 or newer}} +func separateSinglePlatformGroupsMinorVersion() { } // expected-warning {{use '@available(anyAppleOS 26.4, *)' instead of platform specific '@available' attributes with the same version}}{{-6:1-24=@available(anyAppleOS 26.4, *)}}{{-4:1--3:1=}}{{-2:1--1:1=}} + @available(macOS 26, iOS 26, *) // expected-note@-1 {{'alignedVersionsSplitIntoMultipleGropus()' is available in macOS 26 or newer}} // expected-note@-2 {{'alignedVersionsSplitIntoMultipleGropus()' is available in iOS 26 or newer}} @available(tvOS 26, watchOS 26, *) // expected-note@-1 {{'alignedVersionsSplitIntoMultipleGropus()' is available in tvOS 26 or newer}} // expected-note@-2 {{'alignedVersionsSplitIntoMultipleGropus()' is available in watchOS 26 or newer}} -func alignedVersionsSplitIntoMultipleGropus() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func alignedVersionsSplitIntoMultipleGropus() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-6:1-32=@available(anyAppleOS 26, *)}}{{-3:1--2:1=}} @available(macOS, introduced: 26) // expected-note {{'fourOSesAlignedLongForm()' is available in macOS 26 or newer}} @available(iOS, introduced: 26) // expected-note {{'fourOSesAlignedLongForm()' is available in iOS 26 or newer}} @available(tvOS, introduced: 26) // expected-note {{'fourOSesAlignedLongForm()' is available in tvOS 26 or newer}} @available(watchOS, introduced: 26) // expected-note {{'fourOSesAlignedLongForm()' is available in watchOS 26 or newer}} -func fourOSesAlignedLongForm() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fourOSesAlignedLongForm() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-4:1-34=@available(anyAppleOS 26, *)}}{{-3:1-33=}}{{-2:1-34=}}{{-1:1-37=}} @available(macOS, introduced: 26.4) // expected-note {{'fourOSesAlignedLongFormMinorVersion()' is available in macOS 26.4 or newer}} @available(iOS, introduced: 26.4) // expected-note {{'fourOSesAlignedLongFormMinorVersion()' is available in iOS 26.4 or newer}} @available(tvOS, introduced: 26.4) // expected-note {{'fourOSesAlignedLongFormMinorVersion()' is available in tvOS 26.4 or newer}} @available(watchOS, introduced: 26.4) // expected-note {{'fourOSesAlignedLongFormMinorVersion()' is available in watchOS 26.4 or newer}} -func fourOSesAlignedLongFormMinorVersion() { } // expected-warning {{use '@available(anyAppleOS 26.4, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fourOSesAlignedLongFormMinorVersion() { } // expected-warning {{use '@available(anyAppleOS 26.4, *)' instead of platform specific '@available' attributes with the same version}}{{-4:1-36=@available(anyAppleOS 26.4, *)}}{{-3:1-35=}}{{-2:1-36=}}{{-1:1-39=}} @available(macOS, introduced: 26.0) // expected-note {{'fourOSesAlignedLongFormTrailingZeroes()' is available in macOS 26.0 or newer}} @available(iOS, introduced: 26.0) // expected-note {{'fourOSesAlignedLongFormTrailingZeroes()' is available in iOS 26.0 or newer}} @available(tvOS, introduced: 26.0) // expected-note {{'fourOSesAlignedLongFormTrailingZeroes()' is available in tvOS 26.0 or newer}} @available(watchOS, introduced: 26.0) // expected-note {{'fourOSesAlignedLongFormTrailingZeroes()' is available in watchOS 26.0 or newer}} -func fourOSesAlignedLongFormTrailingZeroes() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fourOSesAlignedLongFormTrailingZeroes() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-4:1-36=@available(anyAppleOS 26, *)}}{{-3:1-35=}}{{-2:1-36=}}{{-1:1-39=}} @available(macOS, introduced: 26.0) // expected-note {{'fourOSesAlignedLongFormSomeTrailingZeroes()' is available in macOS 26.0 or newer}} @available(iOS, introduced: 26) // expected-note {{'fourOSesAlignedLongFormSomeTrailingZeroes()' is available in iOS 26 or newer}} @available(tvOS, introduced: 26.0) // expected-note {{'fourOSesAlignedLongFormSomeTrailingZeroes()' is available in tvOS 26.0 or newer}} @available(watchOS, introduced: 26) // expected-note {{'fourOSesAlignedLongFormSomeTrailingZeroes()' is available in watchOS 26 or newer}} -func fourOSesAlignedLongFormSomeTrailingZeroes() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fourOSesAlignedLongFormSomeTrailingZeroes() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-4:1-36=@available(anyAppleOS 26, *)}}{{-3:1-33=}}{{-2:1-36=}}{{-1:1-37=}} @available(macOS, introduced: 26) // expected-note {{'fiveOSesAlignedLongForm()' is available in macOS 26 or newer}} @available(iOS, introduced: 26) // expected-note {{'fiveOSesAlignedLongForm()' is available in iOS 26 or newer}} @available(tvOS, introduced: 26) // expected-note {{'fiveOSesAlignedLongForm()' is available in tvOS 26 or newer}} @available(watchOS, introduced: 26) // expected-note {{'fiveOSesAlignedLongForm()' is available in watchOS 26 or newer}} @available(visionOS, introduced: 26) // expected-note {{'fiveOSesAlignedLongForm()' is available in visionOS 26 or newer}} -func fiveOSesAlignedLongForm() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fiveOSesAlignedLongForm() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-5:1-34=@available(anyAppleOS 26, *)}}{{-4:1-33=}}{{-3:1-34=}}{{-2:1-37=}}{{-1:1-38=}} @available(macOS 26, iOS 26, *) // expected-note@-1 {{'fourOSesAlignedMixedShortFormAndLongForm()' is available in macOS 26 or newer}} // expected-note@-2 {{'fourOSesAlignedMixedShortFormAndLongForm()' is available in iOS 26 or newer}} @available(tvOS, introduced: 26) // expected-note {{'fourOSesAlignedMixedShortFormAndLongForm()' is available in tvOS 26 or newer}} @available(watchOS, introduced: 26) // expected-note {{'fourOSesAlignedMixedShortFormAndLongForm()' is available in watchOS 26 or newer}} -func fourOSesAlignedMixedShortFormAndLongForm() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fourOSesAlignedMixedShortFormAndLongForm() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-5:1-32=@available(anyAppleOS 26, *)}}{{-2:1-34=}}{{-1:1-37=}} @available(macOS, introduced: 26) // expected-note {{'fourOSesAlignedMixedShortFormAndLongFormSwapped()' is available in macOS 26 or newer}} @available(iOS, introduced: 26) // expected-note {{'fourOSesAlignedMixedShortFormAndLongFormSwapped()' is available in iOS 26 or newer}} @available(tvOS 26, watchOS 26, *) // expected-note@-1 {{'fourOSesAlignedMixedShortFormAndLongFormSwapped()' is available in tvOS 26 or newer}} // expected-note@-2 {{'fourOSesAlignedMixedShortFormAndLongFormSwapped()' is available in watchOS 26 or newer}} -func fourOSesAlignedMixedShortFormAndLongFormSwapped() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{none}} +func fourOSesAlignedMixedShortFormAndLongFormSwapped() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-5:1-34=@available(anyAppleOS 26, *)}}{{-4:1-33=}}{{-3:1--2:1=}} @available(macOS, introduced: 26, deprecated: 26.4) @available(iOS, introduced: 26, deprecated: 26.4) @@ -206,3 +250,39 @@ func fourAppExtensionVersionsLongForm() { } @available(Windows 26, Android 26, *) func nonAppleOSesAligned() { } + +@_spi_available(macOS 26, *) +@_spi_available(iOS 26, *) +@_spi_available(watchOS 26, *) +@available(visionOS 26, *) +func someSPIAvailableSeparateShortForm() { } + +@available(macOS 26, *) +@_spi_available(iOS 26, watchOS 26, visionOS 26, *) +func someSPIAvailableSingleShortForm() { } + +@_spi_available(macOS 26, iOS 26, watchOS 26, visionOS 26, *) +func allSPIAvailableSingleShortForm() { } // expected-warning {{symbols that are '@_spi_available' on all platforms should use '@_spi' instead}} + +@available(macOS 26, visionOS 26, *) +// expected-note@-1 {{'someSPIAvailableTwoNonSPIOSesAligned()' is available in macOS 26 or newer}} +// expected-note@-2 {{'someSPIAvailableTwoNonSPIOSesAligned()' is available in visionOS 26 or newer}} +@_spi_available(iOS 26, watchOS 26, *) +func someSPIAvailableTwoNonSPIOSesAligned() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-4:1-37=@available(anyAppleOS 26, *)}} + +@available(macOS 26, visionOS 26, *) +// expected-note@-1 {{'someSPIAvailableTwoNonSPIOSesAligned2()' is available in macOS 26 or newer}} +// expected-note@-2 {{'someSPIAvailableTwoNonSPIOSesAligned2()' is available in visionOS 26 or newer}} +@_spi_available(iOS 26, *) +@_spi_available(watchOS 26, *) +func someSPIAvailableTwoNonSPIOSesAligned2() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}}{{-5:1-37=@available(anyAppleOS 26, *)}} + +class BaseClass { + @available(macOS 26, iOS 26, *) + // expected-note@-1 {{'init()' is available in macOS 26 or newer}} + // expected-note@-2 {{'init()' is available in iOS 26 or newer}} + init() { } // expected-warning {{use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version}} +} + +// The synthesized inherited init should NOT trigger a warning. +class DerivedClass: BaseClass { } diff --git a/test/Availability/availability_suggest_any_apple_os_define.swift b/test/Availability/availability_suggest_any_apple_os_define.swift new file mode 100644 index 0000000000000..7c238fc7c6ce8 --- /dev/null +++ b/test/Availability/availability_suggest_any_apple_os_define.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -typecheck %s \ +// RUN: -Wwarning UseAnyAppleOSAvailability \ +// RUN: -define-availability 'FourOSesAligned 26:macOS 26, iOS 26, tvOS 26, watchOS 26' \ +// RUN: -diagnostic-style llvm 2>&1 | %FileCheck %s + +// CHECK: warning: use '@available(anyAppleOS 26, *)' instead of platform specific '@available' attributes with the same version [#UseAnyAppleOSAvailability] +// CHECK: -define-availability argument:1:55: note: 'allFourOSesAlignedMacro()' is available in watchOS 26 or newer +// CHECK: -define-availability argument:1:43: note: 'allFourOSesAlignedMacro()' is available in tvOS 26 or newer +// CHECK: -define-availability argument:1:34: note: 'allFourOSesAlignedMacro()' is available in iOS 26 or newer +// CHECK: -define-availability argument:1:26: note: 'allFourOSesAlignedMacro()' is available in macOS 26 or newer + +@available(FourOSesAligned 26, *) +func allFourOSesAlignedMacro() { } diff --git a/test/ClangImporter/availability_custom_domains_access_availability.swift b/test/ClangImporter/availability_custom_domains_access_availability.swift index 8aba570eea1aa..04f9975848185 100644 --- a/test/ClangImporter/availability_custom_domains_access_availability.swift +++ b/test/ClangImporter/availability_custom_domains_access_availability.swift @@ -354,7 +354,7 @@ func testPermanentlyUnavailable() { } @available(*, deprecated) extension PublicGenericStruct { @available(Aral) - // expected-warning@-1 {{'@available' has no effect because 'Aral' is always available}}{{3-19=}} + // expected-warning@-1 {{'@available' has no effect because 'Aral' is always available}}{{1-+1:1=}} func testPermanentlyAvailableInDeprecatedExtension() { } @available(Aral, deprecated) diff --git a/test/Concurrency/unsafe_inherit_executor.swift b/test/Concurrency/unsafe_inherit_executor.swift index 7d7b54ca790f6..00bcc4ce01ce4 100644 --- a/test/Concurrency/unsafe_inherit_executor.swift +++ b/test/Concurrency/unsafe_inherit_executor.swift @@ -34,7 +34,7 @@ class NonSendableObject { @_unsafeInheritExecutor func useNonSendable(object: NonSendableObject) async {} -// expected-warning@-1{{'@_unsafeInheritExecutor' is deprecated; consider an 'isolated' parameter defaulted to '#isolation' instead; this is an error in the Swift 6 language mode}}{{35:1-24=}}{{36:46-46=, isolation: isolated (any Actor)? = #isolation}} +// expected-warning@-1{{'@_unsafeInheritExecutor' is deprecated; consider an 'isolated' parameter defaulted to '#isolation' instead; this is an error in the Swift 6 language mode}}{{-1:1-+0:1=}}{{46-46=, isolation: isolated (any Actor)? = #isolation}} actor MyActor { var object = NonSendableObject() @@ -52,7 +52,7 @@ func unsafeCallerA(x: Int) async { // expected-warning@-1{{'@_unsafeInheritExecutor' is deprecated; consider an 'isolated' parameter defaulted to '#isolation' instead}} await inheritsIsolationProperly() - // expected-error@-1{{#isolation (introduced by a default argument) cannot be used within an '@_unsafeInheritExecutor' function}}{{50:1-24=}}{{51:26-26=, isolation: isolated (any Actor)? = #isolation}} + // expected-error@-1{{#isolation (introduced by a default argument) cannot be used within an '@_unsafeInheritExecutor' function}}{{-4:1--3:1=}}{{51:26-26=, isolation: isolated (any Actor)? = #isolation}} } @_unsafeInheritExecutor @@ -60,7 +60,7 @@ func unsafeCallerB() async { // expected-warning@-1{{'@_unsafeInheritExecutor' is deprecated; consider an 'isolated' parameter defaulted to '#isolation' instead}} await inheritsIsolationProperly(isolation: #isolation) - // expected-error@-1{{#isolation cannot be used within an '@_unsafeInheritExecutor' function}}{{58:1-24=}}{{59:20-20=isolation: isolated (any Actor)? = #isolation}} + // expected-error@-1{{#isolation cannot be used within an '@_unsafeInheritExecutor' function}}{{-4:1--3:1=}}{{59:20-20=isolation: isolated (any Actor)? = #isolation}} } @_unsafeInheritExecutor @@ -68,7 +68,7 @@ func unsafeCallerC(x: Int, fn: () -> Void, fn2: () -> Void) async { // expected-warning@-1{{'@_unsafeInheritExecutor' is deprecated; consider an 'isolated' parameter defaulted to '#isolation' instead}} await inheritsIsolationProperly() - // expected-error@-1{{#isolation (introduced by a default argument) cannot be used within an '@_unsafeInheritExecutor' function}}{{66:1-24=}}{{67:28-28=, isolation: isolated (any Actor)? = #isolation, }} + // expected-error@-1{{#isolation (introduced by a default argument) cannot be used within an '@_unsafeInheritExecutor' function}}{{-4:1--3:1=}}{{67:28-28=, isolation: isolated (any Actor)? = #isolation, }} } @_unsafeInheritExecutor diff --git a/test/Generics/associated_type_where_clause_hints.swift b/test/Generics/associated_type_where_clause_hints.swift index b4cadf7090e37..28aef5a3e31f1 100644 --- a/test/Generics/associated_type_where_clause_hints.swift +++ b/test/Generics/associated_type_where_clause_hints.swift @@ -40,14 +40,14 @@ protocol P2d : P1 { } protocol P2e : P1 { - // expected-warning@+1 {{redeclaration of associated type 'A4' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{-2:18-18= where A4: P0, A4 : Collection, A4.Element == A4.Index, A4.SubSequence == A4}}{{3-+2:51=}} + // expected-warning@+1 {{redeclaration of associated type 'A4' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{42:18-18= where A4: P0, A4 : Collection, A4.Element == A4.Index, A4.SubSequence == A4}}{{1-47:1=}} associatedtype A4: P0 where A4: Collection, A4.Element == A4.Index, A4.SubSequence == A4 } protocol P2f : P1 { - // expected-warning@+1 {{redeclaration of associated type 'A5' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{-2:18-18= where A5: P0b & Class, A5 : Collection, A5.Element : Collection}}{{3-+1:62=}} + // expected-warning@+1 {{redeclaration of associated type 'A5' from protocol 'P1' is better expressed as a 'where' clause on the protocol}}{{-2:18-18= where A5: P0b & Class, A5 : Collection, A5.Element : Collection}}{{1-+2:1=}} associatedtype A5: P0b & Class where A5: Collection, A5.Element: Collection } diff --git a/test/Parse/recovery.swift b/test/Parse/recovery.swift index c79310410deda..f642f5a5dae66 100644 --- a/test/Parse/recovery.swift +++ b/test/Parse/recovery.swift @@ -663,7 +663,7 @@ case let (jeb): // expected-error@+2{{expected an identifier to name generic parameter}} // expected-error@+1{{expected '{' in class}} class Ceac<}> {} -// expected-error@+1{{extraneous '}' at top level}} {{1-2=}} +// expected-error@+1{{extraneous '}' at top level}} {{1-+1:1=}} } diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response index 6247e0dcde398..7f4ee65cab053 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response +++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response @@ -21,7 +21,7 @@ key.fixits: [ { key.offset: 36, - key.length: 1, + key.length: 2, key.sourcetext: "" } ] diff --git a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 index 274ffc116948d..cbf7689565270 100644 --- a/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 +++ b/test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2 @@ -21,7 +21,7 @@ key.fixits: [ { key.offset: 36, - key.length: 1, + key.length: 2, key.sourcetext: "" } ] diff --git a/test/attr/attr_abi.swift b/test/attr/attr_abi.swift index cda09f72eb53d..2526fba52081d 100644 --- a/test/attr/attr_abi.swift +++ b/test/attr/attr_abi.swift @@ -1790,7 +1790,7 @@ class NonOverride: Overridden { // if they were allowed on the same decl. @_silgen_name("conflictingAttrsSilgenName") @abi(func silgenName1()) -func silgenName1() {} // expected-error@-2 {{cannot use '@_silgen_name' and '@abi' on the same global function because they serve the same purpose}} {{1-44=}} +func silgenName1() {} // expected-error@-2 {{cannot use '@_silgen_name' and '@abi' on the same global function because they serve the same purpose}} {{1-+1:1=}} @abi(@_silgen_name("silgenNameWithABI") func silgenName2()) // expected-error {{unused '_silgen_name' attribute in '@abi'}} {{6-40=}} func silgenName2() {} diff --git a/test/attr/attr_iboutlet.swift b/test/attr/attr_iboutlet.swift index 5521258fda506..fb3bec4bb6f1e 100644 --- a/test/attr/attr_iboutlet.swift +++ b/test/attr/attr_iboutlet.swift @@ -22,7 +22,7 @@ class IBOutletWrapperTy { @IBOutlet class var staticValue: IBOutletWrapperTy? = 52 // expected-error {{cannot convert value of type 'Int' to specified type 'IBOutletWrapperTy'}} - // expected-error@-2 {{only class instance properties can be declared @IBOutlet}} {{3-12=}} + // expected-error@-2 {{only class instance properties can be declared @IBOutlet}} {{1-+1:1=}} // expected-error@-2 {{class stored properties not supported}} @IBOutlet // expected-error {{@IBOutlet may only be used on 'var' declarations}} {{3-13=}} diff --git a/test/attr/global_actor.swift b/test/attr/global_actor.swift index b77384afdf09c..2d0dd0673872e 100644 --- a/test/attr/global_actor.swift +++ b/test/attr/global_actor.swift @@ -158,7 +158,7 @@ do { var test1: Int { get { 42 } @GA1 - set { } // expected-warning {{setter cannot have a global actor}} {{-1:7-11=}} + set { } // expected-warning {{setter cannot have a global actor}} {{-1:1-+0:1=}} // expected-note@-1 {{move global actor attribute to property 'test1'}} {{-3:5-5=@GA1}} @GA1 _modify { fatalError() } // expected-warning {{_modify accessor cannot have a global actor}} {{7-12=}} @@ -184,7 +184,7 @@ do { var test1: Int { get { 42 } @GA1 - set { } // expected-warning {{setter cannot have a global actor}} {{-1:7-11=}} + set { } // expected-warning {{setter cannot have a global actor}} {{-1:1-+0:1=}} // expected-note@-1 {{move global actor attribute to property 'test1'}} {{-3:5-5=@GA1}} @GA1 _modify { fatalError() } // expected-warning {{_modify accessor cannot have a global actor}} {{7-12=}} // expected-note@-1 {{move global actor attribute to property 'test1'}} {{-5:5-5=@GA1}} diff --git a/test/decl/func/debugger_function.swift b/test/decl/func/debugger_function.swift index 6f33bd8fce495..5cdfe7cf7a9e3 100644 --- a/test/decl/func/debugger_function.swift +++ b/test/decl/func/debugger_function.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift -enable-throw-without-try -debugger-support var invalidAccessor : Int { - // expected-error@+1 {{@LLDBDebuggerFunction may only be used on 'func' declarations}} {{3-24=}} + // expected-error@+1 {{@LLDBDebuggerFunction may only be used on 'func' declarations}} {{1-+1:1=}} @LLDBDebuggerFunction get { return 42 } } diff --git a/test/embedded/availability.swift b/test/embedded/availability.swift index 162699c1f90c8..e7be70c2c72fd 100644 --- a/test/embedded/availability.swift +++ b/test/embedded/availability.swift @@ -110,7 +110,7 @@ class DerivedAvailable: Base { // expected-note@-1 {{remove 'override' modifier to declare a new 'overrideLessUnavailable'}} @_unavailableInEmbedded - override func overrideMoreUnavailable() { } // expected-error {{cannot override 'overrideMoreUnavailable' with a declaration that is marked unavailable}}{{112:3-26=}} + override func overrideMoreUnavailable() { } // expected-error {{cannot override 'overrideMoreUnavailable' with a declaration that is marked unavailable}}{{-1:1-+0:1=}} } @_unavailableInEmbedded