https://github.com/dbartol updated https://github.com/llvm/llvm-project/pull/186257
>From 2e1b6387c7ea700e729bc6a41120aaa81190c324 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo <[email protected]> Date: Fri, 13 Mar 2026 12:15:33 -0400 Subject: [PATCH] [clang] Allow using part of `libClangToolingCore` without depending on Clang I'm about to make a PR that starts using `libClangToolingCore` in tblgen (to emit fixits as YAML), but since the Clang build depends on tblgen, this would create a circular dependency. To break this cycle, I have factored the parts of `libClangToolingCore` that don't depend on Clang into a separate library, `libClangToolingCoreNoClang`. To do this, I removed the Clang header includes from the "clang/tooling/Core" headers, made a few fixups to let those headers compile without the Clang types being complete, and then split the implementations in the ".cpp" files into separate files for the Clang dependencies. I then updated the CMake configuration to build the `NoClang` library first, then have the existing library depend on that. A few random fixups were needed elsewhere in places where code was depending on a stray `using namespace llvm;` somewhere in a header that is no longer included by the tooling headers. I also created a separate header to define a new enum, `DiagnosticLevel`, that is independent of the rest of Clang. Thus, it can be used in the tooling library or within Clang. I've updated `DiagnosticIDs::Level` to depend on this new enum. In the future we should just use this enum directly for all of the various enums that currently have separated definitions with the same values. --- .../IncludeFixerContext.cpp | 2 +- .../clang-include-fixer/IncludeFixerContext.h | 4 +- .../clang-tidy/ClangTidyDiagnosticConsumer.h | 1 + clang/include/clang/Basic/DiagnosticIDs.h | 10 +- clang/include/clang/Basic/DiagnosticLevel.h | 36 ++++ clang/include/clang/Tooling/Core/Diagnostic.h | 21 +- .../include/clang/Tooling/Core/Replacement.h | 54 ++++-- .../include/clang/Tooling/ReplacementsYaml.h | 7 +- clang/lib/Tooling/Core/CMakeLists.txt | 20 +- clang/lib/Tooling/Core/Diagnostic.cpp | 29 +-- clang/lib/Tooling/Core/DiagnosticClang.cpp | 48 +++++ clang/lib/Tooling/Core/Replacement.cpp | 143 +------------- clang/lib/Tooling/Core/ReplacementClang.cpp | 182 ++++++++++++++++++ 13 files changed, 357 insertions(+), 200 deletions(-) create mode 100644 clang/include/clang/Basic/DiagnosticLevel.h create mode 100644 clang/lib/Tooling/Core/DiagnosticClang.cpp create mode 100644 clang/lib/Tooling/Core/ReplacementClang.cpp diff --git a/clang-tools-extra/clang-include-fixer/IncludeFixerContext.cpp b/clang-tools-extra/clang-include-fixer/IncludeFixerContext.cpp index 4eac0617ed4a9..9f6fc33efa340 100644 --- a/clang-tools-extra/clang-include-fixer/IncludeFixerContext.cpp +++ b/clang-tools-extra/clang-include-fixer/IncludeFixerContext.cpp @@ -75,7 +75,7 @@ std::string createQualifiedNameForReplacement( } // anonymous namespace IncludeFixerContext::IncludeFixerContext( - StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols, + llvm::StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols, std::vector<find_all_symbols::SymbolInfo> Symbols) : FilePath(FilePath), QuerySymbolInfos(std::move(QuerySymbols)), MatchedSymbols(std::move(Symbols)) { diff --git a/clang-tools-extra/clang-include-fixer/IncludeFixerContext.h b/clang-tools-extra/clang-include-fixer/IncludeFixerContext.h index e819d30b29156..a381631520f74 100644 --- a/clang-tools-extra/clang-include-fixer/IncludeFixerContext.h +++ b/clang-tools-extra/clang-include-fixer/IncludeFixerContext.h @@ -46,7 +46,7 @@ class IncludeFixerContext { }; IncludeFixerContext() = default; - IncludeFixerContext(StringRef FilePath, + IncludeFixerContext(llvm::StringRef FilePath, std::vector<QuerySymbolInfo> QuerySymbols, std::vector<find_all_symbols::SymbolInfo> Symbols); @@ -61,7 +61,7 @@ class IncludeFixerContext { } /// Get the file path to the file being processed. - StringRef getFilePath() const { return FilePath; } + llvm::StringRef getFilePath() const { return FilePath; } /// Get header information. const std::vector<HeaderInfo> &getHeaderInfos() const { return HeaderInfos; } diff --git a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h index 8de5778dfefb0..b97a3a019e072 100644 --- a/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h +++ b/clang-tools-extra/clang-tidy/ClangTidyDiagnosticConsumer.h @@ -14,6 +14,7 @@ #include "FileExtensionsSet.h" #include "NoLintDirectiveHandler.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LangOptions.h" #include "clang/Tooling/Core/Diagnostic.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringSet.h" diff --git a/clang/include/clang/Basic/DiagnosticIDs.h b/clang/include/clang/Basic/DiagnosticIDs.h index 09e2d12dd040e..09c357cb83d66 100644 --- a/clang/include/clang/Basic/DiagnosticIDs.h +++ b/clang/include/clang/Basic/DiagnosticIDs.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_BASIC_DIAGNOSTICIDS_H #include "clang/Basic/DiagnosticCategories.h" +#include "clang/Basic/DiagnosticLevel.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/StringRef.h" @@ -182,7 +183,14 @@ class DiagnosticMapping { class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> { public: /// The level of the diagnostic, after it has been through mapping. - enum Level : uint8_t { Ignored, Note, Remark, Warning, Error, Fatal }; + enum Level : uint8_t { + Ignored = static_cast<uint8_t>(DiagnosticLevel::Ignored), + Note = static_cast<uint8_t>(DiagnosticLevel::Note), + Remark = static_cast<uint8_t>(DiagnosticLevel::Remark), + Warning = static_cast<uint8_t>(DiagnosticLevel::Warning), + Error = static_cast<uint8_t>(DiagnosticLevel::Error), + Fatal = static_cast<uint8_t>(DiagnosticLevel::Fatal) + }; // Diagnostic classes. enum Class { diff --git a/clang/include/clang/Basic/DiagnosticLevel.h b/clang/include/clang/Basic/DiagnosticLevel.h new file mode 100644 index 0000000000000..e7fbd8f6b945e --- /dev/null +++ b/clang/include/clang/Basic/DiagnosticLevel.h @@ -0,0 +1,36 @@ +//===--- DiagnosticLevel.h - Diagnostic Severity Level-----------*- 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 +/// Defines the DiagnosticLevel enum. +/// +/// This file has no other dependencies on Clang headers, to ensure that it can +/// be included from tblgen. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_DIAGNOSTICLEVEL_H +#define LLVM_CLANG_BASIC_DIAGNOSTICLEVEL_H + +#include <cstdint> + +namespace clang { + +/// The level of the diagnostic, after it has been through mapping. +enum class DiagnosticLevel : uint8_t { + Ignored, + Note, + Remark, + Warning, + Error, + Fatal +}; + +} // namespace clang + +#endif diff --git a/clang/include/clang/Tooling/Core/Diagnostic.h b/clang/include/clang/Tooling/Core/Diagnostic.h index 4553380bcf00e..00c5e81ca2bd8 100644 --- a/clang/include/clang/Tooling/Core/Diagnostic.h +++ b/clang/include/clang/Tooling/Core/Diagnostic.h @@ -16,8 +16,13 @@ #ifndef LLVM_CLANG_TOOLING_CORE_DIAGNOSTIC_H #define LLVM_CLANG_TOOLING_CORE_DIAGNOSTIC_H +// Please do not #include Clang headers in this file. This file can be used +// from clang-tblgen, and consuming Clang headers here will create a circular +// dependency. It _is_ acceptable to forward-declare types from the "clang" +// namespace, as long as the consuming code in clang-tblgen does not need to use +// any of the functions that use the forward-declared types. #include "Replacement.h" -#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticLevel.h" // OK because it only defines the enum. #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -67,19 +72,19 @@ struct DiagnosticMessage { /// fixes to be applied. struct Diagnostic { enum Level { - Remark = DiagnosticsEngine::Remark, - Warning = DiagnosticsEngine::Warning, - Error = DiagnosticsEngine::Error + Remark = static_cast<uint8_t>(DiagnosticLevel::Remark), + Warning = static_cast<uint8_t>(DiagnosticLevel::Warning), + Error = static_cast<uint8_t>(DiagnosticLevel::Error) }; Diagnostic() = default; Diagnostic(llvm::StringRef DiagnosticName, Level DiagLevel, - StringRef BuildDirectory); + llvm::StringRef BuildDirectory); Diagnostic(llvm::StringRef DiagnosticName, const DiagnosticMessage &Message, - const SmallVector<DiagnosticMessage, 1> &Notes, Level DiagLevel, - llvm::StringRef BuildDirectory); + const llvm::SmallVector<DiagnosticMessage, 1> &Notes, + Level DiagLevel, llvm::StringRef BuildDirectory); /// Name identifying the Diagnostic. std::string DiagnosticName; @@ -88,7 +93,7 @@ struct Diagnostic { DiagnosticMessage Message; /// Potential notes about the diagnostic. - SmallVector<DiagnosticMessage, 1> Notes; + llvm::SmallVector<DiagnosticMessage, 1> Notes; /// Diagnostic level. Can indicate either an error or a warning. Level DiagLevel; diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index f9452111e147f..b2cafad8d0042 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -18,8 +18,11 @@ #ifndef LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H #define LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H -#include "clang/Basic/LangOptions.h" -#include "clang/Basic/SourceLocation.h" +// Please do not #include Clang headers in this file. This file can be used +// from clang-tblgen, and consuming Clang headers here will create a circular +// dependency. It _is_ acceptable to forward-declare types from the "clang" +// namespace, as long as the consuming code in clang-tblgen does not need to use +// any of the functions that use the forward-declared types. #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" @@ -34,9 +37,13 @@ namespace clang { +class CharSourceRange; class FileManager; class Rewriter; +class SourceLocation; class SourceManager; +class SourceRange; +class LangOptions; namespace tooling { @@ -91,24 +98,24 @@ class Replacement { /// \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); + Replacement(llvm::StringRef FilePath, unsigned Offset, unsigned Length, + llvm::StringRef ReplacementText); /// Creates a Replacement of the range [Start, Start+Length) with /// ReplacementText. Replacement(const SourceManager &Sources, SourceLocation Start, - unsigned Length, StringRef ReplacementText); + unsigned Length, llvm::StringRef ReplacementText); /// Creates a Replacement of the given range with ReplacementText. Replacement(const SourceManager &Sources, const CharSourceRange &Range, - StringRef ReplacementText, - const LangOptions &LangOpts = LangOptions()); + llvm::StringRef ReplacementText, + const LangOptions &LangOpts = DefaultLangOptions); /// Creates a Replacement of the node with ReplacementText. template <typename Node> Replacement(const SourceManager &Sources, const Node &NodeToReplace, - StringRef ReplacementText, - const LangOptions &LangOpts = LangOptions()); + llvm::StringRef ReplacementText, + const LangOptions &LangOpts = DefaultLangOptions); /// Returns whether this replacement can be applied to a file. /// @@ -117,10 +124,10 @@ class Replacement { /// Accessors. /// @{ - StringRef getFilePath() const { return FilePath; } + llvm::StringRef getFilePath() const { return FilePath; } unsigned getOffset() const { return ReplacementRange.getOffset(); } unsigned getLength() const { return ReplacementRange.getLength(); } - StringRef getReplacementText() const { return ReplacementText; } + llvm::StringRef getReplacementText() const { return ReplacementText; } /// @} /// Applies the replacement on the Rewriter. @@ -131,11 +138,20 @@ class Replacement { private: void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start, - unsigned Length, StringRef ReplacementText); + unsigned Length, llvm::StringRef ReplacementText); void setFromSourceRange(const SourceManager &Sources, const CharSourceRange &Range, - StringRef ReplacementText, + llvm::StringRef ReplacementText, const LangOptions &LangOpts); + void setFromSourceRange(const SourceManager &Sources, + const SourceRange &Range, + llvm::StringRef ReplacementText, + const LangOptions &LangOpts); + + // Used as a default argument to avoid requiring LangOptions to be complete in + // this header. + static const LangOptions DefaultLangOptions; + static const char *const InvalidLocation; std::string FilePath; Range ReplacementRange; @@ -167,7 +183,7 @@ class ReplacementError : public llvm::ErrorInfo<ReplacementError> { std::string message() const override; - void log(raw_ostream &OS) const override { OS << message(); } + void log(llvm::raw_ostream &OS) const override { OS << message(); } replacement_error get() const { return Err; } @@ -328,7 +344,7 @@ bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite); /// 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, +llvm::Expected<std::string> applyAllReplacements(llvm::StringRef Code, const Replacements &Replaces); /// Collection of Replacements generated from a single translation unit. @@ -360,11 +376,11 @@ std::map<std::string, Replacements> groupReplacementsByFile( template <typename Node> Replacement::Replacement(const SourceManager &Sources, - const Node &NodeToReplace, StringRef ReplacementText, + const Node &NodeToReplace, + llvm::StringRef ReplacementText, const LangOptions &LangOpts) { - const CharSourceRange Range = - CharSourceRange::getTokenRange(NodeToReplace->getSourceRange()); - setFromSourceRange(Sources, Range, ReplacementText, LangOpts); + setFromSourceRange(Sources, NodeToReplace->getSourceRange(), ReplacementText, + LangOpts); } } // namespace tooling diff --git a/clang/include/clang/Tooling/ReplacementsYaml.h b/clang/include/clang/Tooling/ReplacementsYaml.h index 838f87fd19785..29825bf0eb440 100644 --- a/clang/include/clang/Tooling/ReplacementsYaml.h +++ b/clang/include/clang/Tooling/ReplacementsYaml.h @@ -15,7 +15,12 @@ #ifndef LLVM_CLANG_TOOLING_REPLACEMENTSYAML_H #define LLVM_CLANG_TOOLING_REPLACEMENTSYAML_H -#include "clang/Tooling/Refactoring.h" +// Please do not #include Clang headers in this file. This file can be used +// from clang-tblgen, and consuming Clang headers here will create a circular +// dependency. It _is_ acceptable to forward-declare types from the "clang" +// namespace, as long as the consuming code in clang-tblgen does not need to use +// any of the functions that use the forward-declared types. +#include "clang/Tooling/Core/Replacement.h" #include "llvm/Support/YAMLTraits.h" #include <string> diff --git a/clang/lib/Tooling/Core/CMakeLists.txt b/clang/lib/Tooling/Core/CMakeLists.txt index e523ca45301e2..ae5dbd55fc9c6 100644 --- a/clang/lib/Tooling/Core/CMakeLists.txt +++ b/clang/lib/Tooling/Core/CMakeLists.txt @@ -1,11 +1,29 @@ set(LLVM_LINK_COMPONENTS support) -add_clang_library(clangToolingCore +set(LLVM_COMMON_DEPENDS_OLD ${LLVM_COMMON_DEPENDS}) + +# Drop clang-tablegen-targets from LLVM_COMMON_DEPENDS. +# so that we could use clangToolingCoreNoClang within clang-tblgen. +list(REMOVE_ITEM LLVM_COMMON_DEPENDS clang-tablegen-targets) + +add_clang_library(clangToolingCoreNoClang Diagnostic.cpp Replacement.cpp + PARTIAL_SOURCES_INTENDED + ) + +set(LLVM_COMMON_DEPENDS ${LLVM_COMMON_DEPENDS_OLD}) + +add_clang_library(clangToolingCore + DiagnosticClang.cpp + ReplacementClang.cpp + + PARTIAL_SOURCES_INTENDED + LINK_LIBS clangBasic clangLex clangRewrite + clangToolingCoreNoClang ) diff --git a/clang/lib/Tooling/Core/Diagnostic.cpp b/clang/lib/Tooling/Core/Diagnostic.cpp index fb3358024692d..8d55accfb881f 100644 --- a/clang/lib/Tooling/Core/Diagnostic.cpp +++ b/clang/lib/Tooling/Core/Diagnostic.cpp @@ -11,40 +11,17 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/Core/Diagnostic.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Basic/SourceManager.h" #include "llvm/ADT/STLExtras.h" +using llvm::SmallVector; +using llvm::StringRef; + namespace clang { namespace tooling { DiagnosticMessage::DiagnosticMessage(llvm::StringRef Message) : Message(Message), FileOffset(0) {} -DiagnosticMessage::DiagnosticMessage(llvm::StringRef Message, - const SourceManager &Sources, - SourceLocation Loc) - : Message(Message), FileOffset(0) { - assert(Loc.isValid() && Loc.isFileID()); - FilePath = std::string(Sources.getFilename(Loc)); - - // Don't store offset in the scratch space. It doesn't tell anything to the - // user. Moreover, it depends on the history of macro expansions and thus - // prevents deduplication of warnings in headers. - if (!FilePath.empty()) - FileOffset = Sources.getFileOffset(Loc); -} - -FileByteRange::FileByteRange( - const SourceManager &Sources, CharSourceRange Range) - : FileOffset(0), Length(0) { - FilePath = std::string(Sources.getFilename(Range.getBegin())); - if (!FilePath.empty()) { - FileOffset = Sources.getFileOffset(Range.getBegin()); - Length = Sources.getFileOffset(Range.getEnd()) - FileOffset; - } -} - Diagnostic::Diagnostic(llvm::StringRef DiagnosticName, Diagnostic::Level DiagLevel, StringRef BuildDirectory) : DiagnosticName(DiagnosticName), DiagLevel(DiagLevel), diff --git a/clang/lib/Tooling/Core/DiagnosticClang.cpp b/clang/lib/Tooling/Core/DiagnosticClang.cpp new file mode 100644 index 0000000000000..a659338f7c6a1 --- /dev/null +++ b/clang/lib/Tooling/Core/DiagnosticClang.cpp @@ -0,0 +1,48 @@ +//===--- Diagnostic.cpp - Framework for clang diagnostics tools ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implements classes to support/store diagnostics refactoring. This file +// contains all of the code that depends on Clang, so that Diagnostic.cpp can +// be used from tools used to build Clang, like tblgen. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Tooling/Core/Diagnostic.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { +namespace tooling { + +DiagnosticMessage::DiagnosticMessage(llvm::StringRef Message, + const SourceManager &Sources, + SourceLocation Loc) + : Message(Message), FileOffset(0) { + assert(Loc.isValid() && Loc.isFileID()); + FilePath = std::string(Sources.getFilename(Loc)); + + // Don't store offset in the scratch space. It doesn't tell anything to the + // user. Moreover, it depends on the history of macro expansions and thus + // prevents deduplication of warnings in headers. + if (!FilePath.empty()) + FileOffset = Sources.getFileOffset(Loc); +} + +FileByteRange::FileByteRange(const SourceManager &Sources, + CharSourceRange Range) + : FileOffset(0), Length(0) { + FilePath = std::string(Sources.getFilename(Range.getBegin())); + if (!FilePath.empty()) { + FileOffset = Sources.getFileOffset(Range.getBegin()); + Length = Sources.getFileOffset(Range.getEnd()) - FileOffset; + } +} + +} // end namespace tooling +} // end namespace clang diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp index 10bdc223e33f2..0891ce8847e1f 100644 --- a/clang/lib/Tooling/Core/Replacement.cpp +++ b/clang/lib/Tooling/Core/Replacement.cpp @@ -11,15 +11,6 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/Core/Replacement.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/DiagnosticIDs.h" -#include "clang/Basic/DiagnosticOptions.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/FileSystemOptions.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Lex/Lexer.h" -#include "clang/Rewrite/Core/Rewriter.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/RewriteBuffer.h" #include "llvm/ADT/SmallPtrSet.h" @@ -39,9 +30,9 @@ using namespace clang; using namespace tooling; +using llvm::StringRef; -static const char * const InvalidLocation = ""; - +const char *const Replacement::InvalidLocation = ""; Replacement::Replacement() : FilePath(InvalidLocation) {} Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, @@ -49,41 +40,10 @@ Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, : FilePath(std::string(FilePath)), ReplacementRange(Offset, Length), ReplacementText(std::string(ReplacementText)) {} -Replacement::Replacement(const SourceManager &Sources, SourceLocation Start, - unsigned Length, StringRef ReplacementText) { - setFromSourceLocation(Sources, Start, Length, ReplacementText); -} - -Replacement::Replacement(const SourceManager &Sources, - const CharSourceRange &Range, - StringRef ReplacementText, - const LangOptions &LangOpts) { - setFromSourceRange(Sources, Range, ReplacementText, LangOpts); -} - bool Replacement::isApplicable() const { return FilePath != InvalidLocation; } -bool Replacement::apply(Rewriter &Rewrite) const { - SourceManager &SM = Rewrite.getSourceMgr(); - auto Entry = SM.getFileManager().getOptionalFileRef(FilePath); - if (!Entry) - return false; - - FileID ID = SM.getOrCreateFileID(*Entry, SrcMgr::C_User); - const SourceLocation Start = - SM.getLocForStartOfFile(ID). - getLocWithOffset(ReplacementRange.getOffset()); - // ReplaceText returns false on success. - // ReplaceText only fails if the source location is not a file location, in - // which case we already returned false earlier. - bool RewriteSucceeded = !Rewrite.ReplaceText( - Start, ReplacementRange.getLength(), ReplacementText); - assert(RewriteSucceeded); - return RewriteSucceeded; -} - std::string Replacement::toString() const { std::string Result; llvm::raw_string_ostream Stream(Result); @@ -117,42 +77,6 @@ bool operator==(const Replacement &LHS, const Replacement &RHS) { } // namespace tooling } // namespace clang -void Replacement::setFromSourceLocation(const SourceManager &Sources, - SourceLocation Start, unsigned Length, - StringRef ReplacementText) { - const FileIDAndOffset DecomposedLocation = Sources.getDecomposedLoc(Start); - OptionalFileEntryRef Entry = - Sources.getFileEntryRefForID(DecomposedLocation.first); - this->FilePath = std::string(Entry ? Entry->getName() : InvalidLocation); - this->ReplacementRange = Range(DecomposedLocation.second, Length); - this->ReplacementText = std::string(ReplacementText); -} - -// FIXME: This should go into the Lexer, but we need to figure out how -// to handle ranges for refactoring in general first - there is no obvious -// good way how to integrate this into the Lexer yet. -static int getRangeSize(const SourceManager &Sources, - const CharSourceRange &Range, - const LangOptions &LangOpts) { - SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); - SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); - FileIDAndOffset Start = Sources.getDecomposedLoc(SpellingBegin); - FileIDAndOffset End = Sources.getDecomposedLoc(SpellingEnd); - if (Start.first != End.first) return -1; - if (Range.isTokenRange()) - End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts); - return End.second - Start.second; -} - -void Replacement::setFromSourceRange(const SourceManager &Sources, - const CharSourceRange &Range, - StringRef ReplacementText, - const LangOptions &LangOpts) { - setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), - getRangeSize(Sources, Range, LangOpts), - ReplacementText); -} - Replacement Replacements::getReplacementInChangedCode(const Replacement &R) const { unsigned NewStart = getShiftedCodePosition(R.getOffset()); @@ -560,66 +484,3 @@ unsigned Replacements::getShiftedCodePosition(unsigned Position) const { } return Position + Offset; } - -namespace clang { -namespace tooling { - -bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { - bool Result = true; - for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { - if (I->isApplicable()) { - Result = I->apply(Rewrite) && Result; - } else { - Result = false; - } - } - return Result; -} - -llvm::Expected<std::string> applyAllReplacements(StringRef Code, - const Replacements &Replaces) { - if (Replaces.empty()) - return Code.str(); - - auto InMemoryFileSystem = - llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); - FileManager Files(FileSystemOptions(), InMemoryFileSystem); - DiagnosticOptions DiagOpts; - DiagnosticsEngine Diagnostics(DiagnosticIDs::create(), DiagOpts); - SourceManager SourceMgr(Diagnostics, Files); - Rewriter Rewrite(SourceMgr, LangOptions()); - InMemoryFileSystem->addFile( - "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>")); - FileID ID = SourceMgr.createFileID(*Files.getOptionalFileRef("<stdin>"), - SourceLocation(), - clang::SrcMgr::C_User); - for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { - Replacement Replace("<stdin>", I->getOffset(), I->getLength(), - I->getReplacementText()); - if (!Replace.apply(Rewrite)) - return llvm::make_error<ReplacementError>( - replacement_error::fail_to_apply, Replace); - } - std::string Result; - llvm::raw_string_ostream OS(Result); - Rewrite.getEditBuffer(ID).write(OS); - return Result; -} - -std::map<std::string, Replacements> groupReplacementsByFile( - FileManager &FileMgr, - const std::map<std::string, Replacements> &FileToReplaces) { - std::map<std::string, Replacements> Result; - llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries; - for (const auto &Entry : FileToReplaces) { - auto FE = FileMgr.getOptionalFileRef(Entry.first); - if (!FE) - llvm::errs() << "File path " << Entry.first << " is invalid.\n"; - else if (ProcessedFileEntries.insert(*FE).second) - Result[Entry.first] = std::move(Entry.second); - } - return Result; -} - -} // namespace tooling -} // namespace clang diff --git a/clang/lib/Tooling/Core/ReplacementClang.cpp b/clang/lib/Tooling/Core/ReplacementClang.cpp new file mode 100644 index 0000000000000..bf02d23fe74c7 --- /dev/null +++ b/clang/lib/Tooling/Core/ReplacementClang.cpp @@ -0,0 +1,182 @@ +//===- ReplacementNoClang.cpp - Framework for clang refactoring tools -----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Implements classes to support/store refactorings. This file contains all +// of the code that depends on Clang, so that Replacement.cpp can be used from +// tools used to build Clang, like tblgen. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/RewriteBuffer.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <limits> +#include <map> +#include <string> +#include <utility> +#include <vector> + +using namespace clang; +using namespace tooling; + +const LangOptions Replacement::DefaultLangOptions; + +Replacement::Replacement(const SourceManager &Sources, SourceLocation Start, + unsigned Length, StringRef ReplacementText) { + setFromSourceLocation(Sources, Start, Length, ReplacementText); +} + +Replacement::Replacement(const SourceManager &Sources, + const CharSourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts) { + setFromSourceRange(Sources, Range, ReplacementText, LangOpts); +} + +bool Replacement::apply(Rewriter &Rewrite) const { + SourceManager &SM = Rewrite.getSourceMgr(); + auto Entry = SM.getFileManager().getOptionalFileRef(FilePath); + if (!Entry) + return false; + + FileID ID = SM.getOrCreateFileID(*Entry, SrcMgr::C_User); + const SourceLocation Start = SM.getLocForStartOfFile(ID).getLocWithOffset( + ReplacementRange.getOffset()); + // ReplaceText returns false on success. + // ReplaceText only fails if the source location is not a file location, in + // which case we already returned false earlier. + bool RewriteSucceeded = !Rewrite.ReplaceText( + Start, ReplacementRange.getLength(), ReplacementText); + assert(RewriteSucceeded); + return RewriteSucceeded; +} + +void Replacement::setFromSourceLocation(const SourceManager &Sources, + SourceLocation Start, unsigned Length, + StringRef ReplacementText) { + const FileIDAndOffset DecomposedLocation = Sources.getDecomposedLoc(Start); + OptionalFileEntryRef Entry = + Sources.getFileEntryRefForID(DecomposedLocation.first); + this->FilePath = std::string(Entry ? Entry->getName() : InvalidLocation); + this->ReplacementRange = Range(DecomposedLocation.second, Length); + this->ReplacementText = std::string(ReplacementText); +} + +// FIXME: This should go into the Lexer, but we need to figure out how +// to handle ranges for refactoring in general first - there is no obvious +// good way how to integrate this into the Lexer yet. +static int getRangeSize(const SourceManager &Sources, + const CharSourceRange &Range, + const LangOptions &LangOpts) { + SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); + SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); + FileIDAndOffset Start = Sources.getDecomposedLoc(SpellingBegin); + FileIDAndOffset End = Sources.getDecomposedLoc(SpellingEnd); + if (Start.first != End.first) + return -1; + if (Range.isTokenRange()) + End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts); + return End.second - Start.second; +} + +void Replacement::setFromSourceRange(const SourceManager &Sources, + const CharSourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts) { + setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), + getRangeSize(Sources, Range, LangOpts), + ReplacementText); +} + +void Replacement::setFromSourceRange(const SourceManager &Sources, + const SourceRange &Range, + StringRef ReplacementText, + const LangOptions &LangOpts) { + setFromSourceRange(Sources, CharSourceRange::getTokenRange(Range), + ReplacementText, LangOpts); +} + +namespace clang { +namespace tooling { + +bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { + bool Result = true; + for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { + if (I->isApplicable()) { + Result = I->apply(Rewrite) && Result; + } else { + Result = false; + } + } + return Result; +} + +llvm::Expected<std::string> applyAllReplacements(StringRef Code, + const Replacements &Replaces) { + if (Replaces.empty()) + return Code.str(); + + auto InMemoryFileSystem = + llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); + FileManager Files(FileSystemOptions(), InMemoryFileSystem); + DiagnosticOptions DiagOpts; + DiagnosticsEngine Diagnostics(DiagnosticIDs::create(), DiagOpts); + SourceManager SourceMgr(Diagnostics, Files); + Rewriter Rewrite(SourceMgr, LangOptions()); + InMemoryFileSystem->addFile( + "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>")); + FileID ID = SourceMgr.createFileID(*Files.getOptionalFileRef("<stdin>"), + SourceLocation(), clang::SrcMgr::C_User); + for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { + Replacement Replace("<stdin>", I->getOffset(), I->getLength(), + I->getReplacementText()); + if (!Replace.apply(Rewrite)) + return llvm::make_error<ReplacementError>( + replacement_error::fail_to_apply, Replace); + } + std::string Result; + llvm::raw_string_ostream OS(Result); + Rewrite.getEditBuffer(ID).write(OS); + return Result; +} + +std::map<std::string, Replacements> groupReplacementsByFile( + FileManager &FileMgr, + const std::map<std::string, Replacements> &FileToReplaces) { + std::map<std::string, Replacements> Result; + llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries; + for (const auto &Entry : FileToReplaces) { + auto FE = FileMgr.getOptionalFileRef(Entry.first); + if (!FE) + llvm::errs() << "File path " << Entry.first << " is invalid.\n"; + else if (ProcessedFileEntries.insert(*FE).second) + Result[Entry.first] = std::move(Entry.second); + } + return Result; +} + +} // namespace tooling +} // namespace clang _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
