| //===- llvm/unittest/TableGen/CodeExpanderTest.cpp - Tests ----------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Common/GlobalISel/CodeExpander.h" |
| #include "Common/GlobalISel/CodeExpansions.h" |
| |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/TableGen/Error.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| static StringRef bufferize(StringRef Str) { |
| std::unique_ptr<MemoryBuffer> Buffer = |
| MemoryBuffer::getMemBufferCopy(Str, "TestBuffer"); |
| StringRef StrBufferRef = Buffer->getBuffer(); |
| SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc()); |
| return StrBufferRef; |
| } |
| |
| class RAIIDiagnosticChecker { |
| std::string EmittedDiags; |
| raw_string_ostream OS; |
| std::vector<SMDiagnostic> Expected; |
| std::vector<SMDiagnostic> Received; |
| |
| public: |
| RAIIDiagnosticChecker() : OS(EmittedDiags) { |
| SrcMgr.setDiagHandler(handler, this); |
| } |
| ~RAIIDiagnosticChecker() { |
| SrcMgr.setDiagHandler(nullptr); |
| EXPECT_EQ(Received.size(), Expected.size()); |
| for (unsigned i = 0; i < Received.size() && i < Expected.size(); ++i) { |
| EXPECT_EQ(Received[i].getLoc(), Expected[i].getLoc()); |
| EXPECT_EQ(Received[i].getFilename(), Expected[i].getFilename()); |
| EXPECT_EQ(Received[i].getKind(), Expected[i].getKind()); |
| EXPECT_EQ(Received[i].getLineNo(), Expected[i].getLineNo()); |
| EXPECT_EQ(Received[i].getColumnNo(), Expected[i].getColumnNo()); |
| EXPECT_EQ(Received[i].getMessage(), Expected[i].getMessage()); |
| EXPECT_EQ(Received[i].getLineContents(), Expected[i].getLineContents()); |
| EXPECT_EQ(Received[i].getRanges(), Expected[i].getRanges()); |
| } |
| |
| if (testing::Test::HasFailure()) |
| errs() << "Emitted diagnostic:\n" << OS.str(); |
| } |
| |
| void expect(SMDiagnostic D) { Expected.push_back(D); } |
| |
| void diag(const SMDiagnostic &D) { |
| Received.push_back(D); |
| } |
| |
| static void handler(const SMDiagnostic &D, void *Context) { |
| RAIIDiagnosticChecker *Self = static_cast<RAIIDiagnosticChecker *>(Context); |
| Self->diag(D); |
| SrcMgr.setDiagHandler(nullptr); |
| SrcMgr.PrintMessage(Self->OS, D); |
| SrcMgr.setDiagHandler(handler, Context); |
| }; |
| }; |
| |
| TEST(CodeExpander, NoExpansions) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("No expansions", Expansions, SMLoc(), false).emit(OS); |
| EXPECT_EQ(OS.str(), "No expansions"); |
| } |
| |
| // Indentation is applied to all lines except the first |
| TEST(CodeExpander, Indentation) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("No expansions\nsecond line\nthird line", Expansions, SMLoc(), |
| false, " ") |
| .emit(OS); |
| EXPECT_EQ(OS.str(), "No expansions\n second line\n third line"); |
| } |
| |
| // \ is an escape character that removes special meanings from the next |
| // character. |
| TEST(CodeExpander, Escape) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("\\\\\\a\\$", Expansions, SMLoc(), false).emit(OS); |
| EXPECT_EQ(OS.str(), "\\a$"); |
| } |
| |
| // $foo is not an expansion. It should warn though. |
| TEST(CodeExpander, NotAnExpansion) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| StringRef In = bufferize(" $foo"); |
| CodeExpander(" $foo", Expansions, SMLoc::getFromPointer(In.data()), false) |
| .emit(OS); |
| EXPECT_EQ(OS.str(), " $foo"); |
| DiagChecker.expect(SMDiagnostic( |
| SrcMgr, SMLoc::getFromPointer(In.data()), "TestBuffer", 1, 0, |
| SourceMgr::DK_Warning, "Assuming missing escape character: \\$", " $foo", {})); |
| } |
| |
| // \$foo is not an expansion but shouldn't warn as it's using the escape. |
| TEST(CodeExpander, EscapedNotAnExpansion) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("\\$foo", Expansions, SMLoc(), false).emit(OS); |
| EXPECT_EQ(OS.str(), "$foo"); |
| } |
| |
| // \${foo is not an expansion but shouldn't warn as it's using the escape. |
| TEST(CodeExpander, EscapedUnterminatedExpansion) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("\\${foo", Expansions, SMLoc(), false).emit(OS); |
| EXPECT_EQ(OS.str(), "${foo"); |
| } |
| |
| // \${foo is not an expansion but shouldn't warn as it's using the escape. |
| TEST(CodeExpander, EscapedExpansion) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("\\${foo}", Expansions, SMLoc(), false).emit(OS); |
| EXPECT_EQ(OS.str(), "${foo}"); |
| } |
| |
| // ${foo} is an undefined expansion and should error. |
| TEST(CodeExpander, UndefinedExpansion) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| Expansions.declare("bar", "expansion"); |
| |
| RAIIDiagnosticChecker DiagChecker; |
| CodeExpander("${foo}${bar}", Expansions, SMLoc(), false).emit(OS); |
| EXPECT_EQ(OS.str(), "expansion"); |
| DiagChecker.expect( |
| SMDiagnostic(SrcMgr, SMLoc(), "<unknown>", 0, -1, SourceMgr::DK_Error, |
| "Attempt to expand an undeclared variable 'foo'", "", {})); |
| } |
| |
| // ${bar is an unterminated expansion. Warn and implicitly terminate it. |
| TEST(CodeExpander, UnterminatedExpansion) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| CodeExpansions Expansions; |
| Expansions.declare("bar", "expansion"); |
| |
| RAIIDiagnosticChecker DiagChecker; |
| StringRef In = bufferize(" ${bar"); |
| CodeExpander(In, Expansions, SMLoc::getFromPointer(In.data()), false) |
| .emit(OS); |
| EXPECT_EQ(OS.str(), " expansion"); |
| DiagChecker.expect(SMDiagnostic(SrcMgr, SMLoc::getFromPointer(In.data()), |
| "TestBuffer", 1, 0, SourceMgr::DK_Warning, |
| "Unterminated expansion '${bar'", " ${bar", {})); |
| } |