diff --git a/clang/docs/ClangPlugins.rst b/clang/docs/ClangPlugins.rst --- a/clang/docs/ClangPlugins.rst +++ b/clang/docs/ClangPlugins.rst @@ -110,6 +110,9 @@ * ``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 +`_. + Putting it all together ======================= diff --git a/clang/examples/Attribute/Attribute.cpp b/clang/examples/Attribute/Attribute.cpp new file mode 100644 --- /dev/null +++ b/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(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(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 X("example", ""); diff --git a/clang/examples/Attribute/CMakeLists.txt b/clang/examples/Attribute/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/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() diff --git a/clang/examples/CMakeLists.txt b/clang/examples/CMakeLists.txt --- a/clang/examples/CMakeLists.txt +++ b/clang/examples/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(clang-interpreter) add_subdirectory(PrintFunctionNames) add_subdirectory(AnnotateFunctions) +add_subdirectory(Attribute) diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt --- a/clang/test/CMakeLists.txt +++ b/clang/test/CMakeLists.txt @@ -81,6 +81,7 @@ if (CLANG_BUILD_EXAMPLES) list(APPEND CLANG_TEST_DEPS + Attribute AnnotateFunctions clang-interpreter PrintFunctionNames diff --git a/clang/test/Frontend/plugin-attribute.cpp b/clang/test/Frontend/plugin-attribute.cpp new file mode 100644 --- /dev/null +++ b/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