https://github.com/ericastor updated https://github.com/llvm/llvm-project/pull/110334
>From 0411b2939e10ca335e84731502126145509bef2d Mon Sep 17 00:00:00 2001 From: Eric Astor <epas...@google.com> Date: Fri, 27 Sep 2024 22:35:28 +0000 Subject: [PATCH 1/3] [clang][frontend] Add support for attribute plugins for statement attributes We already have support for declaration attributes; this is just a matter of extending the plugin infrastructure to cover one more case. --- clang/docs/ClangPlugins.rst | 17 +++++--- clang/examples/Attribute/Attribute.cpp | 49 ++++++++++++++++++++++ clang/include/clang/Basic/ParsedAttrInfo.h | 10 +++++ clang/lib/Sema/SemaStmtAttr.cpp | 4 ++ clang/test/Frontend/plugin-attribute.cpp | 40 ++++++++++++++---- 5 files changed, 108 insertions(+), 12 deletions(-) diff --git a/clang/docs/ClangPlugins.rst b/clang/docs/ClangPlugins.rst index 001e66e434efb1..92e41fb5877fe8 100644 --- a/clang/docs/ClangPlugins.rst +++ b/clang/docs/ClangPlugins.rst @@ -92,11 +92,6 @@ The members of ``ParsedAttrInfo`` that a plugin attribute must define are: 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. The members of ``ParsedAttrInfo`` that may need to be defined, depending on the attribute, are: @@ -105,6 +100,18 @@ attribute, are: arguments to the attribute. * ``diagAppertainsToDecl``, which checks if the attribute has been used on the right kind of declaration and issues a diagnostic 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. + * ``diagAppertainsToStmt``, which checks if the attribute has been used on the + right kind of statement and issues a diagnostic if not. + * ``handleStmtAttribute``, which is the function that applies the attribute to + a statement. It is responsible for checking that the attribute's arguments + are valid, and typically applies the attribute by adding an ``Attr`` to the + ``Stmt``. It returns either ``AttributeApplied``, to indicate that the + attribute was successfully applied, or ``AttributeNotApplied`` if it wasn't. * ``diagLangOpts``, which checks if the attribute is permitted for the current language mode and issues a diagnostic if not. * ``existsInTarget``, which checks if the attribute is permitted for the given diff --git a/clang/examples/Attribute/Attribute.cpp b/clang/examples/Attribute/Attribute.cpp index 9d6cf9ae36c6a6..07dd19321195c8 100644 --- a/clang/examples/Attribute/Attribute.cpp +++ b/clang/examples/Attribute/Attribute.cpp @@ -94,6 +94,55 @@ struct ExampleAttrInfo : public ParsedAttrInfo { } return AttributeApplied; } + + bool diagAppertainsToStmt(Sema &S, const ParsedAttr &Attr, + const Stmt *St) const override { + // This attribute appertains to for loop statements only. + if (!isa<ForStmt>(St)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str) + << Attr << Attr.isRegularKeywordAttribute() << "for loop statements"; + return false; + } + return true; + } + + AttrHandling handleStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &Attr, + class Attr *&Result) const override { + // 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, + "'example' attribute only accepts at most three arguments"); + S.Diag(Attr.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)); + } + Result = AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(), + ArgsBuf.size(), Attr.getRange()); + } else { + // Attach an annotate attribute to the Decl. + Result = AnnotateAttr::Create(S.Context, "example", nullptr, 0, + Attr.getRange()); + } + return AttributeApplied; + } }; } // namespace diff --git a/clang/include/clang/Basic/ParsedAttrInfo.h b/clang/include/clang/Basic/ParsedAttrInfo.h index 537d8f3391d589..fab5c6f1377d27 100644 --- a/clang/include/clang/Basic/ParsedAttrInfo.h +++ b/clang/include/clang/Basic/ParsedAttrInfo.h @@ -24,6 +24,7 @@ namespace clang { +class Attr; class Decl; class LangOptions; class ParsedAttr; @@ -154,6 +155,15 @@ struct ParsedAttrInfo { const ParsedAttr &Attr) const { return NotHandled; } + /// If this ParsedAttrInfo knows how to handle this ParsedAttr applied to this + /// Stmt then do so (referencing the resulting Attr in Result) and return + /// either AttributeApplied if it was applied or AttributeNotApplied if it + /// wasn't. Otherwise return NotHandled. + virtual AttrHandling handleStmtAttribute(Sema &S, Stmt *St, + const ParsedAttr &Attr, + class Attr *&Result) const { + return NotHandled; + } static const ParsedAttrInfo &get(const AttributeCommonInfo &A); static ArrayRef<const ParsedAttrInfo *> getAllBuiltin(); diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index b9b3b4063bc383..3ebd1148e8bbfb 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -680,6 +680,10 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A, case ParsedAttr::AT_NoConvergent: return handleNoConvergentAttr(S, St, A, Range); default: + if (Attr *AT = nullptr; A.getInfo().handleStmtAttribute(S, St, A, AT) != + ParsedAttrInfo::NotHandled) { + return AT; + } // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a // declaration attribute is not written on a statement, but this code is // needed for attributes in Attr.td that do not list any subjects. diff --git a/clang/test/Frontend/plugin-attribute.cpp b/clang/test/Frontend/plugin-attribute.cpp index 1c5a2440b26888..2e9d171a0095a9 100644 --- a/clang/test/Frontend/plugin-attribute.cpp +++ b/clang/test/Frontend/plugin-attribute.cpp @@ -4,11 +4,33 @@ // REQUIRES: plugins, examples //--- good_attr.cpp // expected-no-diagnostics -void fn1a() __attribute__((example)) {} -[[example]] void fn1b() {} -[[plugin::example]] void fn1c() {} -void fn2() __attribute__((example("somestring", 1, 2.0))) {} -// CHECK-COUNT-4: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example" +void fn1a() __attribute__((example)) { + __attribute__((example)) for (int i = 0; i < 10; ++i) {} +} +[[example]] void fn1b() { + [[example]] for (int i = 0; i < 10; ++i) {} +} +[[plugin::example]] void fn1c() { + [[plugin::example]] for (int i = 0; i < 10; ++i) {} +} +void fn2() __attribute__((example("somestring", 1, 2.0))) { + __attribute__((example("abc", 3, 4.0))) for (int i = 0; i < 10; ++i) {} +} +// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "abc" +// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 3 +// CHECK: -FloatingLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'double' 4.000000e+00 +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example" // CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "somestring" // CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 1 // CHECK: -FloatingLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'double' 2.000000e+00 @@ -18,5 +40,9 @@ int var1 __attribute__((example("otherstring"))) = 1; // expected-warning {{'exa class Example { void __attribute__((example)) fn3(); // expected-error {{'example' attribute only allowed at file scope}} }; -void fn4() __attribute__((example(123))) { } // expected-error {{first argument to the 'example' attribute must be a string literal}} -void fn5() __attribute__((example("a","b", 3, 4.0))) { } // expected-error {{'example' attribute only accepts at most three arguments}} +void fn4() __attribute__((example(123))) { // expected-error {{first argument to the 'example' attribute must be a string literal}} + __attribute__((example("somestring"))) while (true); // expected-warning {{'example' attribute only applies to for loop statements}} +} +void fn5() __attribute__((example("a","b", 3, 4.0))) { // expected-error {{'example' attribute only accepts at most three arguments}} + __attribute__((example("a","b", 3, 4.0))) for (int i = 0; i < 10; ++i) {} // expected-error {{'example' attribute only accepts at most three arguments}} +} >From 2398a73022d1f6d2d669e2265e8cdb7a5e3425a9 Mon Sep 17 00:00:00 2001 From: Eric Astor <epas...@google.com> Date: Mon, 30 Sep 2024 13:20:23 +0000 Subject: [PATCH 2/3] Fix desync'd comment --- clang/examples/Attribute/Attribute.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/examples/Attribute/Attribute.cpp b/clang/examples/Attribute/Attribute.cpp index 07dd19321195c8..3b90724ad22205 100644 --- a/clang/examples/Attribute/Attribute.cpp +++ b/clang/examples/Attribute/Attribute.cpp @@ -137,7 +137,6 @@ struct ExampleAttrInfo : public ParsedAttrInfo { Result = AnnotateAttr::Create(S.Context, "example", ArgsBuf.data(), ArgsBuf.size(), Attr.getRange()); } else { - // Attach an annotate attribute to the Decl. Result = AnnotateAttr::Create(S.Context, "example", nullptr, 0, Attr.getRange()); } >From 5a5ff132a96c471c079b35851c6c067687dc28e0 Mon Sep 17 00:00:00 2001 From: Eric Astor <epas...@google.com> Date: Wed, 9 Oct 2024 17:05:13 +0000 Subject: [PATCH 3/3] Add support for template-instantiations of AnnotateAttr --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 14 ++++++++++++++ clang/test/Frontend/plugin-attribute.cpp | 20 ++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index fd51fa4afcacbf..00a5f81dbca8fe 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -1537,6 +1537,7 @@ namespace { NamedDecl *FirstQualifierInScope = nullptr, bool AllowInjectedClassName = false); + const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA); const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA); const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH); const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS, @@ -2125,6 +2126,19 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, Arg, PackIndex); } +const AnnotateAttr * +TemplateInstantiator::TransformAnnotateAttr(const AnnotateAttr *AA) { + SmallVector<Expr *> Args; + for (Expr *Arg : AA->args()) { + ExprResult Res = getDerived().TransformExpr(Arg); + if (!Res.isUsable()) + return AA; + Args.push_back(Res.get()); + } + return AnnotateAttr::CreateImplicit(getSema().Context, AA->getAnnotation(), + Args.data(), Args.size(), AA->getRange()); +} + const CXXAssumeAttr * TemplateInstantiator::TransformCXXAssumeAttr(const CXXAssumeAttr *AA) { ExprResult Res = getDerived().TransformExpr(AA->getAssumption()); diff --git a/clang/test/Frontend/plugin-attribute.cpp b/clang/test/Frontend/plugin-attribute.cpp index 2e9d171a0095a9..094ce9f5cbb85f 100644 --- a/clang/test/Frontend/plugin-attribute.cpp +++ b/clang/test/Frontend/plugin-attribute.cpp @@ -5,17 +5,21 @@ //--- good_attr.cpp // expected-no-diagnostics void fn1a() __attribute__((example)) { - __attribute__((example)) for (int i = 0; i < 10; ++i) {} + __attribute__((example)) for (int i = 0; i < 9; ++i) {} } [[example]] void fn1b() { - [[example]] for (int i = 0; i < 10; ++i) {} + [[example]] for (int i = 0; i < 9; ++i) {} } [[plugin::example]] void fn1c() { - [[plugin::example]] for (int i = 0; i < 10; ++i) {} + [[plugin::example]] for (int i = 0; i < 9; ++i) {} } void fn2() __attribute__((example("somestring", 1, 2.0))) { - __attribute__((example("abc", 3, 4.0))) for (int i = 0; i < 10; ++i) {} + __attribute__((example("abc", 3, 4.0))) for (int i = 0; i < 9; ++i) {} } +template <int N> void template_fn() __attribute__((example("template", N))) { + __attribute__((example("def", N + 1))) for (int i = 0; i < 9; ++i) {} +} +void fn3() { template_fn<5>(); } // CHECK: -AttributedStmt 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} // CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} "example" // CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example" @@ -34,6 +38,14 @@ void fn2() __attribute__((example("somestring", 1, 2.0))) { // CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "somestring" // CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 1 // CHECK: -FloatingLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'double' 2.000000e+00 +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} Implicit "example" +// CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "def" +// CHECK: -BinaryOperator 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' '+' +// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} 'int' 5 +// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 1 +// CHECK: -AnnotateAttr 0x{{[0-9a-z]+}} {{<line:[0-9]+:[0-9]+(, col:[0-9]+)?>}} "example" +// CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'const char[{{[0-9]+}}]' lvalue "template" +// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{<col:[0-9]+(, col:[0-9]+)?>}} 'int' 5 //--- bad_attr.cpp int var1 __attribute__((example("otherstring"))) = 1; // expected-warning {{'example' attribute only applies to functions}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits