diff options
Diffstat (limited to 'include/clang/Tooling')
23 files changed, 825 insertions, 143 deletions
diff --git a/include/clang/Tooling/ASTDiff/ASTDiff.h b/include/clang/Tooling/ASTDiff/ASTDiff.h index d6cbc09dcede0..c1cc124e1e9f0 100644 --- a/include/clang/Tooling/ASTDiff/ASTDiff.h +++ b/include/clang/Tooling/ASTDiff/ASTDiff.h @@ -71,7 +71,7 @@ public: /// Constructs a tree from any AST node. template <class T> SyntaxTree(T *Node, ASTContext &AST) - : TreeImpl(llvm::make_unique<Impl>(this, Node, AST)) {} + : TreeImpl(std::make_unique<Impl>(this, Node, AST)) {} SyntaxTree(SyntaxTree &&Other) = default; ~SyntaxTree(); diff --git a/include/clang/Tooling/AllTUsExecution.h b/include/clang/Tooling/AllTUsExecution.h index e670f54234a67..1e618b5ba2f0f 100644 --- a/include/clang/Tooling/AllTUsExecution.h +++ b/include/clang/Tooling/AllTUsExecution.h @@ -44,8 +44,6 @@ public: StringRef getExecutorName() const override { return ExecutorName; } - bool isSingleProcess() const override { return true; } - using ToolExecutor::execute; llvm::Error @@ -71,6 +69,7 @@ private: unsigned ThreadCount; }; +extern llvm::cl::opt<unsigned> ExecutorConcurrency; extern llvm::cl::opt<std::string> Filter; } // end namespace tooling diff --git a/include/clang/Tooling/ArgumentsAdjusters.h b/include/clang/Tooling/ArgumentsAdjusters.h index bf0886034324a..c48a8725aae90 100644 --- a/include/clang/Tooling/ArgumentsAdjusters.h +++ b/include/clang/Tooling/ArgumentsAdjusters.h @@ -43,6 +43,10 @@ ArgumentsAdjuster getClangSyntaxOnlyAdjuster(); /// arguments. ArgumentsAdjuster getClangStripOutputAdjuster(); +/// Gets an argument adjuster which removes command line arguments related to +/// diagnostic serialization. +ArgumentsAdjuster getClangStripSerializeDiagnosticAdjuster(); + /// Gets an argument adjuster which removes dependency-file /// related command line arguments. ArgumentsAdjuster getClangStripDependencyFileAdjuster(); diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h b/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h new file mode 100644 index 0000000000000..7d0881343478a --- /dev/null +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h @@ -0,0 +1,188 @@ +//===- DependencyScanningFilesystem.h - clang-scan-deps fs ===---*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H + +#include "clang/Basic/LLVM.h" +#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/VirtualFileSystem.h" +#include <mutex> + +namespace clang { +namespace tooling { +namespace dependencies { + +/// An in-memory representation of a file system entity that is of interest to +/// the dependency scanning filesystem. +/// +/// It represents one of the following: +/// - an opened source file with minimized contents and a stat value. +/// - an opened source file with original contents and a stat value. +/// - a directory entry with its stat value. +/// - an error value to represent a file system error. +/// - a placeholder with an invalid stat indicating a not yet initialized entry. +class CachedFileSystemEntry { +public: + /// Default constructor creates an entry with an invalid stat. + CachedFileSystemEntry() : MaybeStat(llvm::vfs::Status()) {} + + CachedFileSystemEntry(std::error_code Error) : MaybeStat(std::move(Error)) {} + + /// Create an entry that represents an opened source file with minimized or + /// original contents. + /// + /// The filesystem opens the file even for `stat` calls open to avoid the + /// issues with stat + open of minimized files that might lead to a + /// mismatching size of the file. If file is not minimized, the full file is + /// read and copied into memory to ensure that it's not memory mapped to avoid + /// running out of file descriptors. + static CachedFileSystemEntry createFileEntry(StringRef Filename, + llvm::vfs::FileSystem &FS, + bool Minimize = true); + + /// Create an entry that represents a directory on the filesystem. + static CachedFileSystemEntry createDirectoryEntry(llvm::vfs::Status &&Stat); + + /// \returns True if the entry is valid. + bool isValid() const { return !MaybeStat || MaybeStat->isStatusKnown(); } + + /// \returns True if the current entry points to a directory. + bool isDirectory() const { return MaybeStat && MaybeStat->isDirectory(); } + + /// \returns The error or the file's contents. + llvm::ErrorOr<StringRef> getContents() const { + if (!MaybeStat) + return MaybeStat.getError(); + assert(!MaybeStat->isDirectory() && "not a file"); + assert(isValid() && "not initialized"); + return StringRef(Contents); + } + + /// \returns The error or the status of the entry. + llvm::ErrorOr<llvm::vfs::Status> getStatus() const { + assert(isValid() && "not initialized"); + return MaybeStat; + } + + /// \returns the name of the file. + StringRef getName() const { + assert(isValid() && "not initialized"); + return MaybeStat->getName(); + } + + /// Return the mapping between location -> distance that is used to speed up + /// the block skipping in the preprocessor. + const PreprocessorSkippedRangeMapping &getPPSkippedRangeMapping() const { + return PPSkippedRangeMapping; + } + + CachedFileSystemEntry(CachedFileSystemEntry &&) = default; + CachedFileSystemEntry &operator=(CachedFileSystemEntry &&) = default; + + CachedFileSystemEntry(const CachedFileSystemEntry &) = delete; + CachedFileSystemEntry &operator=(const CachedFileSystemEntry &) = delete; + +private: + llvm::ErrorOr<llvm::vfs::Status> MaybeStat; + // Store the contents in a small string to allow a + // move from the small string for the minimized contents. + // Note: small size of 1 allows us to store an empty string with an implicit + // null terminator without any allocations. + llvm::SmallString<1> Contents; + PreprocessorSkippedRangeMapping PPSkippedRangeMapping; +}; + +/// This class is a shared cache, that caches the 'stat' and 'open' calls to the +/// underlying real file system. +/// +/// It is sharded based on the hash of the key to reduce the lock contention for +/// the worker threads. +class DependencyScanningFilesystemSharedCache { +public: + struct SharedFileSystemEntry { + std::mutex ValueLock; + CachedFileSystemEntry Value; + }; + + DependencyScanningFilesystemSharedCache(); + + /// Returns a cache entry for the corresponding key. + /// + /// A new cache entry is created if the key is not in the cache. This is a + /// thread safe call. + SharedFileSystemEntry &get(StringRef Key); + +private: + struct CacheShard { + std::mutex CacheLock; + llvm::StringMap<SharedFileSystemEntry, llvm::BumpPtrAllocator> Cache; + }; + std::unique_ptr<CacheShard[]> CacheShards; + unsigned NumShards; +}; + +/// A virtual file system optimized for the dependency discovery. +/// +/// It is primarily designed to work with source files whose contents was was +/// preprocessed to remove any tokens that are unlikely to affect the dependency +/// computation. +/// +/// This is not a thread safe VFS. A single instance is meant to be used only in +/// one thread. Multiple instances are allowed to service multiple threads +/// running in parallel. +class DependencyScanningWorkerFilesystem : public llvm::vfs::ProxyFileSystem { +public: + DependencyScanningWorkerFilesystem( + DependencyScanningFilesystemSharedCache &SharedCache, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, + ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings) + : ProxyFileSystem(std::move(FS)), SharedCache(SharedCache), + PPSkipMappings(PPSkipMappings) {} + + llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override; + llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>> + openFileForRead(const Twine &Path) override; + + /// The set of files that should not be minimized. + llvm::StringSet<> IgnoredFiles; + +private: + void setCachedEntry(StringRef Filename, const CachedFileSystemEntry *Entry) { + bool IsInserted = Cache.try_emplace(Filename, Entry).second; + (void)IsInserted; + assert(IsInserted && "local cache is updated more than once"); + } + + const CachedFileSystemEntry *getCachedEntry(StringRef Filename) { + auto It = Cache.find(Filename); + return It == Cache.end() ? nullptr : It->getValue(); + } + + llvm::ErrorOr<const CachedFileSystemEntry *> + getOrCreateFileSystemEntry(const StringRef Filename); + + DependencyScanningFilesystemSharedCache &SharedCache; + /// The local cache is used by the worker thread to cache file system queries + /// locally instead of querying the global cache every time. + llvm::StringMap<const CachedFileSystemEntry *, llvm::BumpPtrAllocator> Cache; + /// The optional mapping structure which records information about the + /// excluded conditional directive skip mappings that are used by the + /// currently active preprocessor. + ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_FILESYSTEM_H diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/include/clang/Tooling/DependencyScanning/DependencyScanningService.h new file mode 100644 index 0000000000000..fd8ed80b143ca --- /dev/null +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningService.h @@ -0,0 +1,65 @@ +//===- DependencyScanningService.h - clang-scan-deps service ===-*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_SERVICE_H +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_SERVICE_H + +#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h" + +namespace clang { +namespace tooling { +namespace dependencies { + +/// The mode in which the dependency scanner will operate to find the +/// dependencies. +enum class ScanningMode { + /// This mode is used to compute the dependencies by running the preprocessor + /// over + /// the unmodified source files. + CanonicalPreprocessing, + + /// This mode is used to compute the dependencies by running the preprocessor + /// over + /// the source files that have been minimized to contents that might affect + /// the dependencies. + MinimizedSourcePreprocessing +}; + +/// The dependency scanning service contains the shared state that is used by +/// the invidual dependency scanning workers. +class DependencyScanningService { +public: + DependencyScanningService(ScanningMode Mode, bool ReuseFileManager = true, + bool SkipExcludedPPRanges = true); + + ScanningMode getMode() const { return Mode; } + + bool canReuseFileManager() const { return ReuseFileManager; } + + bool canSkipExcludedPPRanges() const { return SkipExcludedPPRanges; } + + DependencyScanningFilesystemSharedCache &getSharedCache() { + return SharedCache; + } + +private: + const ScanningMode Mode; + const bool ReuseFileManager; + /// Set to true to use the preprocessor optimization that skips excluded PP + /// ranges by bumping the buffer pointer in the lexer instead of lexing the + /// tokens in the range until reaching the corresponding directive. + const bool SkipExcludedPPRanges; + /// The global file system cache. + DependencyScanningFilesystemSharedCache SharedCache; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_SERVICE_H diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h new file mode 100644 index 0000000000000..0c9efccb1d8bd --- /dev/null +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -0,0 +1,48 @@ +//===- DependencyScanningTool.h - clang-scan-deps service ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_TOOL_H +#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_TOOL_H + +#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "clang/Tooling/JSONCompilationDatabase.h" +#include <string> + +namespace clang{ +namespace tooling{ +namespace dependencies{ + +/// The high-level implementation of the dependency discovery tool that runs on +/// an individual worker thread. +class DependencyScanningTool { +public: + /// Construct a dependency scanning tool. + /// + /// \param Compilations The reference to the compilation database that's + /// used by the clang tool. + DependencyScanningTool(DependencyScanningService &Service, const clang::tooling::CompilationDatabase &Compilations); + + /// Print out the dependency information into a string using the dependency + /// file format that is specified in the options (-MD is the default) and + /// return it. + /// + /// \returns A \c StringError with the diagnostic output if clang errors + /// occurred, dependency file contents otherwise. + llvm::Expected<std::string> getDependencyFile(const std::string &Input, StringRef CWD); + +private: + DependencyScanningWorker Worker; + const tooling::CompilationDatabase &Compilations; +}; + +} // end namespace dependencies +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_TOOL_H diff --git a/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h index 3ea261a30d0f5..45c9fb4f029d2 100644 --- a/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h @@ -10,17 +10,35 @@ #define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_WORKER_H #include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/LLVM.h" #include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include <string> namespace clang { + +class DependencyOutputOptions; + namespace tooling { namespace dependencies { +class DependencyScanningService; +class DependencyScanningWorkerFilesystem; + +class DependencyConsumer { +public: + virtual ~DependencyConsumer() {} + + virtual void handleFileDependency(const DependencyOutputOptions &Opts, + StringRef Filename) = 0; + + // FIXME: Add support for reporting modular dependencies. +}; + /// An individual dependency scanning worker that is able to run on its own /// thread. /// @@ -29,26 +47,32 @@ namespace dependencies { /// using the regular processing run. class DependencyScanningWorker { public: - DependencyScanningWorker(); + DependencyScanningWorker(DependencyScanningService &Service); - /// Print out the dependency information into a string using the dependency - /// file format that is specified in the options (-MD is the default) and - /// return it. + /// Run the dependency scanning tool for a given clang driver invocation (as + /// specified for the given Input in the CDB), and report the discovered + /// dependencies to the provided consumer. /// /// \returns A \c StringError with the diagnostic output if clang errors - /// occurred, dependency file contents otherwise. - llvm::Expected<std::string> getDependencyFile(const std::string &Input, - StringRef WorkingDirectory, - const CompilationDatabase &CDB); + /// occurred, success otherwise. + llvm::Error computeDependencies(const std::string &Input, + StringRef WorkingDirectory, + const CompilationDatabase &CDB, + DependencyConsumer &Consumer); private: IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; std::shared_ptr<PCHContainerOperations> PCHContainerOps; + std::unique_ptr<ExcludedPreprocessorDirectiveSkipMapping> PPSkipMappings; + llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> RealFS; /// The file system that is used by each worker when scanning for /// dependencies. This filesystem persists accross multiple compiler /// invocations. - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> WorkerFS; + llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS; + /// The file manager that is reused accross multiple invocations by this + /// worker. If null, the file manager will not be reused. + llvm::IntrusiveRefCntPtr<FileManager> Files; }; } // end namespace dependencies diff --git a/include/clang/Tooling/Execution.h b/include/clang/Tooling/Execution.h index 74f0df5a5b912..ca6f22c5da3bf 100644 --- a/include/clang/Tooling/Execution.h +++ b/include/clang/Tooling/Execution.h @@ -115,13 +115,6 @@ public: /// Returns the name of a specific executor. virtual StringRef getExecutorName() const = 0; - /// Should return true iff executor runs all actions in a single process. - /// Clients can use this signal to find out if they can collect results - /// in-memory (e.g. to avoid serialization costs of using ToolResults). - /// The single-process executors can still run multiple threads, but all - /// executions are guaranteed to share the same memory. - virtual bool isSingleProcess() const = 0; - /// Executes each action with a corresponding arguments adjuster. virtual llvm::Error execute(llvm::ArrayRef< diff --git a/include/clang/Tooling/Inclusions/HeaderIncludes.h b/include/clang/Tooling/Inclusions/HeaderIncludes.h index ec6f0ea45ffed..6e6d6d8fb024b 100644 --- a/include/clang/Tooling/Inclusions/HeaderIncludes.h +++ b/include/clang/Tooling/Inclusions/HeaderIncludes.h @@ -32,6 +32,7 @@ public: /// 0. Otherwise, returns the priority of the matching category or INT_MAX. /// NOTE: this API is not thread-safe! int getIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; + int getSortIncludePriority(StringRef IncludeName, bool CheckMainHeader) const; private: bool isMainHeader(StringRef IncludeName) const; diff --git a/include/clang/Tooling/Inclusions/IncludeStyle.h b/include/clang/Tooling/Inclusions/IncludeStyle.h index a0f236e6fc461..266763a5b1bd0 100644 --- a/include/clang/Tooling/Inclusions/IncludeStyle.h +++ b/include/clang/Tooling/Inclusions/IncludeStyle.h @@ -58,6 +58,8 @@ struct IncludeStyle { std::string Regex; /// The priority to assign to this category. int Priority; + /// The custom priority to sort before grouping. + int SortPriority; bool operator==(const IncludeCategory &Other) const { return Regex == Other.Regex && Priority == Other.Priority; } diff --git a/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h b/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h new file mode 100755 index 0000000000000..034a0aaaf6db7 --- /dev/null +++ b/include/clang/Tooling/Refactoring/Extract/SourceExtraction.h @@ -0,0 +1,51 @@ +//===--- SourceExtraction.cpp - Clang refactoring library -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H +#define LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + +class LangOptions; +class SourceManager; +class SourceRange; +class Stmt; + +namespace tooling { + +/// Determines which semicolons should be inserted during extraction. +class ExtractionSemicolonPolicy { +public: + bool isNeededInExtractedFunction() const { + return IsNeededInExtractedFunction; + } + + bool isNeededInOriginalFunction() const { return IsNeededInOriginalFunction; } + + /// Returns the semicolon insertion policy that is needed for extraction of + /// the given statement from the given source range. + static ExtractionSemicolonPolicy compute(const Stmt *S, + SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts); + +private: + ExtractionSemicolonPolicy(bool IsNeededInExtractedFunction, + bool IsNeededInOriginalFunction) + : IsNeededInExtractedFunction(IsNeededInExtractedFunction), + IsNeededInOriginalFunction(IsNeededInOriginalFunction) {} + bool IsNeededInExtractedFunction; + bool IsNeededInOriginalFunction; +}; + +} // end namespace tooling +} // end namespace clang + +#endif //LLVM_CLANG_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H diff --git a/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h b/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h index 41a448f035a40..c0f995d85c14c 100644 --- a/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h +++ b/include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h @@ -98,7 +98,17 @@ public: TypeBeginLoc, TypeEndLoc)) return false; } - return visit(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + if (const Type *TP = Loc.getTypePtr()) { + if (TP->getTypeClass() == clang::Type::Record) + return visit(TP->getAsCXXRecordDecl(), TypeBeginLoc, TypeEndLoc); + } + return true; + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + const SourceLocation TypeEndLoc = + Lexer::getLocForEndOfToken(TL.getBeginLoc(), 0, SM, LangOpts); + return visit(TL.getTypedefNameDecl(), TL.getBeginLoc(), TypeEndLoc); } bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { @@ -122,8 +132,7 @@ private: ND, SourceRange(BeginLoc, EndLoc)); } bool visit(const NamedDecl *ND, SourceLocation Loc) { - return visit(ND, Loc, - Loc.getLocWithOffset(ND->getNameAsString().length() - 1)); + return visit(ND, Loc, Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts)); } }; diff --git a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h index cc6ae83202f15..fb373fcf5029a 100644 --- a/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h +++ b/include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h @@ -47,7 +47,7 @@ template <typename RuleType, typename... RequirementTypes, size_t... Is> void invokeRuleAfterValidatingRequirements( RefactoringResultConsumer &Consumer, RefactoringRuleContext &Context, const std::tuple<RequirementTypes...> &Requirements, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { // Check if the requirements we're interested in can be evaluated. auto Values = std::make_tuple(std::get<Is>(Requirements).evaluate(Context)...); @@ -87,7 +87,7 @@ template <typename... RequirementTypes, size_t... Is> void visitRefactoringOptions( RefactoringOptionVisitor &Visitor, const std::tuple<RequirementTypes...> &Requirements, - llvm::index_sequence<Is...>) { + std::index_sequence<Is...>) { visitRefactoringOptionsImpl(Visitor, std::get<Is>(Requirements)...); } @@ -131,7 +131,7 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { RefactoringRuleContext &Context) override { internal::invokeRuleAfterValidatingRequirements<RuleType>( Consumer, Context, Requirements, - llvm::index_sequence_for<RequirementTypes...>()); + std::index_sequence_for<RequirementTypes...>()); } bool hasSelectionRequirement() override { @@ -142,13 +142,13 @@ createRefactoringActionRule(const RequirementTypes &... Requirements) { void visitRefactoringOptions(RefactoringOptionVisitor &Visitor) override { internal::visitRefactoringOptions( Visitor, Requirements, - llvm::index_sequence_for<RequirementTypes...>()); + std::index_sequence_for<RequirementTypes...>()); } private: std::tuple<RequirementTypes...> Requirements; }; - return llvm::make_unique<Rule>(std::make_tuple(Requirements...)); + return std::make_unique<Rule>(std::make_tuple(Requirements...)); } } // end namespace tooling diff --git a/include/clang/Tooling/StandaloneExecution.h b/include/clang/Tooling/StandaloneExecution.h index 5fbc1e479c59b..8db6229acf7f9 100644 --- a/include/clang/Tooling/StandaloneExecution.h +++ b/include/clang/Tooling/StandaloneExecution.h @@ -52,8 +52,6 @@ public: StringRef getExecutorName() const override { return ExecutorName; } - bool isSingleProcess() const override { return true; } - using ToolExecutor::execute; llvm::Error diff --git a/include/clang/Tooling/Syntax/Tokens.h b/include/clang/Tooling/Syntax/Tokens.h index 4640ccb2d30aa..301432d3888b3 100644 --- a/include/clang/Tooling/Syntax/Tokens.h +++ b/include/clang/Tooling/Syntax/Tokens.h @@ -236,6 +236,16 @@ public: /// #pragma, etc. llvm::ArrayRef<syntax::Token> spelledTokens(FileID FID) const; + /// Get all tokens that expand a macro in \p FID. For the following input + /// #define FOO B + /// #define FOO2(X) int X + /// FOO2(XY) + /// int B; + /// FOO; + /// macroExpansions() returns {"FOO2", "FOO"} (from line 3 and 5 + /// respecitvely). + std::vector<const syntax::Token *> macroExpansions(FileID FID) const; + const SourceManager &sourceManager() const { return *SourceMgr; } std::string dumpForTests() const; diff --git a/include/clang/Tooling/Tooling.h b/include/clang/Tooling/Tooling.h index 83fe43ac59e02..19421f0a39f30 100644 --- a/include/clang/Tooling/Tooling.h +++ b/include/clang/Tooling/Tooling.h @@ -99,9 +99,7 @@ public: DiagnosticConsumer *DiagConsumer) override; /// Returns a new clang::FrontendAction. - /// - /// The caller takes ownership of the returned action. - virtual FrontendAction *create() = 0; + virtual std::unique_ptr<FrontendAction> create() = 0; }; /// Returns a new FrontendActionFactory for a given type. @@ -156,7 +154,7 @@ inline std::unique_ptr<FrontendActionFactory> newFrontendActionFactory( /// clang modules. /// /// \return - True if 'ToolAction' was successfully executed. -bool runToolOnCode(FrontendAction *ToolAction, const Twine &Code, +bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction, const Twine &Code, const Twine &FileName = "input.cc", std::shared_ptr<PCHContainerOperations> PCHContainerOps = std::make_shared<PCHContainerOperations>()); @@ -179,7 +177,7 @@ using FileContentMappings = std::vector<std::pair<std::string, std::string>>; /// /// \return - True if 'ToolAction' was successfully executed. bool runToolOnCodeWithArgs( - FrontendAction *ToolAction, const Twine &Code, + std::unique_ptr<FrontendAction> ToolAction, const Twine &Code, const std::vector<std::string> &Args, const Twine &FileName = "input.cc", const Twine &ToolName = "clang-tool", std::shared_ptr<PCHContainerOperations> PCHContainerOps = @@ -188,7 +186,7 @@ bool runToolOnCodeWithArgs( // Similar to the overload except this takes a VFS. bool runToolOnCodeWithArgs( - FrontendAction *ToolAction, const Twine &Code, + std::unique_ptr<FrontendAction> ToolAction, const Twine &Code, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, const std::vector<std::string> &Args, const Twine &FileName = "input.cc", const Twine &ToolName = "clang-tool", @@ -237,13 +235,13 @@ public: /// uses its binary name (CommandLine[0]) to locate its builtin headers. /// Callers have to ensure that they are installed in a compatible location /// (see clang driver implementation) or mapped in via mapVirtualFile. - /// \param FAction The action to be executed. Class takes ownership. + /// \param FAction The action to be executed. /// \param Files The FileManager used for the execution. Class does not take /// ownership. /// \param PCHContainerOps The PCHContainerOperations for loading and creating /// clang modules. - ToolInvocation(std::vector<std::string> CommandLine, FrontendAction *FAction, - FileManager *Files, + ToolInvocation(std::vector<std::string> CommandLine, + std::unique_ptr<FrontendAction> FAction, FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps = std::make_shared<PCHContainerOperations>()); @@ -314,12 +312,15 @@ public: /// clang modules. /// \param BaseFS VFS used for all underlying file accesses when running the /// tool. + /// \param Files The file manager to use for underlying file operations when + /// running the tool. ClangTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths, std::shared_ptr<PCHContainerOperations> PCHContainerOps = std::make_shared<PCHContainerOperations>(), IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS = - llvm::vfs::getRealFileSystem()); + llvm::vfs::getRealFileSystem(), + IntrusiveRefCntPtr<FileManager> Files = nullptr); ~ClangTool(); @@ -397,7 +398,9 @@ template <typename T> std::unique_ptr<FrontendActionFactory> newFrontendActionFactory() { class SimpleFrontendActionFactory : public FrontendActionFactory { public: - FrontendAction *create() override { return new T; } + std::unique_ptr<FrontendAction> create() override { + return std::make_unique<T>(); + } }; return std::unique_ptr<FrontendActionFactory>( @@ -413,8 +416,9 @@ inline std::unique_ptr<FrontendActionFactory> newFrontendActionFactory( SourceFileCallbacks *Callbacks) : ConsumerFactory(ConsumerFactory), Callbacks(Callbacks) {} - FrontendAction *create() override { - return new ConsumerFactoryAdaptor(ConsumerFactory, Callbacks); + std::unique_ptr<FrontendAction> create() override { + return std::make_unique<ConsumerFactoryAdaptor>(ConsumerFactory, + Callbacks); } private: diff --git a/include/clang/Tooling/Transformer/MatchConsumer.h b/include/clang/Tooling/Transformer/MatchConsumer.h new file mode 100644 index 0000000000000..0a1dbe13ea1e4 --- /dev/null +++ b/include/clang/Tooling/Transformer/MatchConsumer.h @@ -0,0 +1,62 @@ +//===--- MatchConsumer.h - MatchConsumer abstraction ------------*- 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 This file defines the *MatchConsumer* abstraction: a computation over +/// match results, specifically the `ast_matchers::MatchFinder::MatchResult` +/// class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_ + +#include "clang/AST/ASTTypeTraits.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +namespace clang { +namespace transformer { +/// A failable computation over nodes bound by AST matchers. +/// +/// The computation should report any errors though its return value (rather +/// than terminating the program) to enable usage in interactive scenarios like +/// clang-query. +/// +/// This is a central abstraction of the Transformer framework. +template <typename T> +using MatchConsumer = + std::function<Expected<T>(const ast_matchers::MatchFinder::MatchResult &)>; + +/// Creates an error that signals that a `MatchConsumer` expected a certain node +/// to be bound by AST matchers, but it was not actually bound. +inline llvm::Error notBoundError(llvm::StringRef Id) { + return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument, + "Id not bound: " + Id); +} + +/// Chooses between the two consumers, based on whether \p ID is bound in the +/// match. +template <typename T> +MatchConsumer<T> ifBound(std::string ID, MatchConsumer<T> TrueC, + MatchConsumer<T> FalseC) { + return [=](const ast_matchers::MatchFinder::MatchResult &Result) { + auto &Map = Result.Nodes.getMap(); + return (Map.find(ID) != Map.end() ? TrueC : FalseC)(Result); + }; +} +} // namespace transformer + +namespace tooling { +// DEPRECATED: Temporary alias supporting client migration to the `transformer` +// namespace. +using transformer::ifBound; +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_MATCH_CONSUMER_H_ diff --git a/include/clang/Tooling/Refactoring/RangeSelector.h b/include/clang/Tooling/Transformer/RangeSelector.h index b117e4d82ad46..9f556d206321c 100644 --- a/include/clang/Tooling/Refactoring/RangeSelector.h +++ b/include/clang/Tooling/Transformer/RangeSelector.h @@ -17,14 +17,14 @@ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Transformer/MatchConsumer.h" #include "llvm/Support/Error.h" #include <functional> #include <string> namespace clang { -namespace tooling { -using RangeSelector = std::function<Expected<CharSourceRange>( - const ast_matchers::MatchFinder::MatchResult &)>; +namespace transformer { +using RangeSelector = MatchConsumer<CharSourceRange>; inline RangeSelector charRange(CharSourceRange R) { return [R](const ast_matchers::MatchFinder::MatchResult &) @@ -79,10 +79,34 @@ RangeSelector statements(std::string ID); // (all source between the braces). RangeSelector initListElements(std::string ID); +/// Given an \IfStmt (bound to \p ID), selects the range of the else branch, +/// starting from the \c else keyword. +RangeSelector elseBranch(std::string ID); + /// Selects the range from which `S` was expanded (possibly along with other /// source), if `S` is an expansion, and `S` itself, otherwise. Corresponds to /// `SourceManager::getExpansionRange`. RangeSelector expansion(RangeSelector S); +} // namespace transformer + +namespace tooling { +// DEPRECATED: These are temporary aliases supporting client migration to the +// `transformer` namespace. +using RangeSelector = transformer::RangeSelector; + +using transformer::after; +using transformer::before; +using transformer::callArgs; +using transformer::charRange; +using transformer::elseBranch; +using transformer::expansion; +using transformer::initListElements; +using transformer::member; +using transformer::name; +using transformer::node; +using transformer::range; +using transformer::statement; +using transformer::statements; } // namespace tooling } // namespace clang diff --git a/include/clang/Tooling/Refactoring/Transformer.h b/include/clang/Tooling/Transformer/RewriteRule.h index 6d9c5a37cc18f..6e99151c1c7f5 100644 --- a/include/clang/Tooling/Refactoring/Transformer.h +++ b/include/clang/Tooling/Transformer/RewriteRule.h @@ -1,4 +1,4 @@ -//===--- Transformer.h - Clang source-rewriting library ---------*- C++ -*-===// +//===--- RewriteRule.h - RewriteRule 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. @@ -7,42 +7,30 @@ //===----------------------------------------------------------------------===// /// /// \file -/// Defines a library supporting the concise specification of clang-based -/// source-to-source transformations. +/// Defines the RewriteRule class and related functions for creating, +/// modifying and interpreting RewriteRules. /// //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_ -#define LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_ +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_ #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/Tooling/Refactoring/AtomicChange.h" -#include "clang/Tooling/Refactoring/RangeSelector.h" +#include "clang/Tooling/Transformer/MatchConsumer.h" +#include "clang/Tooling/Transformer/RangeSelector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Error.h" -#include <deque> #include <functional> #include <string> -#include <type_traits> #include <utility> namespace clang { -namespace tooling { - -// Note that \p TextGenerator is allowed to fail, e.g. when trying to access a -// matched node that was not bound. Allowing this to fail simplifies error -// handling for interactive tools like clang-query. -using TextGenerator = std::function<Expected<std::string>( - const ast_matchers::MatchFinder::MatchResult &)>; - -/// Wraps a string as a TextGenerator. -inline TextGenerator text(std::string M) { - return [M](const ast_matchers::MatchFinder::MatchResult &) - -> Expected<std::string> { return M; }; -} +namespace transformer { +using TextGenerator = MatchConsumer<std::string>; // Description of a source-code edit, expressed in terms of an AST node. // Includes: an ID for the (bound) node, a selector for source related to the @@ -160,11 +148,9 @@ inline RewriteRule makeRule(ast_matchers::internal::DynTypedMatcher M, void addInclude(RewriteRule &Rule, llvm::StringRef Header, IncludeFormat Format = IncludeFormat::Quoted); -/// Applies the first rule whose pattern matches; other rules are ignored. -/// -/// N.B. All of the rules must use the same kind of matcher (that is, share a -/// base class in the AST hierarchy). However, this constraint is caused by an -/// implementation detail and should be lifted in the future. +/// Applies the first rule whose pattern matches; other rules are ignored. If +/// the matchers are independent then order doesn't matter. In that case, +/// `applyFirst` is simply joining the set of rules into one. // // `applyFirst` is like an `anyOf` matcher with an edit action attached to each // of its cases. Anywhere you'd use `anyOf(m1.bind("id1"), m2.bind("id2"))` and @@ -230,7 +216,9 @@ inline ASTEdit insertAfter(RangeSelector S, TextGenerator Replacement) { /// Removes the source selected by \p S. inline ASTEdit remove(RangeSelector S) { - return change(std::move(S), text("")); + return change(std::move(S), + [](const ast_matchers::MatchFinder::MatchResult &) + -> Expected<std::string> { return ""; }); } /// The following three functions are a low-level part of the RewriteRule @@ -243,8 +231,25 @@ inline ASTEdit remove(RangeSelector S) { // public and well-supported and move them out of `detail`. namespace detail { /// Builds a single matcher for the rule, covering all of the rule's cases. +/// Only supports Rules whose cases' matchers share the same base "kind" +/// (`Stmt`, `Decl`, etc.) Deprecated: use `buildMatchers` instead, which +/// supports mixing matchers of different kinds. ast_matchers::internal::DynTypedMatcher buildMatcher(const RewriteRule &Rule); +/// Builds a set of matchers that cover the rule (one for each distinct node +/// matcher base kind: Stmt, Decl, etc.). Node-matchers for `QualType` and +/// `Type` are not permitted, since such nodes carry no source location +/// information and are therefore not relevant for rewriting. If any such +/// matchers are included, will return an empty vector. +std::vector<ast_matchers::internal::DynTypedMatcher> +buildMatchers(const RewriteRule &Rule); + +/// Gets the beginning location of the source matched by a rewrite rule. If the +/// match occurs within a macro expansion, returns the beginning of the +/// expansion point. `Result` must come from the matching of a rewrite rule. +SourceLocation +getRuleMatchLoc(const ast_matchers::MatchFinder::MatchResult &Result); + /// Returns the \c Case of \c Rule that was selected in the match result. /// Assumes a matcher built with \c buildMatcher. const RewriteRule::Case & @@ -273,36 +278,32 @@ Expected<SmallVector<Transformation, 1>> translateEdits(const ast_matchers::MatchFinder::MatchResult &Result, llvm::ArrayRef<ASTEdit> Edits); } // namespace detail +} // namespace transformer -/// Handles the matcher and callback registration for a single rewrite rule, as -/// defined by the arguments of the constructor. -class Transformer : public ast_matchers::MatchFinder::MatchCallback { -public: - using ChangeConsumer = - std::function<void(Expected<clang::tooling::AtomicChange> Change)>; - - /// \param Consumer Receives each rewrite or error. Will not necessarily be - /// called for each match; for example, if the rewrite is not applicable - /// because of macros, but doesn't fail. Note that clients are responsible - /// for handling the case that independent \c AtomicChanges conflict with each - /// other. - Transformer(RewriteRule Rule, ChangeConsumer Consumer) - : Rule(std::move(Rule)), Consumer(std::move(Consumer)) {} - - /// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not - /// be moved after this call. - void registerMatchers(ast_matchers::MatchFinder *MatchFinder); +namespace tooling { +// DEPRECATED: These are temporary aliases supporting client migration to the +// `transformer` namespace. +/// Wraps a string as a TextGenerator. +using TextGenerator = transformer::TextGenerator; - /// Not called directly by users -- called by the framework, via base class - /// pointer. - void run(const ast_matchers::MatchFinder::MatchResult &Result) override; +inline TextGenerator text(std::string M) { + return [M](const ast_matchers::MatchFinder::MatchResult &) + -> Expected<std::string> { return M; }; +} -private: - RewriteRule Rule; - /// Receives each successful rewrites as an \c AtomicChange. - ChangeConsumer Consumer; -}; +using transformer::addInclude; +using transformer::applyFirst; +using transformer::change; +using transformer::insertAfter; +using transformer::insertBefore; +using transformer::makeRule; +using transformer::remove; +using transformer::RewriteRule; +using transformer::IncludeFormat; +namespace detail { +using namespace transformer::detail; +} // namespace detail } // namespace tooling } // namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_TRANSFORMER_H_ +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_REWRITE_RULE_H_ diff --git a/include/clang/Tooling/Refactoring/SourceCode.h b/include/clang/Tooling/Transformer/SourceCode.h index 498dbea96c701..bc9cc3d2a2580 100644 --- a/include/clang/Tooling/Refactoring/SourceCode.h +++ b/include/clang/Tooling/Transformer/SourceCode.h @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H -#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H +#define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceLocation.h" @@ -72,6 +72,19 @@ StringRef getExtendedText(const T &Node, tok::TokenKind Next, ASTContext &Context) { return getText(getExtendedRange(Node, Next, Context), Context); } + +// Attempts to resolve the given range to one that can be edited by a rewrite; +// generally, one that starts and ends within a particular file. It supports +// a limited set of cases involving source locations in macro expansions. +llvm::Optional<CharSourceRange> +getRangeForEdit(const CharSourceRange &EditRange, const SourceManager &SM, + const LangOptions &LangOpts); + +inline llvm::Optional<CharSourceRange> +getRangeForEdit(const CharSourceRange &EditRange, const ASTContext &Context) { + return getRangeForEdit(EditRange, Context.getSourceManager(), + Context.getLangOpts()); +} } // namespace tooling } // namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_H diff --git a/include/clang/Tooling/Transformer/SourceCodeBuilders.h b/include/clang/Tooling/Transformer/SourceCodeBuilders.h new file mode 100644 index 0000000000000..6c79a7588f28d --- /dev/null +++ b/include/clang/Tooling/Transformer/SourceCodeBuilders.h @@ -0,0 +1,86 @@ +//===--- SourceCodeBuilders.h - Source-code building facilities -*- 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 +/// This file collects facilities for generating source code strings. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_ + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include <string> + +namespace clang { +namespace tooling { + +/// \name Code analysis utilities. +/// @{ +/// Ignores implicit object-construction expressions in addition to the normal +/// implicit expressions that are ignored. +const Expr *reallyIgnoreImplicit(const Expr &E); + +/// Determines whether printing this expression in *any* expression requires +/// parentheses to preserve its meaning. This analyses is necessarily +/// conservative because it lacks information about the target context. +bool mayEverNeedParens(const Expr &E); + +/// Determines whether printing this expression to the left of a dot or arrow +/// operator requires a parentheses to preserve its meaning. Given that +/// dot/arrow are (effectively) the highest precedence, this is equivalent to +/// asking whether it ever needs parens. +inline bool needParensBeforeDotOrArrow(const Expr &E) { + return mayEverNeedParens(E); +} + +/// Determines whether printing this expression to the right of a unary operator +/// requires a parentheses to preserve its meaning. +bool needParensAfterUnaryOperator(const Expr &E); +/// @} + +/// \name Basic code-string generation utilities. +/// @{ + +/// Builds source for an expression, adding parens if needed for unambiguous +/// parsing. +llvm::Optional<std::string> buildParens(const Expr &E, + const ASTContext &Context); + +/// Builds idiomatic source for the dereferencing of `E`: prefix with `*` but +/// simplify when it already begins with `&`. \returns empty string on failure. +llvm::Optional<std::string> buildDereference(const Expr &E, + const ASTContext &Context); + +/// Builds idiomatic source for taking the address of `E`: prefix with `&` but +/// simplify when it already begins with `*`. \returns empty string on failure. +llvm::Optional<std::string> buildAddressOf(const Expr &E, + const ASTContext &Context); + +/// Adds a dot to the end of the given expression, but adds parentheses when +/// needed by the syntax, and simplifies to `->` when possible, e.g.: +/// +/// `x` becomes `x.` +/// `*a` becomes `a->` +/// `a+b` becomes `(a+b).` +llvm::Optional<std::string> buildDot(const Expr &E, const ASTContext &Context); + +/// Adds an arrow to the end of the given expression, but adds parentheses +/// when needed by the syntax, and simplifies to `.` when possible, e.g.: +/// +/// `x` becomes `x->` +/// `&a` becomes `a.` +/// `a+b` becomes `(a+b)->` +llvm::Optional<std::string> buildArrow(const Expr &E, + const ASTContext &Context); +/// @} + +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCE_CODE_BUILDERS_H_ diff --git a/include/clang/Tooling/Refactoring/Stencil.h b/include/clang/Tooling/Transformer/Stencil.h index e57a576e55755..66d1388f97106 100644 --- a/include/clang/Tooling/Refactoring/Stencil.h +++ b/include/clang/Tooling/Transformer/Stencil.h @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// /// -/// /file +/// \file /// This file defines the *Stencil* abstraction: a code-generating object, /// parameterized by named references to (bound) AST nodes. Given a match /// result, a stencil can be evaluated to a string of source code. @@ -17,21 +17,21 @@ /// //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_ -#define LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_ +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/Tooling/Refactoring/RangeSelector.h" +#include "clang/Tooling/Transformer/MatchConsumer.h" +#include "clang/Tooling/Transformer/RangeSelector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include <string> #include <vector> namespace clang { -namespace tooling { - +namespace transformer { /// A stencil is represented as a sequence of "parts" that can each individually /// generate a code string based on a match result. The different kinds of /// parts include (raw) text, references to bound nodes and assorted operations @@ -47,21 +47,18 @@ public: virtual llvm::Error eval(const ast_matchers::MatchFinder::MatchResult &Match, std::string *Result) const = 0; - virtual bool isEqual(const StencilPartInterface &other) const = 0; - - const void *typeId() const { return TypeId; } + /// Constructs a string representation of the StencilPart. StencilParts + /// generated by the `selection` and `run` functions do not have a unique + /// string representation. + virtual std::string toString() const = 0; protected: - StencilPartInterface(const void *DerivedId) : TypeId(DerivedId) {} + StencilPartInterface() = default; // Since this is an abstract class, copying/assigning only make sense for // derived classes implementing `clone()`. StencilPartInterface(const StencilPartInterface &) = default; StencilPartInterface &operator=(const StencilPartInterface &) = default; - - /// Unique identifier of the concrete type of this instance. Supports safe - /// downcasting. - const void *TypeId; }; /// A copyable facade for a std::unique_ptr<StencilPartInterface>. Copies result @@ -77,12 +74,10 @@ public: return Impl->eval(Match, Result); } - bool operator==(const StencilPart &Other) const { - if (Impl == Other.Impl) - return true; - if (Impl == nullptr || Other.Impl == nullptr) - return false; - return Impl->isEqual(*Other.Impl); + std::string toString() const { + if (Impl == nullptr) + return ""; + return Impl->toString(); } private: @@ -119,8 +114,17 @@ public: return eval(Result); } + /// Constructs a string representation of the Stencil. The string is not + /// guaranteed to be unique. + std::string toString() const { + std::vector<std::string> PartStrings; + PartStrings.reserve(Parts.size()); + for (const auto &Part : Parts) + PartStrings.push_back(Part.toString()); + return llvm::join(PartStrings, ", "); + } + private: - friend bool operator==(const Stencil &A, const Stencil &B); static StencilPart wrap(llvm::StringRef Text); static StencilPart wrap(RangeSelector Selector); static StencilPart wrap(StencilPart Part) { return Part; } @@ -128,14 +132,10 @@ private: std::vector<StencilPart> Parts; }; -inline bool operator==(const Stencil &A, const Stencil &B) { - return A.Parts == B.Parts; -} - -inline bool operator!=(const Stencil &A, const Stencil &B) { return !(A == B); } - +// // Functions for conveniently building stencils. -namespace stencil { +// + /// Convenience wrapper for Stencil::cat that can be imported with a using decl. template <typename... Ts> Stencil cat(Ts &&... Parts) { return Stencil::cat(std::forward<Ts>(Parts)...); @@ -147,27 +147,75 @@ StencilPart text(llvm::StringRef Text); /// \returns the source corresponding to the selected range. StencilPart selection(RangeSelector Selector); -/// \returns the source corresponding to the identified node. -/// FIXME: Deprecated. Write `selection(node(Id))` instead. -inline StencilPart node(llvm::StringRef Id) { - return selection(tooling::node(Id)); +/// Generates the source of the expression bound to \p Id, wrapping it in +/// parentheses if it may parse differently depending on context. For example, a +/// binary operation is always wrapped, while a variable reference is never +/// wrapped. +StencilPart expression(llvm::StringRef Id); + +/// Constructs an idiomatic dereferencing of the expression bound to \p ExprId. +/// \p ExprId is wrapped in parentheses, if needed. +StencilPart deref(llvm::StringRef ExprId); + +/// Constructs an expression that idiomatically takes the address of the +/// expression bound to \p ExprId. \p ExprId is wrapped in parentheses, if +/// needed. +StencilPart addressOf(llvm::StringRef ExprId); + +/// Constructs a `MemberExpr` that accesses the named member (\p Member) of the +/// object bound to \p BaseId. The access is constructed idiomatically: if \p +/// BaseId is bound to `e` and \p Member identifies member `m`, then returns +/// `e->m`, when e is a pointer, `e2->m` when e = `*e2` and `e.m` otherwise. +/// Additionally, `e` is wrapped in parentheses, if needed. +StencilPart access(llvm::StringRef BaseId, StencilPart Member); +inline StencilPart access(llvm::StringRef BaseId, llvm::StringRef Member) { + return access(BaseId, text(Member)); } -/// Variant of \c node() that identifies the node as a statement, for purposes -/// of deciding whether to include any trailing semicolon. Only relevant for -/// Expr nodes, which, by default, are *not* considered as statements. -/// \returns the source corresponding to the identified node, considered as a -/// statement. -/// FIXME: Deprecated. Write `selection(statement(Id))` instead. -inline StencilPart sNode(llvm::StringRef Id) { - return selection(tooling::statement(Id)); +/// Chooses between the two stencil parts, based on whether \p ID is bound in +/// the match. +StencilPart ifBound(llvm::StringRef Id, StencilPart TruePart, + StencilPart FalsePart); + +/// Chooses between the two strings, based on whether \p ID is bound in the +/// match. +inline StencilPart ifBound(llvm::StringRef Id, llvm::StringRef TrueText, + llvm::StringRef FalseText) { + return ifBound(Id, text(TrueText), text(FalseText)); } +/// Wraps a MatchConsumer in a StencilPart, so that it can be used in a Stencil. +/// This supports user-defined extensions to the Stencil language. +StencilPart run(MatchConsumer<std::string> C); + /// For debug use only; semantics are not guaranteed. /// /// \returns the string resulting from calling the node's print() method. StencilPart dPrint(llvm::StringRef Id); +} // namespace transformer + +namespace tooling { +// DEPRECATED: These are temporary aliases supporting client migration to the +// `transformer` namespace. +using Stencil = transformer::Stencil; +using StencilPart = transformer::StencilPart; +namespace stencil { +using transformer::access; +using transformer::addressOf; +using transformer::cat; +using transformer::deref; +using transformer::dPrint; +using transformer::expression; +using transformer::ifBound; +using transformer::run; +using transformer::selection; +using transformer::text; +/// \returns the source corresponding to the identified node. +/// FIXME: Deprecated. Write `selection(node(Id))` instead. +inline transformer::StencilPart node(llvm::StringRef Id) { + return selection(tooling::node(Id)); +} } // namespace stencil } // namespace tooling } // namespace clang -#endif // LLVM_CLANG_TOOLING_REFACTOR_STENCIL_H_ +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_STENCIL_H_ diff --git a/include/clang/Tooling/Transformer/Transformer.h b/include/clang/Tooling/Transformer/Transformer.h new file mode 100644 index 0000000000000..31feacba5e286 --- /dev/null +++ b/include/clang/Tooling/Transformer/Transformer.h @@ -0,0 +1,52 @@ +//===--- Transformer.h - Transformer 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_ +#define LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_ + +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring/AtomicChange.h" +#include "clang/Tooling/Transformer/RewriteRule.h" +#include "llvm/Support/Error.h" +#include <functional> +#include <utility> + +namespace clang { +namespace tooling { +/// Handles the matcher and callback registration for a single `RewriteRule`, as +/// defined by the arguments of the constructor. +class Transformer : public ast_matchers::MatchFinder::MatchCallback { +public: + using ChangeConsumer = + std::function<void(Expected<clang::tooling::AtomicChange> Change)>; + + /// \param Consumer Receives each rewrite or error. Will not necessarily be + /// called for each match; for example, if the rewrite is not applicable + /// because of macros, but doesn't fail. Note that clients are responsible + /// for handling the case that independent \c AtomicChanges conflict with each + /// other. + Transformer(transformer::RewriteRule Rule, ChangeConsumer Consumer) + : Rule(std::move(Rule)), Consumer(std::move(Consumer)) {} + + /// N.B. Passes `this` pointer to `MatchFinder`. So, this object should not + /// be moved after this call. + void registerMatchers(ast_matchers::MatchFinder *MatchFinder); + + /// Not called directly by users -- called by the framework, via base class + /// pointer. + void run(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + transformer::RewriteRule Rule; + /// Receives each successful rewrites as an \c AtomicChange. + ChangeConsumer Consumer; +}; +} // namespace tooling +} // namespace clang + +#endif // LLVM_CLANG_TOOLING_TRANSFORMER_TRANSFORMER_H_ |