clang-tools 19.0.0git
TweakTesting.cpp
Go to the documentation of this file.
1//===-- TweakTesting.cpp ------------------------------------------------*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "TweakTesting.h"
10
11#include "SourceCode.h"
12#include "TestTU.h"
13#include "refactor/Tweak.h"
14#include "llvm/Support/Error.h"
15#include "gmock/gmock.h"
16#include "gtest/gtest.h"
17#include <optional>
18#include <string>
19
20namespace clang {
21namespace clangd {
22namespace {
23using Context = TweakTest::CodeContext;
24
25std::pair<llvm::StringRef, llvm::StringRef> wrapping(Context Ctx) {
26 switch (Ctx) {
27 case TweakTest::File:
28 return {"", ""};
30 return {"void wrapperFunction(){\n", "\n}"};
32 return {"auto expressionWrapper(){return\n", "\n;}"};
33 }
34 llvm_unreachable("Unknown TweakTest::CodeContext enum");
35}
36
37std::string wrap(Context Ctx, llvm::StringRef Inner) {
38 auto Wrapping = wrapping(Ctx);
39 return (Wrapping.first + Inner + Wrapping.second).str();
40}
41
42llvm::StringRef unwrap(Context Ctx, llvm::StringRef Outer) {
43 auto Wrapping = wrapping(Ctx);
44 // Unwrap only if the code matches the expected wrapping.
45 // Don't allow the begin/end wrapping to overlap!
46 if (Outer.starts_with(Wrapping.first) && Outer.ends_with(Wrapping.second) &&
47 Outer.size() >= Wrapping.first.size() + Wrapping.second.size())
48 return Outer.drop_front(Wrapping.first.size())
49 .drop_back(Wrapping.second.size());
50 return Outer;
51}
52
53llvm::Annotations::Range rangeOrPoint(const llvm::Annotations &A) {
54 if (A.points().size() != 0) {
55 assert(A.ranges().size() == 0 &&
56 "both a cursor point and a selection range were specified");
57 return {A.point(), A.point()};
58 }
59 return A.range();
60}
61
62// Prepare and apply the specified tweak based on the selection in Input.
63// Returns std::nullopt if and only if prepare() failed.
64std::optional<llvm::Expected<Tweak::Effect>>
65applyTweak(ParsedAST &AST, llvm::Annotations::Range Range, StringRef TweakID,
66 const SymbolIndex *Index, llvm::vfs::FileSystem *FS) {
67 std::optional<llvm::Expected<Tweak::Effect>> Result;
68 SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Range.Begin,
69 Range.End, [&](SelectionTree ST) {
70 Tweak::Selection S(Index, AST, Range.Begin,
71 Range.End, std::move(ST), FS);
72 if (auto T = prepareTweak(TweakID, S, nullptr)) {
73 Result = (*T)->apply(S);
74 return true;
75 } else {
76 llvm::consumeError(T.takeError());
77 return false;
78 }
79 });
80 return Result;
81}
82
83} // namespace
84
85std::string TweakTest::apply(llvm::StringRef MarkedCode,
86 llvm::StringMap<std::string> *EditedFiles) const {
87 std::string WrappedCode = wrap(Context, MarkedCode);
88 llvm::Annotations Input(WrappedCode);
89 TestTU TU;
90 TU.Filename = std::string(FileName);
91 TU.HeaderCode = Header;
92 TU.AdditionalFiles = std::move(ExtraFiles);
93 TU.Code = std::string(Input.code());
94 TU.ExtraArgs = ExtraArgs;
95 ParsedAST AST = TU.build();
96
97 auto Result = applyTweak(
98 AST, rangeOrPoint(Input), TweakID, Index.get(),
99 &AST.getSourceManager().getFileManager().getVirtualFileSystem());
100 if (!Result)
101 return "unavailable";
102 if (!*Result)
103 return "fail: " + llvm::toString(Result->takeError());
104 const auto &Effect = **Result;
105 if ((*Result)->ShowMessage)
106 return "message:\n" + *Effect.ShowMessage;
107 if (Effect.ApplyEdits.empty())
108 return "no effect";
109
110 std::string EditedMainFile;
111 for (auto &It : Effect.ApplyEdits) {
112 auto NewText = It.second.apply();
113 if (!NewText)
114 return "bad edits: " + llvm::toString(NewText.takeError());
115 llvm::StringRef Unwrapped = unwrap(Context, *NewText);
116 if (It.first() == testPath(TU.Filename))
117 EditedMainFile = std::string(Unwrapped);
118 else {
119 if (!EditedFiles)
120 ADD_FAILURE() << "There were changes to additional files, but client "
121 "provided a nullptr for EditedFiles.";
122 else
123 EditedFiles->insert_or_assign(It.first(), Unwrapped.str());
124 }
125 }
126 return EditedMainFile;
127}
128
129bool TweakTest::isAvailable(WrappedAST &AST,
130 llvm::Annotations::Range Range) const {
131 // Adjust range for wrapping offset.
132 Range.Begin += AST.second;
133 Range.End += AST.second;
134 auto Result = applyTweak(
135 AST.first, Range, TweakID, Index.get(),
136 &AST.first.getSourceManager().getFileManager().getVirtualFileSystem());
137 // We only care if prepare() succeeded, but must handle Errors.
138 if (Result && !*Result)
139 consumeError(Result->takeError());
140 return Result.has_value();
141}
142
143TweakTest::WrappedAST TweakTest::build(llvm::StringRef Code) const {
144 TestTU TU;
145 TU.Filename = std::string(FileName);
146 TU.HeaderCode = Header;
147 TU.Code = wrap(Context, Code);
148 TU.ExtraArgs = ExtraArgs;
149 TU.AdditionalFiles = std::move(ExtraFiles);
150 return {TU.build(), wrapping(Context).first.size()};
151}
152
153std::string TweakTest::decorate(llvm::StringRef Code, unsigned Point) {
154 return (Code.substr(0, Point) + "^" + Code.substr(Point)).str();
155}
156
157std::string TweakTest::decorate(llvm::StringRef Code,
158 llvm::Annotations::Range Range) {
159 return (Code.substr(0, Range.Begin) + "[[" +
160 Code.substr(Range.Begin, Range.End - Range.Begin) + "]]" +
161 Code.substr(Range.End))
162 .str();
163}
164
165} // namespace clangd
166} // namespace clang
std::pair< Context, Canceler > Inner
std::string Code
CharSourceRange Range
SourceRange for the file name.
StringRef FileName
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:69
Stores and provides access to parsed AST.
Definition: ParsedAST.h:46
static bool createEach(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End, llvm::function_ref< bool(SelectionTree)> Func)
Definition: Selection.cpp:1055
std::pair< ParsedAST, unsigned > WrappedAST
Definition: TweakTesting.h:95
T && wrap(T &&V)
Definition: Logger.h:42
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::vector< std::string > ExtraArgs
Definition: TestTU.h:60
std::string Code
Definition: TestTU.h:49
ParsedAST build() const
Definition: TestTU.cpp:114
std::string Filename
Definition: TestTU.h:50
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:57
std::string HeaderCode
Definition: TestTU.h:53