1 //===-- CollectMacrosTests.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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 #include "Annotations.h"
9 #include "CollectMacros.h"
10 #include "Matchers.h"
11 #include "SourceCode.h"
12 #include "TestTU.h"
13 #include "index/SymbolID.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "llvm/Support/ScopedPrinter.h"
16 #include "gmock/gmock.h"
17 #include "gtest/gtest.h"
18 
19 namespace clang {
20 namespace clangd {
21 namespace {
22 
23 using testing::UnorderedElementsAreArray;
24 
TEST(CollectMainFileMacros,SelectedMacros)25 TEST(CollectMainFileMacros, SelectedMacros) {
26   // References of the same symbol must have the ranges with the same
27   // name(integer). If there are N different symbols then they must be named
28   // from 1 to N. Macros for which SymbolID cannot be computed must be named
29   // "Unknown".
30   const char *Tests[] = {
31       R"cpp(// Macros: Cursor on definition.
32         #define $1[[FOO]](x,y) (x + y)
33         int main() { int x = $1[[FOO]]($1[[FOO]](3, 4), $1[[FOO]](5, 6)); }
34       )cpp",
35       R"cpp(
36         #define $1[[M]](X) X;
37         #define $2[[abc]] 123
38         int s = $1[[M]]($2[[abc]]);
39       )cpp",
40       // FIXME: Locating macro in duplicate definitions doesn't work. Enable
41       // this once LocateMacro is fixed.
42       // R"cpp(// Multiple definitions.
43       //   #define $1[[abc]] 1
44       //   int func1() { int a = $1[[abc]];}
45       //   #undef $1[[abc]]
46 
47       //   #define $2[[abc]] 2
48       //   int func2() { int a = $2[[abc]];}
49       //   #undef $2[[abc]]
50       // )cpp",
51       R"cpp(
52         #ifdef $Unknown[[UNDEFINED]]
53         #endif
54       )cpp",
55       R"cpp(
56         #ifndef $Unknown[[abc]]
57         #define $1[[abc]]
58         #ifdef $1[[abc]]
59         #endif
60         #endif
61       )cpp",
62       R"cpp(
63         // Macros from token concatenations not included.
64         #define $1[[CONCAT]](X) X##A()
65         #define $2[[PREPEND]](X) MACRO##X()
66         #define $3[[MACROA]]() 123
67         int B = $1[[CONCAT]](MACRO);
68         int D = $2[[PREPEND]](A);
69       )cpp",
70       R"cpp(
71         // FIXME: Macro names in a definition are not detected.
72         #define $1[[MACRO_ARGS2]](X, Y) X Y
73         #define $2[[FOO]] BAR
74         #define $3[[BAR]] 1
75         int A = $2[[FOO]];
76       )cpp"};
77   for (const char *Test : Tests) {
78     Annotations T(Test);
79     auto AST = TestTU::withCode(T.code()).build();
80     auto ActualMacroRefs = AST.getMacros();
81     auto &SM = AST.getSourceManager();
82     auto &PP = AST.getPreprocessor();
83 
84     // Known macros.
85     for (int I = 1;; I++) {
86       const auto ExpectedRefs = T.ranges(llvm::to_string(I));
87       if (ExpectedRefs.empty())
88         break;
89 
90       auto Loc = sourceLocationInMainFile(SM, ExpectedRefs.begin()->start);
91       ASSERT_TRUE(bool(Loc));
92       const auto *Id = syntax::spelledIdentifierTouching(*Loc, AST.getTokens());
93       ASSERT_TRUE(Id);
94       auto Macro = locateMacroAt(*Id, PP);
95       assert(Macro);
96       auto SID = getSymbolID(Macro->Name, Macro->Info, SM);
97 
98       std::vector<Range> Ranges;
99       for (const auto &Ref : ActualMacroRefs.MacroRefs[SID])
100         Ranges.push_back(Ref.Rng);
101       EXPECT_THAT(ExpectedRefs, UnorderedElementsAreArray(Ranges))
102           << "Annotation=" << I << ", MacroName=" << Macro->Name
103           << ", Test = " << Test;
104     }
105     // Unknown macros.
106     std::vector<Range> Ranges;
107     for (const auto &Ref : AST.getMacros().UnknownMacros)
108       Ranges.push_back(Ref.Rng);
109     EXPECT_THAT(Ranges, UnorderedElementsAreArray(T.ranges("Unknown")))
110         << "Unknown macros doesn't match in " << Test;
111   }
112 }
113 } // namespace
114 } // namespace clangd
115 } // namespace clang
116