forked from OSchip/llvm-project
172 lines
5.9 KiB
C++
172 lines
5.9 KiB
C++
//===- unittests/AST/AttrTests.cpp --- Attribute tests --------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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"
|
|
|
|
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(), 2u);
|
|
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(2u)).bind("int"))),
|
|
*Annotate->args_begin()[1], AST->getASTContext())
|
|
.size(),
|
|
1u);
|
|
|
|
// 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
|