Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -7032,6 +7032,37 @@ return dyn_cast(OrigCtx); } +namespace { + +struct AttributeInsertion { + StringRef Prefix; + SourceLocation Loc; + StringRef Suffix; + + static AttributeInsertion createInsertionAfter(const NamedDecl *D) { + return {" ", D->getLocEnd(), ""}; + } + 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) { + if (isa(D)) + return AttributeInsertion::createInsertionAfter(D); + if (const auto *MD = dyn_cast(D)) { + if (MD->hasBody()) + return None; + return AttributeInsertion::createInsertionAfter(D); + } + return AttributeInsertion::createInsertionBefore(D); +} + /// Actually emit an availability diagnostic for a reference to an unavailable /// decl. /// @@ -7221,8 +7252,30 @@ << /*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().getMacroDefinition( + &S.getASTContext().Idents.get("API_AVAILABLE"))) + return; + Optional Insertion = + createAttributeInsertion(Enclosing); + 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()); } } Index: test/FixIt/fixit-availability.c =================================================================== --- test/FixIt/fixit-availability.c +++ test/FixIt/fixit-availability.c @@ -8,3 +8,11 @@ // 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 Index: test/FixIt/fixit-availability.mm =================================================================== --- test/FixIt/fixit-availability.mm +++ test/FixIt/fixit-availability.mm @@ -109,3 +109,28 @@ 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" +} + +// 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