[Clang] Add the annotate_type attribute

This is an analog to the `annotate` attribute but for types. The intent is to allow adding arbitrary annotations to types for use in static analysis tools.

For details, see this RFC:

https://discourse.llvm.org/t/rfc-new-attribute-annotate-type-iteration-2/61378

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D111548
This commit is contained in:
Martin Boehme 2022-06-15 08:08:10 +02:00
parent 3151fb5ef7
commit 665da187cc
14 changed files with 460 additions and 47 deletions

View File

@ -362,6 +362,9 @@ Attribute Changes in Clang
- When the ``weak`` attribute is applied to a const qualified variable clang no longer
tells the backend it is allowed to optimize based on initializer value.
- Added the ``clang::annotate_type`` attribute, which can be used to add
annotations to types (see documentation for details).
Windows Support
---------------

View File

@ -804,6 +804,14 @@ def Annotate : InheritableParamAttr {
let Documentation = [Undocumented];
}
def AnnotateType : TypeAttr {
let Spellings = [CXX11<"clang", "annotate_type">, C2x<"clang", "annotate_type">];
let Args = [StringArgument<"Annotation">, VariadicExprArgument<"Args">];
let HasCustomParsing = 1;
let AcceptsExprPack = 1;
let Documentation = [AnnotateTypeDocs];
}
def ARMInterrupt : InheritableAttr, TargetSpecificAttr<TargetARM> {
// NOTE: If you add any additional spellings, M68kInterrupt's,
// MSP430Interrupt's, MipsInterrupt's and AnyX86Interrupt's spellings

View File

@ -6487,6 +6487,40 @@ The full documentation is available here: https://docs.microsoft.com/en-us/windo
}];
}
def AnnotateTypeDocs : Documentation {
let Category = DocCatType;
let Heading = "annotate_type";
let Content = [{
This attribute is used to add annotations to types, typically for use by static
analysis tools that are not integrated into the core Clang compiler (e.g.,
Clang-Tidy checks or out-of-tree Clang-based tools). It is a counterpart to the
`annotate` attribute, which serves the same purpose, but for declarations.
The attribute takes a mandatory string literal argument specifying the
annotation category and an arbitrary number of optional arguments that provide
additional information specific to the annotation category. The optional
arguments must be constant expressions of arbitrary type.
For example:
.. code-block:: c++
int* [[clang::annotate("category1", "foo", 1)]] f(int[[clang::annotate("category2")]] *);
The attribute does not have any effect on the semantics of the type system,
neither type checking rules, nor runtime semantics. In particular:
- ``std::is_same<T, T [[clang::annotate_type("foo")]]`` is true for all types
``T``.
- It is not permissible for overloaded functions or template specializations
to differ merely by an ``annotate_type`` attribute.
- The presence of an ``annotate_type`` attribute will not affect name
mangling.
}];
}
def WeakDocs : Documentation {
let Category = DocCatDecl;
let Content = [{

View File

@ -10389,6 +10389,13 @@ public:
void AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Annot, MutableArrayRef<Expr *> Args);
/// ConstantFoldAttrArgs - Folds attribute arguments into ConstantExprs
/// (unless they are value dependent or type dependent). Returns false
/// and emits a diagnostic if one or more of the arguments could not be
/// folded into a constant.
bool ConstantFoldAttrArgs(const AttributeCommonInfo &CI,
MutableArrayRef<Expr *> Args);
/// AddLaunchBoundsAttr - Adds a launch_bounds attribute to a particular
/// declaration.
void AddLaunchBoundsAttr(Decl *D, const AttributeCommonInfo &CI,

View File

@ -1706,6 +1706,15 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
if (T->getAttrKind() == attr::AddressSpace)
return;
if (T->getAttrKind() == attr::AnnotateType) {
// FIXME: Print the attribute arguments once we have a way to retrieve these
// here. For the meantime, we just print `[[clang::annotate_type(...)]]`
// without the arguments so that we know at least that we had _some_
// annotation on the type.
OS << " [[clang::annotate_type(...)]]";
return;
}
OS << " __attribute__((";
switch (T->getAttrKind()) {
#define TYPE_ATTR(NAME)
@ -1743,6 +1752,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::UPtr:
case attr::AddressSpace:
case attr::CmseNSCall:
case attr::AnnotateType:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:

View File

@ -3176,10 +3176,23 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
if (!AttrsLastTime)
ProhibitAttributes(attrs);
else {
// Reject C++11 attributes that appertain to decl specifiers as
// we don't support any C++11 attributes that appertain to decl
// specifiers. This also conforms to what g++ 4.8 is doing.
ProhibitCXX11Attributes(attrs, diag::err_attribute_not_type_attr);
// Reject most C++11 / C2x attributes on the decl-specifier-seq, but
// allow `annotate_type` as a special case.
// FIXME: We should more generally allow type attributes to be placed
// on the decl-specifier-seq; https://reviews.llvm.org/D126061 will
// make this change.
for (const ParsedAttr &PA : attrs) {
if (!PA.isCXX11Attribute() && !PA.isC2xAttribute())
continue;
if (PA.getKind() == ParsedAttr::UnknownAttribute)
// We will warn about the unknown attribute elsewhere (in
// SemaDeclAttr.cpp)
continue;
if (PA.getKind() == ParsedAttr::AT_AnnotateType)
continue;
Diag(PA.getLoc(), diag::err_attribute_not_type_attr) << PA;
PA.setInvalid();
}
DS.takeAttributesFrom(attrs);
}

View File

@ -384,6 +384,54 @@ void Sema::ActOnPragmaPack(SourceLocation PragmaLoc, PragmaMsStackAction Action,
AlignPackStack.Act(PragmaLoc, Action, SlotLabel, Info);
}
bool Sema::ConstantFoldAttrArgs(const AttributeCommonInfo &CI,
MutableArrayRef<Expr *> Args) {
llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
for (unsigned Idx = 0; Idx < Args.size(); Idx++) {
Expr *&E = Args.begin()[Idx];
assert(E && "error are handled before");
if (E->isValueDependent() || E->isTypeDependent())
continue;
// FIXME: Use DefaultFunctionArrayLValueConversion() in place of the logic
// that adds implicit casts here.
if (E->getType()->isArrayType())
E = ImpCastExprToType(E, Context.getPointerType(E->getType()),
clang::CK_ArrayToPointerDecay)
.get();
if (E->getType()->isFunctionType())
E = ImplicitCastExpr::Create(Context,
Context.getPointerType(E->getType()),
clang::CK_FunctionToPointerDecay, E, nullptr,
VK_PRValue, FPOptionsOverride());
if (E->isLValue())
E = ImplicitCastExpr::Create(Context, E->getType().getNonReferenceType(),
clang::CK_LValueToRValue, E, nullptr,
VK_PRValue, FPOptionsOverride());
Expr::EvalResult Eval;
Notes.clear();
Eval.Diag = &Notes;
bool Result = E->EvaluateAsConstantExpr(Eval, Context);
/// Result means the expression can be folded to a constant.
/// Note.empty() means the expression is a valid constant expression in the
/// current language mode.
if (!Result || !Notes.empty()) {
Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type)
<< CI << (Idx + 1) << AANT_ArgumentConstantExpr;
for (auto &Note : Notes)
Diag(Note.first, Note.second);
return false;
}
assert(Eval.Val.hasValue());
E = ConstantExpr::Create(Context, E, Eval.Val);
}
return true;
}
void Sema::DiagnoseNonDefaultPragmaAlignPack(PragmaAlignPackDiagnoseKind Kind,
SourceLocation IncludeLoc) {
if (Kind == PragmaAlignPackDiagnoseKind::NonDefaultStateAtInclude) {

View File

@ -4147,48 +4147,10 @@ static void handleTransparentUnionAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
void Sema::AddAnnotationAttr(Decl *D, const AttributeCommonInfo &CI,
StringRef Str, MutableArrayRef<Expr *> Args) {
auto *Attr = AnnotateAttr::Create(Context, Str, Args.data(), Args.size(), CI);
llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
for (unsigned Idx = 0; Idx < Attr->args_size(); Idx++) {
Expr *&E = Attr->args_begin()[Idx];
assert(E && "error are handled before");
if (E->isValueDependent() || E->isTypeDependent())
continue;
if (E->getType()->isArrayType())
E = ImpCastExprToType(E, Context.getPointerType(E->getType()),
clang::CK_ArrayToPointerDecay)
.get();
if (E->getType()->isFunctionType())
E = ImplicitCastExpr::Create(Context,
Context.getPointerType(E->getType()),
clang::CK_FunctionToPointerDecay, E, nullptr,
VK_PRValue, FPOptionsOverride());
if (E->isLValue())
E = ImplicitCastExpr::Create(Context, E->getType().getNonReferenceType(),
clang::CK_LValueToRValue, E, nullptr,
VK_PRValue, FPOptionsOverride());
Expr::EvalResult Eval;
Notes.clear();
Eval.Diag = &Notes;
bool Result =
E->EvaluateAsConstantExpr(Eval, Context);
/// Result means the expression can be folded to a constant.
/// Note.empty() means the expression is a valid constant expression in the
/// current language mode.
if (!Result || !Notes.empty()) {
Diag(E->getBeginLoc(), diag::err_attribute_argument_n_type)
<< CI << (Idx + 1) << AANT_ArgumentConstantExpr;
for (auto &Note : Notes)
Diag(Note.first, Note.second);
return;
}
assert(Eval.Val.hasValue());
E = ConstantExpr::Create(Context, E, Eval.Val);
if (ConstantFoldAttrArgs(
CI, MutableArrayRef<Expr *>(Attr->args_begin(), Attr->args_end()))) {
D->addAttr(Attr);
}
D->addAttr(Attr);
}
static void handleAnnotateAttr(Sema &S, Decl *D, const ParsedAttr &AL) {

View File

@ -8144,6 +8144,34 @@ static void HandleMatrixTypeAttr(QualType &CurType, const ParsedAttr &Attr,
CurType = T;
}
static void HandleAnnotateTypeAttr(TypeProcessingState &State,
QualType &CurType, const ParsedAttr &PA) {
Sema &S = State.getSema();
if (PA.getNumArgs() < 1) {
S.Diag(PA.getLoc(), diag::err_attribute_too_few_arguments) << PA << 1;
return;
}
// Make sure that there is a string literal as the annotation's first
// argument.
StringRef Str;
if (!S.checkStringLiteralArgumentAttr(PA, 0, Str))
return;
llvm::SmallVector<Expr *, 4> Args;
Args.reserve(PA.getNumArgs() - 1);
for (unsigned Idx = 1; Idx < PA.getNumArgs(); Idx++) {
assert(!PA.isArgIdent(Idx));
Args.push_back(PA.getArgAsExpr(Idx));
}
if (!S.ConstantFoldAttrArgs(PA, Args))
return;
auto *AnnotateTypeAttr =
AnnotateTypeAttr::Create(S.Context, Str, Args.data(), Args.size(), PA);
CurType = State.getAttributedType(AnnotateTypeAttr, CurType, CurType);
}
static void HandleLifetimeBoundAttr(TypeProcessingState &State,
QualType &CurType,
ParsedAttr &Attr) {
@ -8206,10 +8234,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
if (!IsTypeAttr)
continue;
}
} else if (TAL != TAL_DeclChunk && !isAddressSpaceKind(attr)) {
} else if (TAL != TAL_DeclChunk && !isAddressSpaceKind(attr) &&
attr.getKind() != ParsedAttr::AT_AnnotateType) {
// Otherwise, only consider type processing for a C++11 attribute if
// it's actually been applied to a type.
// We also allow C++11 address_space and
// We also allow C++11 address_space and annotate_type and
// OpenCL language address space attributes to pass through.
continue;
}
@ -8407,6 +8436,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
attr.setUsedAsTypeAttr();
break;
}
case ParsedAttr::AT_AnnotateType: {
HandleAnnotateTypeAttr(state, type, attr);
attr.setUsedAsTypeAttr();
break;
}
}
// Handle attributes that are defined in a macro. We do not want this to be

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 %s -ast-dump -fdouble-square-bracket-attributes | FileCheck %s
// Verify that we print the [[clang::annotate_type]] attribute.
// FIXME: The arguments are currently not printed -- see also comments in
// TypePrinter.cpp.
// Need to escape the `[[` as a regex to avoid it being interpreted as a
// substitution block.
// CHECK: VarDecl {{.*}} x1 'int {{\[\[}}clang::annotate_type(...){{]]}}':'int'
int [[clang::annotate_type("bar")]] x1;
// CHECK: VarDecl {{.*}} x2 'int * {{\[\[}}clang::annotate_type(...){{]]}}':'int *'
int *[[clang::annotate_type("bar")]] x2;

View File

@ -0,0 +1,17 @@
// RUN: %clang_cc1 -triple %itanium_abi_triple -emit-llvm-only %s -emit-llvm -o - | FileCheck %s
// Test that `annotate_type` does not affect mangled names.
int *[[clang::annotate_type("foo")]] f(int *[[clang::annotate_type("foo")]],
int [[clang::annotate_type("foo")]]) {
return nullptr;
}
// CHECK: @_Z1fPii
template <class T> struct S {};
S<int *[[clang::annotate_type("foo")]]>
g(S<int *[[clang::annotate_type("foo")]]>) {
return {};
}
// CHECK: @_Z1g1SIPiE

View File

@ -0,0 +1,48 @@
// RUN: %clang_cc1 %s -fsyntax-only -fdouble-square-bracket-attributes -verify
const char *some_function();
void foo(float *[[clang::annotate_type("foo")]] a) {
int [[clang::annotate_type("bar")]] x1;
int *[[clang::annotate_type("bar")]] x2;
int *[[clang::annotate_type("bar", 1)]] x3;
int *[[clang::annotate_type("bar", 1 + 2)]] x4;
struct {} [[clang::annotate_type("foo")]] x5;
int [[clang::annotate_type("int")]] *[[clang::annotate_type("ptr")]] array[10] [[clang::annotate_type("arr")]];
typedef int [[clang::annotate_type("bar")]] my_typedef;
// GNU spelling is not supported
int __attribute__((annotate_type("bar"))) y1; // expected-warning {{unknown attribute 'annotate_type' ignored}}
int *__attribute__((annotate_type("bar"))) y2; // expected-warning {{unknown attribute 'annotate_type' ignored}}
// Various error cases
// FIXME: We would want to prohibit the attribute on the following two lines.
// However, Clang currently generally doesn't prohibit type-only C++11
// attributes on declarations. This should be fixed more generally.
[[clang::annotate_type("bar")]] int *z1;
int *z2 [[clang::annotate_type("bar")]];
[[clang::annotate_type("bar")]]; // expected-error {{'annotate_type' attribute cannot be applied to a statement}}
int *[[clang::annotate_type(1)]] z3; // expected-error {{'annotate_type' attribute requires a string}}
int *[[clang::annotate_type()]] z4; // expected-error {{'annotate_type' attribute takes at least 1 argument}}
int *[[clang::annotate_type]] z5; // expected-error {{'annotate_type' attribute takes at least 1 argument}}
int *[[clang::annotate_type(some_function())]] z6; // expected-error {{'annotate_type' attribute requires a string}}
int *[[clang::annotate_type("bar", some_function())]] z7; // expected-error {{'annotate_type' attribute requires parameter 1 to be a constant expression}} expected-note{{subexpression not valid in a constant expression}}
int *[[clang::annotate_type("bar", z7)]] z8; // expected-error {{'annotate_type' attribute requires parameter 1 to be a constant expression}} expected-note{{subexpression not valid in a constant expression}}
int *[[clang::annotate_type("bar", int)]] z9; // expected-error {{expected expression}}
}
// More error cases: Prohibit adding the attribute to declarations.
// Different declarations hit different code paths, so they need separate tests.
// FIXME: Clang currently generally doesn't prohibit type-only C++11
// attributes on declarations.
[[clang::annotate_type("bar")]] int *global;
void annotated_function([[clang::annotate_type("bar")]] int);
void g([[clang::annotate_type("bar")]] int);
struct [[clang::annotate_type("foo")]] S;
struct [[clang::annotate_type("foo")]] S{
[[clang::annotate_type("foo")]] int member;
[[clang::annotate_type("foo")]] union {
int i;
float f;
};
};

View File

@ -0,0 +1,70 @@
// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -fcxx-exceptions -verify
struct S1 {
void f() [[clang::annotate_type("foo")]];
// FIXME: We would want to prohibit the attribute in the following location.
// However, Clang currently generally doesn't prohibit type-only C++11
// attributes on declarations. This should be fixed more generally.
[[clang::annotate_type("foo")]] void g();
};
template <typename T1, typename T2> struct is_same {
static constexpr bool value = false;
};
template <typename T1> struct is_same<T1, T1> {
static constexpr bool value = true;
};
static_assert(is_same<int, int [[clang::annotate_type("foo")]]>::value);
static_assert(is_same<int [[clang::annotate_type("foo")]],
int [[clang::annotate_type("bar")]]>::value);
static_assert(is_same<int *, int *[[clang::annotate_type("foo")]]>::value);
// Cannot overload on types that only differ by `annotate_type` attribute.
void f(int) {} // expected-note {{previous definition is here}}
void f(int [[clang::annotate_type("foo")]]) {} // expected-error {{redefinition of 'f'}}
// Cannot specialize on types that only differ by `annotate_type` attribute.
template <class T> struct S2 {};
template <> struct S2<int> {}; // expected-note {{previous definition is here}}
template <>
struct S2<int [[clang::annotate_type("foo")]]> {}; // expected-error {{redefinition of 'S2<int>'}}
// Test that the attribute supports parameter pack expansion.
template <int... Is> void variadic_func_template() {
int [[clang::annotate_type("foo", Is...)]] val;
}
int f2() { variadic_func_template<1, 2, 3>(); }
// Make sure we correctly diagnose wrong number of arguments for
// [[clang::annotate_type]] inside a template argument.
template <typename Ty> void func_template();
void f3() {
func_template<int [[clang::annotate_type()]]>(); // expected-error {{'annotate_type' attribute takes at least 1 argument}}
}
// More error cases: Prohibit adding the attribute to declarations.
// Different declarations hit different code paths, so they need separate tests.
// FIXME: Clang currently generally doesn't prohibit type-only C++11
// attributes on declarations.
namespace [[clang::annotate_type("foo")]] my_namespace {}
struct [[clang::annotate_type("foo")]] S3;
struct [[clang::annotate_type("foo")]] S3{
[[clang::annotate_type("foo")]] int member;
};
void f4() {
for ([[clang::annotate_type("foo")]] int i = 0; i < 42; ++i) {}
for (; [[clang::annotate_type("foo")]] bool b = false;) {}
while ([[clang::annotate_type("foo")]] bool b = false) {}
if ([[clang::annotate_type("foo")]] bool b = false) {}
try {
} catch ([[clang::annotate_type("foo")]] int i) {
}
}
template <class T>
[[clang::annotate_type("foo")]] T var_template;
[[clang::annotate_type("foo")]] extern "C" int extern_c_func();
extern "C" [[clang::annotate_type("foo")]] int extern_c_func();

View File

@ -7,7 +7,10 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/Attr.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/AttrKinds.h"
#include "clang/Tooling/Tooling.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@ -15,10 +18,154 @@ using namespace clang;
namespace {
using clang::ast_matchers::constantExpr;
using clang::ast_matchers::equals;
using clang::ast_matchers::functionDecl;
using clang::ast_matchers::has;
using clang::ast_matchers::hasDescendant;
using clang::ast_matchers::hasName;
using clang::ast_matchers::integerLiteral;
using clang::ast_matchers::match;
using clang::ast_matchers::selectFirst;
using clang::ast_matchers::stringLiteral;
using clang::ast_matchers::varDecl;
using clang::tooling::buildASTFromCode;
using clang::tooling::buildASTFromCodeWithArgs;
TEST(Attr, Doc) {
EXPECT_THAT(Attr::getDocumentation(attr::Used).str(),
testing::HasSubstr("The compiler must emit the definition even "
"if it appears to be unused"));
}
const FunctionDecl *getFunctionNode(ASTUnit *AST, const std::string &Name) {
auto Result =
match(functionDecl(hasName(Name)).bind("fn"), AST->getASTContext());
EXPECT_EQ(Result.size(), 1u);
return Result[0].getNodeAs<FunctionDecl>("fn");
}
const VarDecl *getVariableNode(ASTUnit *AST, const std::string &Name) {
auto Result = match(varDecl(hasName(Name)).bind("var"), AST->getASTContext());
EXPECT_EQ(Result.size(), 1u);
return Result[0].getNodeAs<VarDecl>("var");
}
template <class ModifiedTypeLoc>
void AssertAnnotatedAs(TypeLoc TL, llvm::StringRef annotation,
ModifiedTypeLoc &ModifiedTL,
const AnnotateTypeAttr **AnnotateOut = nullptr) {
const auto AttributedTL = TL.getAs<AttributedTypeLoc>();
ASSERT_FALSE(AttributedTL.isNull());
ModifiedTL = AttributedTL.getModifiedLoc().getAs<ModifiedTypeLoc>();
ASSERT_TRUE(ModifiedTL);
ASSERT_NE(AttributedTL.getAttr(), nullptr);
const auto *Annotate = dyn_cast<AnnotateTypeAttr>(AttributedTL.getAttr());
ASSERT_NE(Annotate, nullptr);
EXPECT_EQ(Annotate->getAnnotation(), annotation);
if (AnnotateOut) {
*AnnotateOut = Annotate;
}
}
TEST(Attr, AnnotateType) {
// Test that the AnnotateType attribute shows up in the right places and that
// it stores its arguments correctly.
auto AST = buildASTFromCode(R"cpp(
void f(int* [[clang::annotate_type("foo", "arg1", 2)]] *,
int [[clang::annotate_type("bar")]]);
int [[clang::annotate_type("int")]] * [[clang::annotate_type("ptr")]]
array[10] [[clang::annotate_type("arr")]];
void (* [[clang::annotate_type("funcptr")]] fp)(void);
struct S { int mem; };
int [[clang::annotate_type("int")]]
S::* [[clang::annotate_type("ptr_to_mem")]] ptr_to_member = &S::mem;
)cpp");
{
const FunctionDecl *Func = getFunctionNode(AST.get(), "f");
// First parameter.
const auto PointerTL = Func->getParamDecl(0)
->getTypeSourceInfo()
->getTypeLoc()
.getAs<PointerTypeLoc>();
ASSERT_FALSE(PointerTL.isNull());
PointerTypeLoc PointerPointerTL;
const AnnotateTypeAttr *Annotate;
AssertAnnotatedAs(PointerTL.getPointeeLoc(), "foo", PointerPointerTL,
&Annotate);
EXPECT_EQ(Annotate->args_size(), 2);
const auto *StringLit = selectFirst<StringLiteral>(
"str", match(constantExpr(hasDescendant(stringLiteral().bind("str"))),
*Annotate->args_begin()[0], AST->getASTContext()));
ASSERT_NE(StringLit, nullptr);
EXPECT_EQ(StringLit->getString(), "arg1");
EXPECT_EQ(match(constantExpr(has(integerLiteral(equals(2)).bind("int"))),
*Annotate->args_begin()[1], AST->getASTContext())
.size(),
1);
// Second parameter.
BuiltinTypeLoc IntTL;
AssertAnnotatedAs(Func->getParamDecl(1)->getTypeSourceInfo()->getTypeLoc(),
"bar", IntTL);
EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
}
{
const VarDecl *Var = getVariableNode(AST.get(), "array");
ArrayTypeLoc ArrayTL;
AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "arr", ArrayTL);
PointerTypeLoc PointerTL;
AssertAnnotatedAs(ArrayTL.getElementLoc(), "ptr", PointerTL);
BuiltinTypeLoc IntTL;
AssertAnnotatedAs(PointerTL.getPointeeLoc(), "int", IntTL);
EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
}
{
const VarDecl *Var = getVariableNode(AST.get(), "fp");
PointerTypeLoc PointerTL;
AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "funcptr",
PointerTL);
ASSERT_TRUE(
PointerTL.getPointeeLoc().IgnoreParens().getAs<FunctionTypeLoc>());
}
{
const VarDecl *Var = getVariableNode(AST.get(), "ptr_to_member");
MemberPointerTypeLoc MemberPointerTL;
AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "ptr_to_mem",
MemberPointerTL);
BuiltinTypeLoc IntTL;
AssertAnnotatedAs(MemberPointerTL.getPointeeLoc(), "int", IntTL);
EXPECT_EQ(IntTL.getType(), AST->getASTContext().IntTy);
}
// Test type annotation on an `__auto_type` type in C mode.
AST = buildASTFromCodeWithArgs(R"c(
__auto_type [[clang::annotate_type("auto")]] auto_var = 1;
)c",
{"-fdouble-square-bracket-attributes"},
"input.c");
{
const VarDecl *Var = getVariableNode(AST.get(), "auto_var");
AutoTypeLoc AutoTL;
AssertAnnotatedAs(Var->getTypeSourceInfo()->getTypeLoc(), "auto", AutoTL);
}
}
} // namespace