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
bufferize(StringRef Str)18 static StringRef bufferize(StringRef Str) {
19 std::unique_ptr<MemoryBuffer> Buffer =
20 MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
21 StringRef StrBufferRef = Buffer->getBuffer();
22 SrcMgr.AddNewSourceBuffer(std::move(Buffer), 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:
RAIIDiagnosticChecker()33 RAIIDiagnosticChecker() : OS(EmittedDiags) {
34 SrcMgr.setDiagHandler(handler, this);
35 }
~RAIIDiagnosticChecker()36 ~RAIIDiagnosticChecker() {
37 SrcMgr.setDiagHandler(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
expect(SMDiagnostic D)54 void expect(SMDiagnostic D) { Expected.push_back(D); }
55
diag(const SMDiagnostic & D)56 void diag(const SMDiagnostic &D) {
57 Received.push_back(D);
58 }
59
handler(const SMDiagnostic & D,void * Context)60 static void handler(const SMDiagnostic &D, void *Context) {
61 RAIIDiagnosticChecker *Self = static_cast<RAIIDiagnosticChecker *>(Context);
62 Self->diag(D);
63 SrcMgr.setDiagHandler(nullptr);
64 SrcMgr.PrintMessage(Self->OS, D);
65 SrcMgr.setDiagHandler(handler, Context);
66 };
67 };
68
TEST(CodeExpander,NoExpansions)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
TEST(CodeExpander,Indentation)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.
TEST(CodeExpander,Escape)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.
TEST(CodeExpander,NotAnExpansion)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(" $foo");
112 CodeExpander(" $foo", Expansions, SMLoc::getFromPointer(In.data()), false)
113 .emit(OS);
114 EXPECT_EQ(OS.str(), " $foo");
115 DiagChecker.expect(SMDiagnostic(
116 SrcMgr, SMLoc::getFromPointer(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.
TEST(CodeExpander,EscapedNotAnExpansion)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.
TEST(CodeExpander,EscapedUnterminatedExpansion)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.
TEST(CodeExpander,EscapedExpansion)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.
TEST(CodeExpander,UndefinedExpansion)154 TEST(CodeExpander, UndefinedExpansion) {
155 std::string Result;
156 raw_string_ostream OS(Result);
157 CodeExpansions Expansions;
158 Expansions.declare("bar", "expansion");
159
160 RAIIDiagnosticChecker DiagChecker;
161 CodeExpander("${foo}${bar}", Expansions, SMLoc(), false).emit(OS);
162 EXPECT_EQ(OS.str(), "expansion");
163 DiagChecker.expect(
164 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.
TEST(CodeExpander,UnterminatedExpansion)169 TEST(CodeExpander, UnterminatedExpansion) {
170 std::string Result;
171 raw_string_ostream OS(Result);
172 CodeExpansions Expansions;
173 Expansions.declare("bar", "expansion");
174
175 RAIIDiagnosticChecker DiagChecker;
176 StringRef In = bufferize(" ${bar");
177 CodeExpander(In, Expansions, SMLoc::getFromPointer(In.data()), false)
178 .emit(OS);
179 EXPECT_EQ(OS.str(), " expansion");
180 DiagChecker.expect(SMDiagnostic(SrcMgr, SMLoc::getFromPointer(In.data()),
181 "TestBuffer", 1, 0, SourceMgr::DK_Warning,
182 "Unterminated expansion '${bar'", " ${bar", {}));
183 }
184