diff options
Diffstat (limited to 'lib/Sema/SemaDeclAttr.cpp')
-rw-r--r-- | lib/Sema/SemaDeclAttr.cpp | 407 |
1 files changed, 268 insertions, 139 deletions
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 2a310bf41c703..676d00357c960 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -540,14 +540,13 @@ static bool isCapabilityExpr(Sema &S, const Expr *Ex) { // a DeclRefExpr is found, its type should be checked to determine whether it // is a capability or not. - if (const auto *E = dyn_cast<DeclRefExpr>(Ex)) - return typeHasCapability(S, E->getType()); - else if (const auto *E = dyn_cast<CastExpr>(Ex)) + if (const auto *E = dyn_cast<CastExpr>(Ex)) return isCapabilityExpr(S, E->getSubExpr()); else if (const auto *E = dyn_cast<ParenExpr>(Ex)) return isCapabilityExpr(S, E->getSubExpr()); else if (const auto *E = dyn_cast<UnaryOperator>(Ex)) { - if (E->getOpcode() == UO_LNot) + if (E->getOpcode() == UO_LNot || E->getOpcode() == UO_AddrOf || + E->getOpcode() == UO_Deref) return isCapabilityExpr(S, E->getSubExpr()); return false; } else if (const auto *E = dyn_cast<BinaryOperator>(Ex)) { @@ -557,7 +556,7 @@ static bool isCapabilityExpr(Sema &S, const Expr *Ex) { return false; } - return false; + return typeHasCapability(S, Ex->getType()); } /// \brief Checks that all attribute arguments, starting from Sidx, resolve to @@ -1304,14 +1303,28 @@ static void handlePackedAttr(Sema &S, Decl *D, const AttributeList &Attr) { TD->addAttr(::new (S.Context) PackedAttr(Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); else if (FieldDecl *FD = dyn_cast<FieldDecl>(D)) { - // Report warning about changed offset in the newer compiler versions. - if (!FD->getType()->isDependentType() && - !FD->getType()->isIncompleteType() && FD->isBitField() && - S.Context.getTypeAlign(FD->getType()) <= 8) - S.Diag(Attr.getLoc(), diag::warn_attribute_packed_for_bitfield); + bool BitfieldByteAligned = (!FD->getType()->isDependentType() && + !FD->getType()->isIncompleteType() && + FD->isBitField() && + S.Context.getTypeAlign(FD->getType()) <= 8); + + if (S.getASTContext().getTargetInfo().getTriple().isPS4()) { + if (BitfieldByteAligned) + // The PS4 target needs to maintain ABI backwards compatibility. + S.Diag(Attr.getLoc(), diag::warn_attribute_ignored_for_field_of_type) + << Attr.getName() << FD->getType(); + else + FD->addAttr(::new (S.Context) PackedAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + } else { + // Report warning about changed offset in the newer compiler versions. + if (BitfieldByteAligned) + S.Diag(Attr.getLoc(), diag::warn_attribute_packed_for_bitfield); + + FD->addAttr(::new (S.Context) PackedAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); + } - FD->addAttr(::new (S.Context) PackedAttr( - Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } else S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); } @@ -1517,6 +1530,22 @@ static void handleReturnsNonNullAttr(Sema &S, Decl *D, Attr.getAttributeSpellingListIndex())); } +static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (D->isInvalidDecl()) + return; + + // noescape only applies to pointer types. + QualType T = cast<ParmVarDecl>(D)->getType(); + if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only) + << Attr.getName() << Attr.getRange() << 0; + return; + } + + D->addAttr(::new (S.Context) NoEscapeAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); +} + static void handleAssumeAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Expr *E = Attr.getArgAsExpr(0), @@ -1939,20 +1968,20 @@ static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Attr.getAttributeSpellingListIndex())); } -static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { +static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &Attrs) { if (hasDeclarator(D)) return; - if (S.CheckNoReturnAttr(attr)) + if (S.CheckNoReturnAttr(Attrs)) return; if (!isa<ObjCMethodDecl>(D)) { - S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << attr.getName() << ExpectedFunctionOrMethod; + S.Diag(Attrs.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attrs.getName() << ExpectedFunctionOrMethod; return; } D->addAttr(::new (S.Context) NoReturnAttr( - attr.getRange(), S.Context, attr.getAttributeSpellingListIndex())); + Attrs.getRange(), S.Context, Attrs.getAttributeSpellingListIndex())); } static void handleNoCallerSavedRegsAttr(Sema &S, Decl *D, @@ -1964,9 +1993,9 @@ static void handleNoCallerSavedRegsAttr(Sema &S, Decl *D, Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); } -bool Sema::CheckNoReturnAttr(const AttributeList &attr) { - if (!checkAttributeNumArgs(*this, attr, 0)) { - attr.setInvalid(); +bool Sema::CheckNoReturnAttr(const AttributeList &Attrs) { + if (!checkAttributeNumArgs(*this, Attrs, 0)) { + Attrs.setInvalid(); return true; } @@ -2122,10 +2151,10 @@ static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { } static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { - bool IsCXX1zAttr = Attr.isCXX11Attribute() && !Attr.getScopeName(); + bool IsCXX17Attr = Attr.isCXX11Attribute() && !Attr.getScopeName(); - if (IsCXX1zAttr && isa<VarDecl>(D)) { - // The C++1z spelling of this attribute cannot be applied to a static data + if (IsCXX17Attr && isa<VarDecl>(D)) { + // The C++17 spelling of this attribute cannot be applied to a static data // member per [dcl.attr.unused]p2. if (cast<VarDecl>(D)->isStaticDataMember()) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) @@ -2134,10 +2163,10 @@ static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { } } - // If this is spelled as the standard C++1z attribute, but not in C++1z, warn + // If this is spelled as the standard C++17 attribute, but not in C++17, warn // about using it as an extension. - if (!S.getLangOpts().CPlusPlus1z && IsCXX1zAttr) - S.Diag(Attr.getLoc(), diag::ext_cxx1z_attr) << Attr.getName(); + if (!S.getLangOpts().CPlusPlus17 && IsCXX17Attr) + S.Diag(Attr.getLoc(), diag::ext_cxx17_attr) << Attr.getName(); D->addAttr(::new (S.Context) UnusedAttr( Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); @@ -2831,11 +2860,11 @@ static void handleWarnUnusedResult(Sema &S, Decl *D, const AttributeList &Attr) return; } - // If this is spelled as the standard C++1z attribute, but not in C++1z, warn + // If this is spelled as the standard C++17 attribute, but not in C++17, warn // about using it as an extension. - if (!S.getLangOpts().CPlusPlus1z && Attr.isCXX11Attribute() && + if (!S.getLangOpts().CPlusPlus17 && Attr.isCXX11Attribute() && !Attr.getScopeName()) - S.Diag(Attr.getLoc(), diag::ext_cxx1z_attr) << Attr.getName(); + S.Diag(Attr.getLoc(), diag::ext_cxx17_attr) << Attr.getName(); D->addAttr(::new (S.Context) WarnUnusedResultAttr(Attr.getRange(), S.Context, @@ -2993,22 +3022,43 @@ static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) { D->addAttr(NewAttr); } -// Check for things we'd like to warn about, no errors or validation for now. -// TODO: Validation should use a backend target library that specifies -// the allowable subtarget features and cpus. We could use something like a -// TargetCodeGenInfo hook here to do validation. -void Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) { +// Check for things we'd like to warn about. Multiversioning issues are +// handled later in the process, once we know how many exist. +bool Sema::checkTargetAttr(SourceLocation LiteralLoc, StringRef AttrStr) { + enum FirstParam { Unsupported, Duplicate }; + enum SecondParam { None, Architecture }; for (auto Str : {"tune=", "fpmath="}) if (AttrStr.find(Str) != StringRef::npos) - Diag(LiteralLoc, diag::warn_unsupported_target_attribute) << Str; + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << Str; + + TargetAttr::ParsedTargetAttr ParsedAttrs = TargetAttr::parse(AttrStr); + + if (!ParsedAttrs.Architecture.empty() && + !Context.getTargetInfo().isValidCPUName(ParsedAttrs.Architecture)) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << Architecture << ParsedAttrs.Architecture; + + if (ParsedAttrs.DuplicateArchitecture) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Duplicate << None << "arch="; + + for (const auto &Feature : ParsedAttrs.Features) { + auto CurFeature = StringRef(Feature).drop_front(); // remove + or -. + if (!Context.getTargetInfo().isValidFeatureName(CurFeature)) + return Diag(LiteralLoc, diag::warn_unsupported_target_attribute) + << Unsupported << None << CurFeature; + } + + return true; } static void handleTargetAttr(Sema &S, Decl *D, const AttributeList &Attr) { StringRef Str; SourceLocation LiteralLoc; - if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str, &LiteralLoc)) + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str, &LiteralLoc) || + !S.checkTargetAttr(LiteralLoc, Str)) return; - S.checkTargetAttr(LiteralLoc, Str); unsigned Index = Attr.getAttributeSpellingListIndex(); TargetAttr *NewAttr = ::new (S.Context) TargetAttr(Attr.getRange(), S.Context, Str, Index); @@ -3016,12 +3066,6 @@ static void handleTargetAttr(Sema &S, Decl *D, const AttributeList &Attr) { } static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) { - VarDecl *VD = cast<VarDecl>(D); - if (!VD->hasLocalStorage()) { - S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); - return; - } - Expr *E = Attr.getArgAsExpr(0); SourceLocation Loc = E->getExprLoc(); FunctionDecl *FD = nullptr; @@ -3064,7 +3108,7 @@ static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) { // We're currently more strict than GCC about what function types we accept. // If this ever proves to be a problem it should be easy to fix. - QualType Ty = S.Context.getPointerType(VD->getType()); + QualType Ty = S.Context.getPointerType(cast<VarDecl>(D)->getType()); QualType ParamTy = FD->getParamDecl(0)->getType(); if (S.CheckAssignmentConstraints(FD->getParamDecl(0)->getLocation(), ParamTy, Ty) != Sema::Compatible) { @@ -4252,24 +4296,24 @@ static void handleSuppressAttr(Sema &S, Decl *D, const AttributeList &Attr) { DiagnosticIdentifiers.size(), Attr.getAttributeSpellingListIndex())); } -bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, +bool Sema::CheckCallingConvAttr(const AttributeList &Attrs, CallingConv &CC, const FunctionDecl *FD) { - if (attr.isInvalid()) + if (Attrs.isInvalid()) return true; - if (attr.hasProcessingCache()) { - CC = (CallingConv) attr.getProcessingCache(); + if (Attrs.hasProcessingCache()) { + CC = (CallingConv) Attrs.getProcessingCache(); return false; } - unsigned ReqArgs = attr.getKind() == AttributeList::AT_Pcs ? 1 : 0; - if (!checkAttributeNumArgs(*this, attr, ReqArgs)) { - attr.setInvalid(); + unsigned ReqArgs = Attrs.getKind() == AttributeList::AT_Pcs ? 1 : 0; + if (!checkAttributeNumArgs(*this, Attrs, ReqArgs)) { + Attrs.setInvalid(); return true; } // TODO: diagnose uses of these conventions on the wrong target. - switch (attr.getKind()) { + switch (Attrs.getKind()) { case AttributeList::AT_CDecl: CC = CC_C; break; case AttributeList::AT_FastCall: CC = CC_X86FastCall; break; case AttributeList::AT_StdCall: CC = CC_X86StdCall; break; @@ -4288,8 +4332,8 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, break; case AttributeList::AT_Pcs: { StringRef StrRef; - if (!checkStringLiteralArgumentAttr(attr, 0, StrRef)) { - attr.setInvalid(); + if (!checkStringLiteralArgumentAttr(Attrs, 0, StrRef)) { + Attrs.setInvalid(); return true; } if (StrRef == "aapcs") { @@ -4300,8 +4344,8 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, break; } - attr.setInvalid(); - Diag(attr.getLoc(), diag::err_invalid_pcs); + Attrs.setInvalid(); + Diag(Attrs.getLoc(), diag::err_invalid_pcs); return true; } case AttributeList::AT_IntelOclBicc: CC = CC_IntelOclBicc; break; @@ -4314,7 +4358,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, TargetInfo::CallingConvCheckResult A = TI.checkCallingConvention(CC); if (A != TargetInfo::CCCR_OK) { if (A == TargetInfo::CCCR_Warning) - Diag(attr.getLoc(), diag::warn_cconv_ignored) << attr.getName(); + Diag(Attrs.getLoc(), diag::warn_cconv_ignored) << Attrs.getName(); // This convention is not valid for the target. Use the default function or // method calling convention. @@ -4326,7 +4370,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, CC = Context.getDefaultCallingConvention(IsVariadic, IsCXXMethod); } - attr.setProcessingCache((unsigned) CC); + Attrs.setProcessingCache((unsigned) CC); return false; } @@ -4334,7 +4378,7 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, static bool isValidSwiftContextType(QualType type) { if (!type->hasPointerRepresentation()) return type->isDependentType(); - return type->getPointeeType().getAddressSpace() == 0; + return type->getPointeeType().getAddressSpace() == LangAS::Default; } /// Pointers and references in the default address space. @@ -4346,7 +4390,7 @@ static bool isValidSwiftIndirectResultType(QualType type) { } else { return type->isDependentType(); } - return type.getAddressSpace() == 0; + return type.getAddressSpace() == LangAS::Default; } /// Pointers and references to pointers in the default address space. @@ -4363,10 +4407,10 @@ static bool isValidSwiftErrorResultType(QualType type) { return isValidSwiftContextType(type); } -static void handleParameterABIAttr(Sema &S, Decl *D, const AttributeList &attr, - ParameterABI abi) { - S.AddParameterABIAttr(attr.getRange(), D, abi, - attr.getAttributeSpellingListIndex()); +static void handleParameterABIAttr(Sema &S, Decl *D, const AttributeList &Attrs, + ParameterABI Abi) { + S.AddParameterABIAttr(Attrs.getRange(), D, Abi, + Attrs.getAttributeSpellingListIndex()); } void Sema::AddParameterABIAttr(SourceRange range, Decl *D, ParameterABI abi, @@ -4807,11 +4851,11 @@ static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, } static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D, - const AttributeList &attr) { + const AttributeList &Attrs) { const int EP_ObjCMethod = 1; const int EP_ObjCProperty = 2; - SourceLocation loc = attr.getLoc(); + SourceLocation loc = Attrs.getLoc(); QualType resultType; if (isa<ObjCMethodDecl>(D)) resultType = cast<ObjCMethodDecl>(D)->getReturnType(); @@ -4822,7 +4866,7 @@ static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D, (!resultType->isPointerType() || resultType->isObjCRetainableType())) { S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_return_type) << SourceRange(loc) - << attr.getName() + << Attrs.getName() << (isa<ObjCMethodDecl>(D) ? EP_ObjCMethod : EP_ObjCProperty) << /*non-retainable pointer*/ 2; @@ -4831,29 +4875,29 @@ static void handleObjCReturnsInnerPointerAttr(Sema &S, Decl *D, } D->addAttr(::new (S.Context) ObjCReturnsInnerPointerAttr( - attr.getRange(), S.Context, attr.getAttributeSpellingListIndex())); + Attrs.getRange(), S.Context, Attrs.getAttributeSpellingListIndex())); } static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, - const AttributeList &attr) { + const AttributeList &Attrs) { ObjCMethodDecl *method = cast<ObjCMethodDecl>(D); DeclContext *DC = method->getDeclContext(); if (const ObjCProtocolDecl *PDecl = dyn_cast_or_null<ObjCProtocolDecl>(DC)) { S.Diag(D->getLocStart(), diag::warn_objc_requires_super_protocol) - << attr.getName() << 0; + << Attrs.getName() << 0; S.Diag(PDecl->getLocation(), diag::note_protocol_decl); return; } if (method->getMethodFamily() == OMF_dealloc) { S.Diag(D->getLocStart(), diag::warn_objc_requires_super_protocol) - << attr.getName() << 1; + << Attrs.getName() << 1; return; } method->addAttr(::new (S.Context) - ObjCRequiresSuperAttr(attr.getRange(), S.Context, - attr.getAttributeSpellingListIndex())); + ObjCRequiresSuperAttr(Attrs.getRange(), S.Context, + Attrs.getAttributeSpellingListIndex())); } static void handleCFAuditedTransferAttr(Sema &S, Decl *D, @@ -5665,8 +5709,12 @@ static void handleCapabilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { static void handleAssertCapabilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { + SmallVector<Expr*, 1> Args; + if (!checkLockFunAttrCommon(S, D, Attr, Args)) + return; + D->addAttr(::new (S.Context) AssertCapabilityAttr(Attr.getRange(), S.Context, - Attr.getArgAsExpr(0), + Args.data(), Args.size(), Attr.getAttributeSpellingListIndex())); } @@ -5965,6 +6013,14 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_NoMicroMips: handleSimpleAttribute<NoMicroMipsAttr>(S, D, Attr); break; + case AttributeList::AT_MipsLongCall: + handleSimpleAttributeWithExclusions<MipsLongCallAttr, MipsShortCallAttr>( + S, D, Attr); + break; + case AttributeList::AT_MipsShortCall: + handleSimpleAttributeWithExclusions<MipsShortCallAttr, MipsLongCallAttr>( + S, D, Attr); + break; case AttributeList::AT_AMDGPUFlatWorkGroupSize: handleAMDGPUFlatWorkGroupSizeAttr(S, D, Attr); break; @@ -6120,6 +6176,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case AttributeList::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, Attr); break; + case AttributeList::AT_NoEscape: + handleNoEscapeAttr(S, D, Attr); + break; case AttributeList::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, Attr); break; @@ -7003,6 +7062,49 @@ static NamedDecl *findEnclosingDeclToAnnotate(Decl *OrigCtx) { return dyn_cast<NamedDecl>(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<AttributeInsertion> +createAttributeInsertion(const NamedDecl *D, const SourceManager &SM, + const LangOptions &LangOpts) { + if (isa<ObjCPropertyDecl>(D)) + return AttributeInsertion::createInsertionAfter(D); + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (MD->hasBody()) + return None; + return AttributeInsertion::createInsertionAfter(D); + } + if (const auto *TD = dyn_cast<TagDecl>(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. /// @@ -7038,7 +7140,83 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, Ctx)) return; + // The declaration can have multiple availability attributes, we are looking + // at one of them. + const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); + if (A && A->isInherited()) { + for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; + Redecl = Redecl->getPreviousDecl()) { + const AvailabilityAttr *AForRedecl = + getAttrForPlatform(S.Context, Redecl); + if (AForRedecl && !AForRedecl->isInherited()) { + // If D is a declaration with inherited attributes, the note should + // point to the declaration with actual attributes. + NoteLocation = Redecl->getLocation(); + break; + } + } + } + switch (K) { + case AR_NotYetIntroduced: { + // We would like to emit the diagnostic even if -Wunguarded-availability is + // not specified for deployment targets >= to iOS 11 or equivalent or + // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or + // later. + const AvailabilityAttr *AA = + getAttrForPlatform(S.getASTContext(), OffendingDecl); + VersionTuple Introduced = AA->getIntroduced(); + + bool UseNewWarning = shouldDiagnoseAvailabilityByDefault( + S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), + Introduced); + unsigned Warning = UseNewWarning ? diag::warn_unguarded_availability_new + : diag::warn_unguarded_availability; + + S.Diag(Loc, Warning) + << OffendingDecl + << AvailabilityAttr::getPrettyPlatformName( + S.getASTContext().getTargetInfo().getPlatformName()) + << Introduced.getAsString(); + + S.Diag(OffendingDecl->getLocation(), diag::note_availability_specified_here) + << OffendingDecl << /* partial */ 3; + + if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { + if (auto *TD = dyn_cast<TagDecl>(Enclosing)) + if (TD->getDeclName().isEmpty()) { + S.Diag(TD->getLocation(), + diag::note_decl_unguarded_availability_silence) + << /*Anonymous*/ 1 << TD->getKindName(); + return; + } + auto FixitNoteDiag = + S.Diag(Enclosing->getLocation(), + diag::note_decl_unguarded_availability_silence) + << /*Named*/ 0 << Enclosing; + // Don't offer a fixit for declarations with availability attributes. + if (Enclosing->hasAttr<AvailabilityAttr>()) + return; + if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE")) + return; + Optional<AttributeInsertion> 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()); + } + return; + } case AR_Deprecated: diag = !ObjCPropertyAccess ? diag::warn_deprecated : diag::warn_property_method_deprecated; @@ -7046,8 +7224,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, diag_fwdclass_message = diag::warn_deprecated_fwdclass_message; property_note_select = /* deprecated */ 0; available_here_select_kind = /* deprecated */ 2; - if (const auto *attr = OffendingDecl->getAttr<DeprecatedAttr>()) - NoteLocation = attr->getLocation(); + if (const auto *Attr = OffendingDecl->getAttr<DeprecatedAttr>()) + NoteLocation = Attr->getLocation(); break; case AR_Unavailable: @@ -7058,8 +7236,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, property_note_select = /* unavailable */ 1; available_here_select_kind = /* unavailable */ 0; - if (auto attr = OffendingDecl->getAttr<UnavailableAttr>()) { - if (attr->isImplicit() && attr->getImplicitReason()) { + if (auto Attr = OffendingDecl->getAttr<UnavailableAttr>()) { + if (Attr->isImplicit() && Attr->getImplicitReason()) { // Most of these failures are due to extra restrictions in ARC; // reflect that in the primary diagnostic when applicable. auto flagARCError = [&] { @@ -7069,7 +7247,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, diag = diag::err_unavailable_in_arc; }; - switch (attr->getImplicitReason()) { + switch (Attr->getImplicitReason()) { case UnavailableAttr::IR_None: break; case UnavailableAttr::IR_ARCForbiddenType: @@ -7103,28 +7281,6 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, } break; - case AR_NotYetIntroduced: { - // We would like to emit the diagnostic even if -Wunguarded-availability is - // not specified for deployment targets >= to iOS 11 or equivalent or - // for declarations that were introduced in iOS 11 (macOS 10.13, ...) or - // later. - const AvailabilityAttr *AA = - getAttrForPlatform(S.getASTContext(), OffendingDecl); - VersionTuple Introduced = AA->getIntroduced(); - bool NewWarning = shouldDiagnoseAvailabilityByDefault( - S.Context, S.Context.getTargetInfo().getPlatformMinVersion(), - Introduced); - diag = NewWarning ? diag::warn_partial_availability_new - : diag::warn_partial_availability; - diag_message = NewWarning ? diag::warn_partial_message_new - : diag::warn_partial_message; - diag_fwdclass_message = NewWarning ? diag::warn_partial_fwdclass_message_new - : diag::warn_partial_fwdclass_message; - property_note_select = /* partial */ 2; - available_here_select_kind = /* partial */ 3; - break; - } - case AR_Available: llvm_unreachable("Warning for availability of available declaration?"); } @@ -7132,10 +7288,10 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, CharSourceRange UseRange; StringRef Replacement; if (K == AR_Deprecated) { - if (auto attr = OffendingDecl->getAttr<DeprecatedAttr>()) - Replacement = attr->getReplacement(); - if (auto attr = getAttrForPlatform(S.Context, OffendingDecl)) - Replacement = attr->getReplacement(); + if (auto Attr = OffendingDecl->getAttr<DeprecatedAttr>()) + Replacement = Attr->getReplacement(); + if (auto Attr = getAttrForPlatform(S.Context, OffendingDecl)) + Replacement = Attr->getReplacement(); if (!Replacement.empty()) UseRange = @@ -7163,38 +7319,8 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, S.Diag(UnknownObjCClass->getLocation(), diag::note_forward_class); } - // The declaration can have multiple availability attributes, we are looking - // at one of them. - const AvailabilityAttr *A = getAttrForPlatform(S.Context, OffendingDecl); - if (A && A->isInherited()) { - for (const Decl *Redecl = OffendingDecl->getMostRecentDecl(); Redecl; - Redecl = Redecl->getPreviousDecl()) { - const AvailabilityAttr *AForRedecl = getAttrForPlatform(S.Context, - Redecl); - if (AForRedecl && !AForRedecl->isInherited()) { - // If D is a declaration with inherited attributes, the note should - // point to the declaration with actual attributes. - S.Diag(Redecl->getLocation(), diag_available_here) << OffendingDecl - << available_here_select_kind; - break; - } - } - } - else - S.Diag(NoteLocation, diag_available_here) - << OffendingDecl << available_here_select_kind; - - if (K == AR_NotYetIntroduced) - if (const auto *Enclosing = findEnclosingDeclToAnnotate(Ctx)) { - if (auto *TD = dyn_cast<TagDecl>(Enclosing)) - if (TD->getDeclName().isEmpty()) { - S.Diag(TD->getLocation(), diag::note_partial_availability_silence) - << /*Anonymous*/1 << TD->getKindName(); - return; - } - S.Diag(Enclosing->getLocation(), diag::note_partial_availability_silence) - << /*Named*/0 << Enclosing; - } + S.Diag(NoteLocation, diag_available_here) + << OffendingDecl << available_here_select_kind; } static void handleDelayedAvailabilityCheck(Sema &S, DelayedDiagnostic &DD, @@ -7399,6 +7525,10 @@ public: bool TraverseLambdaExpr(LambdaExpr *E) { return true; } + // for 'case X:' statements, don't bother looking at the 'X'; it can't lead + // to any useful diagnostics. + bool TraverseCaseStmt(CaseStmt *CS) { return TraverseStmt(CS->getSubStmt()); } + bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *PRE) { if (PRE->isClassReceiver()) DiagnoseDeclAvailability(PRE->getClassReceiver(), PRE->getReceiverLocation()); @@ -7581,8 +7711,7 @@ bool DiagnoseUnguardedAvailability::TraverseIfStmt(IfStmt *If) { // If we're using the '*' case here or if this check is redundant, then we // use the enclosing version to check both branches. if (CondVersion.empty() || CondVersion <= AvailabilityStack.back()) - return Base::TraverseStmt(If->getThen()) && - Base::TraverseStmt(If->getElse()); + return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse()); } else { // This isn't an availability checking 'if', we can just continue. return Base::TraverseIfStmt(If); |