196 lines · cpp
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-exception6//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 usage36// 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("-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 in59 // 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 {II, 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(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 definition179 // 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} // namespace195} // namespace clang::include_cleaner196