xref: /llvm-project/clang-tools-extra/include-cleaner/unittests/LocateSymbolTest.cpp (revision ec6c3448d31056db5d63d7aed3e9f207edb49321)
1f82f5b05SKadir Cetinkaya //===--- LocateSymbolTest.cpp -------------------------------------- C++-*-===//
2f82f5b05SKadir Cetinkaya //
3f82f5b05SKadir Cetinkaya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f82f5b05SKadir Cetinkaya // See https://llvm.org/LICENSE.txt for license information.
5f82f5b05SKadir Cetinkaya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f82f5b05SKadir Cetinkaya //
7f82f5b05SKadir Cetinkaya //===----------------------------------------------------------------------===//
8f82f5b05SKadir Cetinkaya #include "AnalysisInternal.h"
9749c6a70SKadir Cetinkaya #include "TypesInternal.h"
10f82f5b05SKadir Cetinkaya #include "clang-include-cleaner/Types.h"
11f82f5b05SKadir Cetinkaya #include "clang/AST/Decl.h"
12f82f5b05SKadir Cetinkaya #include "clang/AST/DeclBase.h"
134764ee60SKadir Cetinkaya #include "clang/AST/RecursiveASTVisitor.h"
14*ec6c3448Skadir çetinkaya #include "clang/Basic/LangOptions.h"
15f82f5b05SKadir Cetinkaya #include "clang/Basic/SourceLocation.h"
16f82f5b05SKadir Cetinkaya #include "clang/Lex/Preprocessor.h"
17f82f5b05SKadir Cetinkaya #include "clang/Testing/TestAST.h"
18f82f5b05SKadir Cetinkaya #include "clang/Tooling/Inclusions/StandardLibrary.h"
19f82f5b05SKadir Cetinkaya #include "llvm/ADT/StringRef.h"
209b5a934dSKadir Cetinkaya #include "llvm/Support/Casting.h"
213432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h"
22f82f5b05SKadir Cetinkaya #include "gmock/gmock.h"
23f82f5b05SKadir Cetinkaya #include "gtest/gtest.h"
24749c6a70SKadir Cetinkaya #include <tuple>
25f82f5b05SKadir Cetinkaya #include <vector>
26f82f5b05SKadir Cetinkaya 
27f82f5b05SKadir Cetinkaya namespace clang::include_cleaner {
28f82f5b05SKadir Cetinkaya namespace {
29749c6a70SKadir Cetinkaya using testing::Each;
30f82f5b05SKadir Cetinkaya using testing::ElementsAre;
31f82f5b05SKadir Cetinkaya using testing::ElementsAreArray;
32749c6a70SKadir Cetinkaya using testing::Eq;
33749c6a70SKadir Cetinkaya using testing::Field;
34f82f5b05SKadir Cetinkaya 
35f82f5b05SKadir Cetinkaya // A helper for building ASTs and getting decls out of it by name. Example usage
36f82f5b05SKadir Cetinkaya // looks like:
37f82f5b05SKadir Cetinkaya //   LocateExample X("void ^foo();");
38f82f5b05SKadir Cetinkaya //   Decl &Foo = X.findDecl("foo");
39f82f5b05SKadir Cetinkaya //   X.points(); // returns all the points in annotated test input.
40f82f5b05SKadir Cetinkaya struct LocateExample {
41f82f5b05SKadir Cetinkaya private:
42f82f5b05SKadir Cetinkaya   llvm::Annotations Target;
43f82f5b05SKadir Cetinkaya   TestAST AST;
44f82f5b05SKadir Cetinkaya 
45f82f5b05SKadir Cetinkaya public:
46f82f5b05SKadir Cetinkaya   LocateExample(llvm::StringRef AnnotatedCode)
47f82f5b05SKadir Cetinkaya       : Target(AnnotatedCode), AST([this] {
48f82f5b05SKadir Cetinkaya           TestInputs Inputs(Target.code());
49f82f5b05SKadir Cetinkaya           Inputs.ExtraArgs.push_back("-std=c++17");
50f82f5b05SKadir Cetinkaya           return Inputs;
51f82f5b05SKadir Cetinkaya         }()) {}
52f82f5b05SKadir Cetinkaya 
53f82f5b05SKadir Cetinkaya   const Decl &findDecl(llvm::StringRef SymbolName) {
544764ee60SKadir Cetinkaya     struct Visitor : RecursiveASTVisitor<Visitor> {
554764ee60SKadir Cetinkaya       llvm::StringRef NameToFind;
564764ee60SKadir Cetinkaya       const NamedDecl *Out = nullptr;
574764ee60SKadir Cetinkaya       bool VisitNamedDecl(const NamedDecl *ND) {
58749c6a70SKadir Cetinkaya         // Skip the templated decls, as they have the same name and matches in
59749c6a70SKadir Cetinkaya         // this file care about the outer template name.
60749c6a70SKadir Cetinkaya         if (auto *TD = ND->getDescribedTemplate())
61749c6a70SKadir Cetinkaya           ND = TD;
624764ee60SKadir Cetinkaya         if (ND->getName() == NameToFind) {
634764ee60SKadir Cetinkaya           EXPECT_TRUE(Out == nullptr || Out == ND->getCanonicalDecl())
649b5a934dSKadir Cetinkaya               << "Found multiple matches for " << NameToFind.str();
659b5a934dSKadir Cetinkaya           Out = llvm::cast<NamedDecl>(ND->getCanonicalDecl());
66f82f5b05SKadir Cetinkaya         }
674764ee60SKadir Cetinkaya         return true;
684764ee60SKadir Cetinkaya       }
694764ee60SKadir Cetinkaya     };
704764ee60SKadir Cetinkaya     Visitor V;
714764ee60SKadir Cetinkaya     V.NameToFind = SymbolName;
724764ee60SKadir Cetinkaya     V.TraverseDecl(AST.context().getTranslationUnitDecl());
734764ee60SKadir Cetinkaya     if (!V.Out)
74f82f5b05SKadir Cetinkaya       ADD_FAILURE() << "Couldn't find any decls with name: " << SymbolName;
754764ee60SKadir Cetinkaya     assert(V.Out);
764764ee60SKadir Cetinkaya     return *V.Out;
77f82f5b05SKadir Cetinkaya   }
78f82f5b05SKadir Cetinkaya 
79f82f5b05SKadir Cetinkaya   Macro findMacro(llvm::StringRef Name) {
80f82f5b05SKadir Cetinkaya     auto &PP = AST.preprocessor();
81f82f5b05SKadir Cetinkaya     auto *II = PP.getIdentifierInfo(Name);
82f82f5b05SKadir Cetinkaya     if (!II || !II->hasMacroDefinition()) {
83f82f5b05SKadir Cetinkaya       ADD_FAILURE() << "Couldn't find any macros with name: " << Name;
84f82f5b05SKadir Cetinkaya       return {};
85f82f5b05SKadir Cetinkaya     }
86f82f5b05SKadir Cetinkaya     auto MD = PP.getMacroDefinition(II);
87f82f5b05SKadir Cetinkaya     assert(MD.getMacroInfo());
88f82f5b05SKadir Cetinkaya     return {II, MD.getMacroInfo()->getDefinitionLoc()};
89f82f5b05SKadir Cetinkaya   }
90f82f5b05SKadir Cetinkaya 
91f82f5b05SKadir Cetinkaya   std::vector<SymbolLocation> points() {
92f82f5b05SKadir Cetinkaya     auto &SM = AST.sourceManager();
93f82f5b05SKadir Cetinkaya     auto FID = SM.getMainFileID();
94f82f5b05SKadir Cetinkaya     auto Offsets = Target.points();
95f82f5b05SKadir Cetinkaya     std::vector<SymbolLocation> Results;
96f82f5b05SKadir Cetinkaya     for (auto &Offset : Offsets)
97f82f5b05SKadir Cetinkaya       Results.emplace_back(SM.getComposedLoc(FID, Offset));
98f82f5b05SKadir Cetinkaya     return Results;
99f82f5b05SKadir Cetinkaya   }
100*ec6c3448Skadir çetinkaya 
101*ec6c3448Skadir çetinkaya   const LangOptions &langOpts() { return AST.preprocessor().getLangOpts(); }
102f82f5b05SKadir Cetinkaya };
103f82f5b05SKadir Cetinkaya 
104f82f5b05SKadir Cetinkaya TEST(LocateSymbol, Decl) {
105f82f5b05SKadir Cetinkaya   // Looks for decl with name 'foo' and performs locateSymbol on it.
106f82f5b05SKadir Cetinkaya   // Expects all the locations in the case to be returned as a location.
107f82f5b05SKadir Cetinkaya   const llvm::StringLiteral Cases[] = {
108f82f5b05SKadir Cetinkaya       "struct ^foo; struct ^foo {};",
109f82f5b05SKadir Cetinkaya       "namespace ns { void ^foo(); void ^foo() {} }",
110f82f5b05SKadir Cetinkaya       "enum class ^foo; enum class ^foo {};",
111f82f5b05SKadir Cetinkaya   };
112f82f5b05SKadir Cetinkaya 
113f82f5b05SKadir Cetinkaya   for (auto &Case : Cases) {
114f82f5b05SKadir Cetinkaya     SCOPED_TRACE(Case);
115f82f5b05SKadir Cetinkaya     LocateExample Test(Case);
116*ec6c3448Skadir çetinkaya     EXPECT_THAT(locateSymbol(Test.findDecl("foo"), Test.langOpts()),
117f82f5b05SKadir Cetinkaya                 ElementsAreArray(Test.points()));
118f82f5b05SKadir Cetinkaya   }
119f82f5b05SKadir Cetinkaya }
120f82f5b05SKadir Cetinkaya 
121f82f5b05SKadir Cetinkaya TEST(LocateSymbol, Stdlib) {
122961e32c5SKadir Cetinkaya   {
123f82f5b05SKadir Cetinkaya     LocateExample Test("namespace std { struct vector; }");
124961e32c5SKadir Cetinkaya     EXPECT_THAT(
125*ec6c3448Skadir çetinkaya         locateSymbol(Test.findDecl("vector"), Test.langOpts()),
126f82f5b05SKadir Cetinkaya         ElementsAre(*tooling::stdlib::Symbol::named("std::", "vector")));
127f82f5b05SKadir Cetinkaya   }
128961e32c5SKadir Cetinkaya   {
129961e32c5SKadir Cetinkaya     LocateExample Test("#define assert(x)\nvoid foo() { assert(true); }");
130*ec6c3448Skadir çetinkaya     EXPECT_THAT(locateSymbol(Test.findMacro("assert"), Test.langOpts()),
131961e32c5SKadir Cetinkaya                 ElementsAre(*tooling::stdlib::Symbol::named("", "assert")));
132961e32c5SKadir Cetinkaya   }
133961e32c5SKadir Cetinkaya }
134f82f5b05SKadir Cetinkaya 
135f82f5b05SKadir Cetinkaya TEST(LocateSymbol, Macros) {
136f82f5b05SKadir Cetinkaya   // Make sure we preserve the last one.
137f82f5b05SKadir Cetinkaya   LocateExample Test("#define FOO\n#undef FOO\n#define ^FOO");
138*ec6c3448Skadir çetinkaya   EXPECT_THAT(locateSymbol(Test.findMacro("FOO"), Test.langOpts()),
139f82f5b05SKadir Cetinkaya               ElementsAreArray(Test.points()));
140f82f5b05SKadir Cetinkaya }
141f82f5b05SKadir Cetinkaya 
142749c6a70SKadir Cetinkaya MATCHER_P2(HintedSymbol, Symbol, Hint, "") {
143749c6a70SKadir Cetinkaya   return std::tie(arg.Hint, arg) == std::tie(Hint, Symbol);
144749c6a70SKadir Cetinkaya }
145749c6a70SKadir Cetinkaya TEST(LocateSymbol, CompleteSymbolHint) {
146749c6a70SKadir Cetinkaya   {
147749c6a70SKadir Cetinkaya     // stdlib symbols are always complete.
148749c6a70SKadir Cetinkaya     LocateExample Test("namespace std { struct vector; }");
149*ec6c3448Skadir çetinkaya     EXPECT_THAT(locateSymbol(Test.findDecl("vector"), Test.langOpts()),
150749c6a70SKadir Cetinkaya                 ElementsAre(HintedSymbol(
151749c6a70SKadir Cetinkaya                     *tooling::stdlib::Symbol::named("std::", "vector"),
152749c6a70SKadir Cetinkaya                     Hints::CompleteSymbol)));
153749c6a70SKadir Cetinkaya   }
154749c6a70SKadir Cetinkaya   {
155749c6a70SKadir Cetinkaya     // macros are always complete.
156749c6a70SKadir Cetinkaya     LocateExample Test("#define ^FOO");
157*ec6c3448Skadir çetinkaya     EXPECT_THAT(locateSymbol(Test.findMacro("FOO"), Test.langOpts()),
158749c6a70SKadir Cetinkaya                 ElementsAre(HintedSymbol(Test.points().front(),
159749c6a70SKadir Cetinkaya                                          Hints::CompleteSymbol)));
160749c6a70SKadir Cetinkaya   }
161749c6a70SKadir Cetinkaya   {
162749c6a70SKadir Cetinkaya     // Completeness is only absent in cases that matters.
163749c6a70SKadir Cetinkaya     const llvm::StringLiteral Cases[] = {
164749c6a70SKadir Cetinkaya         "struct ^foo; struct ^foo {};",
165749c6a70SKadir Cetinkaya         "template <typename> struct ^foo; template <typename> struct ^foo {};",
166749c6a70SKadir Cetinkaya         "template <typename> void ^foo(); template <typename> void ^foo() {};",
167749c6a70SKadir Cetinkaya     };
168749c6a70SKadir Cetinkaya     for (auto &Case : Cases) {
169749c6a70SKadir Cetinkaya       SCOPED_TRACE(Case);
170749c6a70SKadir Cetinkaya       LocateExample Test(Case);
171*ec6c3448Skadir çetinkaya       EXPECT_THAT(locateSymbol(Test.findDecl("foo"), Test.langOpts()),
172749c6a70SKadir Cetinkaya                   ElementsAre(HintedSymbol(Test.points().front(), Hints::None),
173749c6a70SKadir Cetinkaya                               HintedSymbol(Test.points().back(),
174749c6a70SKadir Cetinkaya                                            Hints::CompleteSymbol)));
175749c6a70SKadir Cetinkaya     }
176749c6a70SKadir Cetinkaya   }
177749c6a70SKadir Cetinkaya   {
178749c6a70SKadir Cetinkaya     // All declarations should be marked as complete in cases that a definition
179749c6a70SKadir Cetinkaya     // is not usually needed.
180749c6a70SKadir Cetinkaya     const llvm::StringLiteral Cases[] = {
181749c6a70SKadir Cetinkaya         "void foo(); void foo() {}",
182749c6a70SKadir Cetinkaya         "extern int foo; int foo;",
183749c6a70SKadir Cetinkaya     };
184749c6a70SKadir Cetinkaya     for (auto &Case : Cases) {
185749c6a70SKadir Cetinkaya       SCOPED_TRACE(Case);
186749c6a70SKadir Cetinkaya       LocateExample Test(Case);
187*ec6c3448Skadir çetinkaya       EXPECT_THAT(locateSymbol(Test.findDecl("foo"), Test.langOpts()),
188749c6a70SKadir Cetinkaya                   Each(Field(&Hinted<SymbolLocation>::Hint,
189749c6a70SKadir Cetinkaya                              Eq(Hints::CompleteSymbol))));
190749c6a70SKadir Cetinkaya     }
191749c6a70SKadir Cetinkaya   }
192749c6a70SKadir Cetinkaya }
193749c6a70SKadir Cetinkaya 
194f82f5b05SKadir Cetinkaya } // namespace
195f82f5b05SKadir Cetinkaya } // namespace clang::include_cleaner
196