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
16using namespace llvm;
17
18static 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
26class RAIIDiagnosticChecker {
27 std::string EmittedDiags;
28 raw_string_ostream OS;
29 std::vector<SMDiagnostic> Expected;
30 std::vector<SMDiagnostic> Received;
31
32public:
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
69TEST(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
80TEST(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.
94TEST(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.
105TEST(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.
121TEST(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.
132TEST(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.
143TEST(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.
154TEST(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.
169TEST(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

source code of llvm/unittests/TableGen/CodeExpanderTest.cpp