diff --git a/clang/examples/Attribute/Attribute.cpp b/clang/examples/Attribute/Attribute.cpp --- a/clang/examples/Attribute/Attribute.cpp +++ b/clang/examples/Attribute/Attribute.cpp @@ -23,9 +23,10 @@ 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; + // 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"}, @@ -39,7 +40,7 @@ // This attribute appertains to functions only. if (!isa(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str) - << Attr << "functions"; + << Attr << "functions"; return false; } return true; @@ -55,23 +56,39 @@ S.Diag(Attr.getLoc(), ID); return AttributeNotApplied; } - // Check if we have an optional string argument. - StringRef Str = ""; + // 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) { - Expr *ArgExpr = Attr.getArgAsExpr(0); + auto *Arg0 = 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; + dyn_cast(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 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())); } - // Attach an annotate attribute to the Decl. - D->addAttr(AnnotateAttr::Create(S.Context, "example(" + Str.str() + ")", - Attr.getRange())); return AttributeApplied; } }; diff --git a/clang/test/Frontend/plugin-attribute.cpp b/clang/test/Frontend/plugin-attribute.cpp --- a/clang/test/Frontend/plugin-attribute.cpp +++ b/clang/test/Frontend/plugin-attribute.cpp @@ -1,25 +1,22 @@ -// 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 +// RUN: split-file %s %t +// RUN: %clang -cc1 -load %llvmshlibdir/Attribute%pluginext -fsyntax-only -ast-dump -verify %t/good_attr.cpp | FileCheck %s +// RUN: %clang -fplugin=%llvmshlibdir/Attribute%pluginext -fsyntax-only -Xclang -verify %t/bad_attr.cpp // 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]+}} {{}} "example" +// CHECK: -StringLiteral 0x{{[0-9a-z]+}} {{}} 'const char [{{[0-9]+}}]' lvalue "somestring" +// CHECK: -IntegerLiteral 0x{{[0-9a-z]+}} {{}} 'int' 1 +// CHECK: -FloatingLiteral 0x{{[0-9a-z]+}} {{}} 'double' 2.000000e+00 -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 +//--- bad_attr.cpp +int var1 __attribute__((example("otherstring"))) = 1; // expected-warning {{'example' attribute only applies to functions}} class Example { - // BADATTRIBUTE: error: 'example' attribute only allowed at file scope - void __attribute__((example)) fn3(); + void __attribute__((example)) fn3(); // expected-error {{'example' attribute only allowed at file scope}} }; -// 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 +void fn4() __attribute__((example(123))) { } // expected-error {{'example's first argument should be a string literal}} +void fn5() __attribute__((example("a","b", 3, 4.0))) { } // expected-error {{'example' attribute only allowed at most three arguments}}