clang-tools  10.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()).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 MATCHER_P7(TweakIsAvailable, TweakID, Ctx, Header, ExtraArgs, ExtraFiles, Index,
67  FileName,
68  (TweakID + (negation ? " is unavailable" : " is available")).str()) {
69  std::string WrappedCode = wrap(Ctx, arg);
70  Annotations Input(WrappedCode);
71  auto Selection = rangeOrPoint(Input);
72  TestTU TU;
73  TU.Filename = FileName;
74  TU.HeaderCode = Header;
75  TU.Code = Input.code();
76  TU.ExtraArgs = ExtraArgs;
77  TU.AdditionalFiles = std::move(ExtraFiles);
78  ParsedAST AST = TU.build();
79  Tweak::Selection S(Index, AST, Selection.first, Selection.second);
80  auto PrepareResult = prepareTweak(TweakID, S);
81  if (PrepareResult)
82  return true;
83  llvm::consumeError(PrepareResult.takeError());
84  return false;
85 }
86 
87 } // namespace
88 
89 std::string TweakTest::apply(llvm::StringRef MarkedCode,
90  llvm::StringMap<std::string> *EditedFiles) const {
91  std::string WrappedCode = wrap(Context, MarkedCode);
92  Annotations Input(WrappedCode);
93  auto Selection = rangeOrPoint(Input);
94 
95  TestTU TU;
96  TU.Filename = FileName;
97  TU.HeaderCode = Header;
98  TU.AdditionalFiles = std::move(ExtraFiles);
99  TU.Code = Input.code();
100  TU.ExtraArgs = ExtraArgs;
101  ParsedAST AST = TU.build();
102  Tweak::Selection S(Index.get(), AST, Selection.first, Selection.second);
103 
104  auto T = prepareTweak(TweakID, S);
105  if (!T) {
106  llvm::consumeError(T.takeError());
107  return "unavailable";
108  }
109  llvm::Expected<Tweak::Effect> Result = (*T)->apply(S);
110  if (!Result)
111  return "fail: " + llvm::toString(Result.takeError());
112  if (Result->ShowMessage)
113  return "message:\n" + *Result->ShowMessage;
114  if (Result->ApplyEdits.empty())
115  return "no effect";
116 
117  std::string EditedMainFile;
118  for (auto &It : Result->ApplyEdits) {
119  auto NewText = It.second.apply();
120  if (!NewText)
121  return "bad edits: " + llvm::toString(NewText.takeError());
122  llvm::StringRef Unwrapped = unwrap(Context, *NewText);
123  if (It.first() == testPath(TU.Filename))
124  EditedMainFile = Unwrapped;
125  else {
126  if (!EditedFiles)
127  ADD_FAILURE() << "There were changes to additional files, but client "
128  "provided a nullptr for EditedFiles.";
129  else
130  EditedFiles->insert_or_assign(It.first(), Unwrapped.str());
131  }
132  }
133  return EditedMainFile;
134 }
135 
136 ::testing::Matcher<llvm::StringRef> TweakTest::isAvailable() const {
137  return TweakIsAvailable(llvm::StringRef(TweakID), Context, Header, ExtraArgs,
138  ExtraFiles, Index.get(), FileName);
139 }
140 
141 std::vector<std::string> TweakTest::expandCases(llvm::StringRef MarkedCode) {
142  Annotations Test(MarkedCode);
143  llvm::StringRef Code = Test.code();
144  std::vector<std::string> Cases;
145  for (const auto& Point : Test.points()) {
146  size_t Offset = llvm::cantFail(positionToOffset(Code, Point));
147  Cases.push_back((Code.substr(0, Offset) + "^" + Code.substr(Offset)).str());
148  }
149  for (const auto& Range : Test.ranges()) {
150  size_t Begin = llvm::cantFail(positionToOffset(Code, Range.start));
151  size_t End = llvm::cantFail(positionToOffset(Code, Range.end));
152  Cases.push_back((Code.substr(0, Begin) + "[[" +
153  Code.substr(Begin, End - Begin) + "]]" + Code.substr(End))
154  .str());
155  }
156  assert(!Cases.empty() && "No markings in MarkedCode?");
157  return Cases;
158 }
159 
160 } // namespace clangd
161 } // namespace clang
ParsedAST build() const
Definition: TestTU.cpp:22
std::string Code
Position start
The range&#39;s start position.
Definition: Protocol.h:158
std::vector< const char * > ExtraArgs
Definition: TweakTesting.h:69
std::string HeaderCode
Definition: TestTU.h:50
llvm::Expected< std::unique_ptr< Tweak > > prepareTweak(StringRef ID, const Tweak::Selection &S)
Definition: Tweak.cpp:77
::testing::Matcher< llvm::StringRef > isAvailable() const
std::string Code
Definition: TestTU.h:46
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
static std::vector< std::string > expandCases(llvm::StringRef MarkedCode)
std::string apply(llvm::StringRef MarkedCode, llvm::StringMap< std::string > *EditedFiles=nullptr) const
std::vector< clangd::Range > ranges(llvm::StringRef Name="") const
Definition: Annotations.cpp:41
std::unique_ptr< const SymbolIndex > Index
Definition: TweakTesting.h:75
std::vector< const char * > ExtraArgs
Definition: TestTU.h:57
Context Ctx
llvm::StringMap< std::string > ExtraFiles
Definition: TweakTesting.h:56
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:155
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
std::vector< Position > points(llvm::StringRef Name="") const
Definition: Annotations.cpp:19
PathRef FileName
A context is an immutable container for per-request data that must be propagated through layers that ...
Definition: Context.h:69
Input to prepare and apply tweaks.
Definition: Tweak.h:49
Stores and provides access to parsed AST.
Definition: ParsedAST.h:46
T && wrap(T &&V)
Definition: Logger.h:40
size_t Offset
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
llvm::StringRef FileName
Definition: TweakTesting.h:66
std::string Filename
Definition: TestTU.h:47
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges...
Definition: Annotations.h:23
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
Definition: ParsedAST.cpp:218
Position end
The range&#39;s end position.
Definition: Protocol.h:161
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:54
const SymbolIndex * Index
Definition: Dexp.cpp:84