xref: /llvm-project/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h (revision 1feb7af046889728233e67e3163ab30020207bb2)
15934a791SAdam Czachorowski //===--- TweakTesting.h - Test helpers for refactoring actions ---*- C++-*-===//
25934a791SAdam Czachorowski //
35934a791SAdam Czachorowski // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45934a791SAdam Czachorowski // See https://llvm.org/LICENSE.txt for license information.
55934a791SAdam Czachorowski // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65934a791SAdam Czachorowski //
75934a791SAdam Czachorowski //===----------------------------------------------------------------------===//
85934a791SAdam Czachorowski 
95bd643d3SChristian Kühnel #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
105bd643d3SChristian Kühnel #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_TWEAKS_TWEAKTESTING_H
115934a791SAdam Czachorowski 
12a316a981SSam McCall #include "ParsedAST.h"
135934a791SAdam Czachorowski #include "index/Index.h"
145934a791SAdam Czachorowski #include "llvm/ADT/StringMap.h"
155934a791SAdam Czachorowski #include "llvm/ADT/StringRef.h"
163432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h"
175934a791SAdam Czachorowski #include "gmock/gmock.h"
185934a791SAdam Czachorowski #include "gtest/gtest.h"
195934a791SAdam Czachorowski #include <memory>
205934a791SAdam Czachorowski #include <string>
215934a791SAdam Czachorowski 
225934a791SAdam Czachorowski namespace clang {
235934a791SAdam Czachorowski namespace clangd {
245934a791SAdam Czachorowski 
255934a791SAdam Czachorowski // Fixture base for testing tweaks. Intended to be subclassed for each tweak.
265934a791SAdam Czachorowski //
275934a791SAdam Czachorowski // Usage:
28*1feb7af0Sv1nh1shungry // TWEAK_TEST(ExpandDeducedType);
295934a791SAdam Czachorowski //
30*1feb7af0Sv1nh1shungry // TEST_F(ExpandDeducedTypeTest, ShortensTypes) {
315934a791SAdam Czachorowski //   Header = R"cpp(
325934a791SAdam Czachorowski //     namespace foo { template<typename> class X{}; }
335934a791SAdam Czachorowski //     using namespace foo;
345934a791SAdam Czachorowski //   )cpp";
355934a791SAdam Czachorowski //   Context = Function;
365934a791SAdam Czachorowski //   EXPECT_THAT(apply("[[auto]] X = foo<int>();"),
375934a791SAdam Czachorowski //               "foo<int> X = foo<int();");
385934a791SAdam Czachorowski //   EXPECT_AVAILABLE("^a^u^t^o^ X = foo<int>();");
395934a791SAdam Czachorowski //   EXPECT_UNAVAILABLE("auto ^X^ = ^foo<int>();");
405934a791SAdam Czachorowski // }
415934a791SAdam Czachorowski class TweakTest : public ::testing::Test {
425934a791SAdam Czachorowski   const char *TweakID;
435934a791SAdam Czachorowski 
445934a791SAdam Czachorowski public:
455934a791SAdam Czachorowski   // Inputs are wrapped in file boilerplate before attempting to apply a tweak.
465934a791SAdam Czachorowski   // Context describes the type of boilerplate.
475934a791SAdam Czachorowski   enum CodeContext {
485934a791SAdam Czachorowski     // Code snippet is placed directly into the source file. e.g. a declaration.
495934a791SAdam Czachorowski     File,
505934a791SAdam Czachorowski     // Snippet will appear within a function body. e.g. a statement.
515934a791SAdam Czachorowski     Function,
525934a791SAdam Czachorowski     // Snippet is an expression.
535934a791SAdam Czachorowski     Expression,
545934a791SAdam Czachorowski   };
555934a791SAdam Czachorowski 
565934a791SAdam Czachorowski   // Mapping from file name to contents.
575934a791SAdam Czachorowski   llvm::StringMap<std::string> ExtraFiles;
585934a791SAdam Czachorowski 
595934a791SAdam Czachorowski protected:
TweakTest(const char * TweakID)605934a791SAdam Czachorowski   TweakTest(const char *TweakID) : TweakID(TweakID) {}
615934a791SAdam Czachorowski 
625934a791SAdam Czachorowski   // Contents of a header file to be implicitly included.
635934a791SAdam Czachorowski   // This typically contains declarations that will be used for a set of related
645934a791SAdam Czachorowski   // testcases.
655934a791SAdam Czachorowski   std::string Header;
665934a791SAdam Czachorowski 
675934a791SAdam Czachorowski   llvm::StringRef FileName = "TestTU.cpp";
685934a791SAdam Czachorowski 
695934a791SAdam Czachorowski   // Extra flags passed to the compilation in apply().
705934a791SAdam Czachorowski   std::vector<std::string> ExtraArgs;
715934a791SAdam Czachorowski 
725934a791SAdam Czachorowski   // Context in which snippets of code should be placed to run tweaks.
735934a791SAdam Czachorowski   CodeContext Context = File;
745934a791SAdam Czachorowski 
755934a791SAdam Czachorowski   // Index to be passed into Tweak::Selection.
765934a791SAdam Czachorowski   std::unique_ptr<const SymbolIndex> Index = nullptr;
775934a791SAdam Czachorowski 
785934a791SAdam Czachorowski   // Apply the current tweak to the range (or point) in MarkedCode.
795934a791SAdam Czachorowski   // MarkedCode will be wrapped according to the Context.
805934a791SAdam Czachorowski   //  - if the tweak produces edits, returns the edited code (without markings)
815934a791SAdam Czachorowski   //    for the main file.
825934a791SAdam Czachorowski   //    Populates \p EditedFiles if there were changes to other files whenever
835934a791SAdam Czachorowski   //    it is non-null. It is a mapping from absolute path of the edited file to
845934a791SAdam Czachorowski   //    its new contents. Passing a nullptr to \p EditedFiles when there are
855934a791SAdam Czachorowski   //    changes, will result in a failure.
865934a791SAdam Czachorowski   //    The context added to MarkedCode will be stripped away before returning,
875934a791SAdam Czachorowski   //    unless the tweak edited it.
885934a791SAdam Czachorowski   //  - if the tweak produces a message, returns "message:\n<message>"
895934a791SAdam Czachorowski   //  - if prepare() returns false, returns "unavailable"
905934a791SAdam Czachorowski   //  - if apply() returns an error, returns "fail: <message>"
915934a791SAdam Czachorowski   std::string apply(llvm::StringRef MarkedCode,
925934a791SAdam Czachorowski                     llvm::StringMap<std::string> *EditedFiles = nullptr) const;
935934a791SAdam Czachorowski 
94a316a981SSam McCall   // Helpers for EXPECT_AVAILABLE/EXPECT_UNAVAILABLE macros.
95a316a981SSam McCall   using WrappedAST = std::pair<ParsedAST, /*WrappingOffset*/ unsigned>;
96a316a981SSam McCall   WrappedAST build(llvm::StringRef) const;
97a316a981SSam McCall   bool isAvailable(WrappedAST &, llvm::Annotations::Range) const;
98a316a981SSam McCall   // Return code re-decorated with a single point/range.
99a316a981SSam McCall   static std::string decorate(llvm::StringRef, unsigned);
100a316a981SSam McCall   static std::string decorate(llvm::StringRef, llvm::Annotations::Range);
1015934a791SAdam Czachorowski };
1025934a791SAdam Czachorowski 
1035934a791SAdam Czachorowski MATCHER_P2(FileWithContents, FileName, Contents, "") {
1045934a791SAdam Czachorowski   return arg.first() == FileName && arg.second == Contents;
1055934a791SAdam Czachorowski }
1065934a791SAdam Czachorowski 
1075934a791SAdam Czachorowski #define TWEAK_TEST(TweakID)                                                    \
1085934a791SAdam Czachorowski   class TweakID##Test : public ::clang::clangd::TweakTest {                    \
1095934a791SAdam Czachorowski   protected:                                                                   \
1105934a791SAdam Czachorowski     TweakID##Test() : TweakTest(#TweakID) {}                                   \
1115934a791SAdam Czachorowski   }
1125934a791SAdam Czachorowski 
113a316a981SSam McCall #define EXPECT_AVAILABLE_(MarkedCode, Available)                               \
1145934a791SAdam Czachorowski   do {                                                                         \
115a316a981SSam McCall     llvm::Annotations A{llvm::StringRef(MarkedCode)};                          \
116a316a981SSam McCall     auto AST = build(A.code());                                                \
117a316a981SSam McCall     assert(!A.points().empty() || !A.ranges().empty());                        \
118a316a981SSam McCall     for (const auto &P : A.points())                                           \
119a316a981SSam McCall       EXPECT_EQ(Available, isAvailable(AST, {P, P})) << decorate(A.code(), P); \
120a316a981SSam McCall     for (const auto &R : A.ranges())                                           \
121a316a981SSam McCall       EXPECT_EQ(Available, isAvailable(AST, R)) << decorate(A.code(), R);      \
1225934a791SAdam Czachorowski   } while (0)
123a316a981SSam McCall #define EXPECT_AVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, true)
124a316a981SSam McCall #define EXPECT_UNAVAILABLE(MarkedCode) EXPECT_AVAILABLE_(MarkedCode, false)
1255934a791SAdam Czachorowski 
1265934a791SAdam Czachorowski } // namespace clangd
1275934a791SAdam Czachorowski } // namespace clang
1285934a791SAdam Czachorowski 
1295934a791SAdam Czachorowski #endif
130