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