1//===--- LocateSymbolTest.cpp -------------------------------------- C++-*-===//
2//
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
6//
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/LangOptions.h"
15#include "clang/Basic/SourceLocation.h"
16#include "clang/Lex/Preprocessor.h"
17#include "clang/Testing/TestAST.h"
18#include "clang/Tooling/Inclusions/StandardLibrary.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/Casting.h"
21#include "llvm/Testing/Annotations/Annotations.h"
22#include "gmock/gmock.h"
23#include "gtest/gtest.h"
24#include <tuple>
25#include <vector>
26
27namespace clang::include_cleaner {
28namespace {
29using testing::Each;
30using testing::ElementsAre;
31using testing::ElementsAreArray;
32using testing::Eq;
33using testing::Field;
34
35// A helper for building ASTs and getting decls out of it by name. Example usage
36// looks like:
37// LocateExample X("void ^foo();");
38// Decl &Foo = X.findDecl("foo");
39// X.points(); // returns all the points in annotated test input.
40struct LocateExample {
41private:
42 llvm::Annotations Target;
43 TestAST AST;
44
45public:
46 LocateExample(llvm::StringRef AnnotatedCode)
47 : Target(AnnotatedCode), AST([this] {
48 TestInputs Inputs(Target.code());
49 Inputs.ExtraArgs.push_back(x: "-std=c++17");
50 return Inputs;
51 }()) {}
52
53 const Decl &findDecl(llvm::StringRef SymbolName) {
54 struct Visitor : RecursiveASTVisitor<Visitor> {
55 llvm::StringRef NameToFind;
56 const NamedDecl *Out = nullptr;
57 bool VisitNamedDecl(const NamedDecl *ND) {
58 // Skip the templated decls, as they have the same name and matches in
59 // this file care about the outer template name.
60 if (auto *TD = ND->getDescribedTemplate())
61 ND = TD;
62 if (ND->getName() == NameToFind) {
63 EXPECT_TRUE(Out == nullptr || Out == ND->getCanonicalDecl())
64 << "Found multiple matches for " << NameToFind.str();
65 Out = llvm::cast<NamedDecl>(ND->getCanonicalDecl());
66 }
67 return true;
68 }
69 };
70 Visitor V;
71 V.NameToFind = SymbolName;
72 V.TraverseDecl(AST.context().getTranslationUnitDecl());
73 if (!V.Out)
74 ADD_FAILURE() << "Couldn't find any decls with name: " << SymbolName;
75 assert(V.Out);
76 return *V.Out;
77 }
78
79 Macro findMacro(llvm::StringRef Name) {
80 auto &PP = AST.preprocessor();
81 auto *II = PP.getIdentifierInfo(Name);
82 if (!II || !II->hasMacroDefinition()) {
83 ADD_FAILURE() << "Couldn't find any macros with name: " << Name;
84 return {};
85 }
86 auto MD = PP.getMacroDefinition(II);
87 assert(MD.getMacroInfo());
88 return {.Name: II, .Definition: MD.getMacroInfo()->getDefinitionLoc()};
89 }
90
91 std::vector<SymbolLocation> points() {
92 auto &SM = AST.sourceManager();
93 auto FID = SM.getMainFileID();
94 auto Offsets = Target.points();
95 std::vector<SymbolLocation> Results;
96 for (auto &Offset : Offsets)
97 Results.emplace_back(args: SM.getComposedLoc(FID, Offset));
98 return Results;
99 }
100
101 const LangOptions &langOpts() { return AST.preprocessor().getLangOpts(); }
102};
103
104TEST(LocateSymbol, Decl) {
105 // Looks for decl with name 'foo' and performs locateSymbol on it.
106 // Expects all the locations in the case to be returned as a location.
107 const llvm::StringLiteral Cases[] = {
108 "struct ^foo; struct ^foo {};",
109 "namespace ns { void ^foo(); void ^foo() {} }",
110 "enum class ^foo; enum class ^foo {};",
111 };
112
113 for (auto &Case : Cases) {
114 SCOPED_TRACE(Case);
115 LocateExample Test(Case);
116 EXPECT_THAT(locateSymbol(Test.findDecl("foo"), Test.langOpts()),
117 ElementsAreArray(Test.points()));
118 }
119}
120
121TEST(LocateSymbol, Stdlib) {
122 {
123 LocateExample Test("namespace std { struct vector; }");
124 EXPECT_THAT(
125 locateSymbol(Test.findDecl("vector"), Test.langOpts()),
126 ElementsAre(*tooling::stdlib::Symbol::named("std::", "vector")));
127 }
128 {
129 LocateExample Test("#define assert(x)\nvoid foo() { assert(true); }");
130 EXPECT_THAT(locateSymbol(Test.findMacro("assert"), Test.langOpts()),
131 ElementsAre(*tooling::stdlib::Symbol::named("", "assert")));
132 }
133}
134
135TEST(LocateSymbol, Macros) {
136 // Make sure we preserve the last one.
137 LocateExample Test("#define FOO\n#undef FOO\n#define ^FOO");
138 EXPECT_THAT(locateSymbol(Test.findMacro("FOO"), Test.langOpts()),
139 ElementsAreArray(Test.points()));
140}
141
142MATCHER_P2(HintedSymbol, Symbol, Hint, "") {
143 return std::tie(arg.Hint, arg) == std::tie(Hint, Symbol);
144}
145TEST(LocateSymbol, CompleteSymbolHint) {
146 {
147 // stdlib symbols are always complete.
148 LocateExample Test("namespace std { struct vector; }");
149 EXPECT_THAT(locateSymbol(Test.findDecl("vector"), Test.langOpts()),
150 ElementsAre(HintedSymbol(
151 *tooling::stdlib::Symbol::named("std::", "vector"),
152 Hints::CompleteSymbol)));
153 }
154 {
155 // macros are always complete.
156 LocateExample Test("#define ^FOO");
157 EXPECT_THAT(locateSymbol(Test.findMacro("FOO"), Test.langOpts()),
158 ElementsAre(HintedSymbol(Test.points().front(),
159 Hints::CompleteSymbol)));
160 }
161 {
162 // Completeness is only absent in cases that matters.
163 const llvm::StringLiteral Cases[] = {
164 "struct ^foo; struct ^foo {};",
165 "template <typename> struct ^foo; template <typename> struct ^foo {};",
166 "template <typename> void ^foo(); template <typename> void ^foo() {};",
167 };
168 for (auto &Case : Cases) {
169 SCOPED_TRACE(Case);
170 LocateExample Test(Case);
171 EXPECT_THAT(locateSymbol(Test.findDecl("foo"), Test.langOpts()),
172 ElementsAre(HintedSymbol(Test.points().front(), Hints::None),
173 HintedSymbol(Test.points().back(),
174 Hints::CompleteSymbol)));
175 }
176 }
177 {
178 // All declarations should be marked as complete in cases that a definition
179 // is not usually needed.
180 const llvm::StringLiteral Cases[] = {
181 "void foo(); void foo() {}",
182 "extern int foo; int foo;",
183 };
184 for (auto &Case : Cases) {
185 SCOPED_TRACE(Case);
186 LocateExample Test(Case);
187 EXPECT_THAT(locateSymbol(Test.findDecl("foo"), Test.langOpts()),
188 Each(Field(&Hinted<SymbolLocation>::Hint,
189 Eq(Hints::CompleteSymbol))));
190 }
191 }
192}
193
194} // namespace
195} // namespace clang::include_cleaner
196

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/include-cleaner/unittests/LocateSymbolTest.cpp