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

Currently, user-defined attributes (e.g. those registered via Clang plugins) 
don't hold much information or allow for much to be done with them, as their 
argument tokens are discarded entirely in almost all cases.

However, since they are quite flexible with their syntax (pretty much anything 
can go in there), it would be massively helpful if plugins could be handed the 
pre-processed and parsed tokens as a listed to be consumed by third-party 
plugins and the like.

This diff creates a trailing data list for `ParsedAttr` objects and places the 
"recorded" tokens there from the lexer. I would have piggy-backed off of the 
normal backtrack system but unfortunately it doesn't call the handler for 
tokens being replayed from the backtrack cache, which in many cases we *do* 
care about. So I had to create a second recording callback.

The new plugin directory shows how this would be used from a plugin.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D99861

Files:
  clang/examples/CMakeLists.txt
  clang/examples/PrintAttributeTokens/CMakeLists.txt
  clang/examples/PrintAttributeTokens/PrintAttributeTokens.cpp
  clang/examples/PrintAttributeTokens/PrintAttributeTokens.exports
  clang/examples/PrintAttributeTokens/README.txt
  clang/include/clang/Lex/Preprocessor.h
  clang/include/clang/Sema/ParsedAttr.h
  clang/lib/Lex/Preprocessor.cpp
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Parse/ParseDeclCXX.cpp
  clang/lib/Sema/ParsedAttr.cpp
  clang/test/Frontend/plugin-print-attr-tokens.cpp

Index: clang/test/Frontend/plugin-print-attr-tokens.cpp
===================================================================
--- /dev/null
+++ clang/test/Frontend/plugin-print-attr-tokens.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang -cc1 -load %llvmshlibdir/PrintAttributeTokens%pluginext -fsyntax-only -ast-dump -verify %s
+// REQUIRES: plugins, examples
+
+// expected-no-diagnostics
+[[print_tokens(
+    the, mitochondria,
+    <is + the + powerhouse>, Of::The($cell))]] void
+fn1a() {}
+[[plugin::print_tokens("a string")]] void fn1b() {}
+[[plugin::print_tokens()]] void fn1c() {}
+[[plugin::print_tokens(some_ident)]] void fn1d() {}
+[[plugin::print_tokens(int)]] void fn1e() {}
Index: clang/lib/Sema/ParsedAttr.cpp
===================================================================
--- clang/lib/Sema/ParsedAttr.cpp
+++ clang/lib/Sema/ParsedAttr.cpp
@@ -45,10 +45,11 @@
   else if (HasParsedType)
     return totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
                             detail::TypeTagForDatatypeData, ParsedType,
-                            detail::PropertyData>(0, 0, 0, 1, 0);
+                            detail::PropertyData, Token>(0, 0, 0, 1, 0, 0);
   return totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
                           detail::TypeTagForDatatypeData, ParsedType,
