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 "GlobalISel/CodeExpander.h" 10 #include "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(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: 33 RAIIDiagnosticChecker() : OS(EmittedDiags) { 34 SrcMgr.setDiagHandler(handler, this); 35 } 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 54 void expect(SMDiagnostic D) { Expected.push_back(D); } 55 56 void diag(const SMDiagnostic &D) { 57 Received.push_back(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(nullptr); 64 SrcMgr.PrintMessage(Self->OS, D); 65 SrcMgr.setDiagHandler(handler, 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(" $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. 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("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. 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