diff options
Diffstat (limited to 'clang-r353983e/include/clang/Tooling/Refactoring/AtomicChange.h')
| -rw-r--r-- | clang-r353983e/include/clang/Tooling/Refactoring/AtomicChange.h | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/clang-r353983e/include/clang/Tooling/Refactoring/AtomicChange.h b/clang-r353983e/include/clang/Tooling/Refactoring/AtomicChange.h new file mode 100644 index 00000000..32e4624f --- /dev/null +++ b/clang-r353983e/include/clang/Tooling/Refactoring/AtomicChange.h @@ -0,0 +1,178 @@ +//===--- AtomicChange.h - AtomicChange class --------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines AtomicChange which is used to create a set of source +// changes, e.g. replacements and header insertions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H +#define LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H + +#include "clang/Basic/SourceManager.h" +#include "clang/Format/Format.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace tooling { + +/// An atomic change is used to create and group a set of source edits, +/// e.g. replacements or header insertions. Edits in an AtomicChange should be +/// related, e.g. replacements for the same type reference and the corresponding +/// header insertion/deletion. +/// +/// An AtomicChange is uniquely identified by a key and will either be fully +/// applied or not applied at all. +/// +/// Calling setError on an AtomicChange stores the error message and marks it as +/// bad, i.e. none of its source edits will be applied. +class AtomicChange { +public: + /// Creates an atomic change around \p KeyPosition with the key being a + /// concatenation of the file name and the offset of \p KeyPosition. + /// \p KeyPosition should be the location of the key syntactical element that + /// is being changed, e.g. the call to a refactored method. + AtomicChange(const SourceManager &SM, SourceLocation KeyPosition); + + /// Creates an atomic change for \p FilePath with a customized key. + AtomicChange(llvm::StringRef FilePath, llvm::StringRef Key) + : Key(Key), FilePath(FilePath) {} + + AtomicChange(AtomicChange &&) = default; + AtomicChange(const AtomicChange &) = default; + + AtomicChange &operator=(AtomicChange &&) = default; + AtomicChange &operator=(const AtomicChange &) = default; + + bool operator==(const AtomicChange &Other) const; + + /// Returns the atomic change as a YAML string. + std::string toYAMLString(); + + /// Converts a YAML-encoded automic change to AtomicChange. + static AtomicChange convertFromYAML(llvm::StringRef YAMLContent); + + /// Returns the key of this change, which is a concatenation of the + /// file name and offset of the key position. + const std::string &getKey() const { return Key; } + + /// Returns the path of the file containing this atomic change. + const std::string &getFilePath() const { return FilePath; } + + /// If this change could not be created successfully, e.g. because of + /// conflicts among replacements, use this to set an error description. + /// Thereby, places that cannot be fixed automatically can be gathered when + /// applying changes. + void setError(llvm::StringRef Error) { this->Error = Error; } + + /// Returns whether an error has been set on this list. + bool hasError() const { return !Error.empty(); } + + /// Returns the error message or an empty string if it does not exist. + const std::string &getError() const { return Error; } + + /// Adds a replacement that replaces the given Range with + /// ReplacementText. + /// \returns An llvm::Error carrying ReplacementError on error. + llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range, + llvm::StringRef ReplacementText); + + /// Adds a replacement that replaces range [Loc, Loc+Length) with + /// \p Text. + /// \returns An llvm::Error carrying ReplacementError on error. + llvm::Error replace(const SourceManager &SM, SourceLocation Loc, + unsigned Length, llvm::StringRef Text); + + /// Adds a replacement that inserts \p Text at \p Loc. If this + /// insertion conflicts with an existing insertion (at the same position), + /// this will be inserted before/after the existing insertion depending on + /// \p InsertAfter. Users should use `replace` with `Length=0` instead if they + /// do not want conflict resolving by default. If the conflicting replacement + /// is not an insertion, an error is returned. + /// + /// \returns An llvm::Error carrying ReplacementError on error. + llvm::Error insert(const SourceManager &SM, SourceLocation Loc, + llvm::StringRef Text, bool InsertAfter = true); + + /// Adds a header into the file that contains the key position. + /// Header can be in angle brackets or double quotation marks. By default + /// (header is not quoted), header will be surrounded with double quotes. + void addHeader(llvm::StringRef Header); + + /// Removes a header from the file that contains the key position. + void removeHeader(llvm::StringRef Header); + + /// Returns a const reference to existing replacements. + const Replacements &getReplacements() const { return Replaces; } + + llvm::ArrayRef<std::string> getInsertedHeaders() const { + return InsertedHeaders; + } + + llvm::ArrayRef<std::string> getRemovedHeaders() const { + return RemovedHeaders; + } + +private: + AtomicChange() {} + + AtomicChange(std::string Key, std::string FilePath, std::string Error, + std::vector<std::string> InsertedHeaders, + std::vector<std::string> RemovedHeaders, + clang::tooling::Replacements Replaces); + + // This uniquely identifies an AtomicChange. + std::string Key; + std::string FilePath; + std::string Error; + std::vector<std::string> InsertedHeaders; + std::vector<std::string> RemovedHeaders; + tooling::Replacements Replaces; +}; + +using AtomicChanges = std::vector<AtomicChange>; + +// Defines specs for applying changes. +struct ApplyChangesSpec { + // If true, cleans up redundant/erroneous code around changed code with + // clang-format's cleanup functionality, e.g. redundant commas around deleted + // parameter or empty namespaces introduced by deletions. + bool Cleanup = true; + + format::FormatStyle Style = format::getNoStyle(); + + // Options for selectively formatting changes with clang-format: + // kAll: Format all changed lines. + // kNone: Don't format anything. + // kViolations: Format lines exceeding the `ColumnLimit` in `Style`. + enum FormatOption { kAll, kNone, kViolations }; + + FormatOption Format = kNone; +}; + +/// Applies all AtomicChanges in \p Changes to the \p Code. +/// +/// This completely ignores the file path in each change and replaces them with +/// \p FilePath, i.e. callers are responsible for ensuring all changes are for +/// the same file. +/// +/// \returns The changed code if all changes are applied successfully; +/// otherwise, an llvm::Error carrying llvm::StringError is returned (the Error +/// message can be converted to string with `llvm::toString()` and the +/// error_code should be ignored). +llvm::Expected<std::string> +applyAtomicChanges(llvm::StringRef FilePath, llvm::StringRef Code, + llvm::ArrayRef<AtomicChange> Changes, + const ApplyChangesSpec &Spec); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_REFACTOR_ATOMICCHANGE_H |
