diff options
Diffstat (limited to 'unittests/Tooling')
47 files changed, 1167 insertions, 279 deletions
diff --git a/unittests/Tooling/ASTSelectionTest.cpp b/unittests/Tooling/ASTSelectionTest.cpp index 2f5df8f430..7ad5148213 100644 --- a/unittests/Tooling/ASTSelectionTest.cpp +++ b/unittests/Tooling/ASTSelectionTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/CMakeLists.txt b/unittests/Tooling/CMakeLists.txt index 7619c7fb23..994b88b95b 100644 --- a/unittests/Tooling/CMakeLists.txt +++ b/unittests/Tooling/CMakeLists.txt @@ -49,7 +49,10 @@ add_clang_unittest(ToolingTests RefactoringTest.cpp ReplacementsYamlTest.cpp RewriterTest.cpp + SourceCodeTest.cpp + StencilTest.cpp ToolingTest.cpp + TransformerTest.cpp ) target_link_libraries(ToolingTests diff --git a/unittests/Tooling/CastExprTest.cpp b/unittests/Tooling/CastExprTest.cpp index 5310c21254..a9e78d2155 100644 --- a/unittests/Tooling/CastExprTest.cpp +++ b/unittests/Tooling/CastExprTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CastExprTest.cpp ----------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/CommentHandlerTest.cpp b/unittests/Tooling/CommentHandlerTest.cpp index 9c3abdc4b1..5ceed95b98 100644 --- a/unittests/Tooling/CommentHandlerTest.cpp +++ b/unittests/Tooling/CommentHandlerTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/CompilationDatabaseTest.cpp b/unittests/Tooling/CompilationDatabaseTest.cpp index 949d6a3b73..4e27df71d8 100644 --- a/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/unittests/Tooling/CompilationDatabaseTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CompilationDatabaseTest.cpp -----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -89,12 +88,17 @@ TEST(JSONCompilationDatabase, GetAllFiles) { expected_files.push_back(PathStorage.str()); llvm::sys::path::native("//net/dir/file2", PathStorage); expected_files.push_back(PathStorage.str()); + llvm::sys::path::native("//net/file1", PathStorage); + expected_files.push_back(PathStorage.str()); EXPECT_EQ(expected_files, getAllFiles("[{\"directory\":\"//net/dir\"," "\"command\":\"command\"," "\"file\":\"file1\"}," " {\"directory\":\"//net/dir\"," "\"command\":\"command\"," + "\"file\":\"../file1\"}," + " {\"directory\":\"//net/dir\"," + "\"command\":\"command\"," "\"file\":\"file2\"}]", ErrorMessage, JSONCommandLineSyntax::Gnu)) << ErrorMessage; @@ -669,6 +673,27 @@ protected: return llvm::join(Results[0].CommandLine, " "); } + // Parse the file whose command was used out of the Heuristic string. + std::string getProxy(llvm::StringRef F) { + auto Results = + inferMissingCompileCommands(llvm::make_unique<MemCDB>(Entries)) + ->getCompileCommands(path(F)); + if (Results.empty()) + return "none"; + StringRef Proxy = Results.front().Heuristic; + if (!Proxy.consume_front("inferred from ")) + return ""; + // We have a proxy file, convert back to a unix relative path. + // This is a bit messy, but we do need to test these strings somehow... + llvm::SmallString<32> TempDir; + llvm::sys::path::system_temp_directory(false, TempDir); + Proxy.consume_front(TempDir); + Proxy.consume_front(llvm::sys::path::get_separator()); + llvm::SmallString<32> Result = Proxy; + llvm::sys::path::native(Result, llvm::sys::path::Style::posix); + return Result.str(); + } + MemCDB::EntryMap Entries; }; @@ -678,18 +703,16 @@ TEST_F(InterpolateTest, Nearby) { add("an/other/foo.cpp"); // great: dir and name both match (prefix or full, case insensitive) - EXPECT_EQ(getCommand("dir/f.cpp"), "clang -D dir/foo.cpp"); - EXPECT_EQ(getCommand("dir/FOO.cpp"), "clang -D dir/foo.cpp"); + EXPECT_EQ(getProxy("dir/f.cpp"), "dir/foo.cpp"); + EXPECT_EQ(getProxy("dir/FOO.cpp"), "dir/foo.cpp"); // no name match. prefer matching dir, break ties by alpha - EXPECT_EQ(getCommand("dir/a.cpp"), "clang -D dir/bar.cpp"); + EXPECT_EQ(getProxy("dir/a.cpp"), "dir/bar.cpp"); // an exact name match beats one segment of directory match - EXPECT_EQ(getCommand("some/other/bar.h"), - "clang -D dir/bar.cpp -x c++-header"); + EXPECT_EQ(getProxy("some/other/bar.h"), "dir/bar.cpp"); // two segments of directory match beat a prefix name match - EXPECT_EQ(getCommand("an/other/b.cpp"), "clang -D an/other/foo.cpp"); + EXPECT_EQ(getProxy("an/other/b.cpp"), "an/other/foo.cpp"); // if nothing matches at all, we still get the closest alpha match - EXPECT_EQ(getCommand("below/some/obscure/path.cpp"), - "clang -D an/other/foo.cpp"); + EXPECT_EQ(getProxy("below/some/obscure/path.cpp"), "an/other/foo.cpp"); } TEST_F(InterpolateTest, Language) { @@ -723,7 +746,7 @@ TEST_F(InterpolateTest, Case) { add("FOO/BAR/BAZ/SHOUT.cc"); add("foo/bar/baz/quiet.cc"); // Case mismatches are completely ignored, so we choose the name match. - EXPECT_EQ(getCommand("foo/bar/baz/shout.C"), "clang -D FOO/BAR/BAZ/SHOUT.cc"); + EXPECT_EQ(getProxy("foo/bar/baz/shout.C"), "FOO/BAR/BAZ/SHOUT.cc"); } TEST_F(InterpolateTest, Aliasing) { diff --git a/unittests/Tooling/DiagnosticsYamlTest.cpp b/unittests/Tooling/DiagnosticsYamlTest.cpp index f4de53fad2..aaba258911 100644 --- a/unittests/Tooling/DiagnosticsYamlTest.cpp +++ b/unittests/Tooling/DiagnosticsYamlTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Tooling/DiagnosticsYamlTest.cpp - Serialization tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -21,11 +20,13 @@ using namespace clang::tooling; using clang::tooling::Diagnostic; static DiagnosticMessage makeMessage(const std::string &Message, int FileOffset, - const std::string &FilePath) { + const std::string &FilePath, + const StringMap<Replacements> &Fix) { DiagnosticMessage DiagMessage; DiagMessage.Message = Message; DiagMessage.FileOffset = FileOffset; DiagMessage.FilePath = FilePath; + DiagMessage.Fix = Fix; return DiagMessage; } @@ -33,10 +34,52 @@ static Diagnostic makeDiagnostic(StringRef DiagnosticName, const std::string &Message, int FileOffset, const std::string &FilePath, const StringMap<Replacements> &Fix) { - return Diagnostic(DiagnosticName, makeMessage(Message, FileOffset, FilePath), - Fix, {}, Diagnostic::Warning, "path/to/build/directory"); + return Diagnostic(DiagnosticName, + makeMessage(Message, FileOffset, FilePath, Fix), {}, + Diagnostic::Warning, "path/to/build/directory"); } +static const char *YAMLContent = + "---\n" + "MainSourceFile: 'path/to/source.cpp'\n" + "Diagnostics: \n" + " - DiagnosticName: 'diagnostic#1\'\n" + " DiagnosticMessage: \n" + " Message: 'message #1'\n" + " FilePath: 'path/to/source.cpp'\n" + " FileOffset: 55\n" + " Replacements: \n" + " - FilePath: 'path/to/source.cpp'\n" + " Offset: 100\n" + " Length: 12\n" + " ReplacementText: 'replacement #1'\n" + " - DiagnosticName: 'diagnostic#2'\n" + " DiagnosticMessage: \n" + " Message: 'message #2'\n" + " FilePath: 'path/to/header.h'\n" + " FileOffset: 60\n" + " Replacements: \n" + " - FilePath: 'path/to/header.h'\n" + " Offset: 62\n" + " Length: 2\n" + " ReplacementText: 'replacement #2'\n" + " - DiagnosticName: 'diagnostic#3'\n" + " DiagnosticMessage: \n" + " Message: 'message #3'\n" + " FilePath: 'path/to/source2.cpp'\n" + " FileOffset: 72\n" + " Replacements: []\n" + " Notes: \n" + " - Message: Note1\n" + " FilePath: 'path/to/note1.cpp'\n" + " FileOffset: 88\n" + " Replacements: []\n" + " - Message: Note2\n" + " FilePath: 'path/to/note2.cpp'\n" + " FileOffset: 99\n" + " Replacements: []\n" + "...\n"; + TEST(DiagnosticsYamlTest, serializesDiagnostics) { TranslationUnitDiagnostics TUD; TUD.MainSourceFile = "path/to/source.cpp"; @@ -56,9 +99,9 @@ TEST(DiagnosticsYamlTest, serializesDiagnostics) { TUD.Diagnostics.push_back(makeDiagnostic("diagnostic#3", "message #3", 72, "path/to/source2.cpp", {})); TUD.Diagnostics.back().Notes.push_back( - makeMessage("Note1", 88, "path/to/note1.cpp")); + makeMessage("Note1", 88, "path/to/note1.cpp", {})); TUD.Diagnostics.back().Notes.push_back( - makeMessage("Note2", 99, "path/to/note2.cpp")); + makeMessage("Note2", 99, "path/to/note2.cpp", {})); std::string YamlContent; raw_string_ostream YamlContentStream(YamlContent); @@ -66,80 +109,12 @@ TEST(DiagnosticsYamlTest, serializesDiagnostics) { yaml::Output YAML(YamlContentStream); YAML << TUD; - EXPECT_EQ("---\n" - "MainSourceFile: 'path/to/source.cpp'\n" - "Diagnostics: \n" - " - DiagnosticName: 'diagnostic#1\'\n" - " Message: 'message #1'\n" - " FileOffset: 55\n" - " FilePath: 'path/to/source.cpp'\n" - " Replacements: \n" - " - FilePath: 'path/to/source.cpp'\n" - " Offset: 100\n" - " Length: 12\n" - " ReplacementText: 'replacement #1'\n" - " - DiagnosticName: 'diagnostic#2'\n" - " Message: 'message #2'\n" - " FileOffset: 60\n" - " FilePath: 'path/to/header.h'\n" - " Replacements: \n" - " - FilePath: 'path/to/header.h'\n" - " Offset: 62\n" - " Length: 2\n" - " ReplacementText: 'replacement #2'\n" - " - DiagnosticName: 'diagnostic#3'\n" - " Message: 'message #3'\n" - " FileOffset: 72\n" - " FilePath: 'path/to/source2.cpp'\n" - " Notes: \n" - " - Message: Note1\n" - " FilePath: 'path/to/note1.cpp'\n" - " FileOffset: 88\n" - " - Message: Note2\n" - " FilePath: 'path/to/note2.cpp'\n" - " FileOffset: 99\n" - " Replacements: []\n" - "...\n", - YamlContentStream.str()); + EXPECT_EQ(YAMLContent, YamlContentStream.str()); } TEST(DiagnosticsYamlTest, deserializesDiagnostics) { - std::string YamlContent = "---\n" - "MainSourceFile: path/to/source.cpp\n" - "Diagnostics: \n" - " - DiagnosticName: 'diagnostic#1'\n" - " Message: 'message #1'\n" - " FileOffset: 55\n" - " FilePath: path/to/source.cpp\n" - " Replacements: \n" - " - FilePath: path/to/source.cpp\n" - " Offset: 100\n" - " Length: 12\n" - " ReplacementText: 'replacement #1'\n" - " - DiagnosticName: 'diagnostic#2'\n" - " Message: 'message #2'\n" - " FileOffset: 60\n" - " FilePath: path/to/header.h\n" - " Replacements: \n" - " - FilePath: path/to/header.h\n" - " Offset: 62\n" - " Length: 2\n" - " ReplacementText: 'replacement #2'\n" - " - DiagnosticName: 'diagnostic#3'\n" - " Message: 'message #3'\n" - " FileOffset: 98\n" - " FilePath: path/to/source.cpp\n" - " Notes:\n" - " - Message: Note1\n" - " FilePath: 'path/to/note1.cpp'\n" - " FileOffset: 66\n" - " - Message: Note2\n" - " FilePath: 'path/to/note2.cpp'\n" - " FileOffset: 77\n" - " Replacements: []\n" - "...\n"; TranslationUnitDiagnostics TUDActual; - yaml::Input YAML(YamlContent); + yaml::Input YAML(YAMLContent); YAML >> TUDActual; ASSERT_FALSE(YAML.error()); @@ -161,7 +136,7 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { EXPECT_EQ("message #1", D1.Message.Message); EXPECT_EQ(55u, D1.Message.FileOffset); EXPECT_EQ("path/to/source.cpp", D1.Message.FilePath); - std::vector<Replacement> Fixes1 = getFixes(D1.Fix); + std::vector<Replacement> Fixes1 = getFixes(D1.Message.Fix); ASSERT_EQ(1u, Fixes1.size()); EXPECT_EQ("path/to/source.cpp", Fixes1[0].getFilePath()); EXPECT_EQ(100u, Fixes1[0].getOffset()); @@ -173,7 +148,7 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { EXPECT_EQ("message #2", D2.Message.Message); EXPECT_EQ(60u, D2.Message.FileOffset); EXPECT_EQ("path/to/header.h", D2.Message.FilePath); - std::vector<Replacement> Fixes2 = getFixes(D2.Fix); + std::vector<Replacement> Fixes2 = getFixes(D2.Message.Fix); ASSERT_EQ(1u, Fixes2.size()); EXPECT_EQ("path/to/header.h", Fixes2[0].getFilePath()); EXPECT_EQ(62u, Fixes2[0].getOffset()); @@ -183,15 +158,15 @@ TEST(DiagnosticsYamlTest, deserializesDiagnostics) { Diagnostic D3 = TUDActual.Diagnostics[2]; EXPECT_EQ("diagnostic#3", D3.DiagnosticName); EXPECT_EQ("message #3", D3.Message.Message); - EXPECT_EQ(98u, D3.Message.FileOffset); - EXPECT_EQ("path/to/source.cpp", D3.Message.FilePath); + EXPECT_EQ(72u, D3.Message.FileOffset); + EXPECT_EQ("path/to/source2.cpp", D3.Message.FilePath); EXPECT_EQ(2u, D3.Notes.size()); EXPECT_EQ("Note1", D3.Notes[0].Message); - EXPECT_EQ(66u, D3.Notes[0].FileOffset); + EXPECT_EQ(88u, D3.Notes[0].FileOffset); EXPECT_EQ("path/to/note1.cpp", D3.Notes[0].FilePath); EXPECT_EQ("Note2", D3.Notes[1].Message); - EXPECT_EQ(77u, D3.Notes[1].FileOffset); + EXPECT_EQ(99u, D3.Notes[1].FileOffset); EXPECT_EQ("path/to/note2.cpp", D3.Notes[1].FilePath); - std::vector<Replacement> Fixes3 = getFixes(D3.Fix); + std::vector<Replacement> Fixes3 = getFixes(D3.Message.Fix); EXPECT_TRUE(Fixes3.empty()); } diff --git a/unittests/Tooling/ExecutionTest.cpp b/unittests/Tooling/ExecutionTest.cpp index 785ec7c2bc..31d5fe5c42 100644 --- a/unittests/Tooling/ExecutionTest.cpp +++ b/unittests/Tooling/ExecutionTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/ExecutionTest.cpp - Tool execution tests. --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/FixItTest.cpp b/unittests/Tooling/FixItTest.cpp index 365180e67f..ec9801d345 100644 --- a/unittests/Tooling/FixItTest.cpp +++ b/unittests/Tooling/FixItTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/FixitTest.cpp ------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/HeaderIncludesTest.cpp b/unittests/Tooling/HeaderIncludesTest.cpp index ff68f75a6e..635d7ebb1e 100644 --- a/unittests/Tooling/HeaderIncludesTest.cpp +++ b/unittests/Tooling/HeaderIncludesTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/CleanupTest.cpp - Include insertion/deletion tests ===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -15,9 +14,6 @@ #include "gtest/gtest.h" -using clang::tooling::ReplacementTest; -using clang::tooling::toReplacements; - namespace clang { namespace tooling { namespace { @@ -316,6 +312,17 @@ TEST_F(HeaderIncludesTest, RealHeaderGuardAfterComments) { EXPECT_EQ(Expected, insert(Code, "<vector>")); } +TEST_F(HeaderIncludesTest, PragmaOnce) { + std::string Code = "// comment \n" + "#pragma once\n" + "int x;\n"; + std::string Expected = "// comment \n" + "#pragma once\n" + "#include <vector>\n" + "int x;\n"; + EXPECT_EQ(Expected, insert(Code, "<vector>")); +} + TEST_F(HeaderIncludesTest, IfNDefWithNoDefine) { std::string Code = "// comment \n" "#ifndef X\n" diff --git a/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp b/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp index 7387e9c44d..38079f706f 100644 --- a/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp +++ b/unittests/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/LexicallyOrderedRecursiveASTVisitorTest.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/LookupTest.cpp b/unittests/Tooling/LookupTest.cpp index a08b2b418f..372cbbf62b 100644 --- a/unittests/Tooling/LookupTest.cpp +++ b/unittests/Tooling/LookupTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/LookupTest.cpp ------------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -45,8 +44,8 @@ TEST(LookupTest, replaceNestedFunctionName) { const auto *Callee = cast<DeclRefExpr>(Expr->getCallee()->IgnoreImplicit()); const ValueDecl *FD = Callee->getDecl(); return tooling::replaceNestedName( - Callee->getQualifier(), Visitor.DeclStack.back()->getDeclContext(), FD, - ReplacementString); + Callee->getQualifier(), Callee->getLocation(), + Visitor.DeclStack.back()->getDeclContext(), FD, ReplacementString); }; Visitor.OnCall = [&](CallExpr *Expr) { @@ -130,20 +129,38 @@ TEST(LookupTest, replaceNestedFunctionName) { // If the shortest name is ambiguous, we need to add more qualifiers. Visitor.OnCall = [&](CallExpr *Expr) { - EXPECT_EQ("::a::y::bar", replaceCallExpr(Expr, "::a::y::bar")); + EXPECT_EQ("a::y::bar", replaceCallExpr(Expr, "::a::y::bar")); }; Visitor.runOver(R"( namespace a { - namespace b { - namespace x { void foo() {} } - namespace y { void foo() {} } - } + namespace b { + namespace x { void foo() {} } + namespace y { void foo() {} } + } } namespace a { - namespace b { - void f() { x::foo(); } + namespace b { + void f() { x::foo(); } + } + })"); + + Visitor.OnCall = [&](CallExpr *Expr) { + // y::bar would be ambiguous due to "a::b::y". + EXPECT_EQ("::y::bar", replaceCallExpr(Expr, "::y::bar")); + }; + Visitor.runOver(R"( + namespace a { + namespace b { + void foo() {} + namespace y { } + } } + + namespace a { + namespace b { + void f() { foo(); } + } })"); Visitor.OnCall = [&](CallExpr *Expr) { @@ -164,12 +181,12 @@ TEST(LookupTest, replaceNestedFunctionName) { TEST(LookupTest, replaceNestedClassName) { GetDeclsVisitor Visitor; - auto replaceRecordTypeLoc = [&](RecordTypeLoc Loc, + auto replaceRecordTypeLoc = [&](RecordTypeLoc TLoc, StringRef ReplacementString) { - const auto *FD = cast<CXXRecordDecl>(Loc.getDecl()); + const auto *FD = cast<CXXRecordDecl>(TLoc.getDecl()); return tooling::replaceNestedName( - nullptr, Visitor.DeclStack.back()->getDeclContext(), FD, - ReplacementString); + nullptr, TLoc.getBeginLoc(), Visitor.DeclStack.back()->getDeclContext(), + FD, ReplacementString); }; Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) { @@ -194,6 +211,41 @@ TEST(LookupTest, replaceNestedClassName) { }; Visitor.runOver("namespace a { namespace b { class Foo {}; } }\n" "namespace c { using a::b::Foo; Foo f();; }\n"); + + // Rename TypeLoc `x::y::Old` to new name `x::Foo` at [0] and check that the + // type is replaced with "Foo" instead of "x::Foo". Although there is a symbol + // `x::y::Foo` in c.cc [1], it should not make "Foo" at [0] ambiguous because + // it's not visible at [0]. + Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) { + if (Type.getDecl()->getQualifiedNameAsString() == "x::y::Old") { + EXPECT_EQ("Foo", replaceRecordTypeLoc(Type, "::x::Foo")); + } + }; + Visitor.runOver(R"( + // a.h + namespace x { + namespace y { + class Old {}; + class Other {}; + } + } + + // b.h + namespace x { + namespace y { + // This is to be renamed to x::Foo + // The expected replacement is "Foo". + Old f; // [0]. + } + } + + // c.cc + namespace x { + namespace y { + using Foo = ::x::y::Other; // [1] + } + } + )"); } } // end anonymous namespace diff --git a/unittests/Tooling/QualTypeNamesTest.cpp b/unittests/Tooling/QualTypeNamesTest.cpp index b4c56f7bd5..b6c3029778 100644 --- a/unittests/Tooling/QualTypeNamesTest.cpp +++ b/unittests/Tooling/QualTypeNamesTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/QualTypeNameTest.cpp ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -195,6 +194,7 @@ TEST(QualTypeNameTest, getFullyQualifiedName) { GlobalNsPrefix.ExpectedQualTypeNames["ZVal"] = "::A::B::Y::Z"; GlobalNsPrefix.ExpectedQualTypeNames["GlobalZVal"] = "::Z"; GlobalNsPrefix.ExpectedQualTypeNames["CheckK"] = "D::aStruct"; + GlobalNsPrefix.ExpectedQualTypeNames["YZMPtr"] = "::A::B::X ::A::B::Y::Z::*"; GlobalNsPrefix.runOver( "namespace A {\n" " namespace B {\n" @@ -206,8 +206,9 @@ TEST(QualTypeNameTest, getFullyQualifiedName) { " template <typename T>\n" " using Alias = CCC<T>;\n" " Alias<int> IntAliasVal;\n" - " struct Y { struct Z {}; };\n" + " struct Y { struct Z { X YZIPtr; }; };\n" " Y::Z ZVal;\n" + " X Y::Z::*YZMPtr;\n" " }\n" "}\n" "struct Z {};\n" diff --git a/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp index e91873c406..e207f03971 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTestDeclVisitor.cpp ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp index 2e7b398c3d..965bb3d7b7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestPostOrderVisitor.cpp @@ -1,9 +1,8 @@ //===- unittests/Tooling/RecursiveASTVisitorPostOrderASTVisitor.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp index dc2adaf4da..299e1b022a 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTestTypeLocVisitor.cpp ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp index 33163c30e5..022ef8b832 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/Attr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/Attr.cpp -----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp index ca2e4a4691..1fb192dcda 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/CXXBoolLiteralExpr.cpp ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp index a83e55137a..c7b31e06e0 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/CXXMemberCall.cpp --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp b/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp index 414b0f0174..91de8d17c9 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/CXXOperatorCallExprTraverser.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp b/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp index 666c924d1a..3ea5abd46a 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/Class.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/Class.cpp ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp index f775a31e59..b4f4f54dc7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/ConstructExpr.cpp --------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp index cd0e4260a8..adc972e1c3 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/DeclRefExpr.cpp ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp b/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp index c2194ab292..27999e5ef8 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/ImplicitCtor.cpp ---------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp index 396f25de5c..80d9c98735 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPostOrder.cpp -==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp index 587f84bb43..a15f4c83c5 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPostOrderNoQueue.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp index 01f6e19029..401ae6b15d 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPreOrder.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp index d48b5a89c8..1dafeef7cd 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/InitListExprPreOrderNoQueue.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp b/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp index 218f7e0c2d..3fc3cb1a99 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/IntegerLiteral.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp index c3f8e4f419..b1d6d593e7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/LambdaDefaultCapture.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp index d3a3eba15d..560cdf95c0 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp -----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp b/unittests/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp new file mode 100644 index 0000000000..d0e4fb733e --- /dev/null +++ b/unittests/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp @@ -0,0 +1,53 @@ +//===- unittest/Tooling/RecursiveASTVisitorTests/LambdaTemplateParams.cpp -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestVisitor.h" + +using namespace clang; + +namespace { + +// Matches (optional) explicit template parameters. +class LambdaTemplateParametersVisitor + : public ExpectedLocationVisitor<LambdaTemplateParametersVisitor> { +public: + bool shouldVisitImplicitCode() const { return false; } + + bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } + + bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } + + bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + EXPECT_FALSE(D->isImplicit()); + Match(D->getName(), D->getLocStart()); + return true; + } +}; + +TEST(RecursiveASTVisitor, VisitsLambdaExplicitTemplateParameters) { + LambdaTemplateParametersVisitor Visitor; + Visitor.ExpectMatch("T", 2, 15); + Visitor.ExpectMatch("I", 2, 24); + Visitor.ExpectMatch("TT", 2, 31); + EXPECT_TRUE(Visitor.runOver( + "void f() { \n" + " auto l = []<class T, int I, template<class> class TT>(auto p) { }; \n" + "}", + LambdaTemplateParametersVisitor::Lang_CXX2a)); +} + +} // end anonymous namespace diff --git a/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp b/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp index 23afda6e87..868a3988c7 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/NestedNameSpecifiers.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp b/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp index b6a5a18e01..c316f98f40 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/ParenExpr.cpp ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp b/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp index 0c9cbf2eb4..ae427a02bc 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/TemplateArgumentLocTraverser.cpp -===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp b/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp index 72f6c644b3..c05be7f2e3 100644 --- a/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RecursiveASTVisitorTests/TraversalScope.cpp -------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RefactoringActionRulesTest.cpp b/unittests/Tooling/RefactoringActionRulesTest.cpp index acacfa05b4..7daef33337 100644 --- a/unittests/Tooling/RefactoringActionRulesTest.cpp +++ b/unittests/Tooling/RefactoringActionRulesTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RefactoringTestActionRulesTest.cpp ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RefactoringCallbacksTest.cpp b/unittests/Tooling/RefactoringCallbacksTest.cpp index e226522a70..1663581d65 100644 --- a/unittests/Tooling/RefactoringCallbacksTest.cpp +++ b/unittests/Tooling/RefactoringCallbacksTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RefactoringCallbacksTest.cpp ----------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp index d618c0fb07..ed111b7878 100644 --- a/unittests/Tooling/RefactoringTest.cpp +++ b/unittests/Tooling/RefactoringTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RefactoringTest.cpp - Refactoring unit tests ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/ReplacementTest.h b/unittests/Tooling/ReplacementTest.h index b6fe5c79b7..b97e0e7f2f 100644 --- a/unittests/Tooling/ReplacementTest.h +++ b/unittests/Tooling/ReplacementTest.h @@ -1,9 +1,8 @@ //===- unittest/Tooling/ReplacementTest.h - Replacements related test------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/ReplacementsYamlTest.cpp b/unittests/Tooling/ReplacementsYamlTest.cpp index 2e5a87a931..ab9f6c9b5d 100644 --- a/unittests/Tooling/ReplacementsYamlTest.cpp +++ b/unittests/Tooling/ReplacementsYamlTest.cpp @@ -1,9 +1,8 @@ //===- unittests/Tooling/ReplacementsYamlTest.cpp - Serialization tests ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/RewriterTest.cpp b/unittests/Tooling/RewriterTest.cpp index 4305d421e1..e744940783 100644 --- a/unittests/Tooling/RewriterTest.cpp +++ b/unittests/Tooling/RewriterTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/RewriterTest.cpp ----------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/unittests/Tooling/RewriterTestContext.h b/unittests/Tooling/RewriterTestContext.h index 9e66484158..ba919d6472 100644 --- a/unittests/Tooling/RewriterTestContext.h +++ b/unittests/Tooling/RewriterTestContext.h @@ -1,9 +1,8 @@ //===--- RewriterTestContext.h ----------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/unittests/Tooling/SourceCodeTest.cpp b/unittests/Tooling/SourceCodeTest.cpp new file mode 100644 index 0000000000..258947a1a7 --- /dev/null +++ b/unittests/Tooling/SourceCodeTest.cpp @@ -0,0 +1,97 @@ +//===- unittest/Tooling/SourceCodeTest.cpp --------------------------------===// +// +// 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 "TestVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Tooling/Refactoring/SourceCode.h" + +using namespace clang; + +using tooling::getText; +using tooling::getExtendedText; + +namespace { + +struct CallsVisitor : TestVisitor<CallsVisitor> { + bool VisitCallExpr(CallExpr *Expr) { + OnCall(Expr, Context); + return true; + } + + std::function<void(CallExpr *, ASTContext *Context)> OnCall; +}; + +TEST(SourceCodeTest, getText) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo(x, y)", getText(*CE, *Context)); + }; + Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context)); + }; + Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" + "void foo(int x, int y) { APPLY(foo, x, y); }"); +} + +TEST(SourceCodeTest, getTextWithMacro) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("F OO", getText(*CE, *Context)); + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("", getText(*P0, *Context)); + EXPECT_EQ("", getText(*P1, *Context)); + }; + Visitor.runOver("#define F foo(\n" + "#define OO x, y)\n" + "void foo(int x, int y) { F OO ; }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("", getText(*CE, *Context)); + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("x", getText(*P0, *Context)); + EXPECT_EQ("y", getText(*P1, *Context)); + }; + Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" + "void foo(int x, int y) { FOO(x,y) }"); +} + +TEST(SourceCodeTest, getExtendedText) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo(x, y);", + getExtendedText(*CE, tok::TokenKind::semi, *Context)); + + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context)); + EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context)); + EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context)); + }; + Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); + Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }"); + Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }"); + Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }"); + Visitor.runOver( + "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context)); + }; + Visitor.runOver("bool foo() { if (foo()) return true; return false; }"); + Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }"); + Visitor.runOver("int foo() { return foo() + 3; }"); +} + +} // end anonymous namespace diff --git a/unittests/Tooling/StencilTest.cpp b/unittests/Tooling/StencilTest.cpp new file mode 100644 index 0000000000..ffdca0562e --- /dev/null +++ b/unittests/Tooling/StencilTest.cpp @@ -0,0 +1,223 @@ +//===- unittest/Tooling/StencilTest.cpp -----------------------------------===// +// +// 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 "clang/Tooling/Refactoring/Stencil.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/FixIt.h" +#include "clang/Tooling/Tooling.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +using ::testing::AllOf; +using ::testing::Eq; +using ::testing::HasSubstr; +using MatchResult = MatchFinder::MatchResult; +using tooling::stencil::node; +using tooling::stencil::sNode; +using tooling::stencil::text; + +// In tests, we can't directly match on llvm::Expected since its accessors +// mutate the object. So, we collapse it to an Optional. +static llvm::Optional<std::string> toOptional(llvm::Expected<std::string> V) { + if (V) + return *V; + ADD_FAILURE() << "Losing error in conversion to IsSomething: " + << llvm::toString(V.takeError()); + return llvm::None; +} + +// A very simple matcher for llvm::Optional values. +MATCHER_P(IsSomething, ValueMatcher, "") { + if (!arg) + return false; + return ::testing::ExplainMatchResult(ValueMatcher, *arg, result_listener); +} + +// Create a valid translation-unit from a statement. +static std::string wrapSnippet(llvm::Twine StatementCode) { + return ("auto stencil_test_snippet = []{" + StatementCode + "};").str(); +} + +static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { + return varDecl(hasName("stencil_test_snippet"), + hasDescendant(compoundStmt(hasAnySubstatement(Matcher)))); +} + +struct TestMatch { + // The AST unit from which `result` is built. We bundle it because it backs + // the result. Users are not expected to access it. + std::unique_ptr<ASTUnit> AstUnit; + // The result to use in the test. References `ast_unit`. + MatchResult Result; +}; + +// Matches `Matcher` against the statement `StatementCode` and returns the +// result. Handles putting the statement inside a function and modifying the +// matcher correspondingly. `Matcher` should match `StatementCode` exactly -- +// that is, produce exactly one match. +static llvm::Optional<TestMatch> matchStmt(llvm::Twine StatementCode, + StatementMatcher Matcher) { + auto AstUnit = buildASTFromCode(wrapSnippet(StatementCode)); + if (AstUnit == nullptr) { + ADD_FAILURE() << "AST construction failed"; + return llvm::None; + } + ASTContext &Context = AstUnit->getASTContext(); + auto Matches = ast_matchers::match(wrapMatcher(Matcher), Context); + // We expect a single, exact match for the statement. + if (Matches.size() != 1) { + ADD_FAILURE() << "Wrong number of matches: " << Matches.size(); + return llvm::None; + } + return TestMatch{std::move(AstUnit), MatchResult(Matches[0], &Context)}; +} + +class StencilTest : public ::testing::Test { +protected: + // Verifies that the given stencil fails when evaluated on a valid match + // result. Binds a statement to "stmt", a (non-member) ctor-initializer to + // "init", an expression to "expr" and a (nameless) declaration to "decl". + void testError(const Stencil &Stencil, + ::testing::Matcher<std::string> Matcher) { + const std::string Snippet = R"cc( + struct A {}; + class F : public A { + public: + F(int) {} + }; + F(1); + )cc"; + auto StmtMatch = matchStmt( + Snippet, + stmt(hasDescendant( + cxxConstructExpr( + hasDeclaration(decl(hasDescendant(cxxCtorInitializer( + isBaseInitializer()) + .bind("init"))) + .bind("decl"))) + .bind("expr"))) + .bind("stmt")); + ASSERT_TRUE(StmtMatch); + if (auto ResultOrErr = Stencil.eval(StmtMatch->Result)) { + ADD_FAILURE() << "Expected failure but succeeded: " << *ResultOrErr; + } else { + auto Err = llvm::handleErrors(ResultOrErr.takeError(), + [&Matcher](const llvm::StringError &Err) { + EXPECT_THAT(Err.getMessage(), Matcher); + }); + if (Err) { + ADD_FAILURE() << "Unhandled error: " << llvm::toString(std::move(Err)); + } + } + } + + // Tests failures caused by references to unbound nodes. `unbound_id` is the + // id that will cause the failure. + void testUnboundNodeError(const Stencil &Stencil, llvm::StringRef UnboundId) { + testError(Stencil, AllOf(HasSubstr(UnboundId), HasSubstr("not bound"))); + } +}; + +TEST_F(StencilTest, SingleStatement) { + StringRef Condition("C"), Then("T"), Else("E"); + const std::string Snippet = R"cc( + if (true) + return 1; + else + return 0; + )cc"; + auto StmtMatch = matchStmt( + Snippet, ifStmt(hasCondition(expr().bind(Condition)), + hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); + ASSERT_TRUE(StmtMatch); + // Invert the if-then-else. + auto Stencil = Stencil::cat("if (!", node(Condition), ") ", sNode(Else), + " else ", sNode(Then)); + EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), + IsSomething(Eq("if (!true) return 0; else return 1;"))); +} + +TEST_F(StencilTest, SingleStatementCallOperator) { + StringRef Condition("C"), Then("T"), Else("E"); + const std::string Snippet = R"cc( + if (true) + return 1; + else + return 0; + )cc"; + auto StmtMatch = matchStmt( + Snippet, ifStmt(hasCondition(expr().bind(Condition)), + hasThen(stmt().bind(Then)), hasElse(stmt().bind(Else)))); + ASSERT_TRUE(StmtMatch); + // Invert the if-then-else. + Stencil S = Stencil::cat("if (!", node(Condition), ") ", sNode(Else), + " else ", sNode(Then)); + EXPECT_THAT(toOptional(S(StmtMatch->Result)), + IsSomething(Eq("if (!true) return 0; else return 1;"))); +} + +TEST_F(StencilTest, UnboundNode) { + const std::string Snippet = R"cc( + if (true) + return 1; + else + return 0; + )cc"; + auto StmtMatch = matchStmt(Snippet, ifStmt(hasCondition(stmt().bind("a1")), + hasThen(stmt().bind("a2")))); + ASSERT_TRUE(StmtMatch); + auto Stencil = Stencil::cat("if(!", sNode("a1"), ") ", node("UNBOUND"), ";"); + auto ResultOrErr = Stencil.eval(StmtMatch->Result); + EXPECT_TRUE(llvm::errorToBool(ResultOrErr.takeError())) + << "Expected unbound node, got " << *ResultOrErr; +} + +// Tests that a stencil with a single parameter (`Id`) evaluates to the expected +// string, when `Id` is bound to the expression-statement in `Snippet`. +void testExpr(StringRef Id, StringRef Snippet, const Stencil &Stencil, + StringRef Expected) { + auto StmtMatch = matchStmt(Snippet, expr().bind(Id)); + ASSERT_TRUE(StmtMatch); + EXPECT_THAT(toOptional(Stencil.eval(StmtMatch->Result)), + IsSomething(Expected)); +} + +TEST_F(StencilTest, NodeOp) { + StringRef Id = "id"; + testExpr(Id, "3;", Stencil::cat(node(Id)), "3"); +} + +TEST_F(StencilTest, SNodeOp) { + StringRef Id = "id"; + testExpr(Id, "3;", Stencil::cat(sNode(Id)), "3;"); +} + +TEST(StencilEqualityTest, Equality) { + using stencil::dPrint; + auto Lhs = Stencil::cat("foo", node("node"), dPrint("dprint_id")); + auto Rhs = Lhs; + EXPECT_EQ(Lhs, Rhs); +} + +TEST(StencilEqualityTest, InEqualityDifferentOrdering) { + auto Lhs = Stencil::cat("foo", node("node")); + auto Rhs = Stencil::cat(node("node"), "foo"); + EXPECT_NE(Lhs, Rhs); +} + +TEST(StencilEqualityTest, InEqualityDifferentSizes) { + auto Lhs = Stencil::cat("foo", node("node"), "bar", "baz"); + auto Rhs = Stencil::cat("foo", node("node"), "bar"); + EXPECT_NE(Lhs, Rhs); +} +} // namespace diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h index 1a22ae737b..ff90a77a69 100644 --- a/unittests/Tooling/TestVisitor.h +++ b/unittests/Tooling/TestVisitor.h @@ -1,9 +1,8 @@ //===--- TestVisitor.h ------------------------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp index 186463f80a..34f68a6aeb 100644 --- a/unittests/Tooling/ToolingTest.cpp +++ b/unittests/Tooling/ToolingTest.cpp @@ -1,9 +1,8 @@ //===- unittest/Tooling/ToolingTest.cpp - Tooling unit tests --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -383,7 +382,7 @@ TEST(ClangToolTest, ArgumentAdjusters) { ArgumentsAdjuster CheckSyntaxOnlyAdjuster = [&Found, &Ran](const CommandLineArguments &Args, StringRef /*unused*/) { Ran = true; - if (std::find(Args.begin(), Args.end(), "-fsyntax-only") != Args.end()) + if (llvm::is_contained(Args, "-fsyntax-only")) Found = true; return Args; }; @@ -441,8 +440,7 @@ TEST(ClangToolTest, StripDependencyFileAdjuster) { Tool.run(Action.get()); auto HasFlag = [&FinalArgs](const std::string &Flag) { - return std::find(FinalArgs.begin(), FinalArgs.end(), Flag) != - FinalArgs.end(); + return llvm::find(FinalArgs, Flag) != FinalArgs.end(); }; EXPECT_FALSE(HasFlag("-MD")); EXPECT_FALSE(HasFlag("-MMD")); @@ -450,6 +448,36 @@ TEST(ClangToolTest, StripDependencyFileAdjuster) { EXPECT_TRUE(HasFlag("-w")); } +// Check getClangStripPluginsAdjuster strips plugin related args. +TEST(ClangToolTest, StripPluginsAdjuster) { + FixedCompilationDatabase Compilations( + "/", {"-Xclang", "-add-plugin", "-Xclang", "random-plugin"}); + + ClangTool Tool(Compilations, std::vector<std::string>(1, "/a.cc")); + Tool.mapVirtualFile("/a.cc", "void a() {}"); + + std::unique_ptr<FrontendActionFactory> Action( + newFrontendActionFactory<SyntaxOnlyAction>()); + + CommandLineArguments FinalArgs; + ArgumentsAdjuster CheckFlagsAdjuster = + [&FinalArgs](const CommandLineArguments &Args, StringRef /*unused*/) { + FinalArgs = Args; + return Args; + }; + Tool.clearArgumentsAdjusters(); + Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); + Tool.appendArgumentsAdjuster(CheckFlagsAdjuster); + Tool.run(Action.get()); + + auto HasFlag = [&FinalArgs](const std::string &Flag) { + return llvm::find(FinalArgs, Flag) != FinalArgs.end(); + }; + EXPECT_FALSE(HasFlag("-Xclang")); + EXPECT_FALSE(HasFlag("-add-plugin")); + EXPECT_FALSE(HasFlag("-random-plugin")); +} + namespace { /// Find a target name such that looking for it in TargetRegistry by that name /// returns the same target. We expect that there is at least one target diff --git a/unittests/Tooling/TransformerTest.cpp b/unittests/Tooling/TransformerTest.cpp new file mode 100644 index 0000000000..e07d9b7029 --- /dev/null +++ b/unittests/Tooling/TransformerTest.cpp @@ -0,0 +1,462 @@ +//===- unittest/Tooling/TransformerTest.cpp -------------------------------===// +// +// 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 "clang/Tooling/Refactoring/Transformer.h" + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +using ::testing::IsEmpty; + +constexpr char KHeaderContents[] = R"cc( + struct string { + string(const char*); + char* c_str(); + int size(); + }; + int strlen(const char*); + + namespace proto { + struct PCFProto { + int foo(); + }; + struct ProtoCommandLineFlag : PCFProto { + PCFProto& GetProto(); + }; + } // namespace proto + class Logger {}; + void operator<<(Logger& l, string msg); + Logger& log(int level); +)cc"; + +static ast_matchers::internal::Matcher<clang::QualType> +isOrPointsTo(const clang::ast_matchers::DeclarationMatcher &TypeMatcher) { + return anyOf(hasDeclaration(TypeMatcher), pointsTo(TypeMatcher)); +} + +static std::string format(StringRef Code) { + const std::vector<Range> Ranges(1, Range(0, Code.size())); + auto Style = format::getLLVMStyle(); + const auto Replacements = format::reformat(Style, Code, Ranges); + auto Formatted = applyAllReplacements(Code, Replacements); + if (!Formatted) { + ADD_FAILURE() << "Could not format code: " + << llvm::toString(Formatted.takeError()); + return std::string(); + } + return *Formatted; +} + +static void compareSnippets(StringRef Expected, + const llvm::Optional<std::string> &MaybeActual) { + ASSERT_TRUE(MaybeActual) << "Rewrite failed. Expecting: " << Expected; + auto Actual = *MaybeActual; + std::string HL = "#include \"header.h\"\n"; + auto I = Actual.find(HL); + if (I != std::string::npos) + Actual.erase(I, HL.size()); + EXPECT_EQ(format(Expected), format(Actual)); +} + +// FIXME: consider separating this class into its own file(s). +class ClangRefactoringTestBase : public testing::Test { +protected: + void appendToHeader(StringRef S) { FileContents[0].second += S; } + + void addFile(StringRef Filename, StringRef Content) { + FileContents.emplace_back(Filename, Content); + } + + llvm::Optional<std::string> rewrite(StringRef Input) { + std::string Code = ("#include \"header.h\"\n" + Input).str(); + auto Factory = newFrontendActionFactory(&MatchFinder); + if (!runToolOnCodeWithArgs( + Factory->create(), Code, std::vector<std::string>(), "input.cc", + "clang-tool", std::make_shared<PCHContainerOperations>(), + FileContents)) { + llvm::errs() << "Running tool failed.\n"; + return None; + } + if (ErrorCount != 0) { + llvm::errs() << "Generating changes failed.\n"; + return None; + } + auto ChangedCode = + applyAtomicChanges("input.cc", Code, Changes, ApplyChangesSpec()); + if (!ChangedCode) { + llvm::errs() << "Applying changes failed: " + << llvm::toString(ChangedCode.takeError()) << "\n"; + return None; + } + return *ChangedCode; + } + + Transformer::ChangeConsumer consumer() { + return [this](Expected<AtomicChange> C) { + if (C) { + Changes.push_back(std::move(*C)); + } else { + consumeError(C.takeError()); + ++ErrorCount; + } + }; + } + + void testRule(RewriteRule Rule, StringRef Input, StringRef Expected) { + Transformer T(std::move(Rule), consumer()); + T.registerMatchers(&MatchFinder); + compareSnippets(Expected, rewrite(Input)); + } + + clang::ast_matchers::MatchFinder MatchFinder; + // Records whether any errors occurred in individual changes. + int ErrorCount = 0; + AtomicChanges Changes; + +private: + FileContentMappings FileContents = {{"header.h", ""}}; +}; + +class TransformerTest : public ClangRefactoringTestBase { +protected: + TransformerTest() { appendToHeader(KHeaderContents); } +}; + +// Given string s, change strlen($s.c_str()) to $s.size(). +static RewriteRule ruleStrlenSize() { + StringRef StringExpr = "strexpr"; + auto StringType = namedDecl(hasAnyName("::basic_string", "::string")); + auto R = makeRule( + callExpr(callee(functionDecl(hasName("strlen"))), + hasArgument(0, cxxMemberCallExpr( + on(expr(hasType(isOrPointsTo(StringType))) + .bind(StringExpr)), + callee(cxxMethodDecl(hasName("c_str")))))), + change<clang::Expr>("REPLACED")); + R.Explanation = text("Use size() method directly on string."); + return R; +} + +TEST_F(TransformerTest, StrlenSize) { + std::string Input = "int f(string s) { return strlen(s.c_str()); }"; + std::string Expected = "int f(string s) { return REPLACED; }"; + testRule(ruleStrlenSize(), Input, Expected); +} + +// Tests that no change is applied when a match is not expected. +TEST_F(TransformerTest, NoMatch) { + std::string Input = "int f(string s) { return s.size(); }"; + testRule(ruleStrlenSize(), Input, Input); +} + +// Tests that expressions in macro arguments are rewritten (when applicable). +TEST_F(TransformerTest, StrlenSizeMacro) { + std::string Input = R"cc( +#define ID(e) e + int f(string s) { return ID(strlen(s.c_str())); })cc"; + std::string Expected = R"cc( +#define ID(e) e + int f(string s) { return ID(REPLACED); })cc"; + testRule(ruleStrlenSize(), Input, Expected); +} + +// Tests replacing an expression. +TEST_F(TransformerTest, Flag) { + StringRef Flag = "flag"; + RewriteRule Rule = makeRule( + cxxMemberCallExpr(on(expr(hasType(cxxRecordDecl( + hasName("proto::ProtoCommandLineFlag")))) + .bind(Flag)), + unless(callee(cxxMethodDecl(hasName("GetProto"))))), + change<clang::Expr>(Flag, "EXPR")); + + std::string Input = R"cc( + proto::ProtoCommandLineFlag flag; + int x = flag.foo(); + int y = flag.GetProto().foo(); + )cc"; + std::string Expected = R"cc( + proto::ProtoCommandLineFlag flag; + int x = EXPR.foo(); + int y = flag.GetProto().foo(); + )cc"; + + testRule(std::move(Rule), Input, Expected); +} + +TEST_F(TransformerTest, NodePartNameNamedDecl) { + StringRef Fun = "fun"; + RewriteRule Rule = + makeRule(functionDecl(hasName("bad")).bind(Fun), + change<clang::FunctionDecl>(Fun, NodePart::Name, "good")); + + std::string Input = R"cc( + int bad(int x); + int bad(int x) { return x * x; } + )cc"; + std::string Expected = R"cc( + int good(int x); + int good(int x) { return x * x; } + )cc"; + + testRule(Rule, Input, Expected); +} + +TEST_F(TransformerTest, NodePartNameDeclRef) { + std::string Input = R"cc( + template <typename T> + T bad(T x) { + return x; + } + int neutral(int x) { return bad<int>(x) * x; } + )cc"; + std::string Expected = R"cc( + template <typename T> + T bad(T x) { + return x; + } + int neutral(int x) { return good<int>(x) * x; } + )cc"; + + StringRef Ref = "ref"; + testRule(makeRule(declRefExpr(to(functionDecl(hasName("bad")))).bind(Ref), + change<clang::Expr>(Ref, NodePart::Name, "good")), + Input, Expected); +} + +TEST_F(TransformerTest, NodePartNameDeclRefFailure) { + std::string Input = R"cc( + struct Y { + int operator*(); + }; + int neutral(int x) { + Y y; + int (Y::*ptr)() = &Y::operator*; + return *y + x; + } + )cc"; + + StringRef Ref = "ref"; + Transformer T(makeRule(declRefExpr(to(functionDecl())).bind(Ref), + change<clang::Expr>(Ref, NodePart::Name, "good")), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); +} + +TEST_F(TransformerTest, NodePartMember) { + StringRef E = "expr"; + RewriteRule Rule = makeRule(memberExpr(member(hasName("bad"))).bind(E), + change<clang::Expr>(E, NodePart::Member, "good")); + + std::string Input = R"cc( + struct S { + int bad; + }; + int g() { + S s; + return s.bad; + } + )cc"; + std::string Expected = R"cc( + struct S { + int bad; + }; + int g() { + S s; + return s.good; + } + )cc"; + + testRule(Rule, Input, Expected); +} + +TEST_F(TransformerTest, NodePartMemberQualified) { + std::string Input = R"cc( + struct S { + int bad; + int good; + }; + struct T : public S { + int bad; + }; + int g() { + T t; + return t.S::bad; + } + )cc"; + std::string Expected = R"cc( + struct S { + int bad; + int good; + }; + struct T : public S { + int bad; + }; + int g() { + T t; + return t.S::good; + } + )cc"; + + StringRef E = "expr"; + testRule(makeRule(memberExpr().bind(E), + change<clang::Expr>(E, NodePart::Member, "good")), + Input, Expected); +} + +TEST_F(TransformerTest, NodePartMemberMultiToken) { + std::string Input = R"cc( + struct Y { + int operator*(); + int good(); + template <typename T> void foo(T t); + }; + int neutral(int x) { + Y y; + y.template foo<int>(3); + return y.operator *(); + } + )cc"; + std::string Expected = R"cc( + struct Y { + int operator*(); + int good(); + template <typename T> void foo(T t); + }; + int neutral(int x) { + Y y; + y.template good<int>(3); + return y.good(); + } + )cc"; + + StringRef MemExpr = "member"; + testRule(makeRule(memberExpr().bind(MemExpr), + change<clang::Expr>(MemExpr, NodePart::Member, "good")), + Input, Expected); +} + +TEST_F(TransformerTest, MultiChange) { + std::string Input = R"cc( + void foo() { + if (10 > 1.0) + log(1) << "oh no!"; + else + log(0) << "ok"; + } + )cc"; + std::string Expected = R"( + void foo() { + if (true) { /* then */ } + else { /* else */ } + } + )"; + + StringRef C = "C", T = "T", E = "E"; + testRule(makeRule(ifStmt(hasCondition(expr().bind(C)), + hasThen(stmt().bind(T)), hasElse(stmt().bind(E))), + {change<Expr>(C, "true"), change<Stmt>(T, "{ /* then */ }"), + change<Stmt>(E, "{ /* else */ }")}), + Input, Expected); +} + +// +// Negative tests (where we expect no transformation to occur). +// + +// Tests for a conflict in edits from a single match for a rule. +TEST_F(TransformerTest, TextGeneratorFailure) { + std::string Input = "int conflictOneRule() { return 3 + 7; }"; + // Try to change the whole binary-operator expression AND one its operands: + StringRef O = "O"; + auto AlwaysFail = [](const ast_matchers::MatchFinder::MatchResult &) + -> llvm::Expected<std::string> { + return llvm::createStringError(llvm::errc::invalid_argument, "ERROR"); + }; + Transformer T(makeRule(binaryOperator().bind(O), change<Expr>(O, AlwaysFail)), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 1); +} + +// Tests for a conflict in edits from a single match for a rule. +TEST_F(TransformerTest, OverlappingEditsInRule) { + std::string Input = "int conflictOneRule() { return 3 + 7; }"; + // Try to change the whole binary-operator expression AND one its operands: + StringRef O = "O", L = "L"; + Transformer T( + makeRule(binaryOperator(hasLHS(expr().bind(L))).bind(O), + {change<Expr>(O, "DELETE_OP"), change<Expr>(L, "DELETE_LHS")}), + consumer()); + T.registerMatchers(&MatchFinder); + EXPECT_FALSE(rewrite(Input)); + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 1); +} + +// Tests for a conflict in edits across multiple matches (of the same rule). +TEST_F(TransformerTest, OverlappingEditsMultipleMatches) { + std::string Input = "int conflictOneRule() { return -7; }"; + // Try to change the whole binary-operator expression AND one its operands: + StringRef E = "E"; + Transformer T(makeRule(expr().bind(E), change<Expr>(E, "DELETE_EXPR")), + consumer()); + T.registerMatchers(&MatchFinder); + // The rewrite process fails because the changes conflict with each other... + EXPECT_FALSE(rewrite(Input)); + // ... but two changes were produced. + EXPECT_EQ(Changes.size(), 2u); + EXPECT_EQ(ErrorCount, 0); +} + +TEST_F(TransformerTest, ErrorOccurredMatchSkipped) { + // Syntax error in the function body: + std::string Input = "void errorOccurred() { 3 }"; + Transformer T(makeRule(functionDecl(hasName("errorOccurred")), + change<Decl>("DELETED;")), + consumer()); + T.registerMatchers(&MatchFinder); + // The rewrite process itself fails... + EXPECT_FALSE(rewrite(Input)); + // ... and no changes or errors are produced in the process. + EXPECT_THAT(Changes, IsEmpty()); + EXPECT_EQ(ErrorCount, 0); +} + +TEST_F(TransformerTest, NoTransformationInMacro) { + std::string Input = R"cc( +#define MACRO(str) strlen((str).c_str()) + int f(string s) { return MACRO(s); })cc"; + testRule(ruleStrlenSize(), Input, Input); +} + +// This test handles the corner case where a macro called within another macro +// expands to matching code, but the matched code is an argument to the nested +// macro. A simple check of isMacroArgExpansion() vs. isMacroBodyExpansion() +// will get this wrong, and transform the code. This test verifies that no such +// transformation occurs. +TEST_F(TransformerTest, NoTransformationInNestedMacro) { + std::string Input = R"cc( +#define NESTED(e) e +#define MACRO(str) NESTED(strlen((str).c_str())) + int f(string s) { return MACRO(s); })cc"; + testRule(ruleStrlenSize(), Input, Input); +} +} // namespace |