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