1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
//===--- LocateSymbolTest.cpp -------------------------------------- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "AnalysisInternal.h"
#include "TypesInternal.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Testing/TestAST.h"
#include "clang/Tooling/Inclusions/StandardLibrary.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Testing/Annotations/Annotations.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <tuple>
#include <vector>
namespace clang::include_cleaner {
namespace {
using testing::Each;
using testing::ElementsAre;
using testing::ElementsAreArray;
using testing::Eq;
using testing::Field;
using testing::Pair;
using testing::UnorderedElementsAre;
// A helper for building ASTs and getting decls out of it by name. Example usage
// looks like:
// LocateExample X("void ^foo();");
// Decl &Foo = X.findDecl("foo");
// X.points(); // returns all the points in annotated test input.
struct LocateExample {
private:
llvm::Annotations Target;
TestAST AST;
public:
LocateExample(llvm::StringRef AnnotatedCode)
: Target(AnnotatedCode), AST([this] {
TestInputs Inputs(Target.code());
Inputs.ExtraArgs.push_back("-std=c++17");
return Inputs;
}()) {}
const Decl &findDecl(llvm::StringRef SymbolName) {
struct Visitor : RecursiveASTVisitor<Visitor> {
llvm::StringRef NameToFind;
const NamedDecl *Out = nullptr;
bool VisitNamedDecl(const NamedDecl *ND) {
// Skip the templated decls, as they have the same name and matches in
// this file care about the outer template name.
if (auto *TD = ND->getDescribedTemplate())
ND = TD;
if (ND->getName() == NameToFind) {
EXPECT_TRUE(Out == nullptr || Out == ND->getCanonicalDecl())
<< "Found multiple matches for " << NameToFind.str();
Out = llvm::cast<NamedDecl>(ND->getCanonicalDecl());
}
return true;
}
};
Visitor V;
V.NameToFind = SymbolName;
V.TraverseDecl(AST.context().getTranslationUnitDecl());
if (!V.Out)
ADD_FAILURE() << "Couldn't find any decls with name: " << SymbolName;
assert(V.Out);
return *V.Out;
}
Macro findMacro(llvm::StringRef Name) {
auto &PP = AST.preprocessor();
auto *II = PP.getIdentifierInfo(Name);
if (!II || !II->hasMacroDefinition()) {
ADD_FAILURE() << "Couldn't find any macros with name: " << Name;
return {};
}
auto MD = PP.getMacroDefinition(II);
assert(MD.getMacroInfo());
return {II, MD.getMacroInfo()->getDefinitionLoc()};
}
std::vector<SymbolLocation> points() {
auto &SM = AST.sourceManager();
auto FID = SM.getMainFileID();
auto Offsets = Target.points();
std::vector<SymbolLocation> Results;
for (auto &Offset : Offsets)
Results.emplace_back(SM.getComposedLoc(FID, Offset));
return Results;
}
};
TEST(LocateSymbol, Decl) {
// Looks for decl with name 'foo' and performs locateSymbol on it.
// Expects all the locations in the case to be returned as a location.
const llvm::StringLiteral Cases[] = {
"struct ^foo; struct ^foo {};",
"namespace ns { void ^foo(); void ^foo() {} }",
"enum class ^foo; enum class ^foo {};",
};
for (auto &Case : Cases) {
SCOPED_TRACE(Case);
LocateExample Test(Case);
EXPECT_THAT(locateSymbol(Test.findDecl("foo")),
ElementsAreArray(Test.points()));
}
}
TEST(LocateSymbol, Stdlib) {
{
LocateExample Test("namespace std { struct vector; }");
EXPECT_THAT(
locateSymbol(Test.findDecl("vector")),
ElementsAre(*tooling::stdlib::Symbol::named("std::", "vector")));
}
{
LocateExample Test("#define assert(x)\nvoid foo() { assert(true); }");
EXPECT_THAT(locateSymbol(Test.findMacro("assert")),
ElementsAre(*tooling::stdlib::Symbol::named("", "assert")));
}
}
TEST(LocateSymbol, Macros) {
// Make sure we preserve the last one.
LocateExample Test("#define FOO\n#undef FOO\n#define ^FOO");
EXPECT_THAT(locateSymbol(Test.findMacro("FOO")),
ElementsAreArray(Test.points()));
}
MATCHER_P2(HintedSymbol, Symbol, Hint, "") {
return std::tie(arg.Hint, arg) == std::tie(Hint, Symbol);
}
TEST(LocateSymbol, CompleteSymbolHint) {
{
// stdlib symbols are always complete.
LocateExample Test("namespace std { struct vector; }");
EXPECT_THAT(locateSymbol(Test.findDecl("vector")),
ElementsAre(HintedSymbol(
*tooling::stdlib::Symbol::named("std::", "vector"),
Hints::CompleteSymbol)));
}
{
// macros are always complete.
LocateExample Test("#define ^FOO");
EXPECT_THAT(locateSymbol(Test.findMacro("FOO")),
ElementsAre(HintedSymbol(Test.points().front(),
Hints::CompleteSymbol)));
}
{
// Completeness is only absent in cases that matters.
const llvm::StringLiteral Cases[] = {
"struct ^foo; struct ^foo {};",
"template <typename> struct ^foo; template <typename> struct ^foo {};",
"template <typename> void ^foo(); template <typename> void ^foo() {};",
};
for (auto &Case : Cases) {
SCOPED_TRACE(Case);
LocateExample Test(Case);
EXPECT_THAT(locateSymbol(Test.findDecl("foo")),
ElementsAre(HintedSymbol(Test.points().front(), Hints::None),
HintedSymbol(Test.points().back(),
Hints::CompleteSymbol)));
}
}
{
// All declarations should be marked as complete in cases that a definition
// is not usually needed.
const llvm::StringLiteral Cases[] = {
"void foo(); void foo() {}",
"extern int foo; int foo;",
};
for (auto &Case : Cases) {
SCOPED_TRACE(Case);
LocateExample Test(Case);
EXPECT_THAT(locateSymbol(Test.findDecl("foo")),
Each(Field(&Hinted<SymbolLocation>::Hint,
Eq(Hints::CompleteSymbol))));
}
}
}
} // namespace
} // namespace clang::include_cleaner
|