457 lines · cpp
1//===--- IndexTests.cpp - Test indexing actions -----------------*- 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 9#include "clang/AST/ASTConsumer.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Decl.h"12#include "clang/Basic/SourceLocation.h"13#include "clang/Basic/SourceManager.h"14#include "clang/Frontend/CompilerInstance.h"15#include "clang/Frontend/FrontendAction.h"16#include "clang/Index/IndexDataConsumer.h"17#include "clang/Index/IndexSymbol.h"18#include "clang/Index/IndexingAction.h"19#include "clang/Lex/Preprocessor.h"20#include "clang/Tooling/Tooling.h"21#include "llvm/ADT/StringRef.h"22#include "llvm/Support/VirtualFileSystem.h"23#include "gmock/gmock.h"24#include "gtest/gtest.h"25#include <memory>26 27namespace clang {28namespace index {29namespace {30struct Position {31 size_t Line = 0;32 size_t Column = 0;33 34 Position(size_t Line = 0, size_t Column = 0) : Line(Line), Column(Column) {}35 36 static Position fromSourceLocation(SourceLocation Loc,37 const SourceManager &SM) {38 auto [FID, Offset] = SM.getDecomposedSpellingLoc(Loc);39 Position P;40 P.Line = SM.getLineNumber(FID, Offset);41 P.Column = SM.getColumnNumber(FID, Offset);42 return P;43 }44};45 46bool operator==(const Position &LHS, const Position &RHS) {47 return std::tie(LHS.Line, LHS.Column) == std::tie(RHS.Line, RHS.Column);48}49 50llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Position &Pos) {51 return OS << Pos.Line << ':' << Pos.Column;52}53 54struct TestSymbol {55 std::string QName;56 Position WrittenPos;57 Position DeclPos;58 SymbolInfo SymInfo;59 SymbolRoleSet Roles;60 // FIXME: add more information.61};62 63llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TestSymbol &S) {64 return OS << S.QName << '[' << S.WrittenPos << ']' << '@' << S.DeclPos << '('65 << static_cast<unsigned>(S.SymInfo.Kind) << ')';66}67 68class Indexer : public IndexDataConsumer {69public:70 void initialize(ASTContext &Ctx) override {71 AST = &Ctx;72 IndexDataConsumer::initialize(Ctx);73 }74 75 bool handleDeclOccurrence(const Decl *D, SymbolRoleSet Roles,76 ArrayRef<SymbolRelation>, SourceLocation Loc,77 ASTNodeInfo) override {78 const auto *ND = llvm::dyn_cast<NamedDecl>(D);79 if (!ND)80 return true;81 TestSymbol S;82 S.SymInfo = getSymbolInfo(D);83 S.QName = ND->getQualifiedNameAsString();84 S.WrittenPos = Position::fromSourceLocation(Loc, AST->getSourceManager());85 S.DeclPos =86 Position::fromSourceLocation(D->getLocation(), AST->getSourceManager());87 S.Roles = Roles;88 Symbols.push_back(std::move(S));89 return true;90 }91 92 bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI,93 SymbolRoleSet Roles, SourceLocation Loc) override {94 TestSymbol S;95 S.SymInfo = getSymbolInfoForMacro(*MI);96 S.QName = std::string(Name->getName());97 S.WrittenPos = Position::fromSourceLocation(Loc, AST->getSourceManager());98 S.DeclPos = Position::fromSourceLocation(MI->getDefinitionLoc(),99 AST->getSourceManager());100 S.Roles = Roles;101 Symbols.push_back(std::move(S));102 return true;103 }104 105 std::vector<TestSymbol> Symbols;106 const ASTContext *AST = nullptr;107};108 109class IndexAction : public ASTFrontendAction {110public:111 IndexAction(std::shared_ptr<Indexer> Index,112 IndexingOptions Opts = IndexingOptions())113 : Index(std::move(Index)), Opts(Opts) {}114 115protected:116 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,117 StringRef InFile) override {118 class Consumer : public ASTConsumer {119 std::shared_ptr<Indexer> Index;120 std::shared_ptr<Preprocessor> PP;121 IndexingOptions Opts;122 123 public:124 Consumer(std::shared_ptr<Indexer> Index, std::shared_ptr<Preprocessor> PP,125 IndexingOptions Opts)126 : Index(std::move(Index)), PP(std::move(PP)), Opts(Opts) {}127 128 void HandleTranslationUnit(ASTContext &Ctx) override {129 std::vector<Decl *> DeclsToIndex(130 Ctx.getTranslationUnitDecl()->decls().begin(),131 Ctx.getTranslationUnitDecl()->decls().end());132 indexTopLevelDecls(Ctx, *PP, DeclsToIndex, *Index, Opts);133 }134 };135 return std::make_unique<Consumer>(Index, CI.getPreprocessorPtr(), Opts);136 }137 138private:139 std::shared_ptr<Indexer> Index;140 IndexingOptions Opts;141};142 143using testing::AllOf;144using testing::Contains;145using testing::Not;146using testing::UnorderedElementsAre;147 148MATCHER_P(QName, Name, "") { return arg.QName == Name; }149MATCHER_P(WrittenAt, Pos, "") { return arg.WrittenPos == Pos; }150MATCHER_P(DeclAt, Pos, "") { return arg.DeclPos == Pos; }151MATCHER_P(Kind, SymKind, "") { return arg.SymInfo.Kind == SymKind; }152MATCHER_P(HasRole, Role, "") { return arg.Roles & static_cast<unsigned>(Role); }153 154TEST(IndexTest, Simple) {155 auto Index = std::make_shared<Indexer>();156 tooling::runToolOnCode(std::make_unique<IndexAction>(Index),157 "class X {}; void f() {}");158 EXPECT_THAT(Index->Symbols, UnorderedElementsAre(QName("X"), QName("f")));159}160 161TEST(IndexTest, IndexPreprocessorMacros) {162 std::string Code = R"cpp(163 #define INDEX_MAC 1164 #define INDEX_MAC_UNDEF 1165 #undef INDEX_MAC_UNDEF166 #define INDEX_MAC_REDEF 1167 #undef INDEX_MAC_REDEF168 #define INDEX_MAC_REDEF 2169 )cpp";170 auto Index = std::make_shared<Indexer>();171 IndexingOptions Opts;172 Opts.IndexMacrosInPreprocessor = true;173 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);174 EXPECT_THAT(Index->Symbols,175 Contains(AllOf(QName("INDEX_MAC"), WrittenAt(Position(2, 13)),176 DeclAt(Position(2, 13)),177 HasRole(SymbolRole::Definition))));178 EXPECT_THAT(179 Index->Symbols,180 AllOf(Contains(AllOf(QName("INDEX_MAC_UNDEF"), WrittenAt(Position(3, 13)),181 DeclAt(Position(3, 13)),182 HasRole(SymbolRole::Definition))),183 Contains(AllOf(QName("INDEX_MAC_UNDEF"), WrittenAt(Position(4, 12)),184 DeclAt(Position(3, 13)),185 HasRole(SymbolRole::Undefinition)))));186 EXPECT_THAT(187 Index->Symbols,188 AllOf(Contains(AllOf(QName("INDEX_MAC_REDEF"), WrittenAt(Position(5, 13)),189 DeclAt(Position(5, 13)),190 HasRole(SymbolRole::Definition))),191 Contains(AllOf(QName("INDEX_MAC_REDEF"), WrittenAt(Position(6, 12)),192 DeclAt(Position(5, 13)),193 HasRole(SymbolRole::Undefinition))),194 Contains(AllOf(QName("INDEX_MAC_REDEF"), WrittenAt(Position(7, 13)),195 DeclAt(Position(7, 13)),196 HasRole(SymbolRole::Definition)))));197 198 Opts.IndexMacrosInPreprocessor = false;199 Index->Symbols.clear();200 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);201 EXPECT_THAT(Index->Symbols, UnorderedElementsAre());202}203 204TEST(IndexTest, IndexParametersInDecls) {205 std::string Code = "void foo(int bar);";206 auto Index = std::make_shared<Indexer>();207 IndexingOptions Opts;208 Opts.IndexFunctionLocals = true;209 Opts.IndexParametersInDeclarations = true;210 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);211 EXPECT_THAT(Index->Symbols, Contains(QName("bar")));212 213 Opts.IndexParametersInDeclarations = false;214 Index->Symbols.clear();215 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);216 EXPECT_THAT(Index->Symbols, Not(Contains(QName("bar"))));217}218 219TEST(IndexTest, IndexLabels) {220 std::string Code = R"cpp(221 int main() {222 goto theLabel;223 theLabel:224 return 1;225 }226 )cpp";227 auto Index = std::make_shared<Indexer>();228 IndexingOptions Opts;229 Opts.IndexFunctionLocals = true;230 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);231 EXPECT_THAT(Index->Symbols,232 Contains(AllOf(QName("theLabel"), WrittenAt(Position(3, 16)),233 DeclAt(Position(4, 11)))));234 235 Opts.IndexFunctionLocals = false;236 Index->Symbols.clear();237 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);238 EXPECT_THAT(Index->Symbols, Not(Contains(QName("theLabel"))));239}240 241TEST(IndexTest, IndexExplicitTemplateInstantiation) {242 std::string Code = R"cpp(243 template <typename T>244 struct Foo { void bar() {} };245 template <>246 struct Foo<int> { void bar() {} };247 void foo() {248 Foo<char> abc;249 Foo<int> b;250 }251 )cpp";252 auto Index = std::make_shared<Indexer>();253 IndexingOptions Opts;254 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);255 EXPECT_THAT(Index->Symbols,256 AllOf(Contains(AllOf(QName("Foo"), WrittenAt(Position(8, 7)),257 DeclAt(Position(5, 12)))),258 Contains(AllOf(QName("Foo"), WrittenAt(Position(7, 7)),259 DeclAt(Position(3, 12))))));260}261 262TEST(IndexTest, IndexTemplateInstantiationPartial) {263 std::string Code = R"cpp(264 template <typename T1, typename T2>265 struct Foo { void bar() {} };266 template <typename T>267 struct Foo<T, int> { void bar() {} };268 void foo() {269 Foo<char, char> abc;270 Foo<int, int> b;271 }272 )cpp";273 auto Index = std::make_shared<Indexer>();274 IndexingOptions Opts;275 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);276 EXPECT_THAT(Index->Symbols,277 Contains(AllOf(QName("Foo"), WrittenAt(Position(8, 7)),278 DeclAt(Position(5, 12)))));279}280 281TEST(IndexTest, IndexTypeParmDecls) {282 std::string Code = R"cpp(283 template <typename T, int I, template<typename> class C, typename NoRef>284 struct Foo {285 T t = I;286 C<int> x;287 };288 )cpp";289 auto Index = std::make_shared<Indexer>();290 IndexingOptions Opts;291 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);292 EXPECT_THAT(Index->Symbols, AllOf(Not(Contains(QName("Foo::T"))),293 Not(Contains(QName("Foo::I"))),294 Not(Contains(QName("Foo::C"))),295 Not(Contains(QName("Foo::NoRef")))));296 297 Opts.IndexTemplateParameters = true;298 Index->Symbols.clear();299 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);300 EXPECT_THAT(Index->Symbols,301 AllOf(Contains(AllOf(QName("Foo::T"),302 Kind(SymbolKind::TemplateTypeParm))),303 Contains(AllOf(QName("Foo::I"),304 Kind(SymbolKind::NonTypeTemplateParm))),305 Contains(AllOf(QName("Foo::C"),306 Kind(SymbolKind::TemplateTemplateParm))),307 Contains(QName("Foo::NoRef"))));308}309 310TEST(IndexTest, UsingDecls) {311 std::string Code = R"cpp(312 void foo(int bar);313 namespace std {314 using ::foo;315 }316 )cpp";317 auto Index = std::make_shared<Indexer>();318 IndexingOptions Opts;319 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);320 EXPECT_THAT(Index->Symbols,321 Contains(AllOf(QName("std::foo"), Kind(SymbolKind::Using))));322}323 324TEST(IndexTest, Constructors) {325 std::string Code = R"cpp(326 struct Foo {327 Foo(int);328 ~Foo();329 };330 )cpp";331 auto Index = std::make_shared<Indexer>();332 IndexingOptions Opts;333 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);334 EXPECT_THAT(335 Index->Symbols,336 UnorderedElementsAre(337 AllOf(QName("Foo"), Kind(SymbolKind::Struct),338 WrittenAt(Position(2, 12))),339 AllOf(QName("Foo::Foo"), Kind(SymbolKind::Constructor),340 WrittenAt(Position(3, 7))),341 AllOf(QName("Foo"), Kind(SymbolKind::Struct),342 HasRole(SymbolRole::NameReference), WrittenAt(Position(3, 7))),343 AllOf(QName("Foo::~Foo"), Kind(SymbolKind::Destructor),344 WrittenAt(Position(4, 7))),345 AllOf(QName("Foo"), Kind(SymbolKind::Struct),346 HasRole(SymbolRole::NameReference),347 WrittenAt(Position(4, 8)))));348}349 350TEST(IndexTest, InjectedNameClass) {351 std::string Code = R"cpp(352 template <typename T>353 class Foo {354 void f(Foo x);355 };356 )cpp";357 auto Index = std::make_shared<Indexer>();358 IndexingOptions Opts;359 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);360 EXPECT_THAT(Index->Symbols,361 UnorderedElementsAre(AllOf(QName("Foo"), Kind(SymbolKind::Class),362 WrittenAt(Position(3, 11))),363 AllOf(QName("Foo::f"),364 Kind(SymbolKind::InstanceMethod),365 WrittenAt(Position(4, 12))),366 AllOf(QName("Foo"), Kind(SymbolKind::Class),367 HasRole(SymbolRole::Reference),368 WrittenAt(Position(4, 14)))));369}370 371TEST(IndexTest, VisitDefaultArgs) {372 std::string Code = R"cpp(373 int var = 0;374 void f(int s = var) {}375 )cpp";376 auto Index = std::make_shared<Indexer>();377 IndexingOptions Opts;378 Opts.IndexFunctionLocals = true;379 Opts.IndexParametersInDeclarations = true;380 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);381 EXPECT_THAT(Index->Symbols,382 Contains(AllOf(QName("var"), HasRole(SymbolRole::Reference),383 WrittenAt(Position(3, 20)))));384}385 386TEST(IndexTest, RelationBaseOf) {387 std::string Code = R"cpp(388 class A {};389 template <typename> class B {};390 class C : B<A> {};391 )cpp";392 auto Index = std::make_shared<Indexer>();393 tooling::runToolOnCode(std::make_unique<IndexAction>(Index), Code);394 // A should not be the base of anything.395 EXPECT_THAT(Index->Symbols,396 Contains(AllOf(QName("A"), HasRole(SymbolRole::Reference),397 Not(HasRole(SymbolRole::RelationBaseOf)))));398}399 400TEST(IndexTest, EnumBase) {401 std::string Code = R"cpp(402 typedef int MyTypedef;403 enum Foo : MyTypedef;404 enum Foo : MyTypedef {};405 )cpp";406 auto Index = std::make_shared<Indexer>();407 tooling::runToolOnCode(std::make_unique<IndexAction>(Index), Code);408 EXPECT_THAT(409 Index->Symbols,410 AllOf(Contains(AllOf(QName("MyTypedef"), HasRole(SymbolRole::Reference),411 WrittenAt(Position(3, 16)))),412 Contains(AllOf(QName("MyTypedef"), HasRole(SymbolRole::Reference),413 WrittenAt(Position(4, 16))))));414}415 416TEST(IndexTest, NonTypeTemplateParameter) {417 std::string Code = R"cpp(418 enum class Foobar { foo };419 template <Foobar f>420 constexpr void func() {}421 )cpp";422 auto Index = std::make_shared<Indexer>();423 tooling::runToolOnCode(std::make_unique<IndexAction>(Index), Code);424 EXPECT_THAT(Index->Symbols,425 Contains(AllOf(QName("Foobar"), HasRole(SymbolRole::Reference),426 WrittenAt(Position(3, 15)))));427}428 429TEST(IndexTest, ReadWriteRoles) {430 std::string Code = R"cpp(431 int main() {432 int foo = 0;433 foo = 2;434 foo += 1;435 int bar = foo;436 }437 )cpp";438 auto Index = std::make_shared<Indexer>();439 IndexingOptions Opts;440 Opts.IndexFunctionLocals = true;441 tooling::runToolOnCode(std::make_unique<IndexAction>(Index, Opts), Code);442 EXPECT_THAT(443 Index->Symbols,444 AllOf(Contains(AllOf(QName("foo"), HasRole(SymbolRole::Write),445 WrittenAt(Position(4, 7)))),446 Contains(AllOf(QName("foo"),447 HasRole(static_cast<unsigned>(SymbolRole::Read) |448 static_cast<unsigned>(SymbolRole::Write)),449 WrittenAt(Position(5, 7)))),450 Contains(AllOf(QName("foo"), HasRole(SymbolRole::Read),451 WrittenAt(Position(6, 17))))));452}453 454} // namespace455} // namespace index456} // namespace clang457