summaryrefslogtreecommitdiff
path: root/include/clang/Tooling
diff options
context:
space:
mode:
Diffstat (limited to 'include/clang/Tooling')
-rw-r--r--include/clang/Tooling/ASTDiff/ASTDiff.h2
-rw-r--r--include/clang/Tooling/AllTUsExecution.h3
-rw-r--r--include/clang/Tooling/ArgumentsAdjusters.h4
-rw-r--r--include/clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h188
-rw-r--r--include/clang/Tooling/DependencyScanning/DependencyScanningService.h65
-rw-r--r--include/clang/Tooling/DependencyScanning/DependencyScanningTool.h48
-rw-r--r--include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h42
-rw-r--r--include/clang/Tooling/Execution.h7
-rw-r--r--include/clang/Tooling/Inclusions/HeaderIncludes.h1
-rw-r--r--include/clang/Tooling/Inclusions/IncludeStyle.h2
-rwxr-xr-xinclude/clang/Tooling/Refactoring/Extract/SourceExtraction.h51
-rw-r--r--include/clang/Tooling/Refactoring/RecursiveSymbolVisitor.h15
-rw-r--r--include/clang/Tooling/Refactoring/RefactoringActionRulesInternal.h10
-rw-r--r--include/clang/Tooling/StandaloneExecution.h2
-rw-r--r--include/clang/Tooling/Syntax/Tokens.h10
-rw-r--r--include/clang/Tooling/Tooling.h30
-rw-r--r--include/clang/Tooling/Transformer/MatchConsumer.h62
-rw-r--r--include/clang/Tooling/Transformer/RangeSelector.h (renamed from include/clang/Tooling/Refactoring/RangeSelector.h)30
-rw-r--r--include/clang/Tooling/Transformer/RewriteRule.h (renamed from include/clang/Tooling/Refactoring/Transformer.h)109
-rw-r--r--include/clang/Tooling/Transformer/SourceCode.h (renamed from include/clang/Tooling/Refactoring/SourceCode.h)19
-rw-r--r--include/clang/Tooling/Transformer/SourceCodeBuilders.h86
-rw-r--r--include/clang/Tooling/Transformer/Stencil.h (renamed from include/clang/Tooling/Refactoring/Stencil.h)130
-rw-r--r--include/clang/Tooling/Transformer/Transformer.h52
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_