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
20namespace clang {
21namespace clangd {
22namespace {
23
24using testing::UnorderedElementsAreArray;
25
26MATCHER_P(rangeIs, R, "") {
27 return arg.StartOffset == R.Begin && arg.EndOffset == R.End;
28}
29MATCHER(isDef, "") { return arg.IsDefinition; }
30MATCHER(inConditionalDirective, "") { return arg.InConditionalDirective; }
31
32TEST(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(x: testing::AllOf(matchers: rangeIs(gmock_p0: R), matchers: isDef()));
100 else if (Bits == "condit")
101 ExpectedLocations.push_back(
102 x: testing::AllOf(matchers: rangeIs(gmock_p0: R), matchers: inConditionalDirective()));
103 else
104 ExpectedLocations.push_back(x: testing::AllOf(matchers: rangeIs(gmock_p0: R)));
105 }
106 return ExpectedLocations;
107 };
108
109 for (const char *Test : Tests) {
110 llvm::Annotations T(Test);
111 auto Inputs = TestTU::withCode(Code: T.code());
112 Inputs.ExtraArgs.push_back(x: "-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, P: offsetToPosition(Code: T.code(), Offset: Ranges.front().Begin));
127 ASSERT_TRUE(bool(Loc));
128 const auto *Id = syntax::spelledIdentifierTouching(Loc: *Loc, Tokens: AST.getTokens());
129 ASSERT_TRUE(Id);
130 auto Macro = locateMacroAt(SpelledTok: *Id, PP);
131 assert(Macro);
132 auto SID = getSymbolID(MacroName: Macro->Name, MI: 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

source code of clang-tools-extra/clangd/unittests/CollectMacrosTests.cpp