-                          detail::PropertyData>(NumArgs, 0, 0, 0, 0);
+                          detail::PropertyData, Token>(NumArgs, 0, 0, 0, 0,
+                                                       NumTokens);
 }
 
 AttributeFactory::AttributeFactory() {
Index: clang/lib/Parse/ParseDeclCXX.cpp
===================================================================
--- clang/lib/Parse/ParseDeclCXX.cpp
+++ clang/lib/Parse/ParseDeclCXX.cpp
@@ -4096,13 +4096,39 @@
       LO.CPlusPlus ? ParsedAttr::AS_CXX11 : ParsedAttr::AS_C2x;
 
   // If the attribute isn't known, we will not attempt to parse any
-  // arguments.
+  // arguments. Instead, we just record the tokens and add the attribute
+  // directly. The recording happens here because this is the only place
+  // where user-defined (via plugins) attributes are parsed, and thus
+  // they care about the token stream directly.
   if (!hasAttribute(LO.CPlusPlus ? AttrSyntax::CXX : AttrSyntax::C, ScopeName,
                     AttrName, getTargetInfo(), getLangOpts())) {
-    // Eat the left paren, then skip to the ending right paren.
+    // Begin recording session.
+    SmallVector<Token> RecordedTokens;
+    assert(!PP.hasTokenRecorder());
+    PP.setTokenRecorder(
+        [&RecordedTokens](const Token &Tok) { RecordedTokens.push_back(Tok); });
+
+    // Eat the left paren.
     ConsumeParen();
+
+    // skip to the ending right paren.
     SkipUntil(tok::r_paren);
-    return false;
+
+    // End recording session.
+    PP.setTokenRecorder(nullptr);
+
+    // Add new attribute with the token list.
+    // We assert that we have at least one token,
+    // since we have to ignore the final r_paren.
+    assert(RecordedTokens.size() > 0);
+    Attrs.addNew(
+        AttrName,
+        SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrNameLoc, AttrNameLoc),
+        ScopeName, ScopeLoc, nullptr, 0,
+        getLangOpts().CPlusPlus ? ParsedAttr::AS_CXX11 : ParsedAttr::AS_C2x,
+        RecordedTokens.data(), RecordedTokens.size() - 2);
+
+    return true;
   }
 
   if (ScopeName && (ScopeName->isStr("gnu") || ScopeName->isStr("__gnu__"))) {
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ clang/lib/Parse/ParseDecl.cpp
@@ -2828,7 +2828,7 @@
   ArgsVector ArgExprs;
   ArgExprs.push_back(ArgExpr.get());
   Attrs.addNew(KWName, KWLoc, nullptr, KWLoc, ArgExprs.data(), 1,
-               ParsedAttr::AS_Keyword, EllipsisLoc);
+               ParsedAttr::AS_Keyword, nullptr, 0, EllipsisLoc);
 }
 
 ExprResult Parser::ParseExtIntegerArgument() {
Index: clang/lib/Lex/Preprocessor.cpp
===================================================================
--- clang/lib/Lex/Preprocessor.cpp
+++ clang/lib/Lex/Preprocessor.cpp
@@ -970,6 +970,9 @@
     if (OnToken)
       OnToken(Result);
   }
+
+  if (OnRecordedToken)
+    OnRecordedToken(Result);
 }
 
 /// Lex a header-name token (including one formed from header-name-tokens if
Index: clang/include/clang/Sema/ParsedAttr.h
===================================================================
--- clang/include/clang/Sema/ParsedAttr.h
+++ clang/include/clang/Sema/ParsedAttr.h
@@ -18,6 +18,7 @@
 #include "clang/Basic/AttributeCommonInfo.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Token.h"
 #include "clang/Sema/Ownership.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/SmallVector.h"
@@ -201,12 +202,14 @@
 ///
 class ParsedAttr final
     : public AttributeCommonInfo,
-      private llvm::TrailingObjects<
-          ParsedAttr, ArgsUnion, detail::AvailabilityData,
-          detail::TypeTagForDatatypeData, ParsedType, detail::PropertyData> {
+      private llvm::TrailingObjects<ParsedAttr, ArgsUnion,
+                                    detail::AvailabilityData,
+                                    detail::TypeTagForDatatypeData, ParsedType,
+                                    detail::PropertyData, Token> {
   friend TrailingObjects;
 
   size_t numTrailingObjects(OverloadToken<ArgsUnion>) const { return NumArgs; }
+  size_t numTrailingObjects(OverloadToken<Token>) const { return NumTokens; }
   size_t numTrailingObjects(OverloadToken<detail::AvailabilityData>) const {
     return IsAvailability;
   }
@@ -230,6 +233,10 @@
   /// The expressions themselves are stored after the object.
   unsigned NumArgs : 16;
 
+  /// The number of tokens within the argument.
+  /// The tokens themselves are stored after the object.
+  unsigned NumTokens : 32;
+
   /// True if already diagnosed as invalid.
   mutable unsigned Invalid : 1;
 
@@ -273,6 +280,9 @@
     return getTrailingObjects<ArgsUnion>();
   }
 
+  Token *getTokensBuffer() { return getTrailingObjects<Token>(); }
+  Token const *getTokensBuffer() const { return getTrailingObjects<Token>(); }
+
   detail::AvailabilityData *getAvailabilityData() {
     return getTrailingObjects<detail::AvailabilityData>();
   }
@@ -288,16 +298,19 @@
   ParsedAttr(IdentifierInfo *attrName, SourceRange attrRange,
              IdentifierInfo *scopeName, SourceLocation scopeLoc,
              ArgsUnion *args, unsigned numArgs, Syntax syntaxUsed,
-             SourceLocation ellipsisLoc)
+             Token *tokens = nullptr, unsigned numTokens = 0,
+             SourceLocation ellipsisLoc = SourceLocation())
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc,
                             syntaxUsed),
