xref: /llvm-project/clang-tools-extra/clangd/unittests/CollectMacrosTests.cpp (revision 7298bcf7f06145e2d4dfdb177b94dc42fc95dc55)
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 "AST.h"
9 #include "Annotations.h"
10 #include "CollectMacros.h"
11 #include "Matchers.h"
12 #include "SourceCode.h"
13 #include "TestTU.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "llvm/Support/ScopedPrinter.h"
16 #include "gmock/gmock.h"
17 #include "gtest/gtest.h"
18 #include <vector>
19 
20 namespace clang {
21 namespace clangd {
22 namespace {
23 
24 using testing::UnorderedElementsAreArray;
25 
26 MATCHER_P(rangeIs, R, "") {
27   return arg.StartOffset == R.Begin && arg.EndOffset == R.End;
28 }
29 MATCHER(isDef, "") { return arg.IsDefinition; }
30 MATCHER(inConditionalDirective, "") { return arg.InConditionalDirective; }
31 
TEST(CollectMainFileMacros,SelectedMacros)32 TEST(CollectMainFileMacros, SelectedMacros) {
33   // References of the same symbol must have the ranges with the same
34   // name(integer). If there are N different symbols then they must be named
35   // from 1 to N. Macros for which SymbolID cannot be computed must be named
36   // "Unknown". The payload of the annotation describes the extra bit
37   // information of the MacroOccurrence (e.g. $1(def) => IsDefinition).
38   const char *Tests[] = {
39       R"cpp(// Macros: Cursor on definition.
40         #define $1(def)[[FOO]](x,y) (x + y)
41         int main() { int x = $1[[FOO]]($1[[FOO]](3, 4), $1[[FOO]](5, 6)); }
42       )cpp",
43       R"cpp(
44         #define $1(def)[[M]](X) X;
45         #define $2(def)[[abc]] 123
46         int s = $1[[M]]($2[[abc]]);
47       )cpp",
48       // FIXME: Locating macro in duplicate definitions doesn't work. Enable
49       // this once LocateMacro is fixed.
50       // R"cpp(// Multiple definitions.
51       //   #define $1[[abc]] 1
52       //   int func1() { int a = $1[[abc]];}
53       //   #undef $1[[abc]]
54 
55       //   #define $2[[abc]] 2
56       //   int func2() { int a = $2[[abc]];}
57       //   #undef $2[[abc]]
58       // )cpp",
59       R"cpp(
60         #ifdef $Unknown(condit)[[UNDEFINED]]
61         #elifdef $Unknown(condit)[[UNDEFINED]]
62         #endif
63 
64         #ifdef $Unknown(condit)[[UNDEFINED]]
65         #elifndef $Unknown(condit)[[UNDEFINED]]
66         #endif
67 
68         #ifndef $Unknown(condit)[[UNDEFINED]]
69         #endif
70 
71         #if defined($Unknown(condit)[[UNDEFINED]])
72         #endif
73       )cpp",
74       R"cpp(
75         #ifndef $Unknown(condit)[[abc]]
76         #define $1(def)[[abc]]
77         #ifdef $1(condit)[[abc]]
78         #endif
79         #endif
80       )cpp",
81       R"cpp(
82         // Macros from token concatenations not included.
83         #define $1(def)[[CONCAT]](X) X##A()
84         #define $2(def)[[PREPEND]](X) MACRO##X()
85         #define $3(def)[[MACROA]]() 123
86         int B = $1[[CONCAT]](MACRO);
87         int D = $2[[PREPEND]](A);
88       )cpp",
89       R"cpp(
90         #define $1(def)[[MACRO_ARGS2]](X, Y) X Y
91         #define $3(def)[[BAR]] 1
92         #define $2(def)[[FOO]] $3[[BAR]]
93         int A = $2[[FOO]];
94       )cpp"};
95   auto ExpectedResults = [](const llvm::Annotations &T, StringRef Name) {
96     std::vector<Matcher<MacroOccurrence>> ExpectedLocations;
97     for (const auto &[R, Bits] : T.rangesWithPayload(Name)) {
98       if (Bits == "def")
99         ExpectedLocations.push_back(testing::AllOf(rangeIs(R), isDef()));
100       else if (Bits == "condit")
101         ExpectedLocations.push_back(
102             testing::AllOf(rangeIs(R), inConditionalDirective()));
103       else
104         ExpectedLocations.push_back(testing::AllOf(rangeIs(R)));
105     }
106     return ExpectedLocations;
107   };
108 
109   for (const char *Test : Tests) {
110     llvm::Annotations T(Test);
111     auto Inputs = TestTU::withCode(T.code());
112     Inputs.ExtraArgs.push_back("-std=c++2b");
113     auto AST = Inputs.build();
114     auto ActualMacroRefs = AST.getMacros();
115     auto &SM = AST.getSourceManager();
116     auto &PP = AST.getPreprocessor();
117     for (const auto &[Name, Ranges] : T.all_ranges()) {
118       if (Name == "Unknown") {
119         EXPECT_THAT(ActualMacroRefs.UnknownMacros,
120                     UnorderedElementsAreArray(ExpectedResults(T, "Unknown")))
121             << "Unknown macros doesn't match in " << Test;
122         continue;
123       }
124 
125       auto Loc = sourceLocationInMainFile(
126           SM, offsetToPosition(T.code(), Ranges.front().Begin));
127       ASSERT_TRUE(bool(Loc));
128       const auto *Id = syntax::spelledIdentifierTouching(*Loc, AST.getTokens());
129       ASSERT_TRUE(Id);
130       auto Macro = locateMacroAt(*Id, PP);
131       assert(Macro);
132       auto SID = getSymbolID(Macro->Name, Macro->Info, SM);
133 
134       EXPECT_THAT(ActualMacroRefs.MacroRefs[SID],
135                   UnorderedElementsAreArray(ExpectedResults(T, Name)))
136           << "Annotation=" << Name << ", MacroName=" << Macro->Name
137           << ", Test = " << Test;
138     }
139   }
140 }
141 } // namespace
142 } // namespace clangd
143 } // namespace clang
144