[clang] [APINotes] Upstream Sema logic to apply API Notes to decls (PR #73017)
Egor Zhdan via cfe-commits
cfe-commits at lists.llvm.org
Tue Nov 21 10:09:24 PST 2023
https://github.com/egorzhdan updated https://github.com/llvm/llvm-project/pull/73017
>From cea6998f68ffca08edb8bed555df093c8fe48d1c Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at 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/test/Sema/ns_error_enum.m | 3 +-
clang/utils/TableGen/ClangAttrEmitter.cpp | 26 ++++++++++++-
5 files changed, 93 insertions(+), 18 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c2fbdfc66c540d6..a9ad6c878306ad3 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/test/Sema/ns_error_enum.m b/clang/test/Sema/ns_error_enum.m
index 0b8f8f7cced0031..29027f1a54f03fb 100644
--- a/clang/test/Sema/ns_error_enum.m
+++ b/clang/test/Sema/ns_error_enum.m
@@ -53,7 +53,6 @@ typedef NS_ERROR_ENUM(unsigned char, MyCFTypedefErrorEnum, MyCFTypedefErrorDomai
extern char *const WrongErrorDomainType;
enum __attribute__((ns_error_domain(WrongErrorDomainType))) MyWrongErrorDomainType { MyWrongErrorDomain };
-// expected-error at -1{{domain argument 'WrongErrorDomainType' does not point to an NSString or CFString constant}}
struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructWithErrorDomain {};
// expected-error at -1{{'ns_error_domain' attribute only applies to enums}}
@@ -68,7 +67,7 @@ typedef NS_ERROR_ENUM(unsigned char, MyCFTypedefErrorEnum, MyCFTypedefErrorDomai
// expected-error at -1{{'ns_error_domain' attribute takes one argument}}
typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) {
- // expected-error at -1{{use of undeclared identifier 'InvalidDomain'}}
+ // expected-error at -1{{domain argument 'InvalidDomain' does not refer to global constant}}
MyErrFirstInvalid,
MyErrSecondInvalid,
};
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 7abb3d9d078a98cbeac2410944c8c0a0a249b31d Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at 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 a91517f8fc204013debd3db7a5376630beca1d87 Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at 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..1533eeb5099e087 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 474f4f4b21ec11a5ec165a6b44984ed3f1d7a227 Mon Sep 17 00:00:00 2001
From: Egor Zhdan <e_zhdan at 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.
More information about the cfe-commits
mailing list