-        EllipsisLoc(ellipsisLoc), NumArgs(numArgs), Invalid(false),
-        UsedAsTypeAttr(false), IsAvailability(false),
+        EllipsisLoc(ellipsisLoc), NumArgs(numArgs), NumTokens(numTokens),
+        Invalid(false), UsedAsTypeAttr(false), IsAvailability(false),
         IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
         HasProcessingCache(false), IsPragmaClangAttribute(false),
         Info(ParsedAttrInfo::get(*this)) {
     if (numArgs)
       memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
+    if (numTokens)
+      memcpy(getTokensBuffer(), tokens, numTokens * sizeof(Token));
   }
 
   /// Constructor for availability attributes.
@@ -310,11 +323,11 @@
              const Expr *replacementExpr)
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc,
                             syntaxUsed),
-        NumArgs(1), Invalid(false), UsedAsTypeAttr(false), IsAvailability(true),
-        IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
-        HasProcessingCache(false), IsPragmaClangAttribute(false),
-        UnavailableLoc(unavailable), MessageExpr(messageExpr),
-        Info(ParsedAttrInfo::get(*this)) {
+        NumArgs(1), NumTokens(0), Invalid(false), UsedAsTypeAttr(false),
+        IsAvailability(true), IsTypeTagForDatatype(false), IsProperty(false),
+        HasParsedType(false), HasProcessingCache(false),
+        IsPragmaClangAttribute(false), UnavailableLoc(unavailable),
+        MessageExpr(messageExpr), Info(ParsedAttrInfo::get(*this)) {
     ArgsUnion PVal(Parm);
     memcpy(getArgsBuffer(), &PVal, sizeof(ArgsUnion));
     new (getAvailabilityData()) detail::AvailabilityData(
@@ -328,7 +341,7 @@
              Syntax syntaxUsed)
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc,
                             syntaxUsed),
-        NumArgs(3), Invalid(false), UsedAsTypeAttr(false),
+        NumArgs(3), NumTokens(0), Invalid(false), UsedAsTypeAttr(false),
         IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
         HasParsedType(false), HasProcessingCache(false),
         IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
@@ -345,7 +358,7 @@
              bool layoutCompatible, bool mustBeNull, Syntax syntaxUsed)
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc,
                             syntaxUsed),
-        NumArgs(1), Invalid(false), UsedAsTypeAttr(false),
+        NumArgs(1), NumTokens(0), Invalid(false), UsedAsTypeAttr(false),
         IsAvailability(false), IsTypeTagForDatatype(true), IsProperty(false),
         HasParsedType(false), HasProcessingCache(false),
         IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
@@ -363,7 +376,7 @@
              ParsedType typeArg, Syntax syntaxUsed)
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc,
                             syntaxUsed),
-        NumArgs(0), Invalid(false), UsedAsTypeAttr(false),
+        NumArgs(0), NumTokens(0), Invalid(false), UsedAsTypeAttr(false),
         IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(false),
         HasParsedType(true), HasProcessingCache(false),
         IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
@@ -377,7 +390,7 @@
              Syntax syntaxUsed)
       : AttributeCommonInfo(attrName, scopeName, attrRange, scopeLoc,
                             syntaxUsed),
