diff options
Diffstat (limited to 'clang-r353983/include/clang/Tooling/Core')
| -rw-r--r-- | clang-r353983/include/clang/Tooling/Core/Diagnostic.h | 99 | ||||
| -rw-r--r-- | clang-r353983/include/clang/Tooling/Core/Lookup.h | 47 | ||||
| -rw-r--r-- | clang-r353983/include/clang/Tooling/Core/Replacement.h | 371 |
3 files changed, 517 insertions, 0 deletions
diff --git a/clang-r353983/include/clang/Tooling/Core/Diagnostic.h b/clang-r353983/include/clang/Tooling/Core/Diagnostic.h new file mode 100644 index 00000000..646bb56d --- /dev/null +++ b/clang-r353983/include/clang/Tooling/Core/Diagnostic.h @@ -0,0 +1,99 @@ +//===--- Diagnostic.h - Framework for clang diagnostics tools --*- 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 +// +//===----------------------------------------------------------------------===// +// +// \file +// Structures supporting diagnostics and refactorings that span multiple +// translation units. Indicate diagnostics reports and replacements +// suggestions for the analyzed sources. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_CORE_DIAGNOSTIC_H +#define LLVM_CLANG_TOOLING_CORE_DIAGNOSTIC_H + +#include "Replacement.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include <string> + +namespace clang { +namespace tooling { + +/// Represents the diagnostic message with the error message associated +/// and the information on the location of the problem. +struct DiagnosticMessage { + DiagnosticMessage(llvm::StringRef Message = ""); + + /// Constructs a diagnostic message with anoffset to the diagnostic + /// within the file where the problem occurred. + /// + /// \param Loc Should be a file location, it is not meaningful for a macro + /// location. + /// + DiagnosticMessage(llvm::StringRef Message, const SourceManager &Sources, + SourceLocation Loc); + std::string Message; + std::string FilePath; + unsigned FileOffset; +}; + +/// Represents the diagnostic with the level of severity and possible +/// fixes to be applied. +struct Diagnostic { + enum Level { + Warning = DiagnosticsEngine::Warning, + Error = DiagnosticsEngine::Error + }; + + Diagnostic() = default; + + Diagnostic(llvm::StringRef DiagnosticName, Level DiagLevel, + StringRef BuildDirectory); + + Diagnostic(llvm::StringRef DiagnosticName, const DiagnosticMessage &Message, + const llvm::StringMap<Replacements> &Fix, + const SmallVector<DiagnosticMessage, 1> &Notes, Level DiagLevel, + llvm::StringRef BuildDirectory); + + /// Name identifying the Diagnostic. + std::string DiagnosticName; + + /// Message associated to the diagnostic. + DiagnosticMessage Message; + + /// Fixes to apply, grouped by file path. + llvm::StringMap<Replacements> Fix; + + /// Potential notes about the diagnostic. + SmallVector<DiagnosticMessage, 1> Notes; + + /// Diagnostic level. Can indicate either an error or a warning. + Level DiagLevel; + + /// A build directory of the diagnostic source file. + /// + /// It's an absolute path which is `directory` field of the source file in + /// compilation database. If users don't specify the compilation database + /// directory, it is the current directory where clang-tidy runs. + /// + /// Note: it is empty in unittest. + std::string BuildDirectory; +}; + +/// Collection of Diagnostics generated from a single translation unit. +struct TranslationUnitDiagnostics { + /// Name of the main source for the translation unit. + std::string MainSourceFile; + std::vector<Diagnostic> Diagnostics; +}; + +} // end namespace tooling +} // end namespace clang +#endif // LLVM_CLANG_TOOLING_CORE_DIAGNOSTIC_H diff --git a/clang-r353983/include/clang/Tooling/Core/Lookup.h b/clang-r353983/include/clang/Tooling/Core/Lookup.h new file mode 100644 index 00000000..a27e9150 --- /dev/null +++ b/clang-r353983/include/clang/Tooling/Core/Lookup.h @@ -0,0 +1,47 @@ +//===--- Lookup.h - Framework for clang refactoring tools --*- 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 helper methods for clang tools performing name lookup. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_CORE_LOOKUP_H +#define LLVM_CLANG_TOOLING_CORE_LOOKUP_H + +#include "clang/Basic/LLVM.h" +#include <string> + +namespace clang { + +class DeclContext; +class NamedDecl; +class NestedNameSpecifier; + +namespace tooling { + +/// Emulate a lookup to replace one nested name specifier with another using as +/// few additional namespace qualifications as possible. +/// +/// This does not perform a full C++ lookup so ADL will not work. +/// +/// \param Use The nested name to be replaced. +/// \param UseContext The context in which the nested name is contained. This +/// will be used to minimize namespace qualifications. +/// \param FromDecl The declaration to which the nested name points. +/// \param ReplacementString The replacement nested name. Must be fully +/// qualified including a leading "::". +/// \returns The new name to be inserted in place of the current nested name. +std::string replaceNestedName(const NestedNameSpecifier *Use, + const DeclContext *UseContext, + const NamedDecl *FromDecl, + StringRef ReplacementString); + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_CORE_LOOKUP_H diff --git a/clang-r353983/include/clang/Tooling/Core/Replacement.h b/clang-r353983/include/clang/Tooling/Core/Replacement.h new file mode 100644 index 00000000..09374c5b --- /dev/null +++ b/clang-r353983/include/clang/Tooling/Core/Replacement.h @@ -0,0 +1,371 @@ +//===- Replacement.h - Framework for clang refactoring tools ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// Classes supporting refactorings that span multiple translation units. +// While single translation unit refactorings are supported via the Rewriter, +// when refactoring multiple translation units changes must be stored in a +// SourceManager independent form, duplicate changes need to be removed, and +// all changes must be applied at once at the end of the refactoring so that +// the code is always parseable. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H +#define LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H + +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" +#include <map> +#include <set> +#include <string> +#include <system_error> +#include <utility> +#include <vector> + +namespace clang { + +class FileManager; +class Rewriter; +class SourceManager; + +namespace tooling { + +/// A source range independent of the \c SourceManager. +class Range { +public: + Range() = default; + Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {} + + /// Accessors. + /// @{ + unsigned getOffset() const { return Offset; } + unsigned getLength() const { return Length; } + /// @} + + /// \name Range Predicates + /// @{ + /// Whether this range overlaps with \p RHS or not. + bool overlapsWith(Range RHS) const { + return Offset + Length > RHS.Offset && Offset < RHS.Offset + RHS.Length; + } + + /// Whether this range contains \p RHS or not. + bool contains(Range RHS) const { + return RHS.Offset >= Offset && + (RHS.Offset + RHS.Length) <= (Offset + Length); + } + + /// Whether this range equals to \p RHS or not. + bool operator==(const Range &RHS) const { + return Offset == RHS.getOffset() && Length == RHS.getLength(); + } + /// @} + +private: + unsigned Offset = 0; + unsigned Length = 0; +}; + +/// A text replacement. +/// +/// Represents a SourceManager independent replacement of a range of text in a +/// specific file. +class Replacement { +public: + /// Creates an invalid (not applicable) replacement. + Replacement(); + + /// Creates a replacement of the range [Offset, Offset+Length) in + /// FilePath with ReplacementText. + /// + /// \param FilePath A source file accessible via a SourceManager. + /// \param Offset The byte offset of the start of the range in the file. + /// \param Length The length of the range in bytes. + Replacement(StringRef FilePath, unsigned Offset, unsigned Length, + StringRef ReplacementText); + + /// Creates a Replacement of the range [Start, Start+Length) with + /// ReplacementText. + Replacement(const SourceManager &Sources, SourceLocation Start, + unsigned Length, StringRef ReplacementText); + + /// Creates a Replacement of the given range with ReplacementText. + Replacement(const SourceManager &Sources, const CharSourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts = LangOptions()); + + /// Creates a Replacement of the node with ReplacementText. + template <typename Node> + Replacement(const SourceManager &Sources, const Node &NodeToReplace, + StringRef ReplacementText, + const LangOptions &LangOpts = LangOptions()); + + /// Returns whether this replacement can be applied to a file. + /// + /// Only replacements that are in a valid file can be applied. + bool isApplicable() const; + + /// Accessors. + /// @{ + StringRef getFilePath() const { return FilePath; } + unsigned getOffset() const { return ReplacementRange.getOffset(); } + unsigned getLength() const { return ReplacementRange.getLength(); } + StringRef getReplacementText() const { return ReplacementText; } + /// @} + + /// Applies the replacement on the Rewriter. + bool apply(Rewriter &Rewrite) const; + + /// Returns a human readable string representation. + std::string toString() const; + +private: + void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start, + unsigned Length, StringRef ReplacementText); + void setFromSourceRange(const SourceManager &Sources, + const CharSourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts); + + std::string FilePath; + Range ReplacementRange; + std::string ReplacementText; +}; + +enum class replacement_error { + fail_to_apply = 0, + wrong_file_path, + overlap_conflict, + insert_conflict, +}; + +/// Carries extra error information in replacement-related llvm::Error, +/// e.g. fail applying replacements and replacements conflict. +class ReplacementError : public llvm::ErrorInfo<ReplacementError> { +public: + ReplacementError(replacement_error Err) : Err(Err) {} + + /// Constructs an error related to an existing replacement. + ReplacementError(replacement_error Err, Replacement Existing) + : Err(Err), ExistingReplacement(std::move(Existing)) {} + + /// Constructs an error related to a new replacement and an existing + /// replacement in a set of replacements. + ReplacementError(replacement_error Err, Replacement New, Replacement Existing) + : Err(Err), NewReplacement(std::move(New)), + ExistingReplacement(std::move(Existing)) {} + + std::string message() const override; + + void log(raw_ostream &OS) const override { OS << message(); } + + replacement_error get() const { return Err; } + + static char ID; + + const llvm::Optional<Replacement> &getNewReplacement() const { + return NewReplacement; + } + + const llvm::Optional<Replacement> &getExistingReplacement() const { + return ExistingReplacement; + } + +private: + // Users are not expected to use error_code. + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + + replacement_error Err; + + // A new replacement, which is to expected be added into a set of + // replacements, that is causing problem. + llvm::Optional<Replacement> NewReplacement; + + // An existing replacement in a replacements set that is causing problem. + llvm::Optional<Replacement> ExistingReplacement; +}; + +/// Less-than operator between two Replacements. +bool operator<(const Replacement &LHS, const Replacement &RHS); + +/// Equal-to operator between two Replacements. +bool operator==(const Replacement &LHS, const Replacement &RHS); + +/// Maintains a set of replacements that are conflict-free. +/// Two replacements are considered conflicts if they overlap or have the same +/// offset (i.e. order-dependent). +class Replacements { +private: + using ReplacementsImpl = std::set<Replacement>; + +public: + using const_iterator = ReplacementsImpl::const_iterator; + using const_reverse_iterator = ReplacementsImpl::const_reverse_iterator; + + Replacements() = default; + + explicit Replacements(const Replacement &R) { Replaces.insert(R); } + + /// Adds a new replacement \p R to the current set of replacements. + /// \p R must have the same file path as all existing replacements. + /// Returns `success` if the replacement is successfully inserted; otherwise, + /// it returns an llvm::Error, i.e. there is a conflict between R and the + /// existing replacements (i.e. they are order-dependent) or R's file path is + /// different from the filepath of existing replacements. Callers must + /// explicitly check the Error returned, and the returned error can be + /// converted to a string message with `llvm::toString()`. This prevents users + /// from adding order-dependent replacements. To control the order in which + /// order-dependent replacements are applied, use merge({R}) with R referring + /// to the changed code after applying all existing replacements. + /// Two replacements A and B are considered order-independent if applying them + /// in either order produces the same result. Note that the range of the + /// replacement that is applied later still refers to the original code. + /// These include (but not restricted to) replacements that: + /// - don't overlap (being directly adjacent is fine) and + /// - are overlapping deletions. + /// - are insertions at the same offset and applying them in either order + /// has the same effect, i.e. X + Y = Y + X when inserting X and Y + /// respectively. + /// - are identical replacements, i.e. applying the same replacement twice + /// is equivalent to applying it once. + /// Examples: + /// 1. Replacement A(0, 0, "a") and B(0, 0, "aa") are order-independent since + /// applying them in either order gives replacement (0, 0, "aaa"). + /// However, A(0, 0, "a") and B(0, 0, "b") are order-dependent since + /// applying A first gives (0, 0, "ab") while applying B first gives (B, A, + /// "ba"). + /// 2. Replacement A(0, 2, "123") and B(0, 2, "123") are order-independent + /// since applying them in either order gives (0, 2, "123"). + /// 3. Replacement A(0, 3, "123") and B(2, 3, "321") are order-independent + /// since either order gives (0, 5, "12321"). + /// 4. Replacement A(0, 3, "ab") and B(0, 3, "ab") are order-independent since + /// applying the same replacement twice is equivalent to applying it once. + /// Replacements with offset UINT_MAX are special - we do not detect conflicts + /// for such replacements since users may add them intentionally as a special + /// category of replacements. + llvm::Error add(const Replacement &R); + + /// Merges \p Replaces into the current replacements. \p Replaces + /// refers to code after applying the current replacements. + LLVM_NODISCARD Replacements merge(const Replacements &Replaces) const; + + // Returns the affected ranges in the changed code. + std::vector<Range> getAffectedRanges() const; + + // Returns the new offset in the code after replacements being applied. + // Note that if there is an insertion at Offset in the current replacements, + // \p Offset will be shifted to Offset + Length in inserted text. + unsigned getShiftedCodePosition(unsigned Position) const; + + unsigned size() const { return Replaces.size(); } + + void clear() { Replaces.clear(); } + + bool empty() const { return Replaces.empty(); } + + const_iterator begin() const { return Replaces.begin(); } + + const_iterator end() const { return Replaces.end(); } + + const_reverse_iterator rbegin() const { return Replaces.rbegin(); } + + const_reverse_iterator rend() const { return Replaces.rend(); } + + bool operator==(const Replacements &RHS) const { + return Replaces == RHS.Replaces; + } + +private: + Replacements(const_iterator Begin, const_iterator End) + : Replaces(Begin, End) {} + + // Returns `R` with new range that refers to code after `Replaces` being + // applied. + Replacement getReplacementInChangedCode(const Replacement &R) const; + + // Returns a set of replacements that is equivalent to the current + // replacements by merging all adjacent replacements. Two sets of replacements + // are considered equivalent if they have the same effect when they are + // applied. + Replacements getCanonicalReplacements() const; + + // If `R` and all existing replacements are order-indepedent, then merge it + // with `Replaces` and returns the merged replacements; otherwise, returns an + // error. + llvm::Expected<Replacements> + mergeIfOrderIndependent(const Replacement &R) const; + + ReplacementsImpl Replaces; +}; + +/// Apply all replacements in \p Replaces to the Rewriter \p Rewrite. +/// +/// Replacement applications happen independently of the success of +/// other applications. +/// +/// \returns true if all replacements apply. false otherwise. +bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite); + +/// Applies all replacements in \p Replaces to \p Code. +/// +/// This completely ignores the path stored in each replacement. If all +/// replacements are applied successfully, this returns the code with +/// replacements applied; otherwise, an llvm::Error carrying llvm::StringError +/// is returned (the Error message can be converted to string using +/// `llvm::toString()` and 'std::error_code` in the `Error` should be ignored). +llvm::Expected<std::string> applyAllReplacements(StringRef Code, + const Replacements &Replaces); + +/// Collection of Replacements generated from a single translation unit. +struct TranslationUnitReplacements { + /// Name of the main source for the translation unit. + std::string MainSourceFile; + + std::vector<Replacement> Replacements; +}; + +/// Calculates the new ranges after \p Replaces are applied. These +/// include both the original \p Ranges and the affected ranges of \p Replaces +/// in the new code. +/// +/// \pre Replacements must be for the same file. +/// +/// \return The new ranges after \p Replaces are applied. The new ranges will be +/// sorted and non-overlapping. +std::vector<Range> +calculateRangesAfterReplacements(const Replacements &Replaces, + const std::vector<Range> &Ranges); + +/// If there are multiple <File, Replacements> pairs with the same file +/// entry, we only keep one pair and discard the rest. +/// If a file does not exist, its corresponding replacements will be ignored. +std::map<std::string, Replacements> groupReplacementsByFile( + FileManager &FileMgr, + const std::map<std::string, Replacements> &FileToReplaces); + +template <typename Node> +Replacement::Replacement(const SourceManager &Sources, + const Node &NodeToReplace, StringRef ReplacementText, + const LangOptions &LangOpts) { + const CharSourceRange Range = + CharSourceRange::getTokenRange(NodeToReplace->getSourceRange()); + setFromSourceRange(Sources, Range, ReplacementText, LangOpts); +} + +} // namespace tooling + +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H |
