1 | //===--- Rename.h - Symbol-rename refactorings -------------------*- 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 | |
9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_RENAME_H |
10 | #define |
11 | |
12 | #include "Protocol.h" |
13 | #include "SourceCode.h" |
14 | #include "clang/Basic/IdentifierTable.h" |
15 | #include "clang/Basic/LangOptions.h" |
16 | #include "llvm/ADT/SmallVector.h" |
17 | #include "llvm/Support/Error.h" |
18 | #include <optional> |
19 | |
20 | namespace clang { |
21 | namespace clangd { |
22 | class ParsedAST; |
23 | class SymbolIndex; |
24 | |
25 | struct RenameOptions { |
26 | /// The maximum number of affected files (0 means no limit), only meaningful |
27 | /// when AllowCrossFile = true. |
28 | /// If the actual number exceeds the limit, rename is forbidden. |
29 | size_t LimitFiles = 50; |
30 | /// If true, format the rename edits, only meaningful in ClangdServer layer. |
31 | bool WantFormat = false; |
32 | /// Allow rename of virtual method hierarchies. |
33 | /// Disable to support broken index implementations with missing relations. |
34 | /// FIXME: fix those implementations and remove this option. |
35 | bool RenameVirtual = true; |
36 | }; |
37 | |
38 | /// A name of a symbol that should be renamed. |
39 | /// |
40 | /// Symbol's name can be composed of multiple strings. For example, Objective-C |
41 | /// methods can contain multiple argument labels: |
42 | /// |
43 | /// \code |
44 | /// - (void) myMethodNamePiece: (int)x anotherNamePieces:(int)y; |
45 | /// ^~ string 0 ~~~~~ ^~ string 1 ~~~~~ |
46 | /// \endcode |
47 | class RenameSymbolName { |
48 | llvm::SmallVector<std::string, 1> NamePieces; |
49 | |
50 | public: |
51 | RenameSymbolName(); |
52 | |
53 | /// Create a new \c SymbolName with the specified pieces. |
54 | explicit RenameSymbolName(ArrayRef<std::string> NamePieces); |
55 | |
56 | explicit RenameSymbolName(const DeclarationName &Name); |
57 | |
58 | ArrayRef<std::string> getNamePieces() const { return NamePieces; } |
59 | |
60 | /// If this symbol consists of a single piece return it, otherwise return |
61 | /// `None`. |
62 | /// |
63 | /// Only symbols in Objective-C can consist of multiple pieces, so this |
64 | /// function always returns a value for non-Objective-C symbols. |
65 | std::optional<std::string> getSinglePiece() const; |
66 | |
67 | /// Returns a human-readable version of this symbol name. |
68 | /// |
69 | /// If the symbol consists of multiple pieces (aka. it is an Objective-C |
70 | /// selector/method name), the pieces are separated by `:`, otherwise just an |
71 | /// identifier name. |
72 | std::string getAsString() const; |
73 | |
74 | void print(raw_ostream &OS) const; |
75 | |
76 | bool operator==(const RenameSymbolName &Other) const { |
77 | return NamePieces == Other.NamePieces; |
78 | } |
79 | }; |
80 | |
81 | struct RenameInputs { |
82 | Position Pos; // the position triggering the rename |
83 | llvm::StringRef NewName; |
84 | |
85 | ParsedAST &AST; |
86 | llvm::StringRef MainFilePath; |
87 | |
88 | // The filesystem to query when performing cross file renames. |
89 | // If this is set, Index must also be set, likewise if this is nullptr, Index |
90 | // must also be nullptr. |
91 | llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = nullptr; |
92 | |
93 | const SymbolIndex *Index = nullptr; |
94 | |
95 | RenameOptions Opts = {}; |
96 | }; |
97 | |
98 | struct RenameResult { |
99 | // The range of the symbol that the user can attempt to rename. |
100 | Range Target; |
101 | // Placeholder text for the rename operation if non-empty. |
102 | std::string Placeholder; |
103 | // Rename occurrences for the current main file. |
104 | std::vector<Range> LocalChanges; |
105 | // Complete edits for the rename, including LocalChanges. |
106 | // If the full set of changes is unknown, this field is empty. |
107 | FileEdits GlobalChanges; |
108 | }; |
109 | |
110 | /// Represents a symbol range where the symbol can potentially have multiple |
111 | /// tokens. |
112 | struct SymbolRange { |
113 | /// Ranges for the tokens that make up the symbol's name. |
114 | /// Usually a single range, but there can be multiple ranges if the tokens for |
115 | /// the symbol are split, e.g. ObjC selectors. |
116 | std::vector<Range> Ranges; |
117 | |
118 | SymbolRange(Range R); |
119 | SymbolRange(std::vector<Range> Ranges); |
120 | |
121 | /// Returns the first range. |
122 | Range range() const; |
123 | |
124 | friend bool operator==(const SymbolRange &LHS, const SymbolRange &RHS); |
125 | friend bool operator!=(const SymbolRange &LHS, const SymbolRange &RHS); |
126 | friend bool operator<(const SymbolRange &LHS, const SymbolRange &RHS); |
127 | }; |
128 | |
129 | /// Renames all occurrences of the symbol. The result edits are unformatted. |
130 | /// If AllowCrossFile is false, returns an error if rename a symbol that's used |
131 | /// in another file (per the index). |
132 | llvm::Expected<RenameResult> rename(const RenameInputs &RInputs); |
133 | |
134 | /// Generates rename edits that replaces all given occurrences with the |
135 | /// NewName. |
136 | /// Exposed for testing only. |
137 | /// REQUIRED: Occurrences is sorted and doesn't have duplicated ranges. |
138 | llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath, |
139 | llvm::StringRef InitialCode, |
140 | std::vector<SymbolRange> Occurrences, |
141 | llvm::ArrayRef<llvm::StringRef> NewNames); |
142 | |
143 | /// Adjusts indexed occurrences to match the current state of the file. |
144 | /// |
145 | /// The Index is not always up to date. Blindly editing at the locations |
146 | /// reported by the index may mangle the code in such cases. |
147 | /// This function determines whether the indexed occurrences can be applied to |
148 | /// this file, and heuristically repairs the occurrences if necessary. |
149 | /// |
150 | /// The API assumes that Indexed contains only named occurrences (each |
151 | /// occurrence has the same length). |
152 | /// REQUIRED: Indexed is sorted. |
153 | std::optional<std::vector<SymbolRange>> |
154 | adjustRenameRanges(llvm::StringRef DraftCode, const RenameSymbolName &Name, |
155 | std::vector<Range> Indexed, const LangOptions &LangOpts); |
156 | |
157 | /// Calculates the lexed occurrences that the given indexed occurrences map to. |
158 | /// Returns std::nullopt if we don't find a mapping. |
159 | /// |
160 | /// Exposed for testing only. |
161 | /// |
162 | /// REQUIRED: Indexed and Lexed are sorted. |
163 | std::optional<std::vector<SymbolRange>> |
164 | getMappedRanges(ArrayRef<Range> Indexed, ArrayRef<SymbolRange> Lexed); |
165 | /// Evaluates how good the mapped result is. 0 indicates a perfect match. |
166 | /// |
167 | /// Exposed for testing only. |
168 | /// |
169 | /// REQUIRED: Indexed and Lexed are sorted, Indexed and MappedIndex have the |
170 | /// same size. |
171 | size_t renameRangeAdjustmentCost(ArrayRef<Range> Indexed, |
172 | ArrayRef<SymbolRange> Lexed, |
173 | ArrayRef<size_t> MappedIndex); |
174 | |
175 | } // namespace clangd |
176 | } // namespace clang |
177 | |
178 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_REFACTOR_RENAME_H |
179 | |