-        NumArgs(0), Invalid(false), UsedAsTypeAttr(false),
+        NumArgs(0), NumTokens(0), Invalid(false), UsedAsTypeAttr(false),
         IsAvailability(false), IsTypeTagForDatatype(false), IsProperty(true),
         HasParsedType(false), HasProcessingCache(false),
         IsPragmaClangAttribute(false), Info(ParsedAttrInfo::get(*this)) {
@@ -459,12 +472,21 @@
   /// getNumArgs - Return the number of actual arguments to this attribute.
   unsigned getNumArgs() const { return NumArgs; }
 
+  /// getNumTokens - Return the number of tokens recorded by this attribute.
+  unsigned getNumTokens() const { return NumTokens; }
+
   /// getArg - Return the specified argument.
   ArgsUnion getArg(unsigned Arg) const {
     assert(Arg < NumArgs && "Arg access out of range!");
     return getArgsBuffer()[Arg];
   }
 
+  /// getTokens - Return the array of Tokens.
+  const Token *getTokens() const {
+    assert(NumTokens > 0 && "No Tokens to retrieve!");
+    return getTokensBuffer();
+  }
+
   bool isArgExpr(unsigned Arg) const {
     return Arg < NumArgs && getArg(Arg).is<Expr*>();
   }
@@ -653,18 +675,15 @@
 class AttributeFactory {
 public:
   enum {
-    AvailabilityAllocSize =
-        ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
-                                     detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(1, 1, 0, 0, 0),
-    TypeTagForDatatypeAllocSize =
-        ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
-                                     detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(1, 0, 1, 0, 0),
-    PropertyAllocSize =
-        ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
-                                     detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(0, 0, 0, 0, 1),
+    AvailabilityAllocSize = ParsedAttr::totalSizeToAlloc<
+        ArgsUnion, detail::AvailabilityData, detail::TypeTagForDatatypeData,
+        ParsedType, detail::PropertyData, Token>(1, 1, 0, 0, 0, 0),
+    TypeTagForDatatypeAllocSize = ParsedAttr::totalSizeToAlloc<
+        ArgsUnion, detail::AvailabilityData, detail::TypeTagForDatatypeData,
+        ParsedType, detail::PropertyData, Token>(1, 0, 1, 0, 0, 0),
+    PropertyAllocSize = ParsedAttr::totalSizeToAlloc<
+        ArgsUnion, detail::AvailabilityData, detail::TypeTagForDatatypeData,
+        ParsedType, detail::PropertyData, Token>(0, 0, 0, 0, 1, 0),
   };
 
 private:
@@ -754,20 +773,17 @@
   ParsedAttr *create(IdentifierInfo *attrName, SourceRange attrRange,
                      IdentifierInfo *scopeName, SourceLocation scopeLoc,
                      ArgsUnion *args, unsigned numArgs,
-                     ParsedAttr::Syntax syntax,
+                     ParsedAttr::Syntax syntax, Token *tokens = nullptr,
+                     unsigned numTokens = 0,
                      SourceLocation ellipsisLoc = SourceLocation()) {
-    size_t temp =
-        ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
-                                     detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(numArgs, 0, 0, 0, 0);
-    (void)temp;
     void *memory = allocate(
         ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
                                      detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(numArgs, 0, 0, 0,
-                                                           0));
-    return add(new (memory) ParsedAttr(attrName, attrRange, scopeName, scopeLoc,
-                                       args, numArgs, syntax, ellipsisLoc));
+                                     detail::PropertyData, Token>(
+            numArgs, 0, 0, 0, 0, numTokens));
+    return add(new (memory)
+                   ParsedAttr(attrName, attrRange, scopeName, scopeLoc, args,
+                              numArgs, syntax, tokens, numTokens, ellipsisLoc));
   }
 
   ParsedAttr *create(IdentifierInfo *attrName, SourceRange attrRange,
@@ -791,7 +807,8 @@
     void *memory = allocate(
         ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
                                      detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(3, 0, 0, 0, 0));
+                                     detail::PropertyData, Token>(3, 0, 0, 0, 0,
+                                                                  0));
     return add(new (memory) ParsedAttr(attrName, attrRange, scopeName, scopeLoc,
                                        Param1, Param2, Param3, syntax));
   }
