1 //===--- LocateSymbolTest.cpp -------------------------------------- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
8 #include "AnalysisInternal.h"
9 #include "TypesInternal.h"
10 #include "clang-include-cleaner/Types.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/AST/DeclBase.h"
13 #include "clang/AST/RecursiveASTVisitor.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "clang/Lex/Preprocessor.h"
16 #include "clang/Testing/TestAST.h"
17 #include "clang/Tooling/Inclusions/StandardLibrary.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Casting.h"
20 #include "llvm/Testing/Annotations/Annotations.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
26 namespace clang::include_cleaner
{
29 using testing::ElementsAre
;
30 using testing::ElementsAreArray
;
34 // A helper for building ASTs and getting decls out of it by name. Example usage
36 // LocateExample X("void ^foo();");
37 // Decl &Foo = X.findDecl("foo");
38 // X.points(); // returns all the points in annotated test input.
39 struct LocateExample
{
41 llvm::Annotations Target
;
45 LocateExample(llvm::StringRef AnnotatedCode
)
46 : Target(AnnotatedCode
), AST([this] {
47 TestInputs
Inputs(Target
.code());
48 Inputs
.ExtraArgs
.push_back("-std=c++17");
52 const Decl
&findDecl(llvm::StringRef SymbolName
) {
53 struct Visitor
: RecursiveASTVisitor
<Visitor
> {
54 llvm::StringRef NameToFind
;
55 const NamedDecl
*Out
= nullptr;
56 bool VisitNamedDecl(const NamedDecl
*ND
) {
57 // Skip the templated decls, as they have the same name and matches in
58 // this file care about the outer template name.
59 if (auto *TD
= ND
->getDescribedTemplate())
61 if (ND
->getName() == NameToFind
) {
62 EXPECT_TRUE(Out
== nullptr || Out
== ND
->getCanonicalDecl())
63 << "Found multiple matches for " << NameToFind
.str();
64 Out
= llvm::cast
<NamedDecl
>(ND
->getCanonicalDecl());
70 V
.NameToFind
= SymbolName
;
71 V
.TraverseDecl(AST
.context().getTranslationUnitDecl());
73 ADD_FAILURE() << "Couldn't find any decls with name: " << SymbolName
;
78 Macro
findMacro(llvm::StringRef Name
) {
79 auto &PP
= AST
.preprocessor();
80 auto *II
= PP
.getIdentifierInfo(Name
);
81 if (!II
|| !II
->hasMacroDefinition()) {
82 ADD_FAILURE() << "Couldn't find any macros with name: " << Name
;
85 auto MD
= PP
.getMacroDefinition(II
);
86 assert(MD
.getMacroInfo());
87 return {II
, MD
.getMacroInfo()->getDefinitionLoc()};
90 std::vector
<SymbolLocation
> points() {
91 auto &SM
= AST
.sourceManager();
92 auto FID
= SM
.getMainFileID();
93 auto Offsets
= Target
.points();
94 std::vector
<SymbolLocation
> Results
;
95 for (auto &Offset
: Offsets
)
96 Results
.emplace_back(SM
.getComposedLoc(FID
, Offset
));
101 TEST(LocateSymbol
, Decl
) {
102 // Looks for decl with name 'foo' and performs locateSymbol on it.
103 // Expects all the locations in the case to be returned as a location.
104 const llvm::StringLiteral Cases
[] = {
105 "struct ^foo; struct ^foo {};",
106 "namespace ns { void ^foo(); void ^foo() {} }",
107 "enum class ^foo; enum class ^foo {};",
110 for (auto &Case
: Cases
) {
112 LocateExample
Test(Case
);
113 EXPECT_THAT(locateSymbol(Test
.findDecl("foo")),
114 ElementsAreArray(Test
.points()));
118 TEST(LocateSymbol
, Stdlib
) {
120 LocateExample
Test("namespace std { struct vector; }");
122 locateSymbol(Test
.findDecl("vector")),
123 ElementsAre(*tooling::stdlib::Symbol::named("std::", "vector")));
126 LocateExample
Test("#define assert(x)\nvoid foo() { assert(true); }");
127 EXPECT_THAT(locateSymbol(Test
.findMacro("assert")),
128 ElementsAre(*tooling::stdlib::Symbol::named("", "assert")));
132 TEST(LocateSymbol
, Macros
) {
133 // Make sure we preserve the last one.
134 LocateExample
Test("#define FOO\n#undef FOO\n#define ^FOO");
135 EXPECT_THAT(locateSymbol(Test
.findMacro("FOO")),
136 ElementsAreArray(Test
.points()));
139 MATCHER_P2(HintedSymbol
, Symbol
, Hint
, "") {
140 return std::tie(arg
.Hint
, arg
) == std::tie(Hint
, Symbol
);
142 TEST(LocateSymbol
, CompleteSymbolHint
) {
144 // stdlib symbols are always complete.
145 LocateExample
Test("namespace std { struct vector; }");
146 EXPECT_THAT(locateSymbol(Test
.findDecl("vector")),
147 ElementsAre(HintedSymbol(
148 *tooling::stdlib::Symbol::named("std::", "vector"),
149 Hints::CompleteSymbol
)));
152 // macros are always complete.
153 LocateExample
Test("#define ^FOO");
154 EXPECT_THAT(locateSymbol(Test
.findMacro("FOO")),
155 ElementsAre(HintedSymbol(Test
.points().front(),
156 Hints::CompleteSymbol
)));
159 // Completeness is only absent in cases that matters.
160 const llvm::StringLiteral Cases
[] = {
161 "struct ^foo; struct ^foo {};",
162 "template <typename> struct ^foo; template <typename> struct ^foo {};",
163 "template <typename> void ^foo(); template <typename> void ^foo() {};",
165 for (auto &Case
: Cases
) {
167 LocateExample
Test(Case
);
168 EXPECT_THAT(locateSymbol(Test
.findDecl("foo")),
169 ElementsAre(HintedSymbol(Test
.points().front(), Hints::None
),
170 HintedSymbol(Test
.points().back(),
171 Hints::CompleteSymbol
)));
175 // All declarations should be marked as complete in cases that a definition
176 // is not usually needed.
177 const llvm::StringLiteral Cases
[] = {
178 "void foo(); void foo() {}",
179 "extern int foo; int foo;",
181 for (auto &Case
: Cases
) {
183 LocateExample
Test(Case
);
184 EXPECT_THAT(locateSymbol(Test
.findDecl("foo")),
185 Each(Field(&Hinted
<SymbolLocation
>::Hint
,
186 Eq(Hints::CompleteSymbol
))));
192 } // namespace clang::include_cleaner