106f32e7eSjoerg //===--- AtomicChange.h - AtomicChange class --------------------*- C++ -*-===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg //
906f32e7eSjoerg //  This file defines AtomicChange which is used to create a set of source
1006f32e7eSjoerg //  changes, e.g. replacements and header insertions.
1106f32e7eSjoerg //
1206f32e7eSjoerg //===----------------------------------------------------------------------===//
1306f32e7eSjoerg 
1406f32e7eSjoerg #ifndef LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H
1506f32e7eSjoerg #define LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H
1606f32e7eSjoerg 
1706f32e7eSjoerg #include "clang/Basic/SourceManager.h"
1806f32e7eSjoerg #include "clang/Format/Format.h"
1906f32e7eSjoerg #include "clang/Tooling/Core/Replacement.h"
20*13fbcb42Sjoerg #include "llvm/ADT/Any.h"
2106f32e7eSjoerg #include "llvm/ADT/StringRef.h"
2206f32e7eSjoerg #include "llvm/Support/Error.h"
2306f32e7eSjoerg 
2406f32e7eSjoerg namespace clang {
2506f32e7eSjoerg namespace tooling {
2606f32e7eSjoerg 
2706f32e7eSjoerg /// An atomic change is used to create and group a set of source edits,
2806f32e7eSjoerg /// e.g. replacements or header insertions. Edits in an AtomicChange should be
2906f32e7eSjoerg /// related, e.g. replacements for the same type reference and the corresponding
3006f32e7eSjoerg /// header insertion/deletion.
3106f32e7eSjoerg ///
3206f32e7eSjoerg /// An AtomicChange is uniquely identified by a key and will either be fully
3306f32e7eSjoerg /// applied or not applied at all.
3406f32e7eSjoerg ///
3506f32e7eSjoerg /// Calling setError on an AtomicChange stores the error message and marks it as
3606f32e7eSjoerg /// bad, i.e. none of its source edits will be applied.
3706f32e7eSjoerg class AtomicChange {
3806f32e7eSjoerg public:
3906f32e7eSjoerg   /// Creates an atomic change around \p KeyPosition with the key being a
4006f32e7eSjoerg   /// concatenation of the file name and the offset of \p KeyPosition.
4106f32e7eSjoerg   /// \p KeyPosition should be the location of the key syntactical element that
4206f32e7eSjoerg   /// is being changed, e.g. the call to a refactored method.
4306f32e7eSjoerg   AtomicChange(const SourceManager &SM, SourceLocation KeyPosition);
4406f32e7eSjoerg 
45*13fbcb42Sjoerg   AtomicChange(const SourceManager &SM, SourceLocation KeyPosition,
46*13fbcb42Sjoerg                llvm::Any Metadata);
47*13fbcb42Sjoerg 
4806f32e7eSjoerg   /// Creates an atomic change for \p FilePath with a customized key.
AtomicChange(llvm::StringRef FilePath,llvm::StringRef Key)4906f32e7eSjoerg   AtomicChange(llvm::StringRef FilePath, llvm::StringRef Key)
5006f32e7eSjoerg       : Key(Key), FilePath(FilePath) {}
5106f32e7eSjoerg 
5206f32e7eSjoerg   AtomicChange(AtomicChange &&) = default;
5306f32e7eSjoerg   AtomicChange(const AtomicChange &) = default;
5406f32e7eSjoerg 
5506f32e7eSjoerg   AtomicChange &operator=(AtomicChange &&) = default;
5606f32e7eSjoerg   AtomicChange &operator=(const AtomicChange &) = default;
5706f32e7eSjoerg 
5806f32e7eSjoerg   bool operator==(const AtomicChange &Other) const;
5906f32e7eSjoerg 
6006f32e7eSjoerg   /// Returns the atomic change as a YAML string.
6106f32e7eSjoerg   std::string toYAMLString();
6206f32e7eSjoerg 
6306f32e7eSjoerg   /// Converts a YAML-encoded automic change to AtomicChange.
6406f32e7eSjoerg   static AtomicChange convertFromYAML(llvm::StringRef YAMLContent);
6506f32e7eSjoerg 
6606f32e7eSjoerg   /// Returns the key of this change, which is a concatenation of the
6706f32e7eSjoerg   /// file name and offset of the key position.
getKey()6806f32e7eSjoerg   const std::string &getKey() const { return Key; }
6906f32e7eSjoerg 
7006f32e7eSjoerg   /// Returns the path of the file containing this atomic change.
getFilePath()7106f32e7eSjoerg   const std::string &getFilePath() const { return FilePath; }
7206f32e7eSjoerg 
7306f32e7eSjoerg   /// If this change could not be created successfully, e.g. because of
7406f32e7eSjoerg   /// conflicts among replacements, use this to set an error description.
7506f32e7eSjoerg   /// Thereby, places that cannot be fixed automatically can be gathered when
7606f32e7eSjoerg   /// applying changes.
setError(llvm::StringRef Error)77*13fbcb42Sjoerg   void setError(llvm::StringRef Error) { this->Error = std::string(Error); }
7806f32e7eSjoerg 
7906f32e7eSjoerg   /// Returns whether an error has been set on this list.
hasError()8006f32e7eSjoerg   bool hasError() const { return !Error.empty(); }
8106f32e7eSjoerg 
8206f32e7eSjoerg   /// Returns the error message or an empty string if it does not exist.
getError()8306f32e7eSjoerg   const std::string &getError() const { return Error; }
8406f32e7eSjoerg 
8506f32e7eSjoerg   /// Adds a replacement that replaces the given Range with
8606f32e7eSjoerg   /// ReplacementText.
8706f32e7eSjoerg   /// \returns An llvm::Error carrying ReplacementError on error.
8806f32e7eSjoerg   llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range,
8906f32e7eSjoerg                       llvm::StringRef ReplacementText);
9006f32e7eSjoerg 
9106f32e7eSjoerg   /// Adds a replacement that replaces range [Loc, Loc+Length) with
9206f32e7eSjoerg   /// \p Text.
9306f32e7eSjoerg   /// \returns An llvm::Error carrying ReplacementError on error.
9406f32e7eSjoerg   llvm::Error replace(const SourceManager &SM, SourceLocation Loc,
9506f32e7eSjoerg                       unsigned Length, llvm::StringRef Text);
9606f32e7eSjoerg 
9706f32e7eSjoerg   /// Adds a replacement that inserts \p Text at \p Loc. If this
9806f32e7eSjoerg   /// insertion conflicts with an existing insertion (at the same position),
9906f32e7eSjoerg   /// this will be inserted before/after the existing insertion depending on
10006f32e7eSjoerg   /// \p InsertAfter. Users should use `replace` with `Length=0` instead if they
10106f32e7eSjoerg   /// do not want conflict resolving by default. If the conflicting replacement
10206f32e7eSjoerg   /// is not an insertion, an error is returned.
10306f32e7eSjoerg   ///
10406f32e7eSjoerg   /// \returns An llvm::Error carrying ReplacementError on error.
10506f32e7eSjoerg   llvm::Error insert(const SourceManager &SM, SourceLocation Loc,
10606f32e7eSjoerg                      llvm::StringRef Text, bool InsertAfter = true);
10706f32e7eSjoerg 
10806f32e7eSjoerg   /// Adds a header into the file that contains the key position.
10906f32e7eSjoerg   /// Header can be in angle brackets or double quotation marks. By default
11006f32e7eSjoerg   /// (header is not quoted), header will be surrounded with double quotes.
11106f32e7eSjoerg   void addHeader(llvm::StringRef Header);
11206f32e7eSjoerg 
11306f32e7eSjoerg   /// Removes a header from the file that contains the key position.
11406f32e7eSjoerg   void removeHeader(llvm::StringRef Header);
11506f32e7eSjoerg 
11606f32e7eSjoerg   /// Returns a const reference to existing replacements.
getReplacements()11706f32e7eSjoerg   const Replacements &getReplacements() const { return Replaces; }
11806f32e7eSjoerg 
getInsertedHeaders()11906f32e7eSjoerg   llvm::ArrayRef<std::string> getInsertedHeaders() const {
12006f32e7eSjoerg     return InsertedHeaders;
12106f32e7eSjoerg   }
12206f32e7eSjoerg 
getRemovedHeaders()12306f32e7eSjoerg   llvm::ArrayRef<std::string> getRemovedHeaders() const {
12406f32e7eSjoerg     return RemovedHeaders;
12506f32e7eSjoerg   }
12606f32e7eSjoerg 
getMetadata()127*13fbcb42Sjoerg   const llvm::Any &getMetadata() const { return Metadata; }
128*13fbcb42Sjoerg 
12906f32e7eSjoerg private:
AtomicChange()13006f32e7eSjoerg   AtomicChange() {}
13106f32e7eSjoerg 
13206f32e7eSjoerg   AtomicChange(std::string Key, std::string FilePath, std::string Error,
13306f32e7eSjoerg                std::vector<std::string> InsertedHeaders,
13406f32e7eSjoerg                std::vector<std::string> RemovedHeaders,
13506f32e7eSjoerg                clang::tooling::Replacements Replaces);
13606f32e7eSjoerg 
13706f32e7eSjoerg   // This uniquely identifies an AtomicChange.
13806f32e7eSjoerg   std::string Key;
13906f32e7eSjoerg   std::string FilePath;
14006f32e7eSjoerg   std::string Error;
14106f32e7eSjoerg   std::vector<std::string> InsertedHeaders;
14206f32e7eSjoerg   std::vector<std::string> RemovedHeaders;
14306f32e7eSjoerg   tooling::Replacements Replaces;
144*13fbcb42Sjoerg 
145*13fbcb42Sjoerg   // This field stores metadata which is ignored for the purposes of applying
146*13fbcb42Sjoerg   // edits to source, but may be useful for other consumers of AtomicChanges. In
147*13fbcb42Sjoerg   // particular, consumers can use this to direct how they want to consume each
148*13fbcb42Sjoerg   // edit.
149*13fbcb42Sjoerg   llvm::Any Metadata;
15006f32e7eSjoerg };
15106f32e7eSjoerg 
15206f32e7eSjoerg using AtomicChanges = std::vector<AtomicChange>;
15306f32e7eSjoerg 
15406f32e7eSjoerg // Defines specs for applying changes.
15506f32e7eSjoerg struct ApplyChangesSpec {
15606f32e7eSjoerg   // If true, cleans up redundant/erroneous code around changed code with
15706f32e7eSjoerg   // clang-format's cleanup functionality, e.g. redundant commas around deleted
15806f32e7eSjoerg   // parameter or empty namespaces introduced by deletions.
15906f32e7eSjoerg   bool Cleanup = true;
16006f32e7eSjoerg 
16106f32e7eSjoerg   format::FormatStyle Style = format::getNoStyle();
16206f32e7eSjoerg 
16306f32e7eSjoerg   // Options for selectively formatting changes with clang-format:
16406f32e7eSjoerg   // kAll: Format all changed lines.
16506f32e7eSjoerg   // kNone: Don't format anything.
16606f32e7eSjoerg   // kViolations: Format lines exceeding the `ColumnLimit` in `Style`.
16706f32e7eSjoerg   enum FormatOption { kAll, kNone, kViolations };
16806f32e7eSjoerg 
16906f32e7eSjoerg   FormatOption Format = kNone;
17006f32e7eSjoerg };
17106f32e7eSjoerg 
17206f32e7eSjoerg /// Applies all AtomicChanges in \p Changes to the \p Code.
17306f32e7eSjoerg ///
17406f32e7eSjoerg /// This completely ignores the file path in each change and replaces them with
17506f32e7eSjoerg /// \p FilePath, i.e. callers are responsible for ensuring all changes are for
17606f32e7eSjoerg /// the same file.
17706f32e7eSjoerg ///
17806f32e7eSjoerg /// \returns The changed code if all changes are applied successfully;
17906f32e7eSjoerg /// otherwise, an llvm::Error carrying llvm::StringError is returned (the Error
18006f32e7eSjoerg /// message can be converted to string with `llvm::toString()` and the
18106f32e7eSjoerg /// error_code should be ignored).
18206f32e7eSjoerg llvm::Expected<std::string>
18306f32e7eSjoerg applyAtomicChanges(llvm::StringRef FilePath, llvm::StringRef Code,
18406f32e7eSjoerg                    llvm::ArrayRef<AtomicChange> Changes,
18506f32e7eSjoerg                    const ApplyChangesSpec &Spec);
18606f32e7eSjoerg 
18706f32e7eSjoerg } // end namespace tooling
18806f32e7eSjoerg } // end namespace clang
18906f32e7eSjoerg 
19006f32e7eSjoerg #endif // LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H
191