@@ -816,7 +833,8 @@
     void *memory = allocate(
         ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
                                      detail::TypeTagForDatatypeData, ParsedType,
-                                     detail::PropertyData>(0, 0, 0, 1, 0));
+                                     detail::PropertyData, Token>(0, 0, 0, 1, 0,
+                                                                  0));
     return add(new (memory) ParsedAttr(attrName, attrRange, scopeName, scopeLoc,
                                        typeArg, syntaxUsed));
   }
@@ -957,10 +975,12 @@
   ParsedAttr *addNew(IdentifierInfo *attrName, SourceRange attrRange,
                      IdentifierInfo *scopeName, SourceLocation scopeLoc,
                      ArgsUnion *args, unsigned numArgs,
-                     ParsedAttr::Syntax syntax,
+                     ParsedAttr::Syntax syntax, Token *tokens = nullptr,
+                     unsigned numTokens = 0,
                      SourceLocation ellipsisLoc = SourceLocation()) {
-    ParsedAttr *attr = pool.create(attrName, attrRange, scopeName, scopeLoc,
-                                   args, numArgs, syntax, ellipsisLoc);
+    ParsedAttr *attr =
+        pool.create(attrName, attrRange, scopeName, scopeLoc, args, numArgs,
+                    syntax, tokens, numTokens, ellipsisLoc);
     addAtEnd(attr);
     return attr;
   }
Index: clang/include/clang/Lex/Preprocessor.h
===================================================================
--- clang/include/clang/Lex/Preprocessor.h
+++ clang/include/clang/Lex/Preprocessor.h
@@ -131,6 +131,7 @@
   friend class VariadicMacroScopeGuard;
 
   llvm::unique_function<void(const clang::Token &)> OnToken;
+  llvm::unique_function<void(const clang::Token &)> OnRecordedToken;
   std::shared_ptr<PreprocessorOptions> PPOpts;
   DiagnosticsEngine        *Diags;
   LangOptions       &LangOpts;
@@ -1041,10 +1042,34 @@
   /// Register a function that would be called on each token in the final
   /// expanded token stream.
   /// This also reports annotation tokens produced by the parser.
+  ///
+  /// Note that the callback is not invoked for tokens re-emitted from the
+  /// backtrack cache. To receive callbacks for *any* tokens returned from
+  /// Lex(), use setTokenRecorder() instead.
   void setTokenWatcher(llvm::unique_function<void(const clang::Token &)> F) {
     OnToken = std::move(F);
   }
 
