clang-tools  14.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 "Annotations.h"
12 #include "SourceCode.h"
13 #include "TestFS.h"
14 #include "refactor/Tweak.h"
15 #include "clang/Tooling/Core/Replacement.h"
16 #include "llvm/Support/Error.h"
17 #include "gmock/gmock.h"
18 #include "gtest/gtest.h"
19 #include <string>
20 
21 namespace clang {
22 namespace clangd {
23 namespace {
24 using Context = TweakTest::CodeContext;
25 
26 std::pair<llvm::StringRef, llvm::StringRef> wrapping(Context Ctx) {
27  switch (Ctx) {
28  case TweakTest::File:
29  return {"", ""};
31  return {"void wrapperFunction(){\n", "\n}"};
33  return {"auto expressionWrapper(){return\n", "\n;}"};
34  }
35  llvm_unreachable("Unknown TweakTest::CodeContext enum");
36 }
37 
38 std::string wrap(Context Ctx, llvm::StringRef Inner) {
39  auto Wrapping = wrapping(Ctx);
40  return (Wrapping.first + Inner + Wrapping.second).str();
41 }
42 
43 llvm::StringRef unwrap(Context Ctx, llvm::StringRef Outer) {
44  auto Wrapping = wrapping(Ctx);
45  // Unwrap only if the code matches the expected wrapping.
46  // Don't allow the begin/end wrapping to overlap!
47  if (Outer.startswith(Wrapping.first) && Outer.endswith(Wrapping.second) &&
48  Outer.size() >= Wrapping.first.size() + Wrapping.second.size())
49  return Outer.drop_front(Wrapping.first.size())
50  .drop_back(Wrapping.second.size());
51  return Outer;
52 }
53 
54 std::pair<unsigned, unsigned> rangeOrPoint(const Annotations &A) {
55  Range SelectionRng;
56  if (A.points().size() != 0) {
57  assert(A.ranges().size() == 0 &&
58  "both a cursor point and a selection range were specified");
59  SelectionRng = Range{A.point(), A.point()};
60  } else {
61  SelectionRng = A.range();
62  }
63  return {cantFail(positionToOffset(A.code(), SelectionRng.start)),
64  cantFail(positionToOffset(A.code(), SelectionRng.end))};
65 }
66 
67 // Prepare and apply the specified tweak based on the selection in Input.
68 // Returns None if and only if prepare() failed.
69 llvm::Optional<llvm::Expected<Tweak::Effect>>
70 applyTweak(ParsedAST &AST, const Annotations &Input, StringRef TweakID,
71  const SymbolIndex *Index, llvm::vfs::FileSystem *FS) {
72  auto Range = rangeOrPoint(Input);
73  llvm::Optional<llvm::Expected<Tweak::Effect>> Result;
75  Range.second, [&](SelectionTree ST) {
76  Tweak::Selection S(Index, AST, Range.first,
77  Range.second, std::move(ST),
78  FS);
79  if (auto T = prepareTweak(TweakID, S, nullptr)) {
80  Result = (*T)->apply(S);
81  return true;
82  } else {
83  llvm::consumeError(T.takeError());
84  return false;
85  }
86  });
87  return Result;
88 }
89 
90 MATCHER_P7(TweakIsAvailable, TweakID, Ctx, Header, ExtraArgs, ExtraFiles, Index,
91  FileName,
92  (TweakID + (negation ? " is unavailable" : " is available")).str()) {
93  std::string WrappedCode = wrap(Ctx, arg);
94  Annotations Input(WrappedCode);
95  TestTU TU;
96  TU.Filename = std::string(FileName);
97  TU.HeaderCode = Header;
98  TU.Code = std::string(Input.code());
99  TU.ExtraArgs = ExtraArgs;
100  TU.AdditionalFiles = std::move(ExtraFiles);
101  ParsedAST AST = TU.build();
102  auto Result = applyTweak(
103  AST, Input, TweakID, Index,
104  &AST.getSourceManager().getFileManager().getVirtualFileSystem());
105  // We only care if prepare() succeeded, but must handle Errors.
106  if (Result && !*Result)
107  consumeError(Result->takeError());
108  return Result.hasValue();
109 }
110 
111 } // namespace
112 
113 std::string TweakTest::apply(llvm::StringRef MarkedCode,
114  llvm::StringMap<std::string> *EditedFiles) const {
115  std::string WrappedCode = wrap(Context, MarkedCode);
116  Annotations Input(WrappedCode);
117  TestTU TU;
118  TU.Filename = std::string(FileName);
119  TU.HeaderCode = Header;
120  TU.AdditionalFiles = std::move(ExtraFiles);
121  TU.Code = std::string(Input.code());
122  TU.ExtraArgs = ExtraArgs;
123  ParsedAST AST = TU.build();
124 
125  auto Result = applyTweak(
126  AST, Input, TweakID, Index.get(),
127  &AST.getSourceManager().getFileManager().getVirtualFileSystem());
128  if (!Result)
129  return "unavailable";
130  if (!*Result)
131  return "fail: " + llvm::toString(Result->takeError());
132  const auto &Effect = **Result;
133  if ((*Result)->ShowMessage)
134  return "message:\n" + *Effect.ShowMessage;
135  if (Effect.ApplyEdits.empty())
136  return "no effect";
137 
138  std::string EditedMainFile;
139  for (auto &It : Effect.ApplyEdits) {
140  auto NewText = It.second.apply();
141  if (!NewText)
142  return "bad edits: " + llvm::toString(NewText.takeError());
143  llvm::StringRef Unwrapped = unwrap(Context, *NewText);
144  if (It.first() == testPath(TU.Filename))
145  EditedMainFile = std::string(Unwrapped);
146  else {
147  if (!EditedFiles)
148  ADD_FAILURE() << "There were changes to additional files, but client "
149  "provided a nullptr for EditedFiles.";
150  else
151  EditedFiles->insert_or_assign(It.first(), Unwrapped.str());
152  }
153  }
154  return EditedMainFile;
155 }
156 
157 ::testing::Matcher<llvm::StringRef> TweakTest::isAvailable() const {
158  return TweakIsAvailable(llvm::StringRef(TweakID), Context, Header, ExtraArgs,
159  ExtraFiles, Index.get(), FileName);
160 }
161 
162 std::vector<std::string> TweakTest::expandCases(llvm::StringRef MarkedCode) {
163  Annotations Test(MarkedCode);
164  llvm::StringRef Code = Test.code();
165  std::vector<std::string> Cases;
166  for (const auto &Point : Test.points()) {
167  size_t Offset = llvm::cantFail(positionToOffset(Code, Point));
168  Cases.push_back((Code.substr(0, Offset) + "^" + Code.substr(Offset)).str());
169  }
170  for (const auto &Range : Test.ranges()) {
171  size_t Begin = llvm::cantFail(positionToOffset(Code, Range.start));
172  size_t End = llvm::cantFail(positionToOffset(Code, Range.end));
173  Cases.push_back((Code.substr(0, Begin) + "[[" +
174  Code.substr(Begin, End - Begin) + "]]" + Code.substr(End))
175  .str());
176  }
177  assert(!Cases.empty() && "No markings in MarkedCode?");
178  return Cases;
179 }
180 
181 } // namespace clangd
182 } // namespace clang
clang::clangd::TestTU::ExtraArgs
std::vector< std::string > ExtraArgs
Definition: TestTU.h:61
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::clangd::testPath
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:82
clang::clangd::TestTU::build
ParsedAST build() const
Definition: TestTU.cpp:109
clang::clangd::Annotations::ranges
std::vector< clangd::Range > ranges(llvm::StringRef Name="") const
Definition: Annotations.cpp:41
clang::clangd::ParsedAST::getASTContext
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
Definition: ParsedAST.cpp:632
clang::clangd::SelectionTree::createEach
static bool createEach(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End, llvm::function_ref< bool(SelectionTree)> Func)
Definition: Selection.cpp:1005
clang::clangd::Range::start
Position start
The range's start position.
Definition: Protocol.h:184
Ctx
Context Ctx
Definition: TUScheduler.cpp:460
Outer
std::pair< Context, Canceler > Outer
Definition: CancellationTests.cpp:49
clang::clangd::Annotations::points
std::vector< Position > points(llvm::StringRef Name="") const
Definition: Annotations.cpp:19
clang::clangd::TestTU::Code
std::string Code
Definition: TestTU.h:50
clang::clangd::TweakTest::CodeContext
CodeContext
Definition: TweakTesting.h:46
clang::clangd::ParsedAST::build
static llvm::Optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:289
clang::clangd::TweakTest::Function
@ Function
Definition: TweakTesting.h:50
Offset
size_t Offset
Definition: CodeComplete.cpp:1192
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::Annotations
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition: Annotations.h:23
Tweak.h
Inner
std::pair< Context, Canceler > Inner
Definition: CancellationTests.cpp:49
clang::clangd::TestTU::Filename
std::string Filename
Definition: TestTU.h:51
TestFS.h
clang::clangd::positionToOffset
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:176
clang::clangd::Range::end
Position end
The range's end position.
Definition: Protocol.h:187
FileName
StringRef FileName
Definition: KernelNameRestrictionCheck.cpp:46
clang::clangd::TestTU
Definition: TestTU.h:36
Annotations.h
SourceCode.h
Index
const SymbolIndex * Index
Definition: Dexp.cpp:99
clang::clangd::detail::wrap
T && wrap(T &&V)
Definition: Logger.h:43
clang::clangd::Range
Definition: Protocol.h:182
clang::clangd::TweakTest::File
@ File
Definition: TweakTesting.h:48
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::ParsedAST::getTokens
const syntax::TokenBuffer & getTokens() const
Tokens recorded while parsing the main file.
Definition: ParsedAST.h:108
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:55
clang::clangd::ParsedAST
Stores and provides access to parsed AST.
Definition: ParsedAST.h:49
TweakTesting.h
clang::clangd::ParsedAST::getSourceManager
SourceManager & getSourceManager()
Definition: ParsedAST.h:75
clang::clangd::TestTU::AdditionalFiles
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:58
clang::clangd::Context
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:69
clang::clangd::TestTU::HeaderCode
std::string HeaderCode
Definition: TestTU.h:54
clang::clangd::TweakTest::Expression
@ Expression
Definition: TweakTesting.h:52