diff --git a/clang/docs/ClangPlugins.rst b/clang/docs/ClangPlugins.rst --- a/clang/docs/ClangPlugins.rst +++ b/clang/docs/ClangPlugins.rst @@ -81,6 +81,14 @@ // Handle the attribute return AttributeApplied; } + AttrHandling handleTypeAttribute(Sema &S, QualType Type, + TypeAttrLocation TAL, + const Declarator &D, unsigned ChunkIndex, + const ParsedAttr &Parsed, + Attr **OutAttr) const override { + // Handle the attribute + return AttributeApplied; + } }; static ParsedAttrInfoRegistry::Add Z("example_attr","example attribute description"); @@ -92,11 +100,14 @@ attribute, each of which consists of an attribute syntax and how the attribute name is spelled for that syntax. If the syntax allows a scope then the spelling must be "scope::attr" if a scope is present or "::attr" if not. - * ``handleDeclAttribute``, which is the function that applies the attribute to - a declaration. It is responsible for checking that the attribute's arguments - are valid, and typically applies the attribute by adding an ``Attr`` to the - ``Decl``. It returns either ``AttributeApplied``, to indicate that the - attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't. + * Either ``handleDeclAttribute`` or ``handleTypeAttribute`` (or both). These + functions apply the attribute to a declaration or type, respectively. They + are responsible for checking that the attribute's arguments are valid, and + typically apply the attribute by adding an ``Attr`` to the ``Decl`` or + returning an ``Attr`` that should be used to wrap the type with an + ``AttributedType``. They can return ``AttributeApplied`` to indicate that + the attribute was successfully applied, ``AttributeNotApplied`` if it wasn't, + or ``NotHandled`` if the handler doesn't know how to handle the attribute. The members of ``ParsedAttrInfo`` that may need to be defined, depending on the attribute, are: diff --git a/clang/examples/Attribute/Attribute.cpp b/clang/examples/Attribute/Attribute.cpp --- a/clang/examples/Attribute/Attribute.cpp +++ b/clang/examples/Attribute/Attribute.cpp @@ -6,8 +6,10 @@ // //===----------------------------------------------------------------------===// // -// Example clang plugin which adds an an annotation to file-scope declarations -// with the 'example' attribute. +// Example clang plugin which demonstrates custom attribute handling: +// - Adds an an annotation to file-scope declarations with the 'example_decl' +// attribute. +// - Adds an an annotation to pointer types with the 'example_type' attribute. // //===----------------------------------------------------------------------===// @@ -21,17 +23,49 @@ namespace { -struct ExampleAttrInfo : public ParsedAttrInfo { - ExampleAttrInfo() { +bool ParseArgs(Sema &S, const ParsedAttr &Attr, + SmallVector &ArgsBuf) { + // We make some rules here: + // 1. Only accept at most 3 arguments here. + // 2. The first argument must be a string literal if it exists. + if (Attr.getNumArgs() > 3) { + unsigned ID = S.getDiagnostics().getCustomDiagID( + DiagnosticsEngine::Error, + "this attribute only accepts at most three arguments"); + S.Diag(Attr.getLoc(), ID); + return false; + } + // If there are arguments, the first argument should be a string literal. + if (Attr.getNumArgs() > 0) { + auto *Arg0 = Attr.getArgAsExpr(0); + StringLiteral *Literal = dyn_cast(Arg0->IgnoreParenCasts()); + if (!Literal) { + unsigned ID = S.getDiagnostics().getCustomDiagID( + DiagnosticsEngine::Error, "first argument to this " + "attribute must be a string literal"); + S.Diag(Attr.getLoc(), ID); + return false; + } + for (unsigned i = 0; i < Attr.getNumArgs(); i++) { + ArgsBuf.push_back(Attr.getArgAsExpr(i)); + } + } + + return true; +} + +struct ExampleDeclAttrInfo : public ParsedAttrInfo { + ExampleDeclAttrInfo() { // Can take up to 15 optional arguments, to emulate accepting a variadic // number of arguments. This just illustrates how many arguments a // `ParsedAttrInfo` can hold, we will not use that much in this example. OptArgs = 15; - // GNU-style __attribute__(("example")) and C++-style [[example]] and - // [[plugin::example]] supported. - static constexpr Spelling S[] = {{ParsedAttr::AS_GNU, "example"}, - {ParsedAttr::AS_CXX11, "example"}, - {ParsedAttr::AS_CXX11, "plugin::example"}}; + // GNU-style __attribute__(("example_decl")) and C++-style [[example_decl]] + // and [[plugin::example_decl]] supported. + static constexpr Spelling S[] = { + {ParsedAttr::AS_GNU, "example_decl"}, + {ParsedAttr::AS_CXX11, "example_decl"}, + {ParsedAttr::AS_CXX11, "plugin::example_decl"}}; Spellings = S; } @@ -52,47 +86,64 @@ if (!D->getDeclContext()->isFileContext()) { unsigned ID = S.getDiagnostics().getCustomDiagID( DiagnosticsEngine::Error, - "'example' attribute only allowed at file scope"); + "'example_decl' attribute only allowed at file scope"); S.Diag(Attr.getLoc(), ID); return AttributeNotApplied; } - // We make some rules here: - // 1. Only accept at most 3 arguments here. - // 2. The first argument must be a string literal if it exists. - if (Attr.getNumArgs() > 3) { + + SmallVector ArgsBuf; + if (!ParseArgs(S, Attr, ArgsBuf)) { + return AttributeNotApplied; + } + + D->addAttr(AnnotateAttr::Create(S.Context, "example_decl", ArgsBuf.data(), + ArgsBuf.size(), Attr.getRange())); + return AttributeApplied; + } +}; + +struct ExampleTypeAttrInfo : public ParsedAttrInfo { + ExampleTypeAttrInfo() { + OptArgs = 15; + // GNU-style __attribute__(("example_decl")) and C++-style [[example_decl]] + // and [[plugin::example_decl]] supported. + static constexpr Spelling S[] = { + {ParsedAttr::AS_GNU, "example_type"}, + {ParsedAttr::AS_CXX11, "example_type"}, + {ParsedAttr::AS_CXX11, "plugin::example_type"}}; + Spellings = S; + } + + AttrHandling handleTypeAttribute(Sema &S, QualType Type, TypeAttrLocation TAL, + const Declarator &D, unsigned ChunkIndex, + const ParsedAttr &Parsed, + Attr **OutAttr) const override { + // Check if the attribute is applied to a pointer declarator. + if (TAL != clang::TAL_DeclChunk || + D.getTypeObject(ChunkIndex).Kind != DeclaratorChunk::Pointer) { unsigned ID = S.getDiagnostics().getCustomDiagID( DiagnosticsEngine::Error, - "'example' attribute only accepts at most three arguments"); - S.Diag(Attr.getLoc(), ID); + "'example_type' attribute only allowed pointers"); + S.Diag(Parsed.getLoc(), ID); + return AttributeNotApplied; } - // If there are arguments, the first argument should be a string literal. - if (Attr.getNumArgs() > 0) { - auto *Arg0 = Attr.getArgAsExpr(0); - StringLiteral *Literal = - dyn_cast(Arg0->IgnoreParenCasts()); - if (!Literal) { - unsigned ID = S.getDiagnostics().getCustomDiagID( - DiagnosticsEngine::Error, "first argument to the 'example' " - "attribute must be a string literal"); - S.Diag(Attr.getLoc(), ID); - return AttributeNotApplied; - } - SmallVector ArgsBuf; - for (unsigned i = 0; i < Attr.getNumArgs(); i++) { - ArgsBuf.push_back(Attr.getArgAsExpr(i)); - } - D->addAttr(AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(), - ArgsBuf.size(), Attr.getRange())); - } else { - // Attach an annotate attribute to the Decl. - D->addAttr(AnnotateAttr::Create(S.Context, "example", nullptr, 0, - Attr.getRange())); + + SmallVector ArgsBuf; + if (!ParseArgs(S, Parsed, ArgsBuf)) { + return AttributeNotApplied; } + + *OutAttr = clang::AnnotateTypeAttr::Create(S.Context, "example_type", + ArgsBuf.data(), ArgsBuf.size(), + Parsed.getRange()); return AttributeApplied; } }; } // namespace -static ParsedAttrInfoRegistry::Add X("example", ""); +static ParsedAttrInfoRegistry::Add + AddExampleDeclAttribute("example_decl", ""); +static ParsedAttrInfoRegistry::Add + AddExampleTypeAttribute("example_type", ""); diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -34,6 +34,7 @@ class ASTContext; class Decl; +class Declarator; class Expr; class IdentifierInfo; class LangOptions; @@ -42,6 +43,16 @@ class Stmt; class TargetInfo; +/// The location of a type attribute. +enum TypeAttrLocation { + /// The attribute is in the decl-specifier-seq. + TAL_DeclSpec, + /// The attribute is part of a DeclaratorChunk. + TAL_DeclChunk, + /// The attribute is immediately after the declaration's name. + TAL_DeclName +}; + struct ParsedAttrInfo { /// Corresponds to the Kind enum. unsigned AttrKind : 16; @@ -123,6 +134,19 @@ const ParsedAttr &Attr) const { return NotHandled; } + /// If this ParsedAttrInfo knows how to handle this ParsedAttr applied to this + /// Type then do so and return either AttributeApplied if it was applied or + /// AttributeNotApplied if it wasn't. Otherwise return NotHandled. + /// If AttributeApplied is returned, *OutAttr should be set to point to a + /// corresponding Attr. + /// If TAL is TAL_DeclChunk, ChunkIndex contains the index of the chunk in + /// the Declarator that the attribute was applied to. + virtual AttrHandling + handleTypeAttribute(Sema &S, QualType Type, TypeAttrLocation TAL, + const Declarator &D, unsigned ChunkIndex, + const ParsedAttr &Parsed, Attr **OutAttr) const { + return NotHandled; + } static const ParsedAttrInfo &get(const AttributeCommonInfo &A); static ArrayRef getAllBuiltin(); diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -27,6 +27,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/ParsedAttr.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/SemaInternal.h" @@ -350,16 +351,6 @@ toList.addAtEnd(&attr); } -/// The location of a type attribute. -enum TypeAttrLocation { - /// The attribute is in the decl-specifier-seq. - TAL_DeclSpec, - /// The attribute is part of a DeclaratorChunk. - TAL_DeclChunk, - /// The attribute is immediately after the declaration's name. - TAL_DeclName -}; - static void processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, ParsedAttributesView &attrs); @@ -8141,14 +8132,34 @@ // If this is an attribute we can handle, do so now, // otherwise, add it to the FnAttrs list for rechaining. switch (attr.getKind()) { - default: - // A [[]] attribute on a declarator chunk must appertain to a type. - if (attr.isStandardAttributeSyntax() && TAL == TAL_DeclChunk) { - state.getSema().Diag(attr.getLoc(), diag::err_attribute_not_type_attr) - << attr; + default: { + Attr *outAttr = nullptr; + unsigned chunkIndex = 0; + if (TAL == TAL_DeclChunk) { + chunkIndex = state.getCurrentChunkIndex(); + } + switch (attr.getInfo().handleTypeAttribute(state.getSema(), type, TAL, + state.getDeclarator(), + chunkIndex, attr, &outAttr)) { + case ParsedAttrInfo::AttributeApplied: + assert(outAttr != nullptr); + type = state.getAttributedType(outAttr, type, type); attr.setUsedAsTypeAttr(); + break; + case ParsedAttrInfo::AttributeNotApplied: + // Do nothing. + break; + case ParsedAttrInfo::NotHandled: + // A [[]] attribute on a declarator chunk must appertain to a type. + if (attr.isStandardAttributeSyntax() && TAL == TAL_DeclChunk) { + state.getSema().Diag(attr.getLoc(), diag::err_attribute_not_type_attr) + << attr; + attr.setUsedAsTypeAttr(); + } + break; } break; + } case ParsedAttr::UnknownAttribute: if (attr.isStandardAttributeSyntax() && TAL == TAL_DeclChunk)