+  /// True if an active token watcher function has been registered with
+  /// setTokenWatcher().
+  bool hasTokenWatcher() const { return bool(OnToken); }
+
+  /// Register a function that would be called on each token returned by Lex().
+  /// This also reports annotation tokens produced by the parser.
+  ///
+  /// Note that the callback is invoked for tokens re-emitted from the backtrack
+  /// cache. Depending on how the Lexer is used between calls to
+  /// setTokenRecorder(), the callback may be called multiple times for the
+  /// same token. To avoid this and only receive fresh tokens from the
+  /// underlying lexer, use setTokenWatcher() instead.
+  void setTokenRecorder(llvm::unique_function<void(const clang::Token &)> F) {
+    OnRecordedToken = std::move(F);
+  }
+
+  /// True if an active token recorder function has been registered with
+  /// setTokenRecorder().
+  bool hasTokenRecorder() const { return bool(OnRecordedToken); }
+
   void setPreprocessToken(bool Preprocess) { PreprocessToken = Preprocess; }
 
   bool isMacroDefined(StringRef Id) {
Index: clang/examples/PrintAttributeTokens/README.txt
===================================================================
--- /dev/null
+++ clang/examples/PrintAttributeTokens/README.txt
@@ -0,0 +1,12 @@
+This is a simple example demonstrating how to use clang's facility for
+registering custom attribute processors using a plugin.
+
+Build the plugin by running `make` in this directory.
+
+Once the plugin is built, you can run it using:
+--
+Linux:
+$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintAttributeTokens.so -plugin print-attr-tokens some-input-file.c
+
+Mac:
+$ clang -cc1 -load ../../Debug+Asserts/lib/libPrintAttributeTokens.dylib -plugin print-attr-tokens some-input-file.c
Index: clang/examples/PrintAttributeTokens/PrintAttributeTokens.cpp
===================================================================
--- /dev/null
+++ clang/examples/PrintAttributeTokens/PrintAttributeTokens.cpp
@@ -0,0 +1,65 @@
+//===- PrintAttributeTokens.cpp
+//---------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Example clang plugin which prints all argument tokens from within a
+// [[print_tokens(...)]] attribute.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Lex/Token.h"
+#include "clang/Sema/ParsedAttr.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace clang;
+
+namespace {
+
+struct PrintTokensAttrInfo : public ParsedAttrInfo {
+  PrintTokensAttrInfo() {
+    // GNU-style __attribute__(("example")) and C++-style [[example]] and
+    // [[plugin::example]] supported.
+    static constexpr Spelling S[] = {
+        {ParsedAttr::AS_GNU, "print_tokens"},
+        {ParsedAttr::AS_CXX11, "print_tokens"},
+        {ParsedAttr::AS_CXX11, "plugin::print_tokens"}};
+    Spellings = S;
+  }
+
+  AttrHandling handleDeclAttribute(Sema &S, Decl *D,
+                                   const ParsedAttr &Attr) const override {
+    llvm::errs() << "PrintAttributeTokens -----------------------\n";
+    D->dump(llvm::errs());
+    llvm::errs() << "\n";
+
+    if (Attr.getNumTokens() == 0) {
+      llvm::errs() << "<no tokens>\n";
+    } else {
+      const Token *tokens = Attr.getTokens();
+      for (unsigned i = 0; i < Attr.getNumTokens(); i++) {
+        llvm::errs() << tokens[i].getName();
+
+        if (tokens[i].isLiteral()) {
+          llvm::errs() << "\t=" << tokens[i].getLiteralData();
+        }
+
+        llvm::errs() << "\n";
+      }
+    }
+
+    llvm::errs() << "\n--------------------------------------------\n";
+
+    return AttributeApplied;
+  }
+};
+
+} // namespace
+
+static ParsedAttrInfoRegistry::Add<PrintTokensAttrInfo>
+    X("print_tokens", "print tokens inside the attribute arguments");
Index: clang/examples/PrintAttributeTokens/CMakeLists.txt
===================================================================
--- /dev/null
+++ clang/examples/PrintAttributeTokens/CMakeLists.txt
@@ -0,0 +1,23 @@
+# If we don't need RTTI or EH, there's no reason to export anything
+# from the plugin.
+if( NOT MSVC ) # MSVC mangles symbols differently, and
+               # PrintAttributeTokens.export contains C++ symbols.
+  if( NOT LLVM_REQUIRES_RTTI )
+    if( NOT LLVM_REQUIRES_EH )
+      set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/PrintAttributeTokens.exports)
+    endif()
+  endif()
+endif()
+
+add_llvm_library(PrintAttributeTokens MODULE PrintAttributeTokens.cpp PLUGIN_TOOL clang)
+
+if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
+  set(LLVM_LINK_COMPONENTS
+    Support
+  )
+  clang_target_link_libraries(PrintAttributeTokens PRIVATE
+    clangAST
+    clangBasic
+    clangFrontend
+    )
+endif()
Index: clang/examples/CMakeLists.txt
===================================================================
--- clang/examples/CMakeLists.txt
+++ clang/examples/CMakeLists.txt
@@ -5,6 +5,7 @@
 
 add_subdirectory(clang-interpreter)
 add_subdirectory(PrintFunctionNames)
+add_subdirectory(PrintAttributeTokens)
 add_subdirectory(AnnotateFunctions)
 add_subdirectory(Attribute)
 add_subdirectory(CallSuperAttribute)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to