AtomicChange.h 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===--- AtomicChange.h - AtomicChange class --------------------*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // This file defines AtomicChange which is used to create a set of source
  15. // changes, e.g. replacements and header insertions.
  16. //
  17. //===----------------------------------------------------------------------===//
  18. #ifndef LLVM_CLANG_TOOLING_REFACTORING_ATOMICCHANGE_H
  19. #define LLVM_CLANG_TOOLING_REFACTORING_ATOMICCHANGE_H
  20. #include "clang/Basic/SourceManager.h"
  21. #include "clang/Format/Format.h"
  22. #include "clang/Tooling/Core/Replacement.h"
  23. #include "llvm/ADT/Any.h"
  24. #include "llvm/ADT/StringRef.h"
  25. #include "llvm/Support/Error.h"
  26. namespace clang {
  27. namespace tooling {
  28. /// An atomic change is used to create and group a set of source edits,
  29. /// e.g. replacements or header insertions. Edits in an AtomicChange should be
  30. /// related, e.g. replacements for the same type reference and the corresponding
  31. /// header insertion/deletion.
  32. ///
  33. /// An AtomicChange is uniquely identified by a key and will either be fully
  34. /// applied or not applied at all.
  35. ///
  36. /// Calling setError on an AtomicChange stores the error message and marks it as
  37. /// bad, i.e. none of its source edits will be applied.
  38. class AtomicChange {
  39. public:
  40. /// Creates an atomic change around \p KeyPosition with the key being a
  41. /// concatenation of the file name and the offset of \p KeyPosition.
  42. /// \p KeyPosition should be the location of the key syntactical element that
  43. /// is being changed, e.g. the call to a refactored method.
  44. AtomicChange(const SourceManager &SM, SourceLocation KeyPosition);
  45. AtomicChange(const SourceManager &SM, SourceLocation KeyPosition,
  46. llvm::Any Metadata);
  47. /// Creates an atomic change for \p FilePath with a customized key.
  48. AtomicChange(llvm::StringRef FilePath, llvm::StringRef Key)
  49. : Key(Key), FilePath(FilePath) {}
  50. AtomicChange(AtomicChange &&) = default;
  51. AtomicChange(const AtomicChange &) = default;
  52. AtomicChange &operator=(AtomicChange &&) = default;
  53. AtomicChange &operator=(const AtomicChange &) = default;
  54. bool operator==(const AtomicChange &Other) const;
  55. /// Returns the atomic change as a YAML string.
  56. std::string toYAMLString();
  57. /// Converts a YAML-encoded automic change to AtomicChange.
  58. static AtomicChange convertFromYAML(llvm::StringRef YAMLContent);
  59. /// Returns the key of this change, which is a concatenation of the
  60. /// file name and offset of the key position.
  61. const std::string &getKey() const { return Key; }
  62. /// Returns the path of the file containing this atomic change.
  63. const std::string &getFilePath() const { return FilePath; }
  64. /// If this change could not be created successfully, e.g. because of
  65. /// conflicts among replacements, use this to set an error description.
  66. /// Thereby, places that cannot be fixed automatically can be gathered when
  67. /// applying changes.
  68. void setError(llvm::StringRef Error) { this->Error = std::string(Error); }
  69. /// Returns whether an error has been set on this list.
  70. bool hasError() const { return !Error.empty(); }
  71. /// Returns the error message or an empty string if it does not exist.
  72. const std::string &getError() const { return Error; }
  73. /// Adds a replacement that replaces the given Range with
  74. /// ReplacementText.
  75. /// \returns An llvm::Error carrying ReplacementError on error.
  76. llvm::Error replace(const SourceManager &SM, const CharSourceRange &Range,
  77. llvm::StringRef ReplacementText);
  78. /// Adds a replacement that replaces range [Loc, Loc+Length) with
  79. /// \p Text.
  80. /// \returns An llvm::Error carrying ReplacementError on error.
  81. llvm::Error replace(const SourceManager &SM, SourceLocation Loc,
  82. unsigned Length, llvm::StringRef Text);
  83. /// Adds a replacement that inserts \p Text at \p Loc. If this
  84. /// insertion conflicts with an existing insertion (at the same position),
  85. /// this will be inserted before/after the existing insertion depending on
  86. /// \p InsertAfter. Users should use `replace` with `Length=0` instead if they
  87. /// do not want conflict resolving by default. If the conflicting replacement
  88. /// is not an insertion, an error is returned.
  89. ///
  90. /// \returns An llvm::Error carrying ReplacementError on error.
  91. llvm::Error insert(const SourceManager &SM, SourceLocation Loc,
  92. llvm::StringRef Text, bool InsertAfter = true);
  93. /// Adds a header into the file that contains the key position.
  94. /// Header can be in angle brackets or double quotation marks. By default
  95. /// (header is not quoted), header will be surrounded with double quotes.
  96. void addHeader(llvm::StringRef Header);
  97. /// Removes a header from the file that contains the key position.
  98. void removeHeader(llvm::StringRef Header);
  99. /// Returns a const reference to existing replacements.
  100. const Replacements &getReplacements() const { return Replaces; }
  101. Replacements &getReplacements() { return Replaces; }
  102. llvm::ArrayRef<std::string> getInsertedHeaders() const {
  103. return InsertedHeaders;
  104. }
  105. llvm::ArrayRef<std::string> getRemovedHeaders() const {
  106. return RemovedHeaders;
  107. }
  108. const llvm::Any &getMetadata() const { return Metadata; }
  109. private:
  110. AtomicChange() {}
  111. AtomicChange(std::string Key, std::string FilePath, std::string Error,
  112. std::vector<std::string> InsertedHeaders,
  113. std::vector<std::string> RemovedHeaders,
  114. clang::tooling::Replacements Replaces);
  115. // This uniquely identifies an AtomicChange.
  116. std::string Key;
  117. std::string FilePath;
  118. std::string Error;
  119. std::vector<std::string> InsertedHeaders;
  120. std::vector<std::string> RemovedHeaders;
  121. tooling::Replacements Replaces;
  122. // This field stores metadata which is ignored for the purposes of applying
  123. // edits to source, but may be useful for other consumers of AtomicChanges. In
  124. // particular, consumers can use this to direct how they want to consume each
  125. // edit.
  126. llvm::Any Metadata;
  127. };
  128. using AtomicChanges = std::vector<AtomicChange>;
  129. // Defines specs for applying changes.
  130. struct ApplyChangesSpec {
  131. // If true, cleans up redundant/erroneous code around changed code with
  132. // clang-format's cleanup functionality, e.g. redundant commas around deleted
  133. // parameter or empty namespaces introduced by deletions.
  134. bool Cleanup = true;
  135. format::FormatStyle Style = format::getNoStyle();
  136. // Options for selectively formatting changes with clang-format:
  137. // kAll: Format all changed lines.
  138. // kNone: Don't format anything.
  139. // kViolations: Format lines exceeding the `ColumnLimit` in `Style`.
  140. enum FormatOption { kAll, kNone, kViolations };
  141. FormatOption Format = kNone;
  142. };
  143. /// Applies all AtomicChanges in \p Changes to the \p Code.
  144. ///
  145. /// This completely ignores the file path in each change and replaces them with
  146. /// \p FilePath, i.e. callers are responsible for ensuring all changes are for
  147. /// the same file.
  148. ///
  149. /// \returns The changed code if all changes are applied successfully;
  150. /// otherwise, an llvm::Error carrying llvm::StringError is returned (the Error
  151. /// message can be converted to string with `llvm::toString()` and the
  152. /// error_code should be ignored).
  153. llvm::Expected<std::string>
  154. applyAtomicChanges(llvm::StringRef FilePath, llvm::StringRef Code,
  155. llvm::ArrayRef<AtomicChange> Changes,
  156. const ApplyChangesSpec &Spec);
  157. } // end namespace tooling
  158. } // end namespace clang
  159. #endif // LLVM_CLANG_TOOLING_REFACTORING_ATOMICCHANGE_H
  160. #ifdef __GNUC__
  161. #pragma GCC diagnostic pop
  162. #endif