| 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 | |