https://github.com/egorzhdan updated https://github.com/llvm/llvm-project/pull/73017
>From ae8c604e9333b1ea54fc6e80134bdba948eeecb7 Mon Sep 17 00:00:00 2001 From: Egor Zhdan <e_zh...@apple.com> Date: Mon, 20 Nov 2023 18:03:18 +0000 Subject: [PATCH 1/4] [APINotes] Upstream attributes that are created implicitly from APINotes --- clang/include/clang/Basic/Attr.td | 46 ++++++++++++++++++++++- clang/lib/Sema/SemaDeclAttr.cpp | 34 ++++++++++------- clang/lib/Serialization/ASTReaderDecl.cpp | 2 + clang/utils/TableGen/ClangAttrEmitter.cpp | 26 ++++++++++++- 4 files changed, 92 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index c2fbdfc66c540d6..acfb75a3dee3e7a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -301,6 +301,9 @@ class VariadicEnumArgument<string name, string type, list<string> values, bit IsExternalType = isExternalType; } +// Represents an attribute wrapped by another attribute. +class AttrArgument<string name, bit opt = 0> : Argument<name, opt>; + // This handles one spelling of an attribute. class Spelling<string name, string variety, int version = 1> { string Name = name; @@ -2257,7 +2260,7 @@ def ObjCBridgeRelated : InheritableAttr { def NSErrorDomain : InheritableAttr { let Spellings = [GNU<"ns_error_domain">]; let Subjects = SubjectList<[Enum], ErrorDiag>; - let Args = [DeclArgument<Var, "ErrorDomain">]; + let Args = [IdentifierArgument<"ErrorDomain">]; let Documentation = [NSErrorDomainDocs]; } @@ -2593,6 +2596,22 @@ def SwiftError : InheritableAttr { let Documentation = [SwiftErrorDocs]; } +def SwiftImportAsNonGeneric : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def SwiftImportPropertyAsAccessors : InheritableAttr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + def SwiftName : InheritableAttr { let Spellings = [GNU<"swift_name">]; let Args = [StringArgument<"Name">]; @@ -2614,6 +2633,31 @@ def SwiftPrivate : InheritableAttr { let SimpleHandler = 1; } +def SwiftVersioned : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">, + BoolArgument<"IsReplacedByActive">]; + let SemaHandler = 0; + let Documentation = [InternalOnly]; +} + +def SwiftVersionedRemoval : Attr { + // This attribute has no spellings as it is only ever created implicitly + // from API notes. + let Spellings = []; + let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">, + BoolArgument<"IsReplacedByActive">]; + let SemaHandler = 0; + let Documentation = [InternalOnly]; + let AdditionalMembers = [{ + attr::Kind getAttrKindToRemove() const { + return static_cast<attr::Kind>(getRawKind()); + } + }]; +} + def NoDeref : TypeAttr { let Spellings = [Clang<"noderef">]; let Documentation = [NoDerefDocs]; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index fd778793346f502..8552b28e51acd97 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6210,29 +6210,35 @@ static void handleObjCRequiresSuperAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) ObjCRequiresSuperAttr(S.Context, Attrs)); } -static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &AL) { - auto *E = AL.getArgAsExpr(0); - auto Loc = E ? E->getBeginLoc() : AL.getLoc(); - - auto *DRE = dyn_cast<DeclRefExpr>(AL.getArgAsExpr(0)); - if (!DRE) { - S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0; +static void handleNSErrorDomain(Sema &S, Decl *D, const ParsedAttr &Attr) { + if (!isa<TagDecl>(D)) { + S.Diag(D->getBeginLoc(), diag::err_nserrordomain_invalid_decl) << 0; return; } - auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); - if (!VD) { - S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 1 << DRE->getDecl(); + IdentifierLoc *IdentLoc = + Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr; + if (!IdentLoc || !IdentLoc->Ident) { + // Try to locate the argument directly. + SourceLocation Loc = Attr.getLoc(); + if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0)) + Loc = Attr.getArgAsExpr(0)->getBeginLoc(); + + S.Diag(Loc, diag::err_nserrordomain_invalid_decl) << 0; return; } - if (!isNSStringType(VD->getType(), S.Context) && - !isCFStringType(VD->getType(), S.Context)) { - S.Diag(Loc, diag::err_nserrordomain_wrong_type) << VD; + // Verify that the identifier is a valid decl in the C decl namespace. + LookupResult Result(S, DeclarationName(IdentLoc->Ident), SourceLocation(), + Sema::LookupNameKind::LookupOrdinaryName); + if (!S.LookupName(Result, S.TUScope) || !Result.getAsSingle<VarDecl>()) { + S.Diag(IdentLoc->Loc, diag::err_nserrordomain_invalid_decl) + << 1 << IdentLoc->Ident; return; } - D->addAttr(::new (S.Context) NSErrorDomainAttr(S.Context, AL, VD)); + D->addAttr(::new (S.Context) + NSErrorDomainAttr(S.Context, Attr, IdentLoc->Ident)); } static void handleObjCBridgeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 79817b3fb1ec3a0..5a8a1fa99d5a19d 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -3086,6 +3086,8 @@ class AttrReader { Expr *readExpr() { return Reader.readExpr(); } + Attr *readAttr() { return Reader.readAttr(); } + std::string readString() { return Reader.readString(); } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 4ec00573e8a9da8..096c1f7cf6a3700 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1414,7 +1414,29 @@ namespace { } }; -} // end anonymous namespace + class AttrArgument : public SimpleArgument { + public: + AttrArgument(const Record &Arg, StringRef Attr) + : SimpleArgument(Arg, Attr, "Attr *") {} + + void writePCHReadDecls(raw_ostream &OS) const override { + OS << " Attr *" << getLowerName() << " = Record.readAttr();"; + } + + void writePCHWrite(raw_ostream &OS) const override { + OS << " AddAttr(SA->get" << getUpperName() << "());"; + } + + void writeDump(raw_ostream &OS) const override {} + + void writeDumpChildren(raw_ostream &OS) const override { + OS << " Visit(SA->get" << getUpperName() << "());\n"; + } + + void writeHasChildren(raw_ostream &OS) const override { OS << "true"; } + }; + + } // end anonymous namespace static std::unique_ptr<Argument> createArgument(const Record &Arg, StringRef Attr, @@ -1470,6 +1492,8 @@ createArgument(const Record &Arg, StringRef Attr, Ptr = std::make_unique<VariadicIdentifierArgument>(Arg, Attr); else if (ArgName == "VersionArgument") Ptr = std::make_unique<VersionArgument>(Arg, Attr); + else if (ArgName == "AttrArgument") + Ptr = std::make_unique<AttrArgument>(Arg, Attr); else if (ArgName == "OMPTraitInfoArgument") Ptr = std::make_unique<SimpleArgument>(Arg, Attr, "OMPTraitInfo *"); else if (ArgName == "VariadicOMPInteropInfoArgument") >From 923b5f2046f81c201d892885bd9d1ac213f9e54e Mon Sep 17 00:00:00 2001 From: Egor Zhdan <e_zh...@apple.com> Date: Tue, 21 Nov 2023 16:34:52 +0000 Subject: [PATCH 2/4] [APINotes] Upstream Parser support for API Notes --- .../clang/Basic/DiagnosticParseKinds.td | 3 + clang/include/clang/Lex/Lexer.h | 2 +- clang/include/clang/Parse/Parser.h | 12 ++++ clang/include/clang/Sema/Sema.h | 4 ++ clang/lib/Parse/ParseDecl.cpp | 65 +++++++++++++++++++ clang/lib/Parse/Parser.cpp | 5 ++ 6 files changed, 90 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index c3753ca2828e25e..02c5b9527f5d5f3 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1606,6 +1606,9 @@ def err_pragma_invalid_keyword : Error< def err_pragma_pipeline_invalid_keyword : Error< "invalid argument; expected 'disable'">; +// API notes. +def err_type_unparsed : Error<"unparsed tokens following type">; + // Pragma unroll support. def warn_pragma_unroll_cuda_value_in_parens : Warning< "argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">, diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 899e665e7454652..b6ecc7e5ded9e2a 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -198,11 +198,11 @@ class Lexer : public PreprocessorLexer { /// from. Currently this is only used by _Pragma handling. SourceLocation getFileLoc() const { return FileLoc; } -private: /// Lex - Return the next token in the file. If this is the end of file, it /// return the tok::eof token. This implicitly involves the preprocessor. bool Lex(Token &Result); +private: /// Called when the preprocessor is in 'dependency scanning lexing mode'. bool LexDependencyDirectiveToken(Token &Result); diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 465453826c0b982..034a8f90abbfaf7 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3609,6 +3609,18 @@ class Parser : public CodeCompletionHandler { ParseConceptDefinition(const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd); + /// Parse the given string as a type. + /// + /// This is a dangerous utility function currently employed only by API notes. + /// It is not a general entry-point for safely parsing types from strings. + /// + /// \param TypeStr The string to be parsed as a type. + /// \param Context The name of the context in which this string is being + /// parsed, which will be used in diagnostics. + /// \param IncludeLoc The location at which this parse was triggered. + TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context, + SourceLocation IncludeLoc); + //===--------------------------------------------------------------------===// // Modules DeclGroupPtrTy ParseModuleDecl(Sema::ModuleImportState &ImportState); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 59806bcbcbb2dbc..10a2414f3f49423 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -955,6 +955,10 @@ class Sema final { OpaqueParser = P; } + /// Callback to the parser to parse a type expressed as a string. + std::function<TypeResult(StringRef, StringRef, SourceLocation)> + ParseTypeFromStringCallback; + class DelayedDiagnostics; class DelayedDiagnosticsState { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 8cb5b09fd3b0fa6..421fec3bed58534 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -8028,6 +8028,71 @@ bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc, return false; } +TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context, + SourceLocation IncludeLoc) { + // Consume (unexpanded) tokens up to the end-of-directive. + SmallVector<Token, 4> Tokens; + { + // Create a new buffer from which we will parse the type. + auto &SourceMgr = PP.getSourceManager(); + FileID FID = SourceMgr.createFileID( + llvm::MemoryBuffer::getMemBufferCopy(TypeStr, Context), SrcMgr::C_User, + 0, 0, IncludeLoc); + + // Form a new lexer that references the buffer. + Lexer L(FID, SourceMgr.getBufferOrFake(FID), PP); + L.setParsingPreprocessorDirective(true); + + // Lex the tokens from that buffer. + Token Tok; + do { + L.Lex(Tok); + Tokens.push_back(Tok); + } while (Tok.isNot(tok::eod)); + } + + // Replace the "eod" token with an "eof" token identifying the end of + // the provided string. + Token &EndToken = Tokens.back(); + EndToken.startToken(); + EndToken.setKind(tok::eof); + EndToken.setLocation(Tok.getLocation()); + EndToken.setEofData(TypeStr.data()); + + // Add the current token back. + Tokens.push_back(Tok); + + // Enter the tokens into the token stream. + PP.EnterTokenStream(Tokens, /*DisableMacroExpansion=*/false, + /*IsReinject=*/false); + + // Consume the current token so that we'll start parsing the tokens we + // added to the stream. + ConsumeAnyToken(); + + // Enter a new scope. + ParseScope LocalScope(this, 0); + + // Parse the type. + TypeResult Result = ParseTypeName(nullptr); + + // Check if we parsed the whole thing. + if (Result.isUsable() && + (Tok.isNot(tok::eof) || Tok.getEofData() != TypeStr.data())) { + Diag(Tok.getLocation(), diag::err_type_unparsed); + } + + // There could be leftover tokens (e.g. because of an error). + // Skip through until we reach the 'end of directive' token. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + // Consume the end token. + if (Tok.is(tok::eof) && Tok.getEofData() == TypeStr.data()) + ConsumeAnyToken(); + return Result; +} + void Parser::DiagnoseBitIntUse(const Token &Tok) { // If the token is for _ExtInt, diagnose it as being deprecated. Otherwise, // the token is about _BitInt and gets (potentially) diagnosed as use of an diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 1baeb2aeb021faa..97bcbb605dcbd75 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -70,6 +70,11 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) PP.addCommentHandler(CommentSemaHandler.get()); PP.setCodeCompletionHandler(*this); + + Actions.ParseTypeFromStringCallback = + [this](StringRef TypeStr, StringRef Context, SourceLocation IncludeLoc) { + return this->ParseTypeFromString(TypeStr, Context, IncludeLoc); + }; } DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) { >From e7f687d36d510a54c2ab5d355fecb567af579e2f Mon Sep 17 00:00:00 2001 From: Egor Zhdan <e_zh...@apple.com> Date: Tue, 21 Nov 2023 16:42:29 +0000 Subject: [PATCH 3/4] [APINotes] Upstream Sema support for API Notes --- clang/include/clang/Sema/Sema.h | 26 +++++ clang/lib/Sema/SemaDecl.cpp | 31 ++++++ clang/lib/Sema/SemaType.cpp | 181 +++++++++++++++++++------------- 3 files changed, 166 insertions(+), 72 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 10a2414f3f49423..694eba872a2782b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3021,6 +3021,9 @@ class Sema final { ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC, SourceLocation Loc, QualType T); + QualType AdjustParameterTypeForObjCAutoRefCount(QualType T, + SourceLocation NameLoc, + TypeSourceInfo *TSInfo); ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc, SourceLocation NameLoc, IdentifierInfo *Name, QualType T, TypeSourceInfo *TSInfo, @@ -4806,6 +4809,29 @@ class Sema final { /// Valid types should not have multiple attributes with different CCs. const AttributedType *getCallingConvAttributedType(QualType T) const; + /// Check whether a nullability type specifier can be added to the given + /// type through some means not written in source (e.g. API notes). + /// + /// \param Type The type to which the nullability specifier will be + /// added. On success, this type will be updated appropriately. + /// + /// \param Nullability The nullability specifier to add. + /// + /// \param DiagLoc The location to use for diagnostics. + /// + /// \param AllowArrayTypes Whether to accept nullability specifiers on an + /// array type (e.g., because it will decay to a pointer). + /// + /// \param OverrideExisting Whether to override an existing, locally-specified + /// nullability specifier rather than complaining about the conflict. + /// + /// \returns true if nullability cannot be applied, false otherwise. + bool CheckImplicitNullabilityTypeSpecifier(QualType &Type, + NullabilityKind Nullability, + SourceLocation DiagLoc, + bool AllowArrayTypes, + bool OverrideExisting); + /// Process the attributes before creating an attributed statement. Returns /// the semantic attributes that have been processed. void ProcessStmtAttributes(Stmt *Stmt, const ParsedAttributes &InAttrs, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 4e1857b931cc868..def6cf2f6d24baa 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15139,6 +15139,37 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue( } } +QualType Sema::AdjustParameterTypeForObjCAutoRefCount(QualType T, + SourceLocation NameLoc, + TypeSourceInfo *TSInfo) { + // In ARC, infer a lifetime qualifier for appropriate parameter types. + if (getLangOpts().ObjCAutoRefCount && + T.getObjCLifetime() == Qualifiers::OCL_None && T->isObjCLifetimeType()) { + + Qualifiers::ObjCLifetime Lifetime; + + // Special cases for arrays: + // - if it's const, use __unsafe_unretained + // - otherwise, it's an error + if (T->isArrayType()) { + if (!T.isConstQualified()) { + if (DelayedDiagnostics.shouldDelayDiagnostics()) + DelayedDiagnostics.add(sema::DelayedDiagnostic::makeForbiddenType( + NameLoc, diag::err_arc_array_param_no_ownership, T, false)); + else + Diag(NameLoc, diag::err_arc_array_param_no_ownership) + << TSInfo->getTypeLoc().getSourceRange(); + } + Lifetime = Qualifiers::OCL_ExplicitNone; + } else { + Lifetime = T->getObjCARCImplicitLifetime(); + } + T = Context.getLifetimeQualifiedType(T, Lifetime); + } + + return T; +} + ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, SourceLocation NameLoc, IdentifierInfo *Name, QualType T, TypeSourceInfo *TSInfo, diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 560feafa1857cb3..33fd0a42137fc37 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7519,6 +7519,26 @@ static bool HandleWebAssemblyFuncrefAttr(TypeProcessingState &State, return false; } +/// Rebuild an attributed type without the nullability attribute on it. +static QualType rebuildAttributedTypeWithoutNullability(ASTContext &Ctx, + QualType Type) { + auto Attributed = dyn_cast<AttributedType>(Type.getTypePtr()); + if (!Attributed) + return Type; + + // Skip the nullability attribute; we're done. + if (Attributed->getImmediateNullability()) { + return Attributed->getModifiedType(); + } + + // Build the modified type. + auto Modified = rebuildAttributedTypeWithoutNullability( + Ctx, Attributed->getModifiedType()); + assert(Modified.getTypePtr() != Attributed->getModifiedType().getTypePtr()); + return Ctx.getAttributedType(Attributed->getAttrKind(), Modified, + Attributed->getEquivalentType()); +} + /// Map a nullability attribute kind to a nullability kind. static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) { switch (kind) { @@ -7539,74 +7559,65 @@ static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) { } } -/// Applies a nullability type specifier to the given type, if possible. -/// -/// \param state The type processing state. -/// -/// \param type The type to which the nullability specifier will be -/// added. On success, this type will be updated appropriately. -/// -/// \param attr The attribute as written on the type. -/// -/// \param allowOnArrayType Whether to accept nullability specifiers on an -/// array type (e.g., because it will decay to a pointer). -/// -/// \returns true if a problem has been diagnosed, false on success. -static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, - QualType &type, - ParsedAttr &attr, - bool allowOnArrayType) { - Sema &S = state.getSema(); - - NullabilityKind nullability = mapNullabilityAttrKind(attr.getKind()); - SourceLocation nullabilityLoc = attr.getLoc(); - bool isContextSensitive = attr.isContextSensitiveKeywordAttribute(); - - recordNullabilitySeen(S, nullabilityLoc); +static bool CheckNullabilityTypeSpecifier( + Sema &S, TypeProcessingState *State, ParsedAttr *PAttr, QualType &QT, + NullabilityKind Nullability, SourceLocation NullabilityLoc, + bool IsContextSensitive, bool AllowOnArrayType, bool OverrideExisting) { + bool Implicit = (State == nullptr); + if (!Implicit) + recordNullabilitySeen(S, NullabilityLoc); // Check for existing nullability attributes on the type. - QualType desugared = type; - while (auto attributed = dyn_cast<AttributedType>(desugared.getTypePtr())) { + QualType Desugared = QT; + while (auto Attributed = dyn_cast<AttributedType>(Desugared.getTypePtr())) { // Check whether there is already a null - if (auto existingNullability = attributed->getImmediateNullability()) { + if (auto ExistingNullability = Attributed->getImmediateNullability()) { // Duplicated nullability. - if (nullability == *existingNullability) { - S.Diag(nullabilityLoc, diag::warn_nullability_duplicate) - << DiagNullabilityKind(nullability, isContextSensitive) - << FixItHint::CreateRemoval(nullabilityLoc); + if (Nullability == *ExistingNullability) { + if (Implicit) + break; + + S.Diag(NullabilityLoc, diag::warn_nullability_duplicate) + << DiagNullabilityKind(Nullability, IsContextSensitive) + << FixItHint::CreateRemoval(NullabilityLoc); break; } - // Conflicting nullability. - S.Diag(nullabilityLoc, diag::err_nullability_conflicting) - << DiagNullabilityKind(nullability, isContextSensitive) - << DiagNullabilityKind(*existingNullability, false); - return true; + if (!OverrideExisting) { + // Conflicting nullability. + S.Diag(NullabilityLoc, diag::err_nullability_conflicting) + << DiagNullabilityKind(Nullability, IsContextSensitive) + << DiagNullabilityKind(*ExistingNullability, false); + return true; + } + + // Rebuild the attributed type, dropping the existing nullability. + QT = rebuildAttributedTypeWithoutNullability(S.Context, QT); } - desugared = attributed->getModifiedType(); + Desugared = Attributed->getModifiedType(); } // If there is already a different nullability specifier, complain. // This (unlike the code above) looks through typedefs that might // have nullability specifiers on them, which means we cannot // provide a useful Fix-It. - if (auto existingNullability = desugared->getNullability()) { - if (nullability != *existingNullability) { - S.Diag(nullabilityLoc, diag::err_nullability_conflicting) - << DiagNullabilityKind(nullability, isContextSensitive) - << DiagNullabilityKind(*existingNullability, false); + if (auto ExistingNullability = Desugared->getNullability()) { + if (Nullability != *ExistingNullability && !Implicit) { + S.Diag(NullabilityLoc, diag::err_nullability_conflicting) + << DiagNullabilityKind(Nullability, IsContextSensitive) + << DiagNullabilityKind(*ExistingNullability, false); // Try to find the typedef with the existing nullability specifier. - if (auto typedefType = desugared->getAs<TypedefType>()) { - TypedefNameDecl *typedefDecl = typedefType->getDecl(); + if (auto TT = Desugared->getAs<TypedefType>()) { + TypedefNameDecl *typedefDecl = TT->getDecl(); QualType underlyingType = typedefDecl->getUnderlyingType(); - if (auto typedefNullability - = AttributedType::stripOuterNullability(underlyingType)) { - if (*typedefNullability == *existingNullability) { + if (auto typedefNullability = + AttributedType::stripOuterNullability(underlyingType)) { + if (*typedefNullability == *ExistingNullability) { S.Diag(typedefDecl->getLocation(), diag::note_nullability_here) - << DiagNullabilityKind(*existingNullability, false); + << DiagNullabilityKind(*ExistingNullability, false); } } } @@ -7616,44 +7627,73 @@ static bool checkNullabilityTypeSpecifier(TypeProcessingState &state, } // If this definitely isn't a pointer type, reject the specifier. - if (!desugared->canHaveNullability() && - !(allowOnArrayType && desugared->isArrayType())) { - S.Diag(nullabilityLoc, diag::err_nullability_nonpointer) - << DiagNullabilityKind(nullability, isContextSensitive) << type; + if (!Desugared->canHaveNullability() && + !(AllowOnArrayType && Desugared->isArrayType())) { + if (!Implicit) + S.Diag(NullabilityLoc, diag::err_nullability_nonpointer) + << DiagNullabilityKind(Nullability, IsContextSensitive) << QT; + return true; } // For the context-sensitive keywords/Objective-C property // attributes, require that the type be a single-level pointer. - if (isContextSensitive) { + if (IsContextSensitive) { // Make sure that the pointee isn't itself a pointer type. const Type *pointeeType = nullptr; - if (desugared->isArrayType()) - pointeeType = desugared->getArrayElementTypeNoTypeQual(); - else if (desugared->isAnyPointerType()) - pointeeType = desugared->getPointeeType().getTypePtr(); + if (Desugared->isArrayType()) + pointeeType = Desugared->getArrayElementTypeNoTypeQual(); + else if (Desugared->isAnyPointerType()) + pointeeType = Desugared->getPointeeType().getTypePtr(); if (pointeeType && (pointeeType->isAnyPointerType() || pointeeType->isObjCObjectPointerType() || pointeeType->isMemberPointerType())) { - S.Diag(nullabilityLoc, diag::err_nullability_cs_multilevel) - << DiagNullabilityKind(nullability, true) - << type; - S.Diag(nullabilityLoc, diag::note_nullability_type_specifier) - << DiagNullabilityKind(nullability, false) - << type - << FixItHint::CreateReplacement(nullabilityLoc, - getNullabilitySpelling(nullability)); + S.Diag(NullabilityLoc, diag::err_nullability_cs_multilevel) + << DiagNullabilityKind(Nullability, true) << QT; + S.Diag(NullabilityLoc, diag::note_nullability_type_specifier) + << DiagNullabilityKind(Nullability, false) << QT + << FixItHint::CreateReplacement(NullabilityLoc, + getNullabilitySpelling(Nullability)); return true; } } // Form the attributed type. - type = state.getAttributedType( - createNullabilityAttr(S.Context, attr, nullability), type, type); + if (State) { + assert(PAttr); + Attr *A = createNullabilityAttr(S.Context, *PAttr, Nullability); + QT = State->getAttributedType(A, QT, QT); + } else { + attr::Kind attrKind = AttributedType::getNullabilityAttrKind(Nullability); + QT = S.Context.getAttributedType(attrKind, QT, QT); + } return false; } +static bool CheckNullabilityTypeSpecifier(TypeProcessingState &State, + QualType &Type, ParsedAttr &Attr, + bool AllowOnArrayType) { + NullabilityKind Nullability = mapNullabilityAttrKind(Attr.getKind()); + SourceLocation NullabilityLoc = Attr.getLoc(); + bool IsContextSensitive = Attr.isContextSensitiveKeywordAttribute(); + + return CheckNullabilityTypeSpecifier(State.getSema(), &State, &Attr, Type, + Nullability, NullabilityLoc, + IsContextSensitive, AllowOnArrayType, + /*overrideExisting*/ false); +} + +bool Sema::CheckImplicitNullabilityTypeSpecifier(QualType &Type, + NullabilityKind Nullability, + SourceLocation DiagLoc, + bool AllowArrayTypes, + bool OverrideExisting) { + return CheckNullabilityTypeSpecifier( + *this, nullptr, nullptr, Type, Nullability, DiagLoc, + /*isContextSensitive*/ false, AllowArrayTypes, OverrideExisting); +} + /// Check the application of the Objective-C '__kindof' qualifier to /// the given type. static bool checkObjCKindOfType(TypeProcessingState &state, QualType &type, @@ -8858,11 +8898,8 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, bool allowOnArrayType = state.getDeclarator().isPrototypeContext() && !hasOuterPointerLikeChunk(state.getDeclarator(), endIndex); - if (checkNullabilityTypeSpecifier( - state, - type, - attr, - allowOnArrayType)) { + if (CheckNullabilityTypeSpecifier(state, type, attr, + allowOnArrayType)) { attr.setInvalid(); } >From d9e74b67be2e812f576fc67640a56005eb58c99c Mon Sep 17 00:00:00 2001 From: Egor Zhdan <e_zh...@apple.com> Date: Tue, 21 Nov 2023 16:57:26 +0000 Subject: [PATCH 4/4] [APINotes] Upstream Sema logic to apply API Notes to decls --- .../clang/Basic/DiagnosticSemaKinds.td | 7 + clang/include/clang/Sema/Sema.h | 6 + clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/SemaAPINotes.cpp | 1014 +++++++++++++++++ clang/lib/Sema/SemaDecl.cpp | 4 + clang/lib/Sema/SemaDeclAttr.cpp | 3 + clang/lib/Sema/SemaDeclCXX.cpp | 6 +- clang/lib/Sema/SemaDeclObjC.cpp | 4 + clang/lib/Sema/SemaObjCProperty.cpp | 5 + clang/lib/Sema/SemaTemplate.cpp | 7 + 10 files changed, 1056 insertions(+), 1 deletion(-) create mode 100644 clang/lib/Sema/SemaAPINotes.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 990692c06d7d3a8..f09f2899754690d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10667,6 +10667,13 @@ def warn_imp_cast_drops_unaligned : Warning< } // end of sema category +let CategoryName = "API Notes Issue" in { + +def err_incompatible_replacement_type : Error< + "API notes replacement type %0 has a different size from original type %1">; + +} // end of API Notes category + let CategoryName = "OpenMP Issue" in { // OpenMP support. def err_omp_expected_var_arg : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 694eba872a2782b..0b300df3ef5b666 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4749,6 +4749,12 @@ class Sema final { bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A, bool SkipArgCountCheck = false); + /// Map any API notes provided for this declaration to attributes on the + /// declaration. + /// + /// Triggered by declaration-attribute processing. + void ProcessAPINotes(Decl *D); + /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by /// nonnull), but if the second parameter is true, then we treat a reference diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 1856a88e9a3271a..4f72bce98fbbec3 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangSema Sema.cpp SemaAccess.cpp SemaAttr.cpp + SemaAPINotes.cpp SemaAvailability.cpp SemaCXXScopeSpec.cpp SemaCast.cpp diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp new file mode 100644 index 000000000000000..4f2ac1acfc01a9d --- /dev/null +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -0,0 +1,1014 @@ +//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the mapping from API notes to declaration attributes. +// +//===----------------------------------------------------------------------===// + +#include "clang/APINotes/APINotesReader.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaInternal.h" + +using namespace clang; + +namespace { +enum IsActive_t : bool { IsNotActive, IsActive }; +enum IsReplacement_t : bool { IsNotReplacement, IsReplacement }; + +struct VersionedInfoMetadata { + /// An empty version refers to unversioned metadata. + VersionTuple Version; + unsigned IsActive : 1; + unsigned IsReplacement : 1; + + VersionedInfoMetadata(VersionTuple Version, IsActive_t Active, + IsReplacement_t Replacement) + : Version(Version), IsActive(Active == IsActive_t::IsActive), + IsReplacement(Replacement == IsReplacement_t::IsReplacement) {} +}; +} // end anonymous namespace + +/// Determine whether this is a multi-level pointer type. +static bool isMultiLevelPointerType(QualType Type) { + QualType Pointee = Type->getPointeeType(); + if (Pointee.isNull()) + return false; + + return Pointee->isAnyPointerType() || Pointee->isObjCObjectPointerType() || + Pointee->isMemberPointerType(); +} + +/// Apply nullability to the given declaration. +static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability, + VersionedInfoMetadata Metadata) { + if (!Metadata.IsActive) + return; + + QualType Type; + + // Nullability for a function/method appertains to the retain type. + if (auto Function = dyn_cast<FunctionDecl>(D)) + Type = Function->getReturnType(); + else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) + Type = Method->getReturnType(); + else if (auto Value = dyn_cast<ValueDecl>(D)) + Type = Value->getType(); + else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) + Type = Property->getType(); + else + return; + + // Check the nullability specifier on this type. + QualType OrigType = Type; + S.CheckImplicitNullabilityTypeSpecifier(Type, Nullability, D->getLocation(), + isa<ParmVarDecl>(D), + /*overrideExisting=*/true); + if (Type.getTypePtr() == OrigType.getTypePtr()) + return; + + if (auto Function = dyn_cast<FunctionDecl>(D)) { + const FunctionType *FnType = Function->getType()->castAs<FunctionType>(); + if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FnType)) + Function->setType(S.Context.getFunctionType(Type, Proto->getParamTypes(), + Proto->getExtProtoInfo())); + else + Function->setType( + S.Context.getFunctionNoProtoType(Type, FnType->getExtInfo())); + } else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) { + Method->setReturnType(Type); + + // Make it a context-sensitive keyword if we can. + if (!isMultiLevelPointerType(Type)) + Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier( + Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); + + } else if (auto Value = dyn_cast<ValueDecl>(D)) { + Value->setType(Type); + + // Make it a context-sensitive keyword if we can. + if (auto Parm = dyn_cast<ParmVarDecl>(D)) { + if (Parm->isObjCMethodParameter() && !isMultiLevelPointerType(Type)) + Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier( + Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); + } + } else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { + Property->setType(Type, Property->getTypeSourceInfo()); + + // Make it a property attribute if we can. + if (!isMultiLevelPointerType(Type)) + Property->setPropertyAttributes( + ObjCPropertyAttribute::kind_null_resettable); + + } else + llvm_unreachable("cannot handle nullability here"); +} + +/// Copy a string into ASTContext-allocated memory. +static StringRef CopyString(ASTContext &Ctx, StringRef String) { + void *mem = Ctx.Allocate(String.size(), alignof(char)); + memcpy(mem, String.data(), String.size()); + return StringRef(static_cast<char *>(mem), String.size()); +} + +static AttributeCommonInfo getDummyAttrInfo() { + return AttributeCommonInfo(SourceRange(), + AttributeCommonInfo::UnknownAttribute, + {AttributeCommonInfo::AS_GNU, + /*Spelling*/ 0, /*IsAlignas*/ false, + /*IsRegularKeywordAttribute*/ false}); +} + +namespace { +template <typename A> struct AttrKindFor {}; + +#define ATTR(X) \ + template <> struct AttrKindFor<X##Attr> { \ + static const attr::Kind value = attr::X; \ + }; +#include "clang/Basic/AttrList.inc" + +/// Handle an attribute introduced by API notes. +/// +/// \param ShouldAddAttribute Whether we should add a new attribute +/// (otherwise, we might remove an existing attribute). +/// \param CreateAttr Create the new attribute to be added. +template <typename A> +void handleAPINotedAttribute( + Sema &S, Decl *D, bool ShouldAddAttribute, VersionedInfoMetadata Metadata, + llvm::function_ref<A *()> CreateAttr, + llvm::function_ref<Decl::attr_iterator(const Decl *)> GetExistingAttr) { + if (Metadata.IsActive) { + auto Existing = GetExistingAttr(D); + if (Existing != D->attr_end()) { + // Remove the existing attribute, and treat it as a superseded + // non-versioned attribute. + auto *Versioned = SwiftVersionedAttr::CreateImplicit( + S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true); + + D->getAttrs().erase(Existing); + D->addAttr(Versioned); + } + + // If we're supposed to add a new attribute, do so. + if (ShouldAddAttribute) { + if (auto Attr = CreateAttr()) + D->addAttr(Attr); + } + + } else { + if (ShouldAddAttribute) { + if (auto Attr = CreateAttr()) { + auto *Versioned = SwiftVersionedAttr::CreateImplicit( + S.Context, Metadata.Version, Attr, + /*IsReplacedByActive*/ Metadata.IsReplacement); + D->addAttr(Versioned); + } + } else { + // FIXME: This isn't preserving enough information for things like + // availability, where we're trying to remove a /specific/ kind of + // attribute. + auto *Versioned = SwiftVersionedRemovalAttr::CreateImplicit( + S.Context, Metadata.Version, AttrKindFor<A>::value, + /*IsReplacedByActive*/ Metadata.IsReplacement); + D->addAttr(Versioned); + } + } +} + +template <typename A> +void handleAPINotedAttribute(Sema &S, Decl *D, bool ShouldAddAttribute, + VersionedInfoMetadata Metadata, + llvm::function_ref<A *()> CreateAttr) { + handleAPINotedAttribute<A>( + S, D, ShouldAddAttribute, Metadata, CreateAttr, [](const Decl *D) { + return llvm::find_if(D->attrs(), + [](const Attr *Next) { return isa<A>(Next); }); + }); +} +} // namespace + +template <typename A = CFReturnsRetainedAttr> +static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D, + bool ShouldAddAttribute, + VersionedInfoMetadata Metadata) { + // The template argument has a default to make the "removal" case more + // concise; it doesn't matter /which/ attribute is being removed. + handleAPINotedAttribute<A>( + S, D, ShouldAddAttribute, Metadata, + [&] { return new (S.Context) A(S.Context, getDummyAttrInfo()); }, + [](const Decl *D) -> Decl::attr_iterator { + return llvm::find_if(D->attrs(), [](const Attr *Next) -> bool { + return isa<CFReturnsRetainedAttr>(Next) || + isa<CFReturnsNotRetainedAttr>(Next) || + isa<NSReturnsRetainedAttr>(Next) || + isa<NSReturnsNotRetainedAttr>(Next) || + isa<CFAuditedTransferAttr>(Next); + }); + }); +} + +static void handleAPINotedRetainCountConvention( + Sema &S, Decl *D, VersionedInfoMetadata Metadata, + std::optional<api_notes::RetainCountConventionKind> Convention) { + if (!Convention) + return; + switch (*Convention) { + case api_notes::RetainCountConventionKind::None: + if (isa<FunctionDecl>(D)) { + handleAPINotedRetainCountAttribute<CFUnknownTransferAttr>( + S, D, /*shouldAddAttribute*/ true, Metadata); + } else { + handleAPINotedRetainCountAttribute(S, D, /*shouldAddAttribute*/ false, + Metadata); + } + break; + case api_notes::RetainCountConventionKind::CFReturnsRetained: + handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>( + S, D, /*shouldAddAttribute*/ true, Metadata); + break; + case api_notes::RetainCountConventionKind::CFReturnsNotRetained: + handleAPINotedRetainCountAttribute<CFReturnsNotRetainedAttr>( + S, D, /*shouldAddAttribute*/ true, Metadata); + break; + case api_notes::RetainCountConventionKind::NSReturnsRetained: + handleAPINotedRetainCountAttribute<NSReturnsRetainedAttr>( + S, D, /*shouldAddAttribute*/ true, Metadata); + break; + case api_notes::RetainCountConventionKind::NSReturnsNotRetained: + handleAPINotedRetainCountAttribute<NSReturnsNotRetainedAttr>( + S, D, /*shouldAddAttribute*/ true, Metadata); + break; + } +} + +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonEntityInfo &Info, + VersionedInfoMetadata Metadata) { + // Availability + if (Info.Unavailable) { + handleAPINotedAttribute<UnavailableAttr>(S, D, true, Metadata, [&] { + return new (S.Context) + UnavailableAttr(S.Context, getDummyAttrInfo(), + CopyString(S.Context, Info.UnavailableMsg)); + }); + } + + if (Info.UnavailableInSwift) { + handleAPINotedAttribute<AvailabilityAttr>( + S, D, true, Metadata, + [&] { + return new (S.Context) AvailabilityAttr( + S.Context, getDummyAttrInfo(), &S.Context.Idents.get("swift"), + VersionTuple(), VersionTuple(), VersionTuple(), + /*Unavailable=*/true, CopyString(S.Context, Info.UnavailableMsg), + /*Strict=*/false, + /*Replacement=*/StringRef(), + /*Priority=*/Sema::AP_Explicit); + }, + [](const Decl *D) { + return llvm::find_if(D->attrs(), [](const Attr *next) -> bool { + auto *AA = dyn_cast<AvailabilityAttr>(next); + if (!AA) + return false; + const IdentifierInfo *platform = AA->getPlatform(); + if (!platform) + return false; + return platform->isStr("swift"); + }); + }); + } + + // swift_private + if (auto SwiftPrivate = Info.isSwiftPrivate()) { + handleAPINotedAttribute<SwiftPrivateAttr>( + S, D, *SwiftPrivate, Metadata, [&] { + return new (S.Context) + SwiftPrivateAttr(S.Context, getDummyAttrInfo()); + }); + } + + // swift_name + if (!Info.SwiftName.empty()) { + handleAPINotedAttribute<SwiftNameAttr>( + S, D, true, Metadata, [&]() -> SwiftNameAttr * { + AttributeFactory AF{}; + AttributePool AP{AF}; + auto &C = S.getASTContext(); + ParsedAttr *SNA = + AP.create(&C.Idents.get("swift_name"), SourceRange(), nullptr, + SourceLocation(), nullptr, nullptr, nullptr, + ParsedAttr::Form::GNU()); + + if (!S.DiagnoseSwiftName(D, Info.SwiftName, D->getLocation(), *SNA, + /*IsAsync=*/false)) + return nullptr; + + return new (S.Context) + SwiftNameAttr(S.Context, getDummyAttrInfo(), + CopyString(S.Context, Info.SwiftName)); + }); + } +} + +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::CommonTypeInfo &Info, + VersionedInfoMetadata Metadata) { + // swift_bridge + if (auto SwiftBridge = Info.getSwiftBridge()) { + handleAPINotedAttribute<SwiftBridgeAttr>( + S, D, !SwiftBridge->empty(), Metadata, [&] { + return new (S.Context) + SwiftBridgeAttr(S.Context, getDummyAttrInfo(), + CopyString(S.Context, *SwiftBridge)); + }); + } + + // ns_error_domain + if (auto NSErrorDomain = Info.getNSErrorDomain()) { + handleAPINotedAttribute<NSErrorDomainAttr>( + S, D, !NSErrorDomain->empty(), Metadata, [&] { + return new (S.Context) + NSErrorDomainAttr(S.Context, getDummyAttrInfo(), + &S.Context.Idents.get(*NSErrorDomain)); + }); + } + + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info), + Metadata); +} + +/// Check that the replacement type provided by API notes is reasonable. +/// +/// This is a very weak form of ABI check. +static bool checkAPINotesReplacementType(Sema &S, SourceLocation Loc, + QualType OrigType, + QualType ReplacementType) { + if (S.Context.getTypeSize(OrigType) != + S.Context.getTypeSize(ReplacementType)) { + S.Diag(Loc, diag::err_incompatible_replacement_type) + << ReplacementType << OrigType; + return true; + } + + return false; +} + +/// Process API notes for a variable or property. +static void ProcessAPINotes(Sema &S, Decl *D, + const api_notes::VariableInfo &Info, + VersionedInfoMetadata Metadata) { + // Type override. + if (Metadata.IsActive && !Info.getType().empty() && + S.ParseTypeFromStringCallback) { + auto ParsedType = S.ParseTypeFromStringCallback( + Info.getType(), "<API Notes>", D->getLocation()); + if (ParsedType.isUsable()) { + QualType Type = Sema::GetTypeFromParser(ParsedType.get()); + auto TypeInfo = + S.Context.getTrivialTypeSourceInfo(Type, D->getLocation()); + + if (auto Var = dyn_cast<VarDecl>(D)) { + // Make adjustments to parameter types. + if (isa<ParmVarDecl>(Var)) { + Type = S.AdjustParameterTypeForObjCAutoRefCount( + Type, D->getLocation(), TypeInfo); + Type = S.Context.getAdjustedParameterType(Type); + } + + if (!checkAPINotesReplacementType(S, Var->getLocation(), Var->getType(), + Type)) { + Var->setType(Type); + Var->setTypeSourceInfo(TypeInfo); + } + } else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { + if (!checkAPINotesReplacementType(S, Property->getLocation(), + Property->getType(), Type)) + Property->setType(Type, TypeInfo); + + } else + llvm_unreachable("API notes allowed a type on an unknown declaration"); + } + } + + // Nullability. + if (auto Nullability = Info.getNullability()) + applyNullability(S, D, *Nullability, Metadata); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info), + Metadata); +} + +/// Process API notes for a parameter. +static void ProcessAPINotes(Sema &S, ParmVarDecl *D, + const api_notes::ParamInfo &Info, + VersionedInfoMetadata Metadata) { + // noescape + if (auto NoEscape = Info.isNoEscape()) + handleAPINotedAttribute<NoEscapeAttr>(S, D, *NoEscape, Metadata, [&] { + return new (S.Context) NoEscapeAttr(S.Context, getDummyAttrInfo()); + }); + + // Retain count convention + handleAPINotedRetainCountConvention(S, D, Metadata, + Info.getRetainCountConvention()); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info), + Metadata); +} + +/// Process API notes for a global variable. +static void ProcessAPINotes(Sema &S, VarDecl *D, + const api_notes::GlobalVariableInfo &Info, + VersionedInfoMetadata metadata) { + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info), + metadata); +} + +/// Process API notes for an Objective-C property. +static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D, + const api_notes::ObjCPropertyInfo &Info, + VersionedInfoMetadata Metadata) { + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(Info), + Metadata); + + if (auto AsAccessors = Info.getSwiftImportAsAccessors()) { + handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>( + S, D, *AsAccessors, Metadata, [&] { + return new (S.Context) + SwiftImportPropertyAsAccessorsAttr(S.Context, getDummyAttrInfo()); + }); + } +} + +namespace { +typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod; +} + +/// Process API notes for a function or method. +static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc, + const api_notes::FunctionInfo &Info, + VersionedInfoMetadata Metadata) { + // Find the declaration itself. + FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>(); + Decl *D = FD; + ObjCMethodDecl *MD = 0; + if (!D) { + MD = AnyFunc.get<ObjCMethodDecl *>(); + D = MD; + } + + // Nullability of return type. + if (Info.NullabilityAudited) + applyNullability(S, D, Info.getReturnTypeInfo(), Metadata); + + // Parameters. + unsigned NumParams; + if (FD) + NumParams = FD->getNumParams(); + else + NumParams = MD->param_size(); + + bool AnyTypeChanged = false; + for (unsigned I = 0; I != NumParams; ++I) { + ParmVarDecl *Param; + if (FD) + Param = FD->getParamDecl(I); + else + Param = MD->param_begin()[I]; + + QualType ParamTypeBefore = Param->getType(); + + if (I < Info.Params.size()) + ProcessAPINotes(S, Param, Info.Params[I], Metadata); + + // Nullability. + if (Info.NullabilityAudited) + applyNullability(S, Param, Info.getParamTypeInfo(I), Metadata); + + if (ParamTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr()) + AnyTypeChanged = true; + } + + // Result type override. + QualType OverriddenResultType; + if (Metadata.IsActive && !Info.ResultType.empty() && + S.ParseTypeFromStringCallback) { + auto ParsedType = S.ParseTypeFromStringCallback( + Info.ResultType, "<API Notes>", D->getLocation()); + if (ParsedType.isUsable()) { + QualType ResultType = Sema::GetTypeFromParser(ParsedType.get()); + + if (MD) { + if (!checkAPINotesReplacementType(S, D->getLocation(), + MD->getReturnType(), ResultType)) { + auto ResultTypeInfo = + S.Context.getTrivialTypeSourceInfo(ResultType, D->getLocation()); + MD->setReturnType(ResultType); + MD->setReturnTypeSourceInfo(ResultTypeInfo); + } + } else if (!checkAPINotesReplacementType( + S, FD->getLocation(), FD->getReturnType(), ResultType)) { + OverriddenResultType = ResultType; + AnyTypeChanged = true; + } + } + } + + // If the result type or any of the parameter types changed for a function + // declaration, we have to rebuild the type. + if (FD && AnyTypeChanged) { + if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) { + if (OverriddenResultType.isNull()) + OverriddenResultType = fnProtoType->getReturnType(); + + SmallVector<QualType, 4> ParamTypes; + for (auto Param : FD->parameters()) + ParamTypes.push_back(Param->getType()); + + FD->setType(S.Context.getFunctionType(OverriddenResultType, ParamTypes, + fnProtoType->getExtProtoInfo())); + } else if (!OverriddenResultType.isNull()) { + const auto *FnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>(); + FD->setType(S.Context.getFunctionNoProtoType( + OverriddenResultType, FnNoProtoType->getExtInfo())); + } + } + + // Retain count convention + handleAPINotedRetainCountConvention(S, D, Metadata, + Info.getRetainCountConvention()); + + // Handle common entity information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info), + Metadata); +} + +/// Process API notes for a global function. +static void ProcessAPINotes(Sema &S, FunctionDecl *D, + const api_notes::GlobalFunctionInfo &Info, + VersionedInfoMetadata Metadata) { + // Handle common function information. + ProcessAPINotes(S, FunctionOrMethod(D), + static_cast<const api_notes::FunctionInfo &>(Info), Metadata); +} + +/// Process API notes for an enumerator. +static void ProcessAPINotes(Sema &S, EnumConstantDecl *D, + const api_notes::EnumConstantInfo &Info, + VersionedInfoMetadata Metadata) { + // Handle common information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info), + Metadata); +} + +/// Process API notes for an Objective-C method. +static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D, + const api_notes::ObjCMethodInfo &Info, + VersionedInfoMetadata Metadata) { + // Designated initializers. + if (Info.DesignatedInit) { + handleAPINotedAttribute<ObjCDesignatedInitializerAttr>( + S, D, true, Metadata, [&] { + if (ObjCInterfaceDecl *IFace = D->getClassInterface()) + IFace->setHasDesignatedInitializers(); + + return new (S.Context) + ObjCDesignatedInitializerAttr(S.Context, getDummyAttrInfo()); + }); + } + + // Handle common function information. + ProcessAPINotes(S, FunctionOrMethod(D), + static_cast<const api_notes::FunctionInfo &>(Info), Metadata); +} + +/// Process API notes for a tag. +static void ProcessAPINotes(Sema &S, TagDecl *D, const api_notes::TagInfo &Info, + VersionedInfoMetadata Metadata) { + if (auto ImportAs = Info.SwiftImportAs) { + auto attr = SwiftAttrAttr::Create(S.Context, "import_" + ImportAs.value()); + D->addAttr(attr); + } + if (auto RetainOp = Info.SwiftRetainOp) { + auto attr = SwiftAttrAttr::Create(S.Context, "retain:" + RetainOp.value()); + D->addAttr(attr); + } + if (auto ReleaseOp = Info.SwiftReleaseOp) { + auto attr = + SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()); + D->addAttr(attr); + } + + if (auto Extensibility = Info.EnumExtensibility) { + using api_notes::EnumExtensibilityKind; + bool ShouldAddAttribute = (*Extensibility != EnumExtensibilityKind::None); + handleAPINotedAttribute<EnumExtensibilityAttr>( + S, D, ShouldAddAttribute, Metadata, [&] { + EnumExtensibilityAttr::Kind kind; + switch (*Extensibility) { + case EnumExtensibilityKind::None: + llvm_unreachable("remove only"); + case EnumExtensibilityKind::Open: + kind = EnumExtensibilityAttr::Open; + break; + case EnumExtensibilityKind::Closed: + kind = EnumExtensibilityAttr::Closed; + break; + } + return new (S.Context) + EnumExtensibilityAttr(S.Context, getDummyAttrInfo(), kind); + }); + } + + if (auto FlagEnum = Info.isFlagEnum()) { + handleAPINotedAttribute<FlagEnumAttr>(S, D, *FlagEnum, Metadata, [&] { + return new (S.Context) FlagEnumAttr(S.Context, getDummyAttrInfo()); + }); + } + + // Handle common type information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info), + Metadata); +} + +/// Process API notes for a typedef. +static void ProcessAPINotes(Sema &S, TypedefNameDecl *D, + const api_notes::TypedefInfo &Info, + VersionedInfoMetadata Metadata) { + // swift_wrapper + using SwiftWrapperKind = api_notes::SwiftNewTypeKind; + + if (auto SwiftWrapper = Info.SwiftWrapper) { + handleAPINotedAttribute<SwiftNewTypeAttr>( + S, D, *SwiftWrapper != SwiftWrapperKind::None, Metadata, [&] { + SwiftNewTypeAttr::NewtypeKind Kind; + switch (*SwiftWrapper) { + case SwiftWrapperKind::None: + llvm_unreachable("Shouldn't build an attribute"); + + case SwiftWrapperKind::Struct: + Kind = SwiftNewTypeAttr::NK_Struct; + break; + + case SwiftWrapperKind::Enum: + Kind = SwiftNewTypeAttr::NK_Enum; + break; + } + AttributeCommonInfo SyntaxInfo{ + SourceRange(), + AttributeCommonInfo::AT_SwiftNewType, + {AttributeCommonInfo::AS_GNU, SwiftNewTypeAttr::GNU_swift_wrapper, + /*IsAlignas*/ false, /*IsRegularKeywordAttribute*/ false}}; + return new (S.Context) SwiftNewTypeAttr(S.Context, SyntaxInfo, Kind); + }); + } + + // Handle common type information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info), + Metadata); +} + +/// Process API notes for an Objective-C class or protocol. +static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D, + const api_notes::ObjCContextInfo &Info, + VersionedInfoMetadata Metadata) { + // Handle common type information. + ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(Info), + Metadata); +} + +/// Process API notes for an Objective-C class. +static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D, + const api_notes::ObjCContextInfo &Info, + VersionedInfoMetadata Metadata) { + if (auto AsNonGeneric = Info.getSwiftImportAsNonGeneric()) { + handleAPINotedAttribute<SwiftImportAsNonGenericAttr>( + S, D, *AsNonGeneric, Metadata, [&] { + return new (S.Context) + SwiftImportAsNonGenericAttr(S.Context, getDummyAttrInfo()); + }); + } + + if (auto ObjcMembers = Info.getSwiftObjCMembers()) { + handleAPINotedAttribute<SwiftObjCMembersAttr>( + S, D, *ObjcMembers, Metadata, [&] { + return new (S.Context) + SwiftObjCMembersAttr(S.Context, getDummyAttrInfo()); + }); + } + + // Handle information common to Objective-C classes and protocols. + ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), Info, + Metadata); +} + +/// If we're applying API notes with an active, non-default version, and the +/// versioned API notes have a SwiftName but the declaration normally wouldn't +/// have one, add a removal attribute to make it clear that the new SwiftName +/// attribute only applies to the active version of \p D, not to all versions. +/// +/// This must be run \em before processing API notes for \p D, because otherwise +/// any existing SwiftName attribute will have been packaged up in a +/// SwiftVersionedAttr. +template <typename SpecificInfo> +static void maybeAttachUnversionedSwiftName( + Sema &S, Decl *D, + const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) { + if (D->hasAttr<SwiftNameAttr>()) + return; + if (!Info.getSelected()) + return; + + // Is the active slice versioned, and does it set a Swift name? + VersionTuple SelectedVersion; + SpecificInfo SelectedInfoSlice; + std::tie(SelectedVersion, SelectedInfoSlice) = Info[*Info.getSelected()]; + if (SelectedVersion.empty()) + return; + if (SelectedInfoSlice.SwiftName.empty()) + return; + + // Does the unversioned slice /not/ set a Swift name? + for (const auto &VersionAndInfoSlice : Info) { + if (!VersionAndInfoSlice.first.empty()) + continue; + if (!VersionAndInfoSlice.second.SwiftName.empty()) + return; + } + + // Then explicitly call that out with a removal attribute. + VersionedInfoMetadata DummyFutureMetadata(SelectedVersion, IsNotActive, + IsReplacement); + handleAPINotedAttribute<SwiftNameAttr>( + S, D, /*add*/ false, DummyFutureMetadata, []() -> SwiftNameAttr * { + llvm_unreachable("should not try to add an attribute here"); + }); +} + +/// Processes all versions of versioned API notes. +/// +/// Just dispatches to the various ProcessAPINotes functions in this file. +template <typename SpecificDecl, typename SpecificInfo> +static void ProcessVersionedAPINotes( + Sema &S, SpecificDecl *D, + const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) { + + maybeAttachUnversionedSwiftName(S, D, Info); + + unsigned Selected = Info.getSelected().value_or(Info.size()); + + VersionTuple Version; + SpecificInfo InfoSlice; + for (unsigned i = 0, e = Info.size(); i != e; ++i) { + std::tie(Version, InfoSlice) = Info[i]; + auto Active = (i == Selected) ? IsActive : IsNotActive; + auto Replacement = IsNotReplacement; + if (Active == IsNotActive && Version.empty()) { + Replacement = IsReplacement; + Version = Info[Selected].first; + } + ProcessAPINotes(S, D, InfoSlice, + VersionedInfoMetadata(Version, Active, Replacement)); + } +} + +/// Process API notes that are associated with this declaration, mapping them +/// to attributes as appropriate. +void Sema::ProcessAPINotes(Decl *D) { + if (!D) + return; + + // Globals. + if (D->getDeclContext()->isFileContext() || + D->getDeclContext()->isNamespace() || + D->getDeclContext()->isExternCContext() || + D->getDeclContext()->isExternCXXContext()) { + std::optional<api_notes::Context> APINotesContext; + if (auto NamespaceContext = dyn_cast<NamespaceDecl>(D->getDeclContext())) { + for (auto Reader : + APINotes.findAPINotes(NamespaceContext->getLocation())) { + // Retrieve the context ID for the parent namespace of the decl. + std::stack<NamespaceDecl *> NamespaceStack; + { + for (auto CurrentNamespace = NamespaceContext; CurrentNamespace; + CurrentNamespace = + dyn_cast<NamespaceDecl>(CurrentNamespace->getParent())) { + if (!CurrentNamespace->isInlineNamespace()) + NamespaceStack.push(CurrentNamespace); + } + } + std::optional<api_notes::ContextID> NamespaceID; + while (!NamespaceStack.empty()) { + auto CurrentNamespace = NamespaceStack.top(); + NamespaceStack.pop(); + NamespaceID = Reader->lookupNamespaceID(CurrentNamespace->getName(), + NamespaceID); + if (!NamespaceID) + break; + } + if (NamespaceID) + APINotesContext = api_notes::Context( + *NamespaceID, api_notes::ContextKind::Namespace); + } + } + + // Global variables. + if (auto VD = dyn_cast<VarDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = + Reader->lookupGlobalVariable(VD->getName(), APINotesContext); + ProcessVersionedAPINotes(*this, VD, Info); + } + + return; + } + + // Global functions. + if (auto FD = dyn_cast<FunctionDecl>(D)) { + if (FD->getDeclName().isIdentifier()) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = + Reader->lookupGlobalFunction(FD->getName(), APINotesContext); + ProcessVersionedAPINotes(*this, FD, Info); + } + } + + return; + } + + // Objective-C classes. + if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupObjCClassInfo(Class->getName()); + ProcessVersionedAPINotes(*this, Class, Info); + } + + return; + } + + // Objective-C protocols. + if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName()); + ProcessVersionedAPINotes(*this, Protocol, Info); + } + + return; + } + + // Tags + if (auto Tag = dyn_cast<TagDecl>(D)) { + std::string LookupName = Tag->getName().str(); + + // Use the source location to discern if this Tag is an OPTIONS macro. + // For now we would like to limit this trick of looking up the APINote tag + // using the EnumDecl's QualType in the case where the enum is anonymous. + // This is only being used to support APINotes lookup for C++ + // NS/CF_OPTIONS when C++-Interop is enabled. + std::string MacroName = + LookupName.empty() && Tag->getOuterLocStart().isMacroID() + ? clang::Lexer::getImmediateMacroName( + Tag->getOuterLocStart(), + Tag->getASTContext().getSourceManager(), LangOpts) + .str() + : ""; + + if (LookupName.empty() && isa<clang::EnumDecl>(Tag) && + (MacroName == "CF_OPTIONS" || MacroName == "NS_OPTIONS" || + MacroName == "OBJC_OPTIONS" || MacroName == "SWIFT_OPTIONS")) { + + clang::QualType T = llvm::cast<clang::EnumDecl>(Tag)->getIntegerType(); + LookupName = clang::QualType::getAsString( + T.split(), getASTContext().getPrintingPolicy()); + } + + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupTag(LookupName, APINotesContext); + ProcessVersionedAPINotes(*this, Tag, Info); + } + + return; + } + + // Typedefs + if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupTypedef(Typedef->getName(), APINotesContext); + ProcessVersionedAPINotes(*this, Typedef, Info); + } + + return; + } + } + + // Enumerators. + if (D->getDeclContext()->getRedeclContext()->isFileContext() || + D->getDeclContext()->getRedeclContext()->isExternCContext()) { + if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + auto Info = Reader->lookupEnumConstant(EnumConstant->getName()); + ProcessVersionedAPINotes(*this, EnumConstant, Info); + } + + return; + } + } + + if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) { + // Location function that looks up an Objective-C context. + auto GetContext = [&](api_notes::APINotesReader *Reader) + -> std::optional<api_notes::ContextID> { + if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) { + if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName())) + return *Found; + + return std::nullopt; + } + + if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) { + if (auto Cat = Impl->getCategoryDecl()) + ObjCContainer = Cat; + else + return std::nullopt; + } + + if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) { + if (Category->getClassInterface()) + ObjCContainer = Category->getClassInterface(); + else + return std::nullopt; + } + + if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) { + if (Impl->getClassInterface()) + ObjCContainer = Impl->getClassInterface(); + else + return std::nullopt; + } + + if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) { + if (auto Found = Reader->lookupObjCClassID(Class->getName())) + return *Found; + + return std::nullopt; + } + + return std::nullopt; + }; + + // Objective-C methods. + if (auto Method = dyn_cast<ObjCMethodDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetContext(Reader)) { + // Map the selector. + Selector Sel = Method->getSelector(); + SmallVector<StringRef, 2> SelPieces; + if (Sel.isUnarySelector()) + SelPieces.push_back(Sel.getNameForSlot(0)); + else { + for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i) + SelPieces.push_back(Sel.getNameForSlot(i)); + } + + api_notes::ObjCSelectorRef SelectorRef; + SelectorRef.NumArgs = Sel.getNumArgs(); + SelectorRef.Identifiers = SelPieces; + + auto Info = Reader->lookupObjCMethod(*Context, SelectorRef, + Method->isInstanceMethod()); + ProcessVersionedAPINotes(*this, Method, Info); + } + } + } + + // Objective-C properties. + if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { + for (auto Reader : APINotes.findAPINotes(D->getLocation())) { + if (auto Context = GetContext(Reader)) { + bool isInstanceProperty = + (Property->getPropertyAttributesAsWritten() & + ObjCPropertyAttribute::kind_class) == 0; + auto Info = Reader->lookupObjCProperty(*Context, Property->getName(), + isInstanceProperty); + ProcessVersionedAPINotes(*this, Property, Info); + } + } + + return; + } + + return; + } +} diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index def6cf2f6d24baa..302950879e457a8 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16300,6 +16300,7 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) D = TD->getTemplatedDecl(); ProcessDeclAttributeList(S, D, Attrs); + ProcessAPINotes(D); if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D)) if (Method->isStatic()) @@ -19519,6 +19520,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, CDecl->setIvarRBraceLoc(RBrac); } } + ProcessAPINotes(Record); // Check the "counted_by" attribute to ensure that the count field exists in // the struct. Make sure we're performing this check on the outer-most @@ -19847,6 +19849,7 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, // Process attributes. ProcessDeclAttributeList(S, New, Attrs); AddPragmaAttributes(S, New); + ProcessAPINotes(New); // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); @@ -20045,6 +20048,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange, QualType EnumType = Context.getTypeDeclType(Enum); ProcessDeclAttributeList(S, Enum, Attrs); + ProcessAPINotes(Enum); if (Enum->isDependentType()) { for (unsigned i = 0, e = Elements.size(); i != e; ++i) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 8552b28e51acd97..f245b1466de8dd0 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -9948,6 +9948,9 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // Apply additional attributes specified by '#pragma clang attribute'. AddPragmaAttributes(S, D); + + // Look for API notes that map to attributes. + ProcessAPINotes(D); } /// Is the given declaration allowed to use a forbidden type? diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 3688192e6cbe5c5..476f19ef338f68b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11700,6 +11700,7 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope, ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); AddPragmaAttributes(DeclRegionScope, Namespc); + ProcessAPINotes(Namespc); // FIXME: Should we be merging attributes? if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>()) @@ -12219,8 +12220,10 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); } - if (UDir) + if (UDir) { ProcessDeclAttributeList(S, UDir, AttrList); + ProcessAPINotes(UDir); + } return UDir; } @@ -13512,6 +13515,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, ProcessDeclAttributeList(S, NewTD, AttrList); AddPragmaAttributes(S, NewTD); + ProcessAPINotes(NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); Invalid |= NewTD->isInvalidDecl(); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index cdfa6ad3f281a43..526f3a8db30e212 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1072,6 +1072,7 @@ ObjCInterfaceDecl *Sema::ActOnStartClassInterface( ProcessDeclAttributeList(TUScope, IDecl, AttrList); AddPragmaAttributes(TUScope, IDecl); + ProcessAPINotes(IDecl); // Merge attributes from previous declarations. if (PrevIDecl) @@ -1273,6 +1274,7 @@ ObjCProtocolDecl *Sema::ActOnStartProtocolInterface( ProcessDeclAttributeList(TUScope, PDecl, AttrList); AddPragmaAttributes(TUScope, PDecl); + ProcessAPINotes(PDecl); // Merge attributes from previous declarations. if (PrevDecl) @@ -4807,6 +4809,7 @@ Decl *Sema::ActOnMethodDeclaration( // Apply the attributes to the parameter. ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs); AddPragmaAttributes(TUScope, Param); + ProcessAPINotes(Param); if (Param->hasAttr<BlocksAttr>()) { Diag(Param->getLocation(), diag::err_block_on_nonlocal); @@ -4837,6 +4840,7 @@ Decl *Sema::ActOnMethodDeclaration( ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList); AddPragmaAttributes(TUScope, ObjCMethod); + ProcessAPINotes(ObjCMethod); // Add the method now. const ObjCMethodDecl *PrevMethod = nullptr; diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 22540af1cda8c6d..09b6a9cdfbd5763 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -2509,6 +2509,8 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { GetterMethod->addAttr(SectionAttr::CreateImplicit( Context, SA->getName(), Loc, SectionAttr::GNU_section)); + ProcessAPINotes(GetterMethod); + if (getLangOpts().ObjCAutoRefCount) CheckARCMethodDecl(GetterMethod); } else @@ -2578,6 +2580,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { if (const SectionAttr *SA = property->getAttr<SectionAttr>()) SetterMethod->addAttr(SectionAttr::CreateImplicit( Context, SA->getName(), Loc, SectionAttr::GNU_section)); + + ProcessAPINotes(SetterMethod); + // It's possible for the user to have set a very odd custom // setter selector that causes it to have a method family. if (getLangOpts().ObjCAutoRefCount) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c188dd34014a4b3..737b12ac73b6994 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2145,6 +2145,7 @@ DeclResult Sema::CheckClassTemplate( NewClass->startDefinition(); ProcessDeclAttributeList(S, NewClass, Attr); + ProcessAPINotes(NewClass); if (PrevClassTemplate) mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); @@ -8930,6 +8931,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( } ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -10165,6 +10167,7 @@ DeclResult Sema::ActOnExplicitInstantiation( bool PreviouslyDLLExported = Specialization->hasAttr<DLLExportAttr>(); ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add the explicit instantiation into its lexical context. However, // since explicit instantiations are never found by name lookup, we @@ -10575,6 +10578,9 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); // Merge attributes. ProcessDeclAttributeList(S, Prev, D.getDeclSpec().getAttributes()); + if (PrevTemplate) + ProcessAPINotes(Prev); + if (TSK == TSK_ExplicitInstantiationDefinition) InstantiateVariableDefinition(D.getIdentifierLoc(), Prev); } @@ -10750,6 +10756,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, } ProcessDeclAttributeList(S, Specialization, D.getDeclSpec().getAttributes()); + ProcessAPINotes(Specialization); // In MSVC mode, dllimported explicit instantiation definitions are treated as // instantiation declarations. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits