1 | //===- llvm/unittest/TableGen/CodeExpanderTest.cpp - Tests ----------------===// |
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 | |
9 | #include "Common/GlobalISel/CodeExpander.h" |
10 | #include "Common/GlobalISel/CodeExpansions.h" |
11 | |
12 | #include "llvm/Support/raw_ostream.h" |
13 | #include "llvm/TableGen/Error.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | using namespace llvm; |
17 | |
18 | static StringRef bufferize(StringRef Str) { |
19 | std::unique_ptr<MemoryBuffer> Buffer = |
20 | MemoryBuffer::getMemBufferCopy(InputData: Str, BufferName: "TestBuffer" ); |
21 | StringRef StrBufferRef = Buffer->getBuffer(); |
22 | SrcMgr.AddNewSourceBuffer(F: std::move(Buffer), IncludeLoc: SMLoc()); |
23 | return StrBufferRef; |
24 | } |
25 | |
26 | class RAIIDiagnosticChecker { |
27 | std::string EmittedDiags; |
28 | raw_string_ostream OS; |
29 | std::vector<SMDiagnostic> Expected; |
30 | std::vector<SMDiagnostic> Received; |
31 | |
32 | public: |
33 | RAIIDiagnosticChecker() : OS(EmittedDiags) { |
34 | SrcMgr.setDiagHandler(DH: handler, Ctx: this); |
35 | } |
36 | ~RAIIDiagnosticChecker() { |
37 | SrcMgr.setDiagHandler(DH: nullptr); |
38 | EXPECT_EQ(Received.size(), Expected.size()); |
39 | for (unsigned i = 0; i < Received.size() && i < Expected.size(); ++i) { |
40 | EXPECT_EQ(Received[i].getLoc(), Expected[i].getLoc()); |
41 | EXPECT_EQ(Received[i].getFilename(), Expected[i].getFilename()); |
42 | EXPECT_EQ(Received[i].getKind(), Expected[i].getKind()); |
43 | EXPECT_EQ(Received[i].getLineNo(), Expected[i].getLineNo()); |
44 | EXPECT_EQ(Received[i].getColumnNo(), Expected[i].getColumnNo()); |
45 | EXPECT_EQ(Received[i].getMessage(), Expected[i].getMessage()); |
46 | EXPECT_EQ(Received[i].getLineContents(), Expected[i].getLineContents()); |
47 | EXPECT_EQ(Received[i].getRanges(), Expected[i].getRanges()); |
48 | } |
49 | |
50 | if (testing::Test::HasFailure()) |
51 | errs() << "Emitted diagnostic:\n" << OS.str(); |
52 | } |
53 | |
54 | void expect(SMDiagnostic D) { Expected.push_back(x: D); } |
55 | |
56 | void diag(const SMDiagnostic &D) { |
57 | Received.push_back(x: D); |
58 | } |
59 | |
60 | static void handler(const SMDiagnostic &D, void *Context) { |
61 | RAIIDiagnosticChecker *Self = static_cast<RAIIDiagnosticChecker *>(Context); |
62 | Self->diag(D); |
63 | SrcMgr.setDiagHandler(DH: nullptr); |
64 | SrcMgr.PrintMessage(OS&: Self->OS, Diagnostic: D); |
65 | SrcMgr.setDiagHandler(DH: handler, Ctx: Context); |
66 | }; |
67 | }; |
68 | |
69 | TEST(CodeExpander, NoExpansions) { |
70 | std::string Result; |
71 | raw_string_ostream OS(Result); |
72 | CodeExpansions Expansions; |
73 | |
74 | RAIIDiagnosticChecker DiagChecker; |
75 | CodeExpander("No expansions" , Expansions, SMLoc(), false).emit(OS); |
76 | EXPECT_EQ(OS.str(), "No expansions" ); |
77 | } |
78 | |
79 | // Indentation is applied to all lines except the first |
80 | TEST(CodeExpander, Indentation) { |
81 | std::string Result; |
82 | raw_string_ostream OS(Result); |
83 | CodeExpansions Expansions; |
84 | |
85 | RAIIDiagnosticChecker DiagChecker; |
86 | CodeExpander("No expansions\nsecond line\nthird line" , Expansions, SMLoc(), |
87 | false, " " ) |
88 | .emit(OS); |
89 | EXPECT_EQ(OS.str(), "No expansions\n second line\n third line" ); |
90 | } |
91 | |
92 | // \ is an escape character that removes special meanings from the next |
93 | // character. |
94 | TEST(CodeExpander, Escape) { |
95 | std::string Result; |
96 | raw_string_ostream OS(Result); |
97 | CodeExpansions Expansions; |
98 | |
99 | RAIIDiagnosticChecker DiagChecker; |
100 | CodeExpander("\\\\\\a\\$" , Expansions, SMLoc(), false).emit(OS); |
101 | EXPECT_EQ(OS.str(), "\\a$" ); |
102 | } |
103 | |
104 | // $foo is not an expansion. It should warn though. |
105 | TEST(CodeExpander, NotAnExpansion) { |
106 | std::string Result; |
107 | raw_string_ostream OS(Result); |
108 | CodeExpansions Expansions; |
109 | |
110 | RAIIDiagnosticChecker DiagChecker; |
111 | StringRef In = bufferize(Str: " $foo" ); |
112 | CodeExpander(" $foo" , Expansions, SMLoc::getFromPointer(Ptr: In.data()), false) |
113 | .emit(OS); |
114 | EXPECT_EQ(OS.str(), " $foo" ); |
115 | DiagChecker.expect(D: SMDiagnostic( |
116 | SrcMgr, SMLoc::getFromPointer(Ptr: In.data()), "TestBuffer" , 1, 0, |
117 | SourceMgr::DK_Warning, "Assuming missing escape character: \\$" , " $foo" , {})); |
118 | } |
119 | |
120 | // \$foo is not an expansion but shouldn't warn as it's using the escape. |
121 | TEST(CodeExpander, EscapedNotAnExpansion) { |
122 | std::string Result; |
123 | raw_string_ostream OS(Result); |
124 | CodeExpansions Expansions; |
125 | |
126 | RAIIDiagnosticChecker DiagChecker; |
127 | CodeExpander("\\$foo" , Expansions, SMLoc(), false).emit(OS); |
128 | EXPECT_EQ(OS.str(), "$foo" ); |
129 | } |
130 | |
131 | // \${foo is not an expansion but shouldn't warn as it's using the escape. |
132 | TEST(CodeExpander, EscapedUnterminatedExpansion) { |
133 | std::string Result; |
134 | raw_string_ostream OS(Result); |
135 | CodeExpansions Expansions; |
136 | |
137 | RAIIDiagnosticChecker DiagChecker; |
138 | CodeExpander("\\${foo" , Expansions, SMLoc(), false).emit(OS); |
139 | EXPECT_EQ(OS.str(), "${foo" ); |
140 | } |
141 | |
142 | // \${foo is not an expansion but shouldn't warn as it's using the escape. |
143 | TEST(CodeExpander, EscapedExpansion) { |
144 | std::string Result; |
145 | raw_string_ostream OS(Result); |
146 | CodeExpansions Expansions; |
147 | |
148 | RAIIDiagnosticChecker DiagChecker; |
149 | CodeExpander("\\${foo}" , Expansions, SMLoc(), false).emit(OS); |
150 | EXPECT_EQ(OS.str(), "${foo}" ); |
151 | } |
152 | |
153 | // ${foo} is an undefined expansion and should error. |
154 | TEST(CodeExpander, UndefinedExpansion) { |
155 | std::string Result; |
156 | raw_string_ostream OS(Result); |
157 | CodeExpansions Expansions; |
158 | Expansions.declare(Name: "bar" , Expansion: "expansion" ); |
159 | |
160 | RAIIDiagnosticChecker DiagChecker; |
161 | CodeExpander("${foo}${bar}" , Expansions, SMLoc(), false).emit(OS); |
162 | EXPECT_EQ(OS.str(), "expansion" ); |
163 | DiagChecker.expect( |
164 | D: SMDiagnostic(SrcMgr, SMLoc(), "<unknown>" , 0, -1, SourceMgr::DK_Error, |
165 | "Attempt to expand an undeclared variable 'foo'" , "" , {})); |
166 | } |
167 | |
168 | // ${bar is an unterminated expansion. Warn and implicitly terminate it. |
169 | TEST(CodeExpander, UnterminatedExpansion) { |
170 | std::string Result; |
171 | raw_string_ostream OS(Result); |
172 | CodeExpansions Expansions; |
173 | Expansions.declare(Name: "bar" , Expansion: "expansion" ); |
174 | |
175 | RAIIDiagnosticChecker DiagChecker; |
176 | StringRef In = bufferize(Str: " ${bar" ); |
177 | CodeExpander(In, Expansions, SMLoc::getFromPointer(Ptr: In.data()), false) |
178 | .emit(OS); |
179 | EXPECT_EQ(OS.str(), " expansion" ); |
180 | DiagChecker.expect(D: SMDiagnostic(SrcMgr, SMLoc::getFromPointer(Ptr: In.data()), |
181 | "TestBuffer" , 1, 0, SourceMgr::DK_Warning, |
182 | "Unterminated expansion '${bar'" , " ${bar" , {})); |
183 | } |
184 | |