• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- Tweak.cpp -----------------------------------------------*- C++-*-===//
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 #include "Tweak.h"
9 #include "SourceCode.h"
10 #include "index/Index.h"
11 #include "support/Logger.h"
12 #include "support/Path.h"
13 #include "llvm/ADT/None.h"
14 #include "llvm/ADT/Optional.h"
15 #include "llvm/ADT/STLExtras.h"
16 #include "llvm/ADT/StringMap.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/Registry.h"
20 #include <functional>
21 #include <memory>
22 #include <utility>
23 
24 LLVM_INSTANTIATE_REGISTRY(llvm::Registry<clang::clangd::Tweak>)
25 
26 namespace clang {
27 namespace clangd {
28 
29 /// A handy typedef to save some typing.
30 typedef llvm::Registry<Tweak> TweakRegistry;
31 
32 namespace {
33 /// Asserts invariants on TweakRegistry. No-op with assertion disabled.
validateRegistry()34 void validateRegistry() {
35 #ifndef NDEBUG
36   llvm::StringSet<> Seen;
37   for (const auto &E : TweakRegistry::entries()) {
38     // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name.
39     // We check that id() matches it.
40     assert(E.instantiate()->id() == E.getName() &&
41            "id should be equal to class name");
42     assert(Seen.try_emplace(E.getName()).second && "duplicate check id");
43   }
44 #endif
45 }
46 } // namespace
47 
Selection(const SymbolIndex * Index,ParsedAST & AST,unsigned RangeBegin,unsigned RangeEnd,SelectionTree ASTSelection)48 Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST,
49                             unsigned RangeBegin, unsigned RangeEnd,
50                             SelectionTree ASTSelection)
51     : Index(Index), AST(&AST), SelectionBegin(RangeBegin),
52       SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)) {
53   auto &SM = AST.getSourceManager();
54   Code = SM.getBufferData(SM.getMainFileID());
55   Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin);
56 }
57 
58 std::vector<std::unique_ptr<Tweak>>
prepareTweaks(const Tweak::Selection & S,llvm::function_ref<bool (const Tweak &)> Filter)59 prepareTweaks(const Tweak::Selection &S,
60               llvm::function_ref<bool(const Tweak &)> Filter) {
61   validateRegistry();
62 
63   std::vector<std::unique_ptr<Tweak>> Available;
64   for (const auto &E : TweakRegistry::entries()) {
65     std::unique_ptr<Tweak> T = E.instantiate();
66     if (!Filter(*T) || !T->prepare(S))
67       continue;
68     Available.push_back(std::move(T));
69   }
70   // Ensure deterministic order of the results.
71   llvm::sort(Available,
72              [](const std::unique_ptr<Tweak> &L,
73                 const std::unique_ptr<Tweak> &R) { return L->id() < R->id(); });
74   return Available;
75 }
76 
prepareTweak(StringRef ID,const Tweak::Selection & S)77 llvm::Expected<std::unique_ptr<Tweak>> prepareTweak(StringRef ID,
78                                                     const Tweak::Selection &S) {
79   auto It = llvm::find_if(
80       TweakRegistry::entries(),
81       [ID](const TweakRegistry::entry &E) { return E.getName() == ID; });
82   if (It == TweakRegistry::end())
83     return error("tweak ID {0} is invalid", ID);
84   std::unique_ptr<Tweak> T = It->instantiate();
85   if (!T->prepare(S))
86     return error("failed to prepare() tweak {0}", ID);
87   return std::move(T);
88 }
89 
90 llvm::Expected<std::pair<Path, Edit>>
fileEdit(const SourceManager & SM,FileID FID,tooling::Replacements Replacements)91 Tweak::Effect::fileEdit(const SourceManager &SM, FileID FID,
92                         tooling::Replacements Replacements) {
93   Edit Ed(SM.getBufferData(FID), std::move(Replacements));
94   if (auto FilePath = getCanonicalPath(SM.getFileEntryForID(FID), SM))
95     return std::make_pair(*FilePath, std::move(Ed));
96   return error("Failed to get absolute path for edited file: {0}",
97                SM.getFileEntryForID(FID)->getName());
98 }
99 
100 llvm::Expected<Tweak::Effect>
mainFileEdit(const SourceManager & SM,tooling::Replacements Replacements)101 Tweak::Effect::mainFileEdit(const SourceManager &SM,
102                             tooling::Replacements Replacements) {
103   auto PathAndEdit = fileEdit(SM, SM.getMainFileID(), std::move(Replacements));
104   if (!PathAndEdit)
105     return PathAndEdit.takeError();
106   Tweak::Effect E;
107   E.ApplyEdits.try_emplace(PathAndEdit->first, PathAndEdit->second);
108   return E;
109 }
110 
111 } // namespace clangd
112 } // namespace clang
113