1 //===--- Tweak.cpp -----------------------------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "FeatureModule.h"
10 #include "SourceCode.h"
11 #include "index/Index.h"
12 #include "support/Logger.h"
13 #include "support/Path.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"
25 LLVM_INSTANTIATE_REGISTRY(llvm::Registry
<clang::clangd::Tweak
>)
30 /// A handy typedef to save some typing.
31 typedef llvm::Registry
<Tweak
> TweakRegistry
;
34 /// Asserts invariants on TweakRegistry. No-op with assertion disabled.
35 void validateRegistry() {
37 llvm::StringSet
<> Seen
;
38 for (const auto &E
: TweakRegistry::entries()) {
39 // REGISTER_TWEAK ensures E.getName() is equal to the tweak class name.
40 // We check that id() matches it.
41 assert(E
.instantiate()->id() == E
.getName() &&
42 "id should be equal to class name");
43 assert(Seen
.try_emplace(E
.getName()).second
&& "duplicate check id");
48 std::vector
<std::unique_ptr
<Tweak
>>
49 getAllTweaks(const FeatureModuleSet
*Modules
) {
50 std::vector
<std::unique_ptr
<Tweak
>> All
;
51 for (const auto &E
: TweakRegistry::entries())
52 All
.emplace_back(E
.instantiate());
54 for (auto &M
: *Modules
)
55 M
.contributeTweaks(All
);
61 Tweak::Selection::Selection(const SymbolIndex
*Index
, ParsedAST
&AST
,
62 unsigned RangeBegin
, unsigned RangeEnd
,
63 SelectionTree ASTSelection
,
64 llvm::vfs::FileSystem
*FS
)
65 : Index(Index
), AST(&AST
), SelectionBegin(RangeBegin
),
66 SelectionEnd(RangeEnd
), ASTSelection(std::move(ASTSelection
)), FS(FS
) {
67 auto &SM
= AST
.getSourceManager();
68 Code
= SM
.getBufferData(SM
.getMainFileID());
69 Cursor
= SM
.getComposedLoc(SM
.getMainFileID(), RangeBegin
);
72 std::vector
<std::unique_ptr
<Tweak
>>
73 prepareTweaks(const Tweak::Selection
&S
,
74 llvm::function_ref
<bool(const Tweak
&)> Filter
,
75 const FeatureModuleSet
*Modules
) {
78 std::vector
<std::unique_ptr
<Tweak
>> Available
;
79 for (auto &T
: getAllTweaks(Modules
)) {
80 if (!Filter(*T
) || !T
->prepare(S
))
82 Available
.push_back(std::move(T
));
84 // Ensure deterministic order of the results.
86 [](const std::unique_ptr
<Tweak
> &L
,
87 const std::unique_ptr
<Tweak
> &R
) { return L
->id() < R
->id(); });
91 llvm::Expected
<std::unique_ptr
<Tweak
>>
92 prepareTweak(StringRef ID
, const Tweak::Selection
&S
,
93 const FeatureModuleSet
*Modules
) {
94 for (auto &T
: getAllTweaks(Modules
)) {
98 return error("failed to prepare() tweak {0}", ID
);
101 return error("tweak ID {0} is invalid", ID
);
104 llvm::Expected
<std::pair
<Path
, Edit
>>
105 Tweak::Effect::fileEdit(const SourceManager
&SM
, FileID FID
,
106 tooling::Replacements Replacements
) {
107 Edit
Ed(SM
.getBufferData(FID
), std::move(Replacements
));
108 if (auto FilePath
= getCanonicalPath(SM
.getFileEntryForID(FID
), SM
))
109 return std::make_pair(*FilePath
, std::move(Ed
));
110 return error("Failed to get absolute path for edited file: {0}",
111 SM
.getFileEntryRefForID(FID
)->getName());
114 llvm::Expected
<Tweak::Effect
>
115 Tweak::Effect::mainFileEdit(const SourceManager
&SM
,
116 tooling::Replacements Replacements
) {
117 auto PathAndEdit
= fileEdit(SM
, SM
.getMainFileID(), std::move(Replacements
));
119 return PathAndEdit
.takeError();
121 E
.ApplyEdits
.try_emplace(PathAndEdit
->first
, PathAndEdit
->second
);
125 } // namespace clangd