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