mboehme created this revision.
mboehme added a reviewer: aaron.ballman.
mboehme added a project: clang.
mboehme requested review of this revision.
Herald added a subscriber: cfe-commits.

A typical use case would be to allow custom attributes for pointer types. See 
examples/Attribute/Attribute.cpp for an example.

Unfortuantely, it is hard to provide meaningful test coverage for this change.  
The existing handleDeclAttribute() covered by lit tests for
attributes because multiple declaration attributes are marked 'SimpleHandler', 
which causes ClangAttrEmitter.cpp to generate a
handleDeclAttribute() handler for them.  However, all of the existing type 
attributes need custom semantic handling, so it is not possible to simply 
implement them with 'SimpleHandler'.

The modified Attribute.cpp example does at least demonstrate that it is 
possible to use the new API without any build errors, but this is all that it 
does.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D114235

Files:
  clang/docs/ClangPlugins.rst
  clang/examples/Attribute/Attribute.cpp
  clang/include/clang/Sema/ParsedAttr.h
  clang/lib/Sema/SemaType.cpp

Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ 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)
Index: clang/include/clang/Sema/ParsedAttr.h
===================================================================
--- clang/include/clang/Sema/ParsedAttr.h
+++ 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<const ParsedAttrInfo *> getAllBuiltin();
Index: clang/examples/Attribute/Attribute.cpp
===================================================================
--- clang/examples/Attribute/Attribute.cpp
+++ 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<Expr *, 16> &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<StringLiteral>(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<Expr *, 16> 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<StringLiteral>(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<Expr *, 16> 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<Expr *, 16> 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<ExampleAttrInfo> X("example", "");
+static ParsedAttrInfoRegistry::Add<ExampleDeclAttrInfo>
+    AddExampleDeclAttribute("example_decl", "");
+static ParsedAttrInfoRegistry::Add<ExampleTypeAttrInfo>
+    AddExampleTypeAttribute("example_type", "");
Index: clang/docs/ClangPlugins.rst
===================================================================
--- clang/docs/ClangPlugins.rst
+++ 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<ExampleAttrInfo> 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:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to