File: LocateSymbolTest.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (194 lines) | stat: -rw-r--r-- 6,599 bytes parent folder | download | duplicates (2)
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