reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  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