1 //===--- AtomicChange.h - AtomicChange class --------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file defines AtomicChange which is used to create a set of source 11 // changes, e.g. replacements and header insertions. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H 16 #define LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H 17 18 #include "clang/Basic/SourceManager.h" 19 #include "clang/Format/Format.h" 20 #include "clang/Tooling/Core/Replacement.h" 21 #include "llvm/ADT/StringRef.h" 22 #include "llvm/Support/Error.h" 23 24 namespace clang { 25 namespace tooling { 26 27 /// An atomic change is used to create and group a set of source edits, 28 /// e.g. replacements or header insertions. Edits in an AtomicChange should be 29 /// related, e.g. replacements for the same type reference and the corresponding 30 /// header insertion/deletion. 31 /// 32 /// An AtomicChange is uniquely identified by a key and will either be fully 33 /// applied or not applied at all. 34 /// 35 /// Calling setError on an AtomicChange stores the error message and marks it as 36 /// bad, i.e. none of its source edits will be applied. 37 class AtomicChange { 38 public: 39 /// Creates an atomic change around \p KeyPosition with the key being a 40 /// concatenation of the file name and the offset of \p KeyPosition. 41 /// \p KeyPosition should be the location of the key syntactical element that 42 /// is being changed, e.g. the call to a refactored method. 43 AtomicChange(const SourceManager &SM, SourceLocation KeyPosition); 44 45 /// Creates an atomic change for \p FilePath with a customized key. AtomicChange(llvm::StringRef FilePath,llvm::StringRef Key)46 AtomicChange(llvm::StringRef FilePath, llvm::StringRef Key) 47 : Key(Key), FilePath(FilePath) {} 48 49 AtomicChange(AtomicChange &&) = default; 50 AtomicChange(const AtomicChange &) = default; 51 52 AtomicChange &operator=(AtomicChange &&) = default; 53 AtomicChange &operator=(const AtomicChange &) = default; 54 55 bool operator==(const AtomicChange &Other) const; 56 57 /// Returns the atomic change as a YAML string. 58 std::string toYAMLString(); 59 60 /// Converts a YAML-encoded automic change to AtomicChange. 61 static AtomicChange convertFromYAML(llvm::StringRef YAMLContent); 62 63 /// Returns the key of this change, which is a concatenation of the 64 /// file name and offset of the key position. getKey()65 const std::string &getKey() const { return Key; } 66 67 /// Returns the path of the file containing this atomic change. getFilePath()68 const std::string &getFilePath() const { return FilePath; } 69 70 /// If this change could not be created successfully, e.g. because of 71 /// conflicts among replacements, use this to set an error description. 72 /// Thereby, places that cannot be fixed automatically can be gathered when 73 /// applying changes. setError(llvm::StringRef Error)74 void setError(llvm::StringRef Error) { this->Error = Error; } 75 76 /// Returns whether an error has been set on this list. hasError()77 bool hasError() const { return !Error.empty(); } 78 79 /// Returns the error message or an empty string if it does not exist. getError()80 const std::string &getError() const { return Error; } 81 82 /// Adds a replacement that replaces the given Range with 83 /// ReplacementText. 84 /// \returns An llvm::Error carrying ReplacementError on error. 85 llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range, 86 llvm::StringRef ReplacementText); 87 88 /// Adds a replacement that replaces range [Loc, Loc+Length) with 89 /// \p Text. 90 /// \returns An llvm::Error carrying ReplacementError on error. 91 llvm::Error replace(const SourceManager &SM, SourceLocation Loc, 92 unsigned Length, llvm::StringRef Text); 93 94 /// Adds a replacement that inserts \p Text at \p Loc. If this 95 /// insertion conflicts with an existing insertion (at the same position), 96 /// this will be inserted before/after the existing insertion depending on 97 /// \p InsertAfter. Users should use `replace` with `Length=0` instead if they 98 /// do not want conflict resolving by default. If the conflicting replacement 99 /// is not an insertion, an error is returned. 100 /// 101 /// \returns An llvm::Error carrying ReplacementError on error. 102 llvm::Error insert(const SourceManager &SM, SourceLocation Loc, 103 llvm::StringRef Text, bool InsertAfter = true); 104 105 /// Adds a header into the file that contains the key position. 106 /// Header can be in angle brackets or double quotation marks. By default 107 /// (header is not quoted), header will be surrounded with double quotes. 108 void addHeader(llvm::StringRef Header); 109 110 /// Removes a header from the file that contains the key position. 111 void removeHeader(llvm::StringRef Header); 112 113 /// Returns a const reference to existing replacements. getReplacements()114 const Replacements &getReplacements() const { return Replaces; } 115 getInsertedHeaders()116 llvm::ArrayRef<std::string> getInsertedHeaders() const { 117 return InsertedHeaders; 118 } 119 getRemovedHeaders()120 llvm::ArrayRef<std::string> getRemovedHeaders() const { 121 return RemovedHeaders; 122 } 123 124 private: AtomicChange()125 AtomicChange() {} 126 127 AtomicChange(std::string Key, std::string FilePath, std::string Error, 128 std::vector<std::string> InsertedHeaders, 129 std::vector<std::string> RemovedHeaders, 130 clang::tooling::Replacements Replaces); 131 132 // This uniquely identifies an AtomicChange. 133 std::string Key; 134 std::string FilePath; 135 std::string Error; 136 std::vector<std::string> InsertedHeaders; 137 std::vector<std::string> RemovedHeaders; 138 tooling::Replacements Replaces; 139 }; 140 141 using AtomicChanges = std::vector<AtomicChange>; 142 143 // Defines specs for applying changes. 144 struct ApplyChangesSpec { 145 // If true, cleans up redundant/erroneous code around changed code with 146 // clang-format's cleanup functionality, e.g. redundant commas around deleted 147 // parameter or empty namespaces introduced by deletions. 148 bool Cleanup = true; 149 150 format::FormatStyle Style = format::getNoStyle(); 151 152 // Options for selectively formatting changes with clang-format: 153 // kAll: Format all changed lines. 154 // kNone: Don't format anything. 155 // kViolations: Format lines exceeding the `ColumnLimit` in `Style`. 156 enum FormatOption { kAll, kNone, kViolations }; 157 158 FormatOption Format = kNone; 159 }; 160 161 /// Applies all AtomicChanges in \p Changes to the \p Code. 162 /// 163 /// This completely ignores the file path in each change and replaces them with 164 /// \p FilePath, i.e. callers are responsible for ensuring all changes are for 165 /// the same file. 166 /// 167 /// \returns The changed code if all changes are applied successfully; 168 /// otherwise, an llvm::Error carrying llvm::StringError is returned (the Error 169 /// message can be converted to string with `llvm::toString()` and the 170 /// error_code should be ignored). 171 llvm::Expected<std::string> 172 applyAtomicChanges(llvm::StringRef FilePath, llvm::StringRef Code, 173 llvm::ArrayRef<AtomicChange> Changes, 174 const ApplyChangesSpec &Spec); 175 176 } // end namespace tooling 177 } // end namespace clang 178 179 #endif // LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H 180