10b57cec5SDimitry Andric //===--- AtomicChange.h - AtomicChange class --------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This file defines AtomicChange which is used to create a set of source
100b57cec5SDimitry Andric //  changes, e.g. replacements and header insertions.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
1404eeddc0SDimitry Andric #ifndef LLVM_CLANG_TOOLING_REFACTORING_ATOMICCHANGE_H
1504eeddc0SDimitry Andric #define LLVM_CLANG_TOOLING_REFACTORING_ATOMICCHANGE_H
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
180b57cec5SDimitry Andric #include "clang/Format/Format.h"
190b57cec5SDimitry Andric #include "clang/Tooling/Core/Replacement.h"
205ffd83dbSDimitry Andric #include "llvm/ADT/Any.h"
210b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
220b57cec5SDimitry Andric #include "llvm/Support/Error.h"
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric namespace clang {
250b57cec5SDimitry Andric namespace tooling {
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric /// An atomic change is used to create and group a set of source edits,
280b57cec5SDimitry Andric /// e.g. replacements or header insertions. Edits in an AtomicChange should be
290b57cec5SDimitry Andric /// related, e.g. replacements for the same type reference and the corresponding
300b57cec5SDimitry Andric /// header insertion/deletion.
310b57cec5SDimitry Andric ///
320b57cec5SDimitry Andric /// An AtomicChange is uniquely identified by a key and will either be fully
330b57cec5SDimitry Andric /// applied or not applied at all.
340b57cec5SDimitry Andric ///
350b57cec5SDimitry Andric /// Calling setError on an AtomicChange stores the error message and marks it as
360b57cec5SDimitry Andric /// bad, i.e. none of its source edits will be applied.
370b57cec5SDimitry Andric class AtomicChange {
380b57cec5SDimitry Andric public:
390b57cec5SDimitry Andric   /// Creates an atomic change around \p KeyPosition with the key being a
400b57cec5SDimitry Andric   /// concatenation of the file name and the offset of \p KeyPosition.
410b57cec5SDimitry Andric   /// \p KeyPosition should be the location of the key syntactical element that
420b57cec5SDimitry Andric   /// is being changed, e.g. the call to a refactored method.
430b57cec5SDimitry Andric   AtomicChange(const SourceManager &SM, SourceLocation KeyPosition);
440b57cec5SDimitry Andric 
455ffd83dbSDimitry Andric   AtomicChange(const SourceManager &SM, SourceLocation KeyPosition,
465ffd83dbSDimitry Andric                llvm::Any Metadata);
475ffd83dbSDimitry Andric 
480b57cec5SDimitry Andric   /// Creates an atomic change for \p FilePath with a customized key.
AtomicChange(llvm::StringRef FilePath,llvm::StringRef Key)490b57cec5SDimitry Andric   AtomicChange(llvm::StringRef FilePath, llvm::StringRef Key)
500b57cec5SDimitry Andric       : Key(Key), FilePath(FilePath) {}
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   AtomicChange(AtomicChange &&) = default;
530b57cec5SDimitry Andric   AtomicChange(const AtomicChange &) = default;
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric   AtomicChange &operator=(AtomicChange &&) = default;
560b57cec5SDimitry Andric   AtomicChange &operator=(const AtomicChange &) = default;
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric   bool operator==(const AtomicChange &Other) const;
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric   /// Returns the atomic change as a YAML string.
610b57cec5SDimitry Andric   std::string toYAMLString();
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric   /// Converts a YAML-encoded automic change to AtomicChange.
640b57cec5SDimitry Andric   static AtomicChange convertFromYAML(llvm::StringRef YAMLContent);
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   /// Returns the key of this change, which is a concatenation of the
670b57cec5SDimitry Andric   /// file name and offset of the key position.
getKey()680b57cec5SDimitry Andric   const std::string &getKey() const { return Key; }
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric   /// Returns the path of the file containing this atomic change.
getFilePath()710b57cec5SDimitry Andric   const std::string &getFilePath() const { return FilePath; }
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric   /// If this change could not be created successfully, e.g. because of
740b57cec5SDimitry Andric   /// conflicts among replacements, use this to set an error description.
750b57cec5SDimitry Andric   /// Thereby, places that cannot be fixed automatically can be gathered when
760b57cec5SDimitry Andric   /// applying changes.
setError(llvm::StringRef Error)775ffd83dbSDimitry Andric   void setError(llvm::StringRef Error) { this->Error = std::string(Error); }
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   /// Returns whether an error has been set on this list.
hasError()800b57cec5SDimitry Andric   bool hasError() const { return !Error.empty(); }
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric   /// Returns the error message or an empty string if it does not exist.
getError()830b57cec5SDimitry Andric   const std::string &getError() const { return Error; }
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric   /// Adds a replacement that replaces the given Range with
860b57cec5SDimitry Andric   /// ReplacementText.
870b57cec5SDimitry Andric   /// \returns An llvm::Error carrying ReplacementError on error.
880b57cec5SDimitry Andric   llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range,
890b57cec5SDimitry Andric                       llvm::StringRef ReplacementText);
900b57cec5SDimitry Andric 
910b57cec5SDimitry Andric   /// Adds a replacement that replaces range [Loc, Loc+Length) with
920b57cec5SDimitry Andric   /// \p Text.
930b57cec5SDimitry Andric   /// \returns An llvm::Error carrying ReplacementError on error.
940b57cec5SDimitry Andric   llvm::Error replace(const SourceManager &SM, SourceLocation Loc,
950b57cec5SDimitry Andric                       unsigned Length, llvm::StringRef Text);
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric   /// Adds a replacement that inserts \p Text at \p Loc. If this
980b57cec5SDimitry Andric   /// insertion conflicts with an existing insertion (at the same position),
990b57cec5SDimitry Andric   /// this will be inserted before/after the existing insertion depending on
1000b57cec5SDimitry Andric   /// \p InsertAfter. Users should use `replace` with `Length=0` instead if they
1010b57cec5SDimitry Andric   /// do not want conflict resolving by default. If the conflicting replacement
1020b57cec5SDimitry Andric   /// is not an insertion, an error is returned.
1030b57cec5SDimitry Andric   ///
1040b57cec5SDimitry Andric   /// \returns An llvm::Error carrying ReplacementError on error.
1050b57cec5SDimitry Andric   llvm::Error insert(const SourceManager &SM, SourceLocation Loc,
1060b57cec5SDimitry Andric                      llvm::StringRef Text, bool InsertAfter = true);
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   /// Adds a header into the file that contains the key position.
1090b57cec5SDimitry Andric   /// Header can be in angle brackets or double quotation marks. By default
1100b57cec5SDimitry Andric   /// (header is not quoted), header will be surrounded with double quotes.
1110b57cec5SDimitry Andric   void addHeader(llvm::StringRef Header);
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   /// Removes a header from the file that contains the key position.
1140b57cec5SDimitry Andric   void removeHeader(llvm::StringRef Header);
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric   /// Returns a const reference to existing replacements.
getReplacements()1170b57cec5SDimitry Andric   const Replacements &getReplacements() const { return Replaces; }
1180b57cec5SDimitry Andric 
getReplacements()11981ad6265SDimitry Andric   Replacements &getReplacements() { return Replaces; }
12081ad6265SDimitry Andric 
getInsertedHeaders()1210b57cec5SDimitry Andric   llvm::ArrayRef<std::string> getInsertedHeaders() const {
1220b57cec5SDimitry Andric     return InsertedHeaders;
1230b57cec5SDimitry Andric   }
1240b57cec5SDimitry Andric 
getRemovedHeaders()1250b57cec5SDimitry Andric   llvm::ArrayRef<std::string> getRemovedHeaders() const {
1260b57cec5SDimitry Andric     return RemovedHeaders;
1270b57cec5SDimitry Andric   }
1280b57cec5SDimitry Andric 
getMetadata()1295ffd83dbSDimitry Andric   const llvm::Any &getMetadata() const { return Metadata; }
1305ffd83dbSDimitry Andric 
1310b57cec5SDimitry Andric private:
AtomicChange()1320b57cec5SDimitry Andric   AtomicChange() {}
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric   AtomicChange(std::string Key, std::string FilePath, std::string Error,
1350b57cec5SDimitry Andric                std::vector<std::string> InsertedHeaders,
1360b57cec5SDimitry Andric                std::vector<std::string> RemovedHeaders,
1370b57cec5SDimitry Andric                clang::tooling::Replacements Replaces);
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric   // This uniquely identifies an AtomicChange.
1400b57cec5SDimitry Andric   std::string Key;
1410b57cec5SDimitry Andric   std::string FilePath;
1420b57cec5SDimitry Andric   std::string Error;
1430b57cec5SDimitry Andric   std::vector<std::string> InsertedHeaders;
1440b57cec5SDimitry Andric   std::vector<std::string> RemovedHeaders;
1450b57cec5SDimitry Andric   tooling::Replacements Replaces;
1465ffd83dbSDimitry Andric 
1475ffd83dbSDimitry Andric   // This field stores metadata which is ignored for the purposes of applying
1485ffd83dbSDimitry Andric   // edits to source, but may be useful for other consumers of AtomicChanges. In
1495ffd83dbSDimitry Andric   // particular, consumers can use this to direct how they want to consume each
1505ffd83dbSDimitry Andric   // edit.
1515ffd83dbSDimitry Andric   llvm::Any Metadata;
1520b57cec5SDimitry Andric };
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric using AtomicChanges = std::vector<AtomicChange>;
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric // Defines specs for applying changes.
1570b57cec5SDimitry Andric struct ApplyChangesSpec {
1580b57cec5SDimitry Andric   // If true, cleans up redundant/erroneous code around changed code with
1590b57cec5SDimitry Andric   // clang-format's cleanup functionality, e.g. redundant commas around deleted
1600b57cec5SDimitry Andric   // parameter or empty namespaces introduced by deletions.
1610b57cec5SDimitry Andric   bool Cleanup = true;
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric   format::FormatStyle Style = format::getNoStyle();
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   // Options for selectively formatting changes with clang-format:
1660b57cec5SDimitry Andric   // kAll: Format all changed lines.
1670b57cec5SDimitry Andric   // kNone: Don't format anything.
1680b57cec5SDimitry Andric   // kViolations: Format lines exceeding the `ColumnLimit` in `Style`.
1690b57cec5SDimitry Andric   enum FormatOption { kAll, kNone, kViolations };
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric   FormatOption Format = kNone;
1720b57cec5SDimitry Andric };
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric /// Applies all AtomicChanges in \p Changes to the \p Code.
1750b57cec5SDimitry Andric ///
1760b57cec5SDimitry Andric /// This completely ignores the file path in each change and replaces them with
1770b57cec5SDimitry Andric /// \p FilePath, i.e. callers are responsible for ensuring all changes are for
1780b57cec5SDimitry Andric /// the same file.
1790b57cec5SDimitry Andric ///
1800b57cec5SDimitry Andric /// \returns The changed code if all changes are applied successfully;
1810b57cec5SDimitry Andric /// otherwise, an llvm::Error carrying llvm::StringError is returned (the Error
1820b57cec5SDimitry Andric /// message can be converted to string with `llvm::toString()` and the
1830b57cec5SDimitry Andric /// error_code should be ignored).
1840b57cec5SDimitry Andric llvm::Expected<std::string>
1850b57cec5SDimitry Andric applyAtomicChanges(llvm::StringRef FilePath, llvm::StringRef Code,
1860b57cec5SDimitry Andric                    llvm::ArrayRef<AtomicChange> Changes,
1870b57cec5SDimitry Andric                    const ApplyChangesSpec &Spec);
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric } // end namespace tooling
1900b57cec5SDimitry Andric } // end namespace clang
1910b57cec5SDimitry Andric 
19204eeddc0SDimitry Andric #endif // LLVM_CLANG_TOOLING_REFACTORING_ATOMICCHANGE_H
193