clang-tools  11.0.0
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()).drop_back(Wrapping.second.size());
50  return Outer;
51 }
52 
53 std::pair<unsigned, unsigned> rangeOrPoint(const Annotations &A) {
54  Range SelectionRng;
55  if (A.points().size() != 0) {
56  assert(A.ranges().size() == 0 &&
57  "both a cursor point and a selection range were specified");
58  SelectionRng = Range{A.point(), A.point()};
59  } else {
60  SelectionRng = A.range();
61  }
62  return {cantFail(positionToOffset(A.code(), SelectionRng.start)),
63  cantFail(positionToOffset(A.code(), SelectionRng.end))};
64 }
65 
66 // Prepare and apply the specified tweak based on the selection in Input.
67 // Returns None if and only if prepare() failed.
68 llvm::Optional<llvm::Expected<Tweak::Effect>>
69 applyTweak(ParsedAST &AST, const Annotations &Input, StringRef TweakID,
70  const SymbolIndex *Index) {
71  auto Range = rangeOrPoint(Input);
72  llvm::Optional<llvm::Expected<Tweak::Effect>> Result;
74  Range.second, [&](SelectionTree ST) {
75  Tweak::Selection S(Index, AST, Range.first,
76  Range.second, std::move(ST));
77  if (auto T = prepareTweak(TweakID, S)) {
78  Result = (*T)->apply(S);
79  return true;
80  } else {
81  llvm::consumeError(T.takeError());
82  return false;
83  }
84  });
85  return Result;
86 }
87 
88 MATCHER_P7(TweakIsAvailable, TweakID, Ctx, Header, ExtraArgs, ExtraFiles, Index,
89  FileName,
90  (TweakID + (negation ? " is unavailable" : " is available")).str()) {
91  std::string WrappedCode = wrap(Ctx, arg);
92  Annotations Input(WrappedCode);
93  TestTU TU;
94  TU.Filename = std::string(FileName);
95  TU.HeaderCode = Header;
96  TU.Code = std::string(Input.code());
97  TU.ExtraArgs = ExtraArgs;
98  TU.AdditionalFiles = std::move(ExtraFiles);
99  ParsedAST AST = TU.build();
100  auto Result = applyTweak(AST, Input, TweakID, Index);
101  // We only care if prepare() succeeded, but must handle Errors.
102  if (Result && !*Result)
103  consumeError(Result->takeError());
104  return Result.hasValue();
105 }
106 
107 } // namespace
108 
109 std::string TweakTest::apply(llvm::StringRef MarkedCode,
110  llvm::StringMap<std::string> *EditedFiles) const {
111  std::string WrappedCode = wrap(Context, MarkedCode);
112  Annotations Input(WrappedCode);
113  TestTU TU;
114  TU.Filename = std::string(FileName);
115  TU.HeaderCode = Header;
116  TU.AdditionalFiles = std::move(ExtraFiles);
117  TU.Code = std::string(Input.code());
118  TU.ExtraArgs = ExtraArgs;
119  ParsedAST AST = TU.build();
120 
121  auto Result = applyTweak(AST, Input, TweakID, Index.get());
122  if (!Result)
123  return "unavailable";
124  if (!*Result)
125  return "fail: " + llvm::toString(Result->takeError());
126  const auto &Effect = **Result;
127  if ((*Result)->ShowMessage)
128  return "message:\n" + *Effect.ShowMessage;
129  if (Effect.ApplyEdits.empty())
130  return "no effect";
131 
132  std::string EditedMainFile;
133  for (auto &It : Effect.ApplyEdits) {
134  auto NewText = It.second.apply();
135  if (!NewText)
136  return "bad edits: " + llvm::toString(NewText.takeError());
137  llvm::StringRef Unwrapped = unwrap(Context, *NewText);
138  if (It.first() == testPath(TU.Filename))
139  EditedMainFile = std::string(Unwrapped);
140  else {
141  if (!EditedFiles)
142  ADD_FAILURE() << "There were changes to additional files, but client "
143  "provided a nullptr for EditedFiles.";
144  else
145  EditedFiles->insert_or_assign(It.first(), Unwrapped.str());
146  }
147  }
148  return EditedMainFile;
149 }
150 
151 ::testing::Matcher<llvm::StringRef> TweakTest::isAvailable() const {
152  return TweakIsAvailable(llvm::StringRef(TweakID), Context, Header, ExtraArgs,
153  ExtraFiles, Index.get(), FileName);
154 }
155 
156 std::vector<std::string> TweakTest::expandCases(llvm::StringRef MarkedCode) {
157  Annotations Test(MarkedCode);
158  llvm::StringRef Code = Test.code();
159  std::vector<std::string> Cases;
160  for (const auto& Point : Test.points()) {
161  size_t Offset = llvm::cantFail(positionToOffset(Code, Point));
162  Cases.push_back((Code.substr(0, Offset) + "^" + Code.substr(Offset)).str());
163  }
164  for (const auto& Range : Test.ranges()) {
165  size_t Begin = llvm::cantFail(positionToOffset(Code, Range.start));
166  size_t End = llvm::cantFail(positionToOffset(Code, Range.end));
167  Cases.push_back((Code.substr(0, Begin) + "[[" +
168  Code.substr(Begin, End - Begin) + "]]" + Code.substr(End))
169  .str());
170  }
171  assert(!Cases.empty() && "No markings in MarkedCode?");
172  return Cases;
173 }
174 
175 } // namespace clangd
176 } // namespace clang
clang::clangd::TestTU::ExtraArgs
std::vector< std::string > ExtraArgs
Definition: TestTU.h:59
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
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:471
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:80
clang::clangd::Annotations::ranges
std::vector< clangd::Range > ranges(llvm::StringRef Name="") const
Definition: Annotations.cpp:41
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:775
clang::clangd::Range::start
Position start
The range's start position.
Definition: Protocol.h:175
clang::clangd::TweakTest::apply
std::string apply(llvm::StringRef MarkedCode, llvm::StringMap< std::string > *EditedFiles=nullptr) const
Definition: TweakTesting.cpp:109
Ctx
Context Ctx
Definition: TUScheduler.cpp:324
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::prepareTweak
llvm::Expected< std::unique_ptr< Tweak > > prepareTweak(StringRef ID, const Tweak::Selection &S)
Definition: Tweak.cpp:77
clang::clangd::TestTU::Code
std::string Code
Definition: TestTU.h:48
clang::clangd::TweakTest::Header
std::string Header
Definition: TweakTesting.h:64
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:246
clang::clangd::TweakTest::Function
Definition: TweakTesting.h:50
Offset
size_t Offset
Definition: CodeComplete.cpp:1044
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::TweakTest::isAvailable
::testing::Matcher< llvm::StringRef > isAvailable() const
Definition: TweakTesting.cpp:151
clang::clangd::TestTU::Filename
std::string Filename
Definition: TestTU.h:49
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:175
clang::clangd::TweakTest::expandCases
static std::vector< std::string > expandCases(llvm::StringRef MarkedCode)
Definition: TweakTesting.cpp:156
clang::clangd::Range::end
Position end
The range's end position.
Definition: Protocol.h:178
clang::clangd::TweakTest::Index
std::unique_ptr< const SymbolIndex > Index
Definition: TweakTesting.h:75
clang::clangd::TestTU
Definition: TestTU.h:34
clang::clangd::TweakTest::ExtraFiles
llvm::StringMap< std::string > ExtraFiles
Definition: TweakTesting.h:56
clang::clangd::SymbolOrigin::AST
Annotations.h
SourceCode.h
Index
const SymbolIndex * Index
Definition: Dexp.cpp:95
clang::clangd::detail::wrap
T && wrap(T &&V)
Definition: Logger.h:40
clang::clangd::Range
Definition: Protocol.h:173
clang::clangd::TweakTest::ExtraArgs
std::vector< std::string > ExtraArgs
Definition: TweakTesting.h:69
clang::clangd::TweakTest::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:103
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:60
clang::clangd::TweakTest::FileName
llvm::StringRef FileName
Definition: TweakTesting.h:66
clang::clangd::ParsedAST
Stores and provides access to parsed AST.
Definition: ParsedAST.h:48
TweakTesting.h
FileName
PathRef FileName
Definition: CodeComplete.cpp:1043
clang::clangd::TestTU::AdditionalFiles
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:56
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:52
clang::clangd::TweakTest::Expression
Definition: TweakTesting.h:52