Skip to content

Commit

Permalink
Add an attribute plugin example
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D31343
  • Loading branch information
john-brawn-arm committed Mar 25, 2020
1 parent bc3f171 commit 3f03c12
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 0 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ClangPlugins.rst
Expand Up @@ -110,6 +110,9 @@ attribute, are:
* ``existsInTarget``, which checks if the attribute is permitted for the given
target.

To see a working example of an attribute plugin, see `the Attribute.cpp example
<https://github.com/llvm/llvm-project/blob/master/clang/examples/Attribute/Attribute.cpp>`_.

Putting it all together
=======================

Expand Down
80 changes: 80 additions & 0 deletions clang/examples/Attribute/Attribute.cpp
@@ -0,0 +1,80 @@
//===- Attribute.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 adds an an annotation to file-scope declarations
// with the 'example' attribute.
//
//===----------------------------------------------------------------------===//

#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/IR/Attributes.h"
using namespace clang;

namespace {

struct ExampleAttrInfo : public ParsedAttrInfo {
ExampleAttrInfo() {
// Can take an optional string argument (the check that the argument
// actually is a string happens in handleDeclAttribute).
OptArgs = 1;
// GNU-style __attribute__(("example")) and C++-style [[example]] and
// [[plugin::example]] supported.
Spellings.push_back({ParsedAttr::AS_GNU, "example"});
Spellings.push_back({ParsedAttr::AS_CXX11, "example"});
Spellings.push_back({ParsedAttr::AS_CXX11, "plugin::example"});
}

bool diagAppertainsToDecl(Sema &S, const ParsedAttr &Attr,
const Decl *D) const override {
// This attribute appertains to functions only.
if (!isa<FunctionDecl>(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str)
<< Attr << "functions";
return false;
}
return true;
}

AttrHandling handleDeclAttribute(Sema &S, Decl *D,
const ParsedAttr &Attr) const override {
// Check if the decl is at file scope.
if (!D->getDeclContext()->isFileContext()) {
unsigned ID = S.getDiagnostics().getCustomDiagID(
DiagnosticsEngine::Error,
"'example' attribute only allowed at file scope");
S.Diag(Attr.getLoc(), ID);
return AttributeNotApplied;
}
// Check if we have an optional string argument.
StringRef Str = "";
if (Attr.getNumArgs() > 0) {
Expr *ArgExpr = Attr.getArgAsExpr(0);
StringLiteral *Literal =
dyn_cast<StringLiteral>(ArgExpr->IgnoreParenCasts());
if (Literal) {
Str = Literal->getString();
} else {
S.Diag(ArgExpr->getExprLoc(), diag::err_attribute_argument_type)
<< Attr.getAttrName() << AANT_ArgumentString;
return AttributeNotApplied;
}
}
// Attach an annotate attribute to the Decl.
D->addAttr(AnnotateAttr::Create(S.Context, "example(" + Str.str() + ")",
Attr.getRange()));
return AttributeApplied;
}
};

} // namespace

static ParsedAttrInfoRegistry::Add<ExampleAttrInfo> X("example", "");
11 changes: 11 additions & 0 deletions clang/examples/Attribute/CMakeLists.txt
@@ -0,0 +1,11 @@
add_llvm_library(Attribute MODULE Attribute.cpp PLUGIN_TOOL clang)

if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
target_link_libraries(AnnotateFunctions ${cmake_2_8_12_PRIVATE}
clangAST
clangBasic
clangFrontend
clangLex
LLVMSupport
)
endif()
1 change: 1 addition & 0 deletions clang/examples/CMakeLists.txt
Expand Up @@ -6,3 +6,4 @@ endif()
add_subdirectory(clang-interpreter)
add_subdirectory(PrintFunctionNames)
add_subdirectory(AnnotateFunctions)
add_subdirectory(Attribute)
1 change: 1 addition & 0 deletions clang/test/CMakeLists.txt
Expand Up @@ -81,6 +81,7 @@ endif ()

if (CLANG_BUILD_EXAMPLES)
list(APPEND CLANG_TEST_DEPS
Attribute
AnnotateFunctions
clang-interpreter
PrintFunctionNames
Expand Down
25 changes: 25 additions & 0 deletions clang/test/Frontend/plugin-attribute.cpp
@@ -0,0 +1,25 @@
// RUN: %clang -fplugin=%llvmshlibdir/Attribute%pluginext -emit-llvm -S %s -o - 2>&1 | FileCheck %s --check-prefix=ATTRIBUTE
// RUN: not %clang -fplugin=%llvmshlibdir/Attribute%pluginext -emit-llvm -DBAD_ATTRIBUTE -S %s -o - 2>&1 | FileCheck %s --check-prefix=BADATTRIBUTE
// REQUIRES: plugins, examples

void fn1a() __attribute__((example)) { }
[[example]] void fn1b() { }
[[plugin::example]] void fn1c() { }
void fn2() __attribute__((example("somestring"))) { }
// ATTRIBUTE: warning: 'example' attribute only applies to functions
int var1 __attribute__((example("otherstring"))) = 1;

// ATTRIBUTE: [[STR1_VAR:@.+]] = private unnamed_addr constant [10 x i8] c"example()\00"
// ATTRIBUTE: [[STR2_VAR:@.+]] = private unnamed_addr constant [20 x i8] c"example(somestring)\00"
// ATTRIBUTE: @llvm.global.annotations = {{.*}}@{{.*}}fn1a{{.*}}[[STR1_VAR]]{{.*}}@{{.*}}fn1b{{.*}}[[STR1_VAR]]{{.*}}@{{.*}}fn1c{{.*}}[[STR1_VAR]]{{.*}}@{{.*}}fn2{{.*}}[[STR2_VAR]]

#ifdef BAD_ATTRIBUTE
class Example {
// BADATTRIBUTE: error: 'example' attribute only allowed at file scope
void __attribute__((example)) fn3();
};
// BADATTRIBUTE: error: 'example' attribute requires a string
void fn4() __attribute__((example(123))) { }
// BADATTRIBUTE: error: 'example' attribute takes no more than 1 argument
void fn5() __attribute__((example("a","b"))) { }
#endif

0 comments on commit 3f03c12

Please sign in to comment.