xref: /llvm-project/llvm/unittests/TableGen/CodeExpanderTest.cpp (revision fa3d789df15bd1f58fb8ba4ea3be909218cf7f03)
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