1 //===- llvm/unittest/TableGen/CodeExpanderTest.cpp - Tests ----------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "Common/GlobalISel/CodeExpander.h"
10 #include "Common/GlobalISel/CodeExpansions.h"
12 #include "llvm/Support/raw_ostream.h"
13 #include "llvm/TableGen/Error.h"
14 #include "gtest/gtest.h"
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());
26 class RAIIDiagnosticChecker
{
27 std::string EmittedDiags
;
28 raw_string_ostream OS
;
29 std::vector
<SMDiagnostic
> Expected
;
30 std::vector
<SMDiagnostic
> Received
;
33 RAIIDiagnosticChecker() : OS(EmittedDiags
) {
34 SrcMgr
.setDiagHandler(handler
, this);
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());
50 if (testing::Test::HasFailure())
51 errs() << "Emitted diagnostic:\n" << OS
.str();
54 void expect(SMDiagnostic D
) { Expected
.push_back(D
); }
56 void diag(const SMDiagnostic
&D
) {
57 Received
.push_back(D
);
60 static void handler(const SMDiagnostic
&D
, void *Context
) {
61 RAIIDiagnosticChecker
*Self
= static_cast<RAIIDiagnosticChecker
*>(Context
);
63 SrcMgr
.setDiagHandler(nullptr);
64 SrcMgr
.PrintMessage(Self
->OS
, D
);
65 SrcMgr
.setDiagHandler(handler
, Context
);
69 TEST(CodeExpander
, NoExpansions
) {
71 raw_string_ostream
OS(Result
);
72 CodeExpansions Expansions
;
74 RAIIDiagnosticChecker DiagChecker
;
75 CodeExpander("No expansions", Expansions
, SMLoc(), false).emit(OS
);
76 EXPECT_EQ(OS
.str(), "No expansions");
79 // Indentation is applied to all lines except the first
80 TEST(CodeExpander
, Indentation
) {
82 raw_string_ostream
OS(Result
);
83 CodeExpansions Expansions
;
85 RAIIDiagnosticChecker DiagChecker
;
86 CodeExpander("No expansions\nsecond line\nthird line", Expansions
, SMLoc(),
89 EXPECT_EQ(OS
.str(), "No expansions\n second line\n third line");
92 // \ is an escape character that removes special meanings from the next
94 TEST(CodeExpander
, Escape
) {
96 raw_string_ostream
OS(Result
);
97 CodeExpansions Expansions
;
99 RAIIDiagnosticChecker DiagChecker
;
100 CodeExpander("\\\\\\a\\$", Expansions
, SMLoc(), false).emit(OS
);
101 EXPECT_EQ(OS
.str(), "\\a$");
104 // $foo is not an expansion. It should warn though.
105 TEST(CodeExpander
, NotAnExpansion
) {
107 raw_string_ostream
OS(Result
);
108 CodeExpansions Expansions
;
110 RAIIDiagnosticChecker DiagChecker
;
111 StringRef In
= bufferize(" $foo");
112 CodeExpander(" $foo", Expansions
, SMLoc::getFromPointer(In
.data()), false)
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", {}));
120 // \$foo is not an expansion but shouldn't warn as it's using the escape.
121 TEST(CodeExpander
, EscapedNotAnExpansion
) {
123 raw_string_ostream
OS(Result
);
124 CodeExpansions Expansions
;
126 RAIIDiagnosticChecker DiagChecker
;
127 CodeExpander("\\$foo", Expansions
, SMLoc(), false).emit(OS
);
128 EXPECT_EQ(OS
.str(), "$foo");
131 // \${foo is not an expansion but shouldn't warn as it's using the escape.
132 TEST(CodeExpander
, EscapedUnterminatedExpansion
) {
134 raw_string_ostream
OS(Result
);
135 CodeExpansions Expansions
;
137 RAIIDiagnosticChecker DiagChecker
;
138 CodeExpander("\\${foo", Expansions
, SMLoc(), false).emit(OS
);
139 EXPECT_EQ(OS
.str(), "${foo");
142 // \${foo is not an expansion but shouldn't warn as it's using the escape.
143 TEST(CodeExpander
, EscapedExpansion
) {
145 raw_string_ostream
OS(Result
);
146 CodeExpansions Expansions
;
148 RAIIDiagnosticChecker DiagChecker
;
149 CodeExpander("\\${foo}", Expansions
, SMLoc(), false).emit(OS
);
150 EXPECT_EQ(OS
.str(), "${foo}");
153 // ${foo} is an undefined expansion and should error.
154 TEST(CodeExpander
, UndefinedExpansion
) {
156 raw_string_ostream
OS(Result
);
157 CodeExpansions Expansions
;
158 Expansions
.declare("bar", "expansion");
160 RAIIDiagnosticChecker DiagChecker
;
161 CodeExpander("${foo}${bar}", Expansions
, SMLoc(), false).emit(OS
);
162 EXPECT_EQ(OS
.str(), "expansion");
164 SMDiagnostic(SrcMgr
, SMLoc(), "<unknown>", 0, -1, SourceMgr::DK_Error
,
165 "Attempt to expand an undeclared variable 'foo'", "", {}));
168 // ${bar is an unterminated expansion. Warn and implicitly terminate it.
169 TEST(CodeExpander
, UnterminatedExpansion
) {
171 raw_string_ostream
OS(Result
);
172 CodeExpansions Expansions
;
173 Expansions
.declare("bar", "expansion");
175 RAIIDiagnosticChecker DiagChecker
;
176 StringRef In
= bufferize(" ${bar");
177 CodeExpander(In
, Expansions
, SMLoc::getFromPointer(In
.data()), false)
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", {}));