From 727c21e309d91331334d6059e4623509d75bba69 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 26 Jul 2017 13:58:02 +0000 Subject: [PATCH] unguarded availability: add a fixit for the "annotate '...' with an availability attribute to silence" note rdar://33539233 Differential Revision: https://reviews.llvm.org/D35726 llvm-svn: 309116 --- clang/lib/Sema/SemaDeclAttr.cpp | 68 +++++++++++++++++++++++++++++++++- clang/test/FixIt/fixit-availability.c | 8 ++++ clang/test/FixIt/fixit-availability.mm | 31 ++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index c46bcff..6e29717 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7032,6 +7032,49 @@ static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { return dyn_cast(OrigCtx); } +namespace { + +struct AttributeInsertion { + StringRef Prefix; + SourceLocation Loc; + StringRef Suffix; + + static AttributeInsertion createInsertionAfter(const NamedDecl *D) { + return {" ", D->getLocEnd(), ""}; + } + static AttributeInsertion createInsertionAfter(SourceLocation Loc) { + return {" ", Loc, ""}; + } + static AttributeInsertion createInsertionBefore(const NamedDecl *D) { + return {"", D->getLocStart(), "\n"}; + } +}; + +} // end anonymous namespace + +/// Returns a source location in which it's appropriate to insert a new +/// attribute for the given declaration \D. +static Optional +createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, + const LangOptions &LangOpts) { + if (isa(D)) + return AttributeInsertion::createInsertionAfter(D); + if (const auto *MD = dyn_cast(D)) { + if (MD->hasBody()) + return None; + return AttributeInsertion::createInsertionAfter(D); + } + if (const auto *TD = dyn_cast(D)) { + SourceLocation Loc = + Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts); + if (Loc.isInvalid()) + return None; + // Insert after the 'struct'/whatever keyword. + return AttributeInsertion::createInsertionAfter(Loc); + } + return AttributeInsertion::createInsertionBefore(D); +} + /// Actually emit an availability diagnostic for a reference to an unavailable /// decl. /// @@ -7221,8 +7264,29 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, << /*Anonymous*/1 << TD->getKindName(); return; } - S.Diag(Enclosing->getLocation(), diag::note_partial_availability_silence) - << /*Named*/0 << Enclosing; + auto FixitNoteDiag = S.Diag(Enclosing->getLocation(), + diag::note_partial_availability_silence) + << /*Named*/ 0 << Enclosing; + // Don't offer a fixit for declarations with availability attributes. + if (Enclosing->hasAttr()) + return; + if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE")) + return; + Optional Insertion = createAttributeInsertion( + Enclosing, S.getSourceManager(), S.getLangOpts()); + if (!Insertion) + return; + std::string PlatformName = + AvailabilityAttr::getPlatformNameSourceSpelling( + S.getASTContext().getTargetInfo().getPlatformName()) + .lower(); + std::string Introduced = + OffendingDecl->getVersionIntroduced().getAsString(); + FixitNoteDiag << FixItHint::CreateInsertion( + Insertion->Loc, + (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName + + "(" + Introduced + "))" + Insertion->Suffix) + .str()); } } diff --git a/clang/test/FixIt/fixit-availability.c b/clang/test/FixIt/fixit-availability.c index 038dee0..4b3c8a4 100644 --- a/clang/test/FixIt/fixit-availability.c +++ b/clang/test/FixIt/fixit-availability.c @@ -8,3 +8,11 @@ void use() { // CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"if (__builtin_available(macOS 10.12, *)) {\n " // CHECK-NEXT: fix-it:{{.*}}:{[[@LINE-2]]:14-[[@LINE-2]]:14}:"\n } else {\n // Fallback on earlier versions\n }" } + +__attribute__((availability(macos, introduced=10.12))) +struct New { }; + +struct NoFixit { + struct New field; +}; +// CHECK-NOT: API_AVAILABLE diff --git a/clang/test/FixIt/fixit-availability.mm b/clang/test/FixIt/fixit-availability.mm index d044a73..f6a1855 100644 --- a/clang/test/FixIt/fixit-availability.mm +++ b/clang/test/FixIt/fixit-availability.mm @@ -109,3 +109,34 @@ void wrapDeclStmtUses() { anotherFunction(y); anotherFunction(x); } + +#define API_AVAILABLE(X) __attribute__((availability(macos, introduced=10.12))) // dummy macro + +API_AVAILABLE(macos(10.12)) +@interface NewClass +@end + +@interface OldButOfferFixit +@property(copy) NewClass *prop; +// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"API_AVAILABLE(macos(10.12))\n" + +- (NewClass *)fixitMe; +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:22-[[@LINE-1]]:22}:" API_AVAILABLE(macos(10.12))" +@end + +void oldButOfferFixitFn(NewClass *) { +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"API_AVAILABLE(macos(10.12))\n" +} + +template +struct OldButOfferFixitTag { +// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:7-[[@LINE-1]]:7}:" API_AVAILABLE(macos(10.12))" + NewClass *x; +}; + +// Avoid a fixit for declarations that already have an attribute: +__attribute__((availability(macos, introduced=10.11))) +@interface WithoutFixit +@property(copy) NewClass *prop; +// CHECK-NOT: API_AVAILABLE +@end -- 2.7.4