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