brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.6 KiB · 1e7baf1 Raw
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