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/STLExtras.h"
15 #include "llvm/ADT/StringMap.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/Error.h"
18 #include "llvm/Support/Registry.h"
24 LLVM_INSTANTIATE_REGISTRY(llvm::Registry
<clang::clangd::Tweak
>)
29 /// A handy typedef to save some typing.
30 typedef llvm::Registry
<Tweak
> TweakRegistry
;
33 /// Asserts invariants on TweakRegistry. No-op with assertion disabled.
34 void validateRegistry() {
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");
47 std::vector
<std::unique_ptr
<Tweak
>>
48 getAllTweaks(const FeatureModuleSet
*Modules
) {
49 std::vector
<std::unique_ptr
<Tweak
>> All
;
50 for (const auto &E
: TweakRegistry::entries())
51 All
.emplace_back(E
.instantiate());
53 for (auto &M
: *Modules
)
54 M
.contributeTweaks(All
);
60 Tweak::Selection::Selection(const SymbolIndex
*Index
, ParsedAST
&AST
,
61 unsigned RangeBegin
, unsigned RangeEnd
,
62 SelectionTree ASTSelection
,
63 llvm::vfs::FileSystem
*FS
)
64 : Index(Index
), AST(&AST
), SelectionBegin(RangeBegin
),
65 SelectionEnd(RangeEnd
), ASTSelection(std::move(ASTSelection
)), FS(FS
) {
66 auto &SM
= AST
.getSourceManager();
67 Code
= SM
.getBufferData(SM
.getMainFileID());
68 Cursor
= SM
.getComposedLoc(SM
.getMainFileID(), RangeBegin
);
71 std::vector
<std::unique_ptr
<Tweak
>>
72 prepareTweaks(const Tweak::Selection
&S
,
73 llvm::function_ref
<bool(const Tweak
&)> Filter
,
74 const FeatureModuleSet
*Modules
) {
77 std::vector
<std::unique_ptr
<Tweak
>> Available
;
78 for (auto &T
: getAllTweaks(Modules
)) {
79 if (!Filter(*T
) || !T
->prepare(S
))
81 Available
.push_back(std::move(T
));
83 // Ensure deterministic order of the results.
85 [](const std::unique_ptr
<Tweak
> &L
,
86 const std::unique_ptr
<Tweak
> &R
) { return L
->id() < R
->id(); });
90 llvm::Expected
<std::unique_ptr
<Tweak
>>
91 prepareTweak(StringRef ID
, const Tweak::Selection
&S
,
92 const FeatureModuleSet
*Modules
) {
93 for (auto &T
: getAllTweaks(Modules
)) {
97 return error("failed to prepare() tweak {0}", ID
);
100 return error("tweak ID {0} is invalid", ID
);
103 llvm::Expected
<std::pair
<Path
, Edit
>>
104 Tweak::Effect::fileEdit(const SourceManager
&SM
, FileID FID
,
105 tooling::Replacements Replacements
) {
106 Edit
Ed(SM
.getBufferData(FID
), std::move(Replacements
));
107 if (const auto FE
= SM
.getFileEntryRefForID(FID
))
108 if (auto FilePath
= getCanonicalPath(*FE
, SM
.getFileManager()))
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