aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Tooling
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /clang/lib/Tooling
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
Diffstat (limited to 'clang/lib/Tooling')
-rw-r--r--clang/lib/Tooling/ASTDiff/ASTDiff.cpp15
-rw-r--r--clang/lib/Tooling/AllTUsExecution.cpp2
-rw-r--r--clang/lib/Tooling/ArgumentsAdjusters.cpp2
-rw-r--r--clang/lib/Tooling/CommonOptionsParser.cpp8
-rw-r--r--clang/lib/Tooling/Core/Replacement.cpp4
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp5
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp8
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp170
-rw-r--r--clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp267
-rw-r--r--clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp478
-rw-r--r--clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.cpp2
-rw-r--r--clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h2
-rw-r--r--clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp2
-rw-r--r--clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp9
-rw-r--r--clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp119
-rw-r--r--clang/lib/Tooling/Inclusions/HeaderIncludes.cpp34
-rw-r--r--clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp (renamed from clang/lib/Tooling/Inclusions/StandardLibrary.cpp)18
-rw-r--r--clang/lib/Tooling/InterpolatingCompilationDatabase.cpp21
-rw-r--r--clang/lib/Tooling/JSONCompilationDatabase.cpp9
-rw-r--r--clang/lib/Tooling/Refactoring/ASTSelection.cpp19
-rw-r--r--clang/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp8
-rw-r--r--clang/lib/Tooling/Refactoring/Extract/Extract.cpp3
-rw-r--r--clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp3
-rw-r--r--clang/lib/Tooling/Syntax/BuildTree.cpp8
-rw-r--r--clang/lib/Tooling/Syntax/ComputeReplacements.cpp12
-rw-r--r--clang/lib/Tooling/Syntax/Mutations.cpp1
-rw-r--r--clang/lib/Tooling/Syntax/Tokens.cpp259
-rw-r--r--clang/lib/Tooling/Tooling.cpp19
-rw-r--r--clang/lib/Tooling/Transformer/Parsing.cpp14
-rw-r--r--clang/lib/Tooling/Transformer/RewriteRule.cpp40
-rw-r--r--clang/lib/Tooling/Transformer/SourceCode.cpp92
-rw-r--r--clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp49
-rw-r--r--clang/lib/Tooling/Transformer/Stencil.cpp4
33 files changed, 1146 insertions, 560 deletions
diff --git a/clang/lib/Tooling/ASTDiff/ASTDiff.cpp b/clang/lib/Tooling/ASTDiff/ASTDiff.cpp
index 0821863adcc6..52e57976ac09 100644
--- a/clang/lib/Tooling/ASTDiff/ASTDiff.cpp
+++ b/clang/lib/Tooling/ASTDiff/ASTDiff.cpp
@@ -19,6 +19,7 @@
#include <limits>
#include <memory>
+#include <optional>
#include <unordered_set>
using namespace llvm;
@@ -117,13 +118,11 @@ public:
Impl(SyntaxTree *Parent, Stmt *N, ASTContext &AST);
template <class T>
Impl(SyntaxTree *Parent,
- std::enable_if_t<std::is_base_of<Stmt, T>::value, T> *Node,
- ASTContext &AST)
+ std::enable_if_t<std::is_base_of_v<Stmt, T>, T> *Node, ASTContext &AST)
: Impl(Parent, dyn_cast<Stmt>(Node), AST) {}
template <class T>
Impl(SyntaxTree *Parent,
- std::enable_if_t<std::is_base_of<Decl, T>::value, T> *Node,
- ASTContext &AST)
+ std::enable_if_t<std::is_base_of_v<Decl, T>, T> *Node, ASTContext &AST)
: Impl(Parent, dyn_cast<Decl>(Node), AST) {}
SyntaxTree *Parent;
@@ -688,20 +687,20 @@ ASTNodeKind Node::getType() const { return ASTNode.getNodeKind(); }
StringRef Node::getTypeLabel() const { return getType().asStringRef(); }
-llvm::Optional<std::string> Node::getQualifiedIdentifier() const {
+std::optional<std::string> Node::getQualifiedIdentifier() const {
if (auto *ND = ASTNode.get<NamedDecl>()) {
if (ND->getDeclName().isIdentifier())
return ND->getQualifiedNameAsString();
}
- return llvm::None;
+ return std::nullopt;
}
-llvm::Optional<StringRef> Node::getIdentifier() const {
+std::optional<StringRef> Node::getIdentifier() const {
if (auto *ND = ASTNode.get<NamedDecl>()) {
if (ND->getDeclName().isIdentifier())
return ND->getName();
}
- return llvm::None;
+ return std::nullopt;
}
namespace {
diff --git a/clang/lib/Tooling/AllTUsExecution.cpp b/clang/lib/Tooling/AllTUsExecution.cpp
index 5565da9b548a..f327d0139941 100644
--- a/clang/lib/Tooling/AllTUsExecution.cpp
+++ b/clang/lib/Tooling/AllTUsExecution.cpp
@@ -121,7 +121,7 @@ llvm::Error AllTUsToolExecutor::execute(
[&](std::string Path) {
Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
"] Processing file " + Path);
- // Each thread gets an indepent copy of a VFS to allow different
+ // Each thread gets an independent copy of a VFS to allow different
// concurrent working directories.
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
llvm::vfs::createPhysicalFileSystem();
diff --git a/clang/lib/Tooling/ArgumentsAdjusters.cpp b/clang/lib/Tooling/ArgumentsAdjusters.cpp
index 7f5dc4d62f11..e40df6257378 100644
--- a/clang/lib/Tooling/ArgumentsAdjusters.cpp
+++ b/clang/lib/Tooling/ArgumentsAdjusters.cpp
@@ -122,7 +122,7 @@ ArgumentsAdjuster getInsertArgumentAdjuster(const CommandLineArguments &Extra,
CommandLineArguments::iterator I;
if (Pos == ArgumentInsertPosition::END) {
- I = std::find(Return.begin(), Return.end(), "--");
+ I = llvm::find(Return, "--");
} else {
I = Return.begin();
++I; // To leave the program name in place
diff --git a/clang/lib/Tooling/CommonOptionsParser.cpp b/clang/lib/Tooling/CommonOptionsParser.cpp
index 7d48dd505464..59ef47cc0166 100644
--- a/clang/lib/Tooling/CommonOptionsParser.cpp
+++ b/clang/lib/Tooling/CommonOptionsParser.cpp
@@ -86,21 +86,21 @@ llvm::Error CommonOptionsParser::init(
static cl::opt<std::string> BuildPath("p", cl::desc("Build path"),
cl::Optional, cl::cat(Category),
- cl::sub(*cl::AllSubCommands));
+ cl::sub(cl::SubCommand::getAll()));
static cl::list<std::string> SourcePaths(
cl::Positional, cl::desc("<source0> [... <sourceN>]"), OccurrencesFlag,
- cl::cat(Category), cl::sub(*cl::AllSubCommands));
+ cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
static cl::list<std::string> ArgsAfter(
"extra-arg",
cl::desc("Additional argument to append to the compiler command line"),
- cl::cat(Category), cl::sub(*cl::AllSubCommands));
+ cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
static cl::list<std::string> ArgsBefore(
"extra-arg-before",
cl::desc("Additional argument to prepend to the compiler command line"),
- cl::cat(Category), cl::sub(*cl::AllSubCommands));
+ cl::cat(Category), cl::sub(cl::SubCommand::getAll()));
cl::ResetAllOptionOccurrences();
diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp
index aca2afceea44..020ad08a65e7 100644
--- a/clang/lib/Tooling/Core/Replacement.cpp
+++ b/clang/lib/Tooling/Core/Replacement.cpp
@@ -270,7 +270,7 @@ llvm::Error Replacements::add(const Replacement &R) {
assert(R.getLength() == 0);
// `I` is also an insertion, `R` and `I` conflict.
if (I->getLength() == 0) {
- // Check if two insertions are order-indepedent: if inserting them in
+ // Check if two insertions are order-independent: if inserting them in
// either order produces the same text, they are order-independent.
if ((R.getReplacementText() + I->getReplacementText()).str() !=
(I->getReplacementText() + R.getReplacementText()).str())
@@ -319,7 +319,7 @@ llvm::Error Replacements::add(const Replacement &R) {
Replaces.insert(R);
} else {
// `I` overlaps with `R`. We need to check `R` against all overlapping
- // replacements to see if they are order-indepedent. If they are, merge `R`
+ // replacements to see if they are order-independent. If they are, merge `R`
// with them and replace them with the merged replacements.
auto MergeBegin = I;
auto MergeEnd = std::next(I);
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
index 026bdfe03f28..97b41fc68917 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningFilesystem.cpp
@@ -10,6 +10,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "llvm/Support/Threading.h"
+#include <optional>
using namespace clang;
using namespace tooling;
@@ -67,7 +68,7 @@ EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
Directives)) {
Contents->DepDirectiveTokens.clear();
// FIXME: Propagate the diagnostic if desired by the client.
- Contents->DepDirectives.store(new Optional<DependencyDirectivesTy>());
+ Contents->DepDirectives.store(new std::optional<DependencyDirectivesTy>());
return EntryRef(Filename, Entry);
}
@@ -76,7 +77,7 @@ EntryRef DependencyScanningWorkerFilesystem::scanForDirectivesIfNecessary(
// threads may skip the
// critical section (`DepDirectives != nullptr`), leading to a data race.
Contents->DepDirectives.store(
- new Optional<DependencyDirectivesTy>(std::move(Directives)));
+ new std::optional<DependencyDirectivesTy>(std::move(Directives)));
return EntryRef(Filename, Entry);
}
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
index cda3e2dd8550..6dccb3d131cd 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
@@ -14,10 +14,10 @@ using namespace tooling;
using namespace dependencies;
DependencyScanningService::DependencyScanningService(
- ScanningMode Mode, ScanningOutputFormat Format, bool ReuseFileManager,
- bool OptimizeArgs)
- : Mode(Mode), Format(Format), ReuseFileManager(ReuseFileManager),
- OptimizeArgs(OptimizeArgs) {
+ ScanningMode Mode, ScanningOutputFormat Format, bool OptimizeArgs,
+ bool EagerLoadModules)
+ : Mode(Mode), Format(Format), OptimizeArgs(OptimizeArgs),
+ EagerLoadModules(EagerLoadModules) {
// Initialize targets for object file support.
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
index 411fd9676ffd..3fcef00a5780 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -8,32 +8,18 @@
#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
#include "clang/Frontend/Utils.h"
+#include <optional>
using namespace clang;
using namespace tooling;
using namespace dependencies;
-std::vector<std::string> FullDependencies::getCommandLine(
- llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>
- LookupModuleOutput) const {
- std::vector<std::string> Ret = getCommandLineWithoutModulePaths();
-
- for (ModuleID MID : ClangModuleDeps) {
- auto PCM = LookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
- Ret.push_back("-fmodule-file=" + PCM);
- }
-
- return Ret;
-}
-
-std::vector<std::string>
-FullDependencies::getCommandLineWithoutModulePaths() const {
+static std::vector<std::string>
+makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine) {
std::vector<std::string> Args = OriginalCommandLine;
Args.push_back("-fno-implicit-modules");
Args.push_back("-fno-implicit-module-maps");
- for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
- Args.push_back("-fmodule-file=" + PMD.PCMFile);
// These arguments are unused in explicit compiles.
llvm::erase_if(Args, [](StringRef Arg) {
@@ -56,10 +42,12 @@ DependencyScanningTool::DependencyScanningTool(
llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
const std::vector<std::string> &CommandLine, StringRef CWD,
- llvm::Optional<StringRef> ModuleName) {
+ std::optional<StringRef> ModuleName) {
/// Prints out all of the gathered dependencies into a string.
class MakeDependencyPrinterConsumer : public DependencyConsumer {
public:
+ void handleBuildCommand(Command) override {}
+
void
handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
@@ -81,6 +69,11 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
void handleContextHash(std::string Hash) override {}
+ std::string lookupModuleOutput(const ModuleID &ID,
+ ModuleOutputKind Kind) override {
+ llvm::report_fatal_error("unexpected call to lookupModuleOutput");
+ }
+
void printDependencies(std::string &S) {
assert(Opts && "Handled dependency output options.");
@@ -122,77 +115,98 @@ llvm::Expected<FullDependenciesResult>
DependencyScanningTool::getFullDependencies(
const std::vector<std::string> &CommandLine, StringRef CWD,
const llvm::StringSet<> &AlreadySeen,
- llvm::Optional<StringRef> ModuleName) {
- class FullDependencyPrinterConsumer : public DependencyConsumer {
- public:
- FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
- : AlreadySeen(AlreadySeen) {}
-
- void
- handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
-
- void handleFileDependency(StringRef File) override {
- Dependencies.push_back(std::string(File));
- }
+ LookupModuleOutputCallback LookupModuleOutput,
+ std::optional<StringRef> ModuleName) {
+ FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
+ Worker.shouldEagerLoadModules());
+ llvm::Error Result =
+ Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
+ if (Result)
+ return std::move(Result);
+ return Consumer.takeFullDependencies();
+}
- void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
- PrebuiltModuleDeps.emplace_back(std::move(PMD));
- }
+llvm::Expected<FullDependenciesResult>
+DependencyScanningTool::getFullDependenciesLegacyDriverCommand(
+ const std::vector<std::string> &CommandLine, StringRef CWD,
+ const llvm::StringSet<> &AlreadySeen,
+ LookupModuleOutputCallback LookupModuleOutput,
+ std::optional<StringRef> ModuleName) {
+ FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
+ Worker.shouldEagerLoadModules());
+ llvm::Error Result =
+ Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
+ if (Result)
+ return std::move(Result);
+ return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine);
+}
- void handleModuleDependency(ModuleDeps MD) override {
- ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
- }
+FullDependenciesResult FullDependencyConsumer::takeFullDependencies() {
+ FullDependenciesResult FDR;
+ FullDependencies &FD = FDR.FullDeps;
+
+ FD.ID.ContextHash = std::move(ContextHash);
+ FD.FileDeps = std::move(Dependencies);
+ FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
+ FD.Commands = std::move(Commands);
+
+ for (auto &&M : ClangModuleDeps) {
+ auto &MD = M.second;
+ if (MD.ImportedByMainFile)
+ FD.ClangModuleDeps.push_back(MD.ID);
+ // TODO: Avoid handleModuleDependency even being called for modules
+ // we've already seen.
+ if (AlreadySeen.count(M.first))
+ continue;
+ FDR.DiscoveredModules.push_back(std::move(MD));
+ }
- void handleContextHash(std::string Hash) override {
- ContextHash = std::move(Hash);
- }
+ return FDR;
+}
- FullDependenciesResult getFullDependencies(
- const std::vector<std::string> &OriginalCommandLine) const {
- FullDependencies FD;
+FullDependenciesResult
+FullDependencyConsumer::getFullDependenciesLegacyDriverCommand(
+ const std::vector<std::string> &OriginalCommandLine) const {
+ FullDependencies FD;
- FD.OriginalCommandLine =
- ArrayRef<std::string>(OriginalCommandLine).slice(1);
+ FD.DriverCommandLine = makeTUCommandLineWithoutPaths(
+ ArrayRef<std::string>(OriginalCommandLine).slice(1));
- FD.ID.ContextHash = std::move(ContextHash);
+ FD.ID.ContextHash = std::move(ContextHash);
- FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
+ FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
- for (auto &&M : ClangModuleDeps) {
- auto &MD = M.second;
- if (MD.ImportedByMainFile)
- FD.ClangModuleDeps.push_back(MD.ID);
+ for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
+ FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile);
+
+ for (auto &&M : ClangModuleDeps) {
+ auto &MD = M.second;
+ if (MD.ImportedByMainFile) {
+ FD.ClangModuleDeps.push_back(MD.ID);
+ auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile);
+ if (EagerLoadModules) {
+ FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath);
+ } else {
+ FD.DriverCommandLine.push_back("-fmodule-map-file=" +
+ MD.ClangModuleMapFile);
+ FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName +
+ "=" + PCMPath);
}
+ }
+ }
- FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
-
- FullDependenciesResult FDR;
-
- for (auto &&M : ClangModuleDeps) {
- // TODO: Avoid handleModuleDependency even being called for modules
- // we've already seen.
- if (AlreadySeen.count(M.first))
- continue;
- FDR.DiscoveredModules.push_back(std::move(M.second));
- }
+ FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
- FDR.FullDeps = std::move(FD);
- return FDR;
- }
+ FullDependenciesResult FDR;
- private:
- std::vector<std::string> Dependencies;
- std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
- llvm::MapVector<std::string, ModuleDeps, llvm::StringMap<unsigned>> ClangModuleDeps;
- std::string ContextHash;
- std::vector<std::string> OutputPaths;
- const llvm::StringSet<> &AlreadySeen;
- };
+ for (auto &&M : ClangModuleDeps) {
+ // TODO: Avoid handleModuleDependency even being called for modules
+ // we've already seen.
+ if (AlreadySeen.count(M.first))
+ continue;
+ FDR.DiscoveredModules.push_back(std::move(M.second));
+ }
- FullDependencyPrinterConsumer Consumer(AlreadySeen);
- llvm::Error Result =
- Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
- if (Result)
- return std::move(Result);
- return Consumer.getFullDependencies(CommandLine);
+ FDR.FullDeps = std::move(FD);
+ return FDR;
}
diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 474808d888ec..b54b8de9157e 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -7,7 +7,12 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
+#include "clang/Driver/Compilation.h"
+#include "clang/Driver/Driver.h"
+#include "clang/Driver/Job.h"
+#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/FrontendActions.h"
@@ -17,6 +22,8 @@
#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/Host.h"
+#include <optional>
using namespace clang;
using namespace tooling;
@@ -28,8 +35,9 @@ namespace {
class DependencyConsumerForwarder : public DependencyFileGenerator {
public:
DependencyConsumerForwarder(std::unique_ptr<DependencyOutputOptions> Opts,
- DependencyConsumer &C)
- : DependencyFileGenerator(*Opts), Opts(std::move(Opts)), C(C) {}
+ StringRef WorkingDirectory, DependencyConsumer &C)
+ : DependencyFileGenerator(*Opts), WorkingDirectory(WorkingDirectory),
+ Opts(std::move(Opts)), C(C) {}
void finishedMainFile(DiagnosticsEngine &Diags) override {
C.handleDependencyOutputOpts(*Opts);
@@ -37,11 +45,13 @@ public:
for (const auto &File : getDependencies()) {
CanonPath = File;
llvm::sys::path::remove_dots(CanonPath, /*remove_dot_dot=*/true);
+ llvm::sys::fs::make_absolute(WorkingDirectory, CanonPath);
C.handleFileDependency(CanonPath);
}
}
private:
+ StringRef WorkingDirectory;
std::unique_ptr<DependencyOutputOptions> Opts;
DependencyConsumer &C;
};
@@ -94,7 +104,7 @@ static void visitPrebuiltModule(StringRef PrebuiltModuleFilename,
while (!Worklist.empty())
ASTReader::readASTFileControlBlock(
- Worklist.pop_back_val(), CI.getFileManager(),
+ Worklist.pop_back_val(), CI.getFileManager(), CI.getModuleCache(),
CI.getPCHContainerReader(),
/*FindModuleFileExtensions=*/false, Listener,
/*ValidateDiagnosticOptions=*/false);
@@ -126,8 +136,8 @@ static void sanitizeDiagOpts(DiagnosticOptions &DiagOpts) {
DiagOpts.ShowCarets = false;
// Don't write out diagnostic file.
DiagOpts.DiagnosticSerializationFile.clear();
- // Don't treat warnings as errors.
- DiagOpts.Warnings.push_back("no-error");
+ // Don't emit warnings as errors (and all other warnings too).
+ DiagOpts.IgnoreWarnings = true;
}
/// A clang tool that runs the preprocessor in a mode that's optimized for
@@ -137,11 +147,12 @@ public:
DependencyScanningAction(
StringRef WorkingDirectory, DependencyConsumer &Consumer,
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
- ScanningOutputFormat Format, bool OptimizeArgs, bool DisableFree,
- llvm::Optional<StringRef> ModuleName = None)
+ ScanningOutputFormat Format, bool OptimizeArgs, bool EagerLoadModules,
+ bool DisableFree, std::optional<StringRef> ModuleName = std::nullopt)
: WorkingDirectory(WorkingDirectory), Consumer(Consumer),
DepFS(std::move(DepFS)), Format(Format), OptimizeArgs(OptimizeArgs),
- DisableFree(DisableFree), ModuleName(ModuleName) {}
+ EagerLoadModules(EagerLoadModules), DisableFree(DisableFree),
+ ModuleName(ModuleName) {}
bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
FileManager *FileMgr,
@@ -152,8 +163,20 @@ public:
// Restore the value of DisableFree, which may be modified by Tooling.
OriginalInvocation.getFrontendOpts().DisableFree = DisableFree;
+ if (Scanned) {
+ // Scanning runs once for the first -cc1 invocation in a chain of driver
+ // jobs. For any dependent jobs, reuse the scanning result and just
+ // update the LastCC1Arguments to correspond to the new invocation.
+ // FIXME: to support multi-arch builds, each arch requires a separate scan
+ setLastCC1Arguments(std::move(OriginalInvocation));
+ return true;
+ }
+
+ Scanned = true;
+
// Create a compiler instance to handle the actual work.
- CompilerInstance ScanInstance(std::move(PCHContainerOps));
+ ScanInstanceStorage.emplace(std::move(PCHContainerOps));
+ CompilerInstance &ScanInstance = *ScanInstanceStorage;
ScanInstance.setInvocation(std::move(Invocation));
// Create the compiler's actual diagnostics engine.
@@ -167,9 +190,14 @@ public:
ScanInstance.getFrontendOpts().GenerateGlobalModuleIndex = false;
ScanInstance.getFrontendOpts().UseGlobalModuleIndex = false;
+ ScanInstance.getFrontendOpts().ModulesShareFileManager = false;
- FileMgr->getFileSystemOpts().WorkingDir = std::string(WorkingDirectory);
ScanInstance.setFileManager(FileMgr);
+ // Support for virtual file system overlays.
+ FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
+ ScanInstance.getInvocation(), ScanInstance.getDiagnostics(),
+ FileMgr->getVirtualFileSystemPtr()));
+
ScanInstance.createSourceManager(*FileMgr);
llvm::StringSet<> PrebuiltModulesInputFiles;
@@ -184,20 +212,15 @@ public:
// Use the dependency scanning optimized file system if requested to do so.
if (DepFS) {
- // Support for virtual file system overlays on top of the caching
- // filesystem.
- FileMgr->setVirtualFileSystem(createVFSFromCompilerInvocation(
- ScanInstance.getInvocation(), ScanInstance.getDiagnostics(), DepFS));
-
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> LocalDepFS =
DepFS;
ScanInstance.getPreprocessorOpts().DependencyDirectivesForFile =
[LocalDepFS = std::move(LocalDepFS)](FileEntryRef File)
- -> Optional<ArrayRef<dependency_directives_scan::Directive>> {
+ -> std::optional<ArrayRef<dependency_directives_scan::Directive>> {
if (llvm::ErrorOr<EntryRef> Entry =
LocalDepFS->getOrCreateFileSystemEntry(File.getName()))
return Entry->getDirectiveTokens();
- return None;
+ return std::nullopt;
};
}
@@ -221,13 +244,14 @@ public:
switch (Format) {
case ScanningOutputFormat::Make:
ScanInstance.addDependencyCollector(
- std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
- Consumer));
+ std::make_shared<DependencyConsumerForwarder>(
+ std::move(Opts), WorkingDirectory, Consumer));
break;
case ScanningOutputFormat::Full:
- ScanInstance.addDependencyCollector(std::make_shared<ModuleDepCollector>(
- std::move(Opts), ScanInstance, Consumer,
- std::move(OriginalInvocation), OptimizeArgs));
+ MDC = std::make_shared<ModuleDepCollector>(
+ std::move(Opts), ScanInstance, Consumer, OriginalInvocation,
+ OptimizeArgs, EagerLoadModules);
+ ScanInstance.addDependencyCollector(MDC);
break;
}
@@ -246,19 +270,44 @@ public:
Action = std::make_unique<ReadPCHAndPreprocessAction>();
const bool Result = ScanInstance.ExecuteAction(*Action);
- if (!DepFS)
- FileMgr->clearStatCache();
+
+ if (Result)
+ setLastCC1Arguments(std::move(OriginalInvocation));
+
return Result;
}
+ bool hasScanned() const { return Scanned; }
+
+ /// Take the cc1 arguments corresponding to the most recent invocation used
+ /// with this action. Any modifications implied by the discovered dependencies
+ /// will have already been applied.
+ std::vector<std::string> takeLastCC1Arguments() {
+ std::vector<std::string> Result;
+ std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
+ return Result;
+ }
+
+private:
+ void setLastCC1Arguments(CompilerInvocation &&CI) {
+ if (MDC)
+ MDC->applyDiscoveredDependencies(CI);
+ LastCC1Arguments = CI.getCC1CommandLine();
+ }
+
private:
StringRef WorkingDirectory;
DependencyConsumer &Consumer;
llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
ScanningOutputFormat Format;
bool OptimizeArgs;
+ bool EagerLoadModules;
bool DisableFree;
- llvm::Optional<StringRef> ModuleName;
+ std::optional<StringRef> ModuleName;
+ std::optional<CompilerInstance> ScanInstanceStorage;
+ std::shared_ptr<ModuleDepCollector> MDC;
+ std::vector<std::string> LastCC1Arguments;
+ bool Scanned = false;
};
} // end anonymous namespace
@@ -266,7 +315,8 @@ private:
DependencyScanningWorker::DependencyScanningWorker(
DependencyScanningService &Service,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
- : Format(Service.getFormat()), OptimizeArgs(Service.canOptimizeArgs()) {
+ : Format(Service.getFormat()), OptimizeArgs(Service.canOptimizeArgs()),
+ EagerLoadModules(Service.shouldEagerLoadModules()) {
PCHContainerOps = std::make_shared<PCHContainerOperations>();
PCHContainerOps->registerReader(
std::make_unique<ObjectFilePCHContainerReader>());
@@ -275,80 +325,151 @@ DependencyScanningWorker::DependencyScanningWorker(
PCHContainerOps->registerWriter(
std::make_unique<ObjectFilePCHContainerWriter>());
- auto OverlayFS =
- llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(std::move(FS));
- InMemoryFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
- OverlayFS->pushOverlay(InMemoryFS);
- RealFS = OverlayFS;
-
- if (Service.getMode() == ScanningMode::DependencyDirectivesScan)
- DepFS = new DependencyScanningWorkerFilesystem(Service.getSharedCache(),
- RealFS);
- if (Service.canReuseFileManager())
- Files = new FileManager(FileSystemOptions(), RealFS);
+ switch (Service.getMode()) {
+ case ScanningMode::DependencyDirectivesScan:
+ DepFS =
+ new DependencyScanningWorkerFilesystem(Service.getSharedCache(), FS);
+ BaseFS = DepFS;
+ break;
+ case ScanningMode::CanonicalPreprocessing:
+ DepFS = nullptr;
+ BaseFS = FS;
+ break;
+ }
}
-static llvm::Error
-runWithDiags(DiagnosticOptions *DiagOpts,
- llvm::function_ref<bool(DiagnosticConsumer &, DiagnosticOptions &)>
- BodyShouldSucceed) {
+llvm::Error DependencyScanningWorker::computeDependencies(
+ StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
+ DependencyConsumer &Consumer, std::optional<StringRef> ModuleName) {
+ std::vector<const char *> CLI;
+ for (const std::string &Arg : CommandLine)
+ CLI.push_back(Arg.c_str());
+ auto DiagOpts = CreateAndPopulateDiagOpts(CLI);
sanitizeDiagOpts(*DiagOpts);
// Capture the emitted diagnostics and report them to the client
// in the case of a failure.
std::string DiagnosticOutput;
llvm::raw_string_ostream DiagnosticsOS(DiagnosticOutput);
- TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts);
+ TextDiagnosticPrinter DiagPrinter(DiagnosticsOS, DiagOpts.release());
- if (BodyShouldSucceed(DiagPrinter, *DiagOpts))
+ if (computeDependencies(WorkingDirectory, CommandLine, Consumer, DiagPrinter,
+ ModuleName))
return llvm::Error::success();
return llvm::make_error<llvm::StringError>(DiagnosticsOS.str(),
llvm::inconvertibleErrorCode());
}
-llvm::Error DependencyScanningWorker::computeDependencies(
+static bool forEachDriverJob(
+ ArrayRef<std::string> Args, DiagnosticsEngine &Diags, FileManager &FM,
+ llvm::function_ref<bool(const driver::Command &Cmd)> Callback) {
+ std::unique_ptr<driver::Driver> Driver = std::make_unique<driver::Driver>(
+ Args[0], llvm::sys::getDefaultTargetTriple(), Diags,
+ "clang LLVM compiler", &FM.getVirtualFileSystem());
+ Driver->setTitle("clang_based_tool");
+
+ std::vector<const char *> Argv;
+ for (const std::string &Arg : Args)
+ Argv.push_back(Arg.c_str());
+
+ const std::unique_ptr<driver::Compilation> Compilation(
+ Driver->BuildCompilation(llvm::ArrayRef(Argv)));
+ if (!Compilation)
+ return false;
+
+ for (const driver::Command &Job : Compilation->getJobs()) {
+ if (!Callback(Job))
+ return false;
+ }
+ return true;
+}
+
+bool DependencyScanningWorker::computeDependencies(
StringRef WorkingDirectory, const std::vector<std::string> &CommandLine,
- DependencyConsumer &Consumer, llvm::Optional<StringRef> ModuleName) {
+ DependencyConsumer &Consumer, DiagnosticConsumer &DC,
+ std::optional<StringRef> ModuleName) {
// Reset what might have been modified in the previous worker invocation.
- RealFS->setCurrentWorkingDirectory(WorkingDirectory);
- if (Files)
- Files->setVirtualFileSystem(RealFS);
-
- llvm::IntrusiveRefCntPtr<FileManager> CurrentFiles =
- Files ? Files : new FileManager(FileSystemOptions(), RealFS);
+ BaseFS->setCurrentWorkingDirectory(WorkingDirectory);
- Optional<std::vector<std::string>> ModifiedCommandLine;
+ std::optional<std::vector<std::string>> ModifiedCommandLine;
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> ModifiedFS;
if (ModuleName) {
ModifiedCommandLine = CommandLine;
- InMemoryFS->addFile(*ModuleName, 0, llvm::MemoryBuffer::getMemBuffer(""));
ModifiedCommandLine->emplace_back(*ModuleName);
+
+ auto OverlayFS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(BaseFS);
+ auto InMemoryFS =
+ llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
+ InMemoryFS->setCurrentWorkingDirectory(WorkingDirectory);
+ InMemoryFS->addFile(*ModuleName, 0, llvm::MemoryBuffer::getMemBuffer(""));
+ OverlayFS->pushOverlay(InMemoryFS);
+ ModifiedFS = OverlayFS;
}
const std::vector<std::string> &FinalCommandLine =
ModifiedCommandLine ? *ModifiedCommandLine : CommandLine;
+ FileSystemOptions FSOpts;
+ FSOpts.WorkingDir = WorkingDirectory.str();
+ auto FileMgr = llvm::makeIntrusiveRefCnt<FileManager>(
+ FSOpts, ModifiedFS ? ModifiedFS : BaseFS);
+
std::vector<const char *> FinalCCommandLine(CommandLine.size(), nullptr);
llvm::transform(CommandLine, FinalCCommandLine.begin(),
[](const std::string &Str) { return Str.c_str(); });
- return runWithDiags(CreateAndPopulateDiagOpts(FinalCCommandLine).release(),
- [&](DiagnosticConsumer &DC, DiagnosticOptions &DiagOpts) {
- // DisableFree is modified by Tooling for running
- // in-process; preserve the original value, which is
- // always true for a driver invocation.
- bool DisableFree = true;
- DependencyScanningAction Action(
- WorkingDirectory, Consumer, DepFS, Format,
- OptimizeArgs, DisableFree, ModuleName);
- // Create an invocation that uses the underlying file
- // system to ensure that any file system requests that
- // are made by the driver do not go through the
- // dependency scanning filesystem.
- ToolInvocation Invocation(FinalCommandLine, &Action,
- CurrentFiles.get(),
- PCHContainerOps);
- Invocation.setDiagnosticConsumer(&DC);
- Invocation.setDiagnosticOptions(&DiagOpts);
- return Invocation.run();
- });
+ auto DiagOpts = CreateAndPopulateDiagOpts(FinalCCommandLine);
+ sanitizeDiagOpts(*DiagOpts);
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(DiagOpts.release(), &DC,
+ /*ShouldOwnClient=*/false);
+
+ // Although `Diagnostics` are used only for command-line parsing, the
+ // custom `DiagConsumer` might expect a `SourceManager` to be present.
+ SourceManager SrcMgr(*Diags, *FileMgr);
+ Diags->setSourceManager(&SrcMgr);
+ // DisableFree is modified by Tooling for running
+ // in-process; preserve the original value, which is
+ // always true for a driver invocation.
+ bool DisableFree = true;
+ DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS, Format,
+ OptimizeArgs, EagerLoadModules, DisableFree,
+ ModuleName);
+ bool Success = forEachDriverJob(
+ FinalCommandLine, *Diags, *FileMgr, [&](const driver::Command &Cmd) {
+ if (StringRef(Cmd.getCreator().getName()) != "clang") {
+ // Non-clang command. Just pass through to the dependency
+ // consumer.
+ Consumer.handleBuildCommand(
+ {Cmd.getExecutable(),
+ {Cmd.getArguments().begin(), Cmd.getArguments().end()}});
+ return true;
+ }
+
+ std::vector<std::string> Argv;
+ Argv.push_back(Cmd.getExecutable());
+ Argv.insert(Argv.end(), Cmd.getArguments().begin(),
+ Cmd.getArguments().end());
+
+ // Create an invocation that uses the underlying file
+ // system to ensure that any file system requests that
+ // are made by the driver do not go through the
+ // dependency scanning filesystem.
+ ToolInvocation Invocation(std::move(Argv), &Action, &*FileMgr,
+ PCHContainerOps);
+ Invocation.setDiagnosticConsumer(Diags->getClient());
+ Invocation.setDiagnosticOptions(&Diags->getDiagnosticOptions());
+ if (!Invocation.run())
+ return false;
+
+ std::vector<std::string> Args = Action.takeLastCC1Arguments();
+ Consumer.handleBuildCommand({Cmd.getExecutable(), std::move(Args)});
+ return true;
+ });
+
+ if (Success && !Action.hasScanned())
+ Diags->Report(diag::err_fe_expected_compiler_job)
+ << llvm::join(FinalCommandLine, " ");
+ return Success && Action.hasScanned();
}
diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 725bb2c318ac..cb1c66b8d63f 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -12,7 +12,9 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/StringSaver.h"
+#include <optional>
using namespace clang;
using namespace tooling;
@@ -41,124 +43,260 @@ static void optimizeHeaderSearchOpts(HeaderSearchOptions &Opts,
Opts.UserEntries.push_back(Entries[Idx]);
}
-CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
+static std::vector<std::string> splitString(std::string S, char Separator) {
+ SmallVector<StringRef> Segments;
+ StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
+ std::vector<std::string> Result;
+ Result.reserve(Segments.size());
+ for (StringRef Segment : Segments)
+ Result.push_back(Segment.str());
+ return Result;
+}
+
+void ModuleDepCollector::addOutputPaths(CompilerInvocation &CI,
+ ModuleDeps &Deps) {
+ CI.getFrontendOpts().OutputFile =
+ Consumer.lookupModuleOutput(Deps.ID, ModuleOutputKind::ModuleFile);
+ if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
+ CI.getDiagnosticOpts().DiagnosticSerializationFile =
+ Consumer.lookupModuleOutput(
+ Deps.ID, ModuleOutputKind::DiagnosticSerializationFile);
+ if (!CI.getDependencyOutputOpts().OutputFile.empty()) {
+ CI.getDependencyOutputOpts().OutputFile =
+ Consumer.lookupModuleOutput(Deps.ID, ModuleOutputKind::DependencyFile);
+ CI.getDependencyOutputOpts().Targets =
+ splitString(Consumer.lookupModuleOutput(
+ Deps.ID, ModuleOutputKind::DependencyTargets),
+ '\0');
+ if (!CI.getDependencyOutputOpts().OutputFile.empty() &&
+ CI.getDependencyOutputOpts().Targets.empty()) {
+ // Fallback to -o as dependency target, as in the driver.
+ SmallString<128> Target;
+ quoteMakeTarget(CI.getFrontendOpts().OutputFile, Target);
+ CI.getDependencyOutputOpts().Targets.push_back(std::string(Target));
+ }
+ }
+}
+
+CompilerInvocation
+ModuleDepCollector::makeInvocationForModuleBuildWithoutOutputs(
const ModuleDeps &Deps,
llvm::function_ref<void(CompilerInvocation &)> Optimize) const {
// Make a deep copy of the original Clang invocation.
CompilerInvocation CI(OriginalInvocation);
- CI.getLangOpts()->resetNonModularOptions();
- CI.getPreprocessorOpts().resetNonModularOptions();
+ CI.resetNonModularOptions();
+ CI.clearImplicitModuleBuildOptions();
// Remove options incompatible with explicit module build or are likely to
// differ between identical modules discovered from different translation
// units.
CI.getFrontendOpts().Inputs.clear();
CI.getFrontendOpts().OutputFile.clear();
+
+ // TODO: Figure out better way to set options to their default value.
CI.getCodeGenOpts().MainFileName.clear();
CI.getCodeGenOpts().DwarfDebugFlags.clear();
- CI.getDiagnosticOpts().DiagnosticSerializationFile.clear();
- CI.getDependencyOutputOpts().OutputFile.clear();
+ if (!CI.getLangOpts()->ModulesCodegen) {
+ CI.getCodeGenOpts().DebugCompilationDir.clear();
+ CI.getCodeGenOpts().CoverageCompilationDir.clear();
+ }
+
+ // Map output paths that affect behaviour to "-" so their existence is in the
+ // context hash. The final path will be computed in addOutputPaths.
+ if (!CI.getDiagnosticOpts().DiagnosticSerializationFile.empty())
+ CI.getDiagnosticOpts().DiagnosticSerializationFile = "-";
+ if (!CI.getDependencyOutputOpts().OutputFile.empty())
+ CI.getDependencyOutputOpts().OutputFile = "-";
CI.getDependencyOutputOpts().Targets.clear();
CI.getFrontendOpts().ProgramAction = frontend::GenerateModule;
CI.getLangOpts()->ModuleName = Deps.ID.ModuleName;
CI.getFrontendOpts().IsSystemModule = Deps.IsSystem;
- // Disable implicit modules and canonicalize options that are only used by
- // implicit modules.
- CI.getLangOpts()->ImplicitModules = false;
- CI.getHeaderSearchOpts().ImplicitModuleMaps = false;
- CI.getHeaderSearchOpts().ModuleCachePath.clear();
- CI.getHeaderSearchOpts().ModulesValidateOncePerBuildSession = false;
- CI.getHeaderSearchOpts().BuildSessionTimestamp = 0;
- // The specific values we canonicalize to for pruning don't affect behaviour,
- /// so use the default values so they will be dropped from the command-line.
- CI.getHeaderSearchOpts().ModuleCachePruneInterval = 7 * 24 * 60 * 60;
- CI.getHeaderSearchOpts().ModuleCachePruneAfter = 31 * 24 * 60 * 60;
+ // Inputs
+ InputKind ModuleMapInputKind(CI.getFrontendOpts().DashX.getLanguage(),
+ InputKind::Format::ModuleMap);
+ CI.getFrontendOpts().Inputs.emplace_back(Deps.ClangModuleMapFile,
+ ModuleMapInputKind);
+
+ auto CurrentModuleMapEntry =
+ ScanInstance.getFileManager().getFile(Deps.ClangModuleMapFile);
+ assert(CurrentModuleMapEntry && "module map file entry not found");
+
+ auto DepModuleMapFiles = collectModuleMapFiles(Deps.ClangModuleDeps);
+ for (StringRef ModuleMapFile : Deps.ModuleMapFileDeps) {
+ // TODO: Track these as `FileEntryRef` to simplify the equality check below.
+ auto ModuleMapEntry = ScanInstance.getFileManager().getFile(ModuleMapFile);
+ assert(ModuleMapEntry && "module map file entry not found");
+
+ // Don't report module maps describing eagerly-loaded dependency. This
+ // information will be deserialized from the PCM.
+ // TODO: Verify this works fine when modulemap for module A is eagerly
+ // loaded from A.pcm, and module map passed on the command line contains
+ // definition of a submodule: "explicit module A.Private { ... }".
+ if (EagerLoadModules && DepModuleMapFiles.contains(*ModuleMapEntry))
+ continue;
+
+ // Don't report module map file of the current module unless it also
+ // describes a dependency (for symmetry).
+ if (*ModuleMapEntry == *CurrentModuleMapEntry &&
+ !DepModuleMapFiles.contains(*ModuleMapEntry))
+ continue;
+
+ CI.getFrontendOpts().ModuleMapFiles.emplace_back(ModuleMapFile);
+ }
// Report the prebuilt modules this module uses.
for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps)
CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
- CI.getFrontendOpts().ModuleMapFiles = Deps.ModuleMapFileDeps;
+ // Add module file inputs from dependencies.
+ addModuleFiles(CI, Deps.ClangModuleDeps);
+
+ // Remove any macro definitions that are explicitly ignored.
+ if (!CI.getHeaderSearchOpts().ModulesIgnoreMacros.empty()) {
+ llvm::erase_if(
+ CI.getPreprocessorOpts().Macros,
+ [&CI](const std::pair<std::string, bool> &Def) {
+ StringRef MacroDef = Def.first;
+ return CI.getHeaderSearchOpts().ModulesIgnoreMacros.contains(
+ llvm::CachedHashString(MacroDef.split('=').first));
+ });
+ // Remove the now unused option.
+ CI.getHeaderSearchOpts().ModulesIgnoreMacros.clear();
+ }
Optimize(CI);
- // The original invocation probably didn't have strict context hash enabled.
- // We will use the context hash of this invocation to distinguish between
- // multiple incompatible versions of the same module and will use it when
- // reporting dependencies to the clients. Let's make sure we're using
- // **strict** context hash in order to prevent accidental sharing of
- // incompatible modules (e.g. with differences in search paths).
- CI.getHeaderSearchOpts().ModulesStrictContextHash = true;
-
return CI;
}
-static std::vector<std::string>
-serializeCompilerInvocation(const CompilerInvocation &CI) {
- // Set up string allocator.
- llvm::BumpPtrAllocator Alloc;
- llvm::StringSaver Strings(Alloc);
- auto SA = [&Strings](const Twine &Arg) { return Strings.save(Arg).data(); };
+llvm::DenseSet<const FileEntry *> ModuleDepCollector::collectModuleMapFiles(
+ ArrayRef<ModuleID> ClangModuleDeps) const {
+ llvm::DenseSet<const FileEntry *> ModuleMapFiles;
+ for (const ModuleID &MID : ClangModuleDeps) {
+ ModuleDeps *MD = ModuleDepsByID.lookup(MID);
+ assert(MD && "Inconsistent dependency info");
+ // TODO: Track ClangModuleMapFile as `FileEntryRef`.
+ auto FE = ScanInstance.getFileManager().getFile(MD->ClangModuleMapFile);
+ assert(FE && "Missing module map file that was previously found");
+ ModuleMapFiles.insert(*FE);
+ }
+ return ModuleMapFiles;
+}
- // Synthesize full command line from the CompilerInvocation, including "-cc1".
- SmallVector<const char *, 32> Args{"-cc1"};
- CI.generateCC1CommandLine(Args, SA);
+void ModuleDepCollector::addModuleMapFiles(
+ CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
+ if (EagerLoadModules)
+ return; // Only pcm is needed for eager load.
- // Convert arguments to the return type.
- return std::vector<std::string>{Args.begin(), Args.end()};
+ for (const ModuleID &MID : ClangModuleDeps) {
+ ModuleDeps *MD = ModuleDepsByID.lookup(MID);
+ assert(MD && "Inconsistent dependency info");
+ CI.getFrontendOpts().ModuleMapFiles.push_back(MD->ClangModuleMapFile);
+ }
}
-static std::vector<std::string> splitString(std::string S, char Separator) {
- SmallVector<StringRef> Segments;
- StringRef(S).split(Segments, Separator, /*MaxSplit=*/-1, /*KeepEmpty=*/false);
- std::vector<std::string> Result;
- Result.reserve(Segments.size());
- for (StringRef Segment : Segments)
- Result.push_back(Segment.str());
- return Result;
+void ModuleDepCollector::addModuleFiles(
+ CompilerInvocation &CI, ArrayRef<ModuleID> ClangModuleDeps) const {
+ for (const ModuleID &MID : ClangModuleDeps) {
+ std::string PCMPath =
+ Consumer.lookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
+ if (EagerLoadModules)
+ CI.getFrontendOpts().ModuleFiles.push_back(std::move(PCMPath));
+ else
+ CI.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
+ {MID.ModuleName, std::move(PCMPath)});
+ }
+}
+
+static bool needsModules(FrontendInputFile FIF) {
+ switch (FIF.getKind().getLanguage()) {
+ case Language::Unknown:
+ case Language::Asm:
+ case Language::LLVM_IR:
+ return false;
+ default:
+ return true;
+ }
}
-std::vector<std::string> ModuleDeps::getCanonicalCommandLine(
- llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>
- LookupModuleOutput) const {
- CompilerInvocation CI(BuildInvocation);
- FrontendOptions &FrontendOpts = CI.getFrontendOpts();
+void ModuleDepCollector::applyDiscoveredDependencies(CompilerInvocation &CI) {
+ CI.clearImplicitModuleBuildOptions();
- InputKind ModuleMapInputKind(FrontendOpts.DashX.getLanguage(),
- InputKind::Format::ModuleMap);
- FrontendOpts.Inputs.emplace_back(ClangModuleMapFile, ModuleMapInputKind);
- FrontendOpts.OutputFile =
- LookupModuleOutput(ID, ModuleOutputKind::ModuleFile);
- if (HadSerializedDiagnostics)
- CI.getDiagnosticOpts().DiagnosticSerializationFile =
- LookupModuleOutput(ID, ModuleOutputKind::DiagnosticSerializationFile);
- if (HadDependencyFile) {
- DependencyOutputOptions &DepOpts = CI.getDependencyOutputOpts();
- DepOpts.OutputFile =
- LookupModuleOutput(ID, ModuleOutputKind::DependencyFile);
- DepOpts.Targets = splitString(
- LookupModuleOutput(ID, ModuleOutputKind::DependencyTargets), '\0');
- if (!DepOpts.OutputFile.empty() && DepOpts.Targets.empty()) {
- // Fallback to -o as dependency target, as in the driver.
- SmallString<128> Target;
- quoteMakeTarget(FrontendOpts.OutputFile, Target);
- DepOpts.Targets.push_back(std::string(Target));
- }
+ if (llvm::any_of(CI.getFrontendOpts().Inputs, needsModules)) {
+ Preprocessor &PP = ScanInstance.getPreprocessor();
+ if (Module *CurrentModule = PP.getCurrentModuleImplementation())
+ if (OptionalFileEntryRef CurrentModuleMap =
+ PP.getHeaderSearchInfo()
+ .getModuleMap()
+ .getModuleMapFileForUniquing(CurrentModule))
+ CI.getFrontendOpts().ModuleMapFiles.emplace_back(
+ CurrentModuleMap->getName());
+
+ SmallVector<ModuleID> DirectDeps;
+ for (const auto &KV : ModularDeps)
+ if (KV.second->ImportedByMainFile)
+ DirectDeps.push_back(KV.second->ID);
+
+ // TODO: Report module maps the same way it's done for modular dependencies.
+ addModuleMapFiles(CI, DirectDeps);
+
+ addModuleFiles(CI, DirectDeps);
+
+ for (const auto &KV : DirectPrebuiltModularDeps)
+ CI.getFrontendOpts().ModuleFiles.push_back(KV.second.PCMFile);
+ }
+}
+
+static std::string getModuleContextHash(const ModuleDeps &MD,
+ const CompilerInvocation &CI,
+ bool EagerLoadModules) {
+ llvm::HashBuilder<llvm::TruncatedBLAKE3<16>,
+ llvm::support::endianness::native>
+ HashBuilder;
+ SmallString<32> Scratch;
+
+ // Hash the compiler version and serialization version to ensure the module
+ // will be readable.
+ HashBuilder.add(getClangFullRepositoryVersion());
+ HashBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR);
+
+ // Hash the BuildInvocation without any input files.
+ SmallVector<const char *, 32> DummyArgs;
+ CI.generateCC1CommandLine(DummyArgs, [&](const Twine &Arg) {
+ Scratch.clear();
+ StringRef Str = Arg.toStringRef(Scratch);
+ HashBuilder.add(Str);
+ return "<unused>";
+ });
+
+ // Hash the module dependencies. These paths may differ even if the invocation
+ // is identical if they depend on the contents of the files in the TU -- for
+ // example, case-insensitive paths to modulemap files. Usually such a case
+ // would indicate a missed optimization to canonicalize, but it may be
+ // difficult to canonicalize all cases when there is a VFS.
+ for (const auto &ID : MD.ClangModuleDeps) {
+ HashBuilder.add(ID.ModuleName);
+ HashBuilder.add(ID.ContextHash);
}
- for (ModuleID MID : ClangModuleDeps)
- FrontendOpts.ModuleFiles.push_back(
- LookupModuleOutput(MID, ModuleOutputKind::ModuleFile));
+ HashBuilder.add(EagerLoadModules);
- return serializeCompilerInvocation(CI);
+ llvm::BLAKE3Result<16> Hash = HashBuilder.final();
+ std::array<uint64_t, 2> Words;
+ static_assert(sizeof(Hash) == sizeof(Words), "Hash must match Words");
+ std::memcpy(Words.data(), Hash.data(), sizeof(Hash));
+ return toString(llvm::APInt(sizeof(Words) * 8, Words), 36, /*Signed=*/false);
}
-std::vector<std::string>
-ModuleDeps::getCanonicalCommandLineWithoutModulePaths() const {
- return serializeCompilerInvocation(BuildInvocation);
+void ModuleDepCollector::associateWithContextHash(const CompilerInvocation &CI,
+ ModuleDeps &Deps) {
+ Deps.ID.ContextHash = getModuleContextHash(Deps, CI, EagerLoadModules);
+ bool Inserted = ModuleDepsByID.insert({Deps.ID, &Deps}).second;
+ (void)Inserted;
+ assert(Inserted && "duplicate module mapping");
}
void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
@@ -180,21 +318,20 @@ void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,
// Dependency generation really does want to go all the way to the
// file entry for a source location to find out what is depended on.
// We do not want #line markers to affect dependency generation!
- if (Optional<StringRef> Filename =
+ if (std::optional<StringRef> Filename =
SM.getNonBuiltinFilenameForID(SM.getFileID(SM.getExpansionLoc(Loc))))
- MDC.FileDeps.push_back(
- std::string(llvm::sys::path::remove_leading_dotslash(*Filename)));
+ MDC.addFileDep(llvm::sys::path::remove_leading_dotslash(*Filename));
}
void ModuleDepCollectorPP::InclusionDirective(
SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
- bool IsAngled, CharSourceRange FilenameRange, Optional<FileEntryRef> File,
+ bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,
StringRef SearchPath, StringRef RelativePath, const Module *Imported,
SrcMgr::CharacteristicKind FileType) {
if (!File && !Imported) {
// This is a non-modular include that HeaderSearch failed to find. Add it
// here as `FileChanged` will never see it.
- MDC.FileDeps.push_back(std::string(FileName));
+ MDC.addFileDep(FileName);
}
handleImport(Imported);
}
@@ -212,7 +349,8 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) {
const Module *TopLevelModule = Imported->getTopLevelModule();
if (MDC.isPrebuiltModule(TopLevelModule))
- DirectPrebuiltModularDeps.insert(TopLevelModule);
+ MDC.DirectPrebuiltModularDeps.insert(
+ {TopLevelModule, PrebuiltModuleDep{TopLevelModule}});
else
DirectModularDeps.insert(TopLevelModule);
}
@@ -224,18 +362,15 @@ void ModuleDepCollectorPP::EndOfMainFile() {
->getName());
if (!MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude.empty())
- MDC.FileDeps.push_back(
- MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
-
- for (const Module *M : DirectModularDeps) {
- // A top-level module might not be actually imported as a module when
- // -fmodule-name is used to compile a translation unit that imports this
- // module. In that case it can be skipped. The appropriate header
- // dependencies will still be reported as expected.
- if (!M->getASTFile())
- continue;
+ MDC.addFileDep(MDC.ScanInstance.getPreprocessorOpts().ImplicitPCHInclude);
+
+ for (const Module *M :
+ MDC.ScanInstance.getPreprocessor().getAffectingClangModules())
+ if (!MDC.isPrebuiltModule(M))
+ DirectModularDeps.insert(M);
+
+ for (const Module *M : DirectModularDeps)
handleTopLevelModule(M);
- }
MDC.Consumer.handleDependencyOutputOpts(*MDC.Opts);
@@ -245,13 +380,21 @@ void ModuleDepCollectorPP::EndOfMainFile() {
for (auto &&I : MDC.FileDeps)
MDC.Consumer.handleFileDependency(I);
- for (auto &&I : DirectPrebuiltModularDeps)
- MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
+ for (auto &&I : MDC.DirectPrebuiltModularDeps)
+ MDC.Consumer.handlePrebuiltModuleDependency(I.second);
}
-ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
+std::optional<ModuleID>
+ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
assert(M == M->getTopLevelModule() && "Expected top level module!");
+ // A top-level module might not be actually imported as a module when
+ // -fmodule-name is used to compile a translation unit that imports this
+ // module. In that case it can be skipped. The appropriate header
+ // dependencies will still be reported as expected.
+ if (!M->getASTFile())
+ return {};
+
// If this module has been handled already, just return its ID.
auto ModI = MDC.ModularDeps.insert({M, nullptr});
if (!ModI.second)
@@ -262,18 +405,16 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
MD.ID.ModuleName = M->getFullModuleName();
MD.ImportedByMainFile = DirectModularDeps.contains(M);
- MD.ImplicitModulePCMPath = std::string(M->getASTFile()->getName());
MD.IsSystem = M->IsSystem;
- const FileEntry *ModuleMap = MDC.ScanInstance.getPreprocessor()
- .getHeaderSearchInfo()
- .getModuleMap()
- .getModuleMapFileForUniquing(M);
+ ModuleMap &ModMapInfo =
+ MDC.ScanInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
+
+ OptionalFileEntryRef ModuleMap = ModMapInfo.getModuleMapFileForUniquing(M);
if (ModuleMap) {
- StringRef Path = ModuleMap->tryGetRealPathName();
- if (Path.empty())
- Path = ModuleMap->getName();
+ SmallString<128> Path = ModuleMap->getNameAsRequested();
+ ModMapInfo.canonicalizeModuleMapPath(Path);
MD.ClangModuleMapFile = std::string(Path);
}
@@ -288,70 +429,37 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
// handle it like normal. With explicitly built modules we don't need
// to play VFS tricks, so replace it with the correct module map.
if (IF.getFile()->getName().endswith("__inferred_module.map")) {
- MD.FileDeps.insert(ModuleMap->getName());
+ MDC.addFileDep(MD, ModuleMap->getName());
return;
}
- MD.FileDeps.insert(IF.getFile()->getName());
+ MDC.addFileDep(MD, IF.getFile()->getName());
});
- // We usually don't need to list the module map files of our dependencies when
- // building a module explicitly: their semantics will be deserialized from PCM
- // files.
- //
- // However, some module maps loaded implicitly during the dependency scan can
- // describe anti-dependencies. That happens when this module, let's call it
- // M1, is marked as '[no_undeclared_includes]' and tries to access a header
- // "M2/M2.h" from another module, M2, but doesn't have a 'use M2;'
- // declaration. The explicit build needs the module map for M2 so that it
- // knows that textually including "M2/M2.h" is not allowed.
- // E.g., '__has_include("M2/M2.h")' should return false, but without M2's
- // module map the explicit build would return true.
- //
- // An alternative approach would be to tell the explicit build what its
- // textual dependencies are, instead of having it re-discover its
- // anti-dependencies. For example, we could create and use an `-ivfs-overlay`
- // with `fall-through: false` that explicitly listed the dependencies.
- // However, that's more complicated to implement and harder to reason about.
- if (M->NoUndeclaredIncludes) {
- // We don't have a good way to determine which module map described the
- // anti-dependency (let alone what's the corresponding top-level module
- // map). We simply specify all the module maps in the order they were loaded
- // during the implicit build during scan.
- // TODO: Resolve this by serializing and only using Module::UndeclaredUses.
- MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps(
- *MF, [&](const FileEntry *FE) {
- if (FE->getName().endswith("__inferred_module.map"))
- return;
- // The top-level modulemap of this module will be the input file. We
- // don't need to specify it as a module map.
- if (FE == ModuleMap)
- return;
- MD.ModuleMapFileDeps.push_back(FE->getName().str());
- });
- }
+ llvm::DenseSet<const Module *> SeenDeps;
+ addAllSubmodulePrebuiltDeps(M, MD, SeenDeps);
+ addAllSubmoduleDeps(M, MD, SeenDeps);
+ addAllAffectingClangModules(M, MD, SeenDeps);
- // Add direct prebuilt module dependencies now, so that we can use them when
- // creating a CompilerInvocation and computing context hash for this
- // ModuleDeps instance.
- llvm::DenseSet<const Module *> SeenModules;
- addAllSubmodulePrebuiltDeps(M, MD, SeenModules);
+ MDC.ScanInstance.getASTReader()->visitTopLevelModuleMaps(
+ *MF, [&](FileEntryRef FE) {
+ if (FE.getNameAsRequested().endswith("__inferred_module.map"))
+ return;
+ MD.ModuleMapFileDeps.emplace_back(FE.getNameAsRequested());
+ });
- MD.BuildInvocation = MDC.makeInvocationForModuleBuildWithoutPaths(
+ CompilerInvocation CI = MDC.makeInvocationForModuleBuildWithoutOutputs(
MD, [&](CompilerInvocation &BuildInvocation) {
if (MDC.OptimizeArgs)
optimizeHeaderSearchOpts(BuildInvocation.getHeaderSearchOpts(),
*MDC.ScanInstance.getASTReader(), *MF);
});
- MD.HadSerializedDiagnostics = !MDC.OriginalInvocation.getDiagnosticOpts()
- .DiagnosticSerializationFile.empty();
- MD.HadDependencyFile =
- !MDC.OriginalInvocation.getDependencyOutputOpts().OutputFile.empty();
- // FIXME: HadSerializedDiagnostics and HadDependencyFile should be included in
- // the context hash since it can affect the command-line.
- MD.ID.ContextHash = MD.BuildInvocation.getModuleHash();
- llvm::DenseSet<const Module *> AddedModules;
- addAllSubmoduleDeps(M, MD, AddedModules);
+ MDC.associateWithContextHash(CI, MD);
+
+ // Finish the compiler invocation. Requires dependencies and the context hash.
+ MDC.addOutputPaths(CI, MD);
+
+ MD.BuildArguments = CI.getCC1CommandLine();
return MD.ID;
}
@@ -406,9 +514,33 @@ void ModuleDepCollectorPP::addModuleDep(
for (const Module *Import : M->Imports) {
if (Import->getTopLevelModule() != M->getTopLevelModule() &&
!MDC.isPrebuiltModule(Import)) {
- ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
- if (AddedModules.insert(Import->getTopLevelModule()).second)
- MD.ClangModuleDeps.push_back(ImportID);
+ if (auto ImportID = handleTopLevelModule(Import->getTopLevelModule()))
+ if (AddedModules.insert(Import->getTopLevelModule()).second)
+ MD.ClangModuleDeps.push_back(*ImportID);
+ }
+ }
+}
+
+void ModuleDepCollectorPP::addAllAffectingClangModules(
+ const Module *M, ModuleDeps &MD,
+ llvm::DenseSet<const Module *> &AddedModules) {
+ addAffectingClangModule(M, MD, AddedModules);
+
+ for (const Module *SubM : M->submodules())
+ addAllAffectingClangModules(SubM, MD, AddedModules);
+}
+
+void ModuleDepCollectorPP::addAffectingClangModule(
+ const Module *M, ModuleDeps &MD,
+ llvm::DenseSet<const Module *> &AddedModules) {
+ for (const Module *Affecting : M->AffectingClangModules) {
+ assert(Affecting == Affecting->getTopLevelModule() &&
+ "Not quite import not top-level module");
+ if (Affecting != M->getTopLevelModule() &&
+ !MDC.isPrebuiltModule(Affecting)) {
+ if (auto ImportID = handleTopLevelModule(Affecting))
+ if (AddedModules.insert(Affecting).second)
+ MD.ClangModuleDeps.push_back(*ImportID);
}
}
}
@@ -416,9 +548,10 @@ void ModuleDepCollectorPP::addModuleDep(
ModuleDepCollector::ModuleDepCollector(
std::unique_ptr<DependencyOutputOptions> Opts,
CompilerInstance &ScanInstance, DependencyConsumer &C,
- CompilerInvocation &&OriginalCI, bool OptimizeArgs)
+ CompilerInvocation OriginalCI, bool OptimizeArgs, bool EagerLoadModules)
: ScanInstance(ScanInstance), Consumer(C), Opts(std::move(Opts)),
- OriginalInvocation(std::move(OriginalCI)), OptimizeArgs(OptimizeArgs) {}
+ OriginalInvocation(std::move(OriginalCI)), OptimizeArgs(OptimizeArgs),
+ EagerLoadModules(EagerLoadModules) {}
void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(*this));
@@ -437,3 +570,26 @@ bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
PrebuiltModuleFileIt->second == M->getASTFile()->getName());
return true;
}
+
+static StringRef makeAbsoluteAndPreferred(CompilerInstance &CI, StringRef Path,
+ SmallVectorImpl<char> &Storage) {
+ if (llvm::sys::path::is_absolute(Path) &&
+ !llvm::sys::path::is_style_windows(llvm::sys::path::Style::native))
+ return Path;
+ Storage.assign(Path.begin(), Path.end());
+ CI.getFileManager().makeAbsolutePath(Storage);
+ llvm::sys::path::make_preferred(Storage);
+ return StringRef(Storage.data(), Storage.size());
+}
+
+void ModuleDepCollector::addFileDep(StringRef Path) {
+ llvm::SmallString<256> Storage;
+ Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage);
+ FileDeps.push_back(std::string(Path));
+}
+
+void ModuleDepCollector::addFileDep(ModuleDeps &MD, StringRef Path) {
+ llvm::SmallString<256> Storage;
+ Path = makeAbsoluteAndPreferred(ScanInstance, Path, Storage);
+ MD.FileDeps.insert(Path);
+}
diff --git a/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.cpp b/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.cpp
index 2f97067f6171..42691d556d98 100644
--- a/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.cpp
+++ b/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.cpp
@@ -161,7 +161,7 @@ CaptureMethods(std::string TypeString, const clang::CXXRecordDecl *ASTClass,
optionally(
isDerivedFrom(cxxRecordDecl(hasName("clang::TypeLoc"))
.bind("typeLocBase"))))),
- returns(asString(TypeString)))
+ returns(hasCanonicalType(asString(TypeString))))
.bind("classMethod")),
*ASTClass, *Result.Context);
diff --git a/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h b/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h
index 05c4f92676e8..5f2b48173f28 100644
--- a/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h
+++ b/clang/lib/Tooling/DumpTool/ASTSrcLocProcessor.h
@@ -35,7 +35,7 @@ public:
private:
void run(const ast_matchers::MatchFinder::MatchResult &Result) override;
- llvm::Optional<TraversalKind> getCheckTraversalKind() const override {
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
diff --git a/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp b/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
index 9c825428f2ea..e67ffb037095 100644
--- a/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
+++ b/clang/lib/Tooling/DumpTool/ClangSrcLocDump.cpp
@@ -116,7 +116,7 @@ int main(int argc, const char **argv) {
"ast-api-dump-tool", OFS);
std::unique_ptr<clang::driver::Compilation> Comp(
- Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
+ Driver->BuildCompilation(llvm::ArrayRef(Argv)));
if (!Comp)
return 1;
diff --git a/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp b/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp
index 75d0d50d851f..88d20ba3957d 100644
--- a/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp
+++ b/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp
@@ -60,9 +60,12 @@ private:
if (!SeenRSPFile)
continue;
llvm::BumpPtrAllocator Alloc;
- llvm::StringSaver Saver(Alloc);
- llvm::cl::ExpandResponseFiles(Saver, Tokenizer, Argv, false, false, false,
- llvm::StringRef(Cmd.Directory), *FS);
+ llvm::cl::ExpansionContext ECtx(Alloc, Tokenizer);
+ llvm::Error Err = ECtx.setVFS(FS.get())
+ .setCurrentDir(Cmd.Directory)
+ .expandResponseFiles(Argv);
+ if (Err)
+ llvm::errs() << Err;
// Don't assign directly, Argv aliases CommandLine.
std::vector<std::string> ExpandedArgv(Argv.begin(), Argv.end());
Cmd.CommandLine = std::move(ExpandedArgv);
diff --git a/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp b/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
new file mode 100644
index 000000000000..49d23908d33b
--- /dev/null
+++ b/clang/lib/Tooling/Inclusions/HeaderAnalysis.cpp
@@ -0,0 +1,119 @@
+//===--- HeaderAnalysis.cpp -------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Inclusions/HeaderAnalysis.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/HeaderSearch.h"
+
+namespace clang::tooling {
+namespace {
+
+// Is Line an #if or #ifdef directive?
+// FIXME: This makes headers with #ifdef LINUX/WINDOWS/MACOS marked as non
+// self-contained and is probably not what we want.
+bool isIf(llvm::StringRef Line) {
+ Line = Line.ltrim();
+ if (!Line.consume_front("#"))
+ return false;
+ Line = Line.ltrim();
+ return Line.startswith("if");
+}
+
+// Is Line an #error directive mentioning includes?
+bool isErrorAboutInclude(llvm::StringRef Line) {
+ Line = Line.ltrim();
+ if (!Line.consume_front("#"))
+ return false;
+ Line = Line.ltrim();
+ if (!Line.startswith("error"))
+ return false;
+ return Line.contains_insensitive(
+ "includ"); // Matches "include" or "including".
+}
+
+// Heuristically headers that only want to be included via an umbrella.
+bool isDontIncludeMeHeader(StringRef Content) {
+ llvm::StringRef Line;
+ // Only sniff up to 100 lines or 10KB.
+ Content = Content.take_front(100 * 100);
+ for (unsigned I = 0; I < 100 && !Content.empty(); ++I) {
+ std::tie(Line, Content) = Content.split('\n');
+ if (isIf(Line) && isErrorAboutInclude(Content.split('\n').first))
+ return true;
+ }
+ return false;
+}
+
+bool isImportLine(llvm::StringRef Line) {
+ Line = Line.ltrim();
+ if (!Line.consume_front("#"))
+ return false;
+ Line = Line.ltrim();
+ return Line.startswith("import");
+}
+
+llvm::StringRef getFileContents(const FileEntry *FE, const SourceManager &SM) {
+ return const_cast<SourceManager &>(SM)
+ .getMemoryBufferForFileOrNone(FE)
+ .value_or(llvm::MemoryBufferRef())
+ .getBuffer();
+}
+
+} // namespace
+
+bool isSelfContainedHeader(const FileEntry *FE, const SourceManager &SM,
+ HeaderSearch &HeaderInfo) {
+ assert(FE);
+ if (!HeaderInfo.isFileMultipleIncludeGuarded(FE) &&
+ !HeaderInfo.hasFileBeenImported(FE) &&
+ // Any header that contains #imports is supposed to be #import'd so no
+ // need to check for anything but the main-file.
+ (SM.getFileEntryForID(SM.getMainFileID()) != FE ||
+ !codeContainsImports(getFileContents(FE, SM))))
+ return false;
+ // This pattern indicates that a header can't be used without
+ // particular preprocessor state, usually set up by another header.
+ return !isDontIncludeMeHeader(getFileContents(FE, SM));
+}
+
+bool codeContainsImports(llvm::StringRef Code) {
+ // Only sniff up to 100 lines or 10KB.
+ Code = Code.take_front(100 * 100);
+ llvm::StringRef Line;
+ for (unsigned I = 0; I < 100 && !Code.empty(); ++I) {
+ std::tie(Line, Code) = Code.split('\n');
+ if (isImportLine(Line))
+ return true;
+ }
+ return false;
+}
+
+std::optional<StringRef> parseIWYUPragma(const char *Text) {
+ // Skip the comment start, // or /*.
+ if (Text[0] != '/' || (Text[1] != '/' && Text[1] != '*'))
+ return std::nullopt;
+ bool BlockComment = Text[1] == '*';
+ Text += 2;
+
+ // Per spec, direcitves are whitespace- and case-sensitive.
+ constexpr llvm::StringLiteral IWYUPragma = " IWYU pragma: ";
+ if (strncmp(Text, IWYUPragma.data(), IWYUPragma.size()))
+ return std::nullopt;
+ Text += IWYUPragma.size();
+ const char *End = Text;
+ while (*End != 0 && *End != '\n')
+ ++End;
+ StringRef Rest(Text, End - Text);
+ // Strip off whitespace and comment markers to avoid confusion. This isn't
+ // fully-compatible with IWYU, which splits into whitespace-delimited tokens.
+ if (BlockComment)
+ Rest.consume_back("*/");
+ return Rest.trim();
+}
+
+} // namespace clang::tooling
diff --git a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
index fc8773e60c58..172eff1bf6ab 100644
--- a/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
+++ b/clang/lib/Tooling/Inclusions/HeaderIncludes.cpp
@@ -10,9 +10,9 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
+#include <optional>
namespace clang {
namespace tooling {
@@ -58,7 +58,7 @@ unsigned getOffsetAfterTokenSequence(
// (second) raw_identifier name is checked.
bool checkAndConsumeDirectiveWithName(
Lexer &Lex, StringRef Name, Token &Tok,
- llvm::Optional<StringRef> RawIDName = llvm::None) {
+ std::optional<StringRef> RawIDName = std::nullopt) {
bool Matched = Tok.is(tok::hash) && !Lex.LexFromRawLexer(Tok) &&
Tok.is(tok::raw_identifier) &&
Tok.getRawIdentifier() == Name && !Lex.LexFromRawLexer(Tok) &&
@@ -266,6 +266,8 @@ bool IncludeCategoryManager::isMainHeader(StringRef IncludeName) const {
return false;
}
+const llvm::Regex HeaderIncludes::IncludeRegex(IncludeRegexPattern);
+
HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
const IncludeStyle &Style)
: FileName(FileName), Code(Code), FirstIncludeOffset(-1),
@@ -274,8 +276,7 @@ HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
MaxInsertOffset(MinInsertOffset +
getMaxHeaderInsertionOffset(
FileName, Code.drop_front(MinInsertOffset), Style)),
- Categories(Style, FileName),
- IncludeRegex(llvm::Regex(IncludeRegexPattern)) {
+ Categories(Style, FileName) {
// Add 0 for main header and INT_MAX for headers that are not in any
// category.
Priorities = {0, INT_MAX};
@@ -295,7 +296,9 @@ HeaderIncludes::HeaderIncludes(StringRef FileName, StringRef Code,
addExistingInclude(
Include(Matches[2],
tooling::Range(
- Offset, std::min(Line.size() + 1, Code.size() - Offset))),
+ Offset, std::min(Line.size() + 1, Code.size() - Offset)),
+ Matches[1] == "import" ? tooling::IncludeDirective::Import
+ : tooling::IncludeDirective::Include),
NextLineOffset);
}
Offset = NextLineOffset;
@@ -340,18 +343,21 @@ void HeaderIncludes::addExistingInclude(Include IncludeToAdd,
}
}
-llvm::Optional<tooling::Replacement>
-HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const {
+std::optional<tooling::Replacement>
+HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled,
+ IncludeDirective Directive) const {
assert(IncludeName == trimInclude(IncludeName));
// If a <header> ("header") already exists in code, "header" (<header>) with
- // different quotation will still be inserted.
+ // different quotation and/or directive will still be inserted.
// FIXME: figure out if this is the best behavior.
auto It = ExistingIncludes.find(IncludeName);
- if (It != ExistingIncludes.end())
+ if (It != ExistingIncludes.end()) {
for (const auto &Inc : It->second)
- if ((IsAngled && StringRef(Inc.Name).startswith("<")) ||
- (!IsAngled && StringRef(Inc.Name).startswith("\"")))
- return llvm::None;
+ if (Inc.Directive == Directive &&
+ ((IsAngled && StringRef(Inc.Name).startswith("<")) ||
+ (!IsAngled && StringRef(Inc.Name).startswith("\""))))
+ return std::nullopt;
+ }
std::string Quoted =
std::string(llvm::formatv(IsAngled ? "<{0}>" : "\"{0}\"", IncludeName));
StringRef QuotedName = Quoted;
@@ -370,8 +376,10 @@ HeaderIncludes::insert(llvm::StringRef IncludeName, bool IsAngled) const {
}
}
assert(InsertOffset <= Code.size());
+ llvm::StringRef DirectiveSpelling =
+ Directive == IncludeDirective::Include ? "include" : "import";
std::string NewInclude =
- std::string(llvm::formatv("#include {0}\n", QuotedName));
+ llvm::formatv("#{0} {1}\n", DirectiveSpelling, QuotedName);
// When inserting headers at end of the code, also append '\n' to the code
// if it does not end with '\n'.
// FIXME: when inserting multiple #includes at the end of code, only one
diff --git a/clang/lib/Tooling/Inclusions/StandardLibrary.cpp b/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp
index 8fb0c8474e64..9e5e421fdebc 100644
--- a/clang/lib/Tooling/Inclusions/StandardLibrary.cpp
+++ b/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Tooling/Inclusions/StandardLibrary.h"
-#include "llvm/ADT/Optional.h"
+#include "clang/AST/Decl.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
@@ -76,17 +76,17 @@ static void ensureInitialized() {
(void)Dummy;
}
-llvm::Optional<Header> Header::named(llvm::StringRef Name) {
+std::optional<Header> Header::named(llvm::StringRef Name) {
ensureInitialized();
auto It = HeaderIDs->find(Name);
if (It == HeaderIDs->end())
- return llvm::None;
+ return std::nullopt;
return Header(It->second);
}
llvm::StringRef Header::name() const { return HeaderNames[ID]; }
llvm::StringRef Symbol::scope() const { return SymbolNames[ID].first; }
llvm::StringRef Symbol::name() const { return SymbolNames[ID].second; }
-llvm::Optional<Symbol> Symbol::named(llvm::StringRef Scope,
+std::optional<Symbol> Symbol::named(llvm::StringRef Scope,
llvm::StringRef Name) {
ensureInitialized();
if (NSSymbolMap *NSSymbols = NamespaceSymbols->lookup(Scope)) {
@@ -94,7 +94,7 @@ llvm::Optional<Symbol> Symbol::named(llvm::StringRef Scope,
if (It != NSSymbols->end())
return Symbol(It->second);
}
- return llvm::None;
+ return std::nullopt;
}
Header Symbol::header() const { return Header(SymbolHeaderIDs[ID]); }
llvm::SmallVector<Header> Symbol::headers() const {
@@ -123,7 +123,7 @@ NSSymbolMap *Recognizer::namespaceSymbols(const NamespaceDecl *D) {
return Result;
}
-llvm::Optional<Symbol> Recognizer::operator()(const Decl *D) {
+std::optional<Symbol> Recognizer::operator()(const Decl *D) {
// If D is std::vector::iterator, `vector` is the outer symbol to look up.
// We keep all the candidate DCs as some may turn out to be anon enums.
// Do this resolution lazily as we may turn out not to have a std namespace.
@@ -136,7 +136,7 @@ llvm::Optional<Symbol> Recognizer::operator()(const Decl *D) {
}
NSSymbolMap *Symbols = namespaceSymbols(cast_or_null<NamespaceDecl>(DC));
if (!Symbols)
- return llvm::None;
+ return std::nullopt;
llvm::StringRef Name = [&]() -> llvm::StringRef {
for (const auto *SymDC : llvm::reverse(IntermediateDecl)) {
@@ -152,11 +152,11 @@ llvm::Optional<Symbol> Recognizer::operator()(const Decl *D) {
return "";
}();
if (Name.empty())
- return llvm::None;
+ return std::nullopt;
auto It = Symbols->find(Name);
if (It == Symbols->end())
- return llvm::None;
+ return std::nullopt;
return Symbol(It->second);
}
diff --git a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
index 0143b5f8df6b..ff9cb4b62073 100644
--- a/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
+++ b/clang/lib/Tooling/InterpolatingCompilationDatabase.cpp
@@ -49,9 +49,7 @@
#include "clang/Tooling/CompilationDatabase.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Support/Debug.h"
@@ -59,6 +57,7 @@
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
+#include <optional>
namespace clang {
namespace tooling {
@@ -129,7 +128,7 @@ struct TransferableCommand {
// Flags that should not apply to all files are stripped from CommandLine.
CompileCommand Cmd;
// Language detected from -x or the filename. Never TY_INVALID.
- Optional<types::ID> Type;
+ std::optional<types::ID> Type;
// Standard specified by -std.
LangStandard::Kind Std = LangStandard::lang_unspecified;
// Whether the command line is for the cl-compatible driver.
@@ -148,7 +147,7 @@ struct TransferableCommand {
TmpArgv.push_back(S.c_str());
ClangCLMode = !TmpArgv.empty() &&
driver::IsClangCL(driver::getDriverMode(
- TmpArgv.front(), llvm::makeArrayRef(TmpArgv).slice(1)));
+ TmpArgv.front(), llvm::ArrayRef(TmpArgv).slice(1)));
ArgList = {TmpArgv.begin(), TmpArgv.end()};
}
@@ -165,8 +164,8 @@ struct TransferableCommand {
const unsigned OldPos = Pos;
std::unique_ptr<llvm::opt::Arg> Arg(OptTable.ParseOneArg(
ArgList, Pos,
- /* Include */ ClangCLMode ? CoreOption | CLOption : 0,
- /* Exclude */ ClangCLMode ? 0 : CLOption));
+ /* Include */ ClangCLMode ? CoreOption | CLOption | CLDXCOption : 0,
+ /* Exclude */ ClangCLMode ? 0 : CLOption | CLDXCOption));
if (!Arg)
continue;
@@ -208,7 +207,7 @@ struct TransferableCommand {
Type = foldType(*Type);
// The contract is to store None instead of TY_INVALID.
if (Type == types::TY_INVALID)
- Type = llvm::None;
+ Type = std::nullopt;
}
// Produce a CompileCommand for \p filename, based on this one.
@@ -280,7 +279,7 @@ private:
}
// Try to interpret the argument as a type specifier, e.g. '-x'.
- Optional<types::ID> tryParseTypeArg(const llvm::opt::Arg &Arg) {
+ std::optional<types::ID> tryParseTypeArg(const llvm::opt::Arg &Arg) {
const llvm::opt::Option &Opt = Arg.getOption();
using namespace driver::options;
if (ClangCLMode) {
@@ -292,15 +291,15 @@ private:
if (Opt.matches(driver::options::OPT_x))
return types::lookupTypeForTypeSpecifier(Arg.getValue());
}
- return None;
+ return std::nullopt;
}
// Try to interpret the argument as '-std='.
- Optional<LangStandard::Kind> tryParseStdArg(const llvm::opt::Arg &Arg) {
+ std::optional<LangStandard::Kind> tryParseStdArg(const llvm::opt::Arg &Arg) {
using namespace driver::options;
if (Arg.getOption().matches(ClangCLMode ? OPT__SLASH_std : OPT_std_EQ))
return LangStandard::getLangKind(Arg.getValue());
- return None;
+ return std::nullopt;
}
};
diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp
index 5e18d7a576c0..cdda587d0925 100644
--- a/clang/lib/Tooling/JSONCompilationDatabase.cpp
+++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp
@@ -15,7 +15,6 @@
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/CompilationDatabasePluginRegistry.h"
#include "clang/Tooling/Tooling.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
@@ -34,6 +33,7 @@
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <memory>
+#include <optional>
#include <string>
#include <system_error>
#include <tuple>
@@ -349,7 +349,7 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
return false;
}
llvm::yaml::ScalarNode *Directory = nullptr;
- llvm::Optional<std::vector<llvm::yaml::ScalarNode *>> Command;
+ std::optional<std::vector<llvm::yaml::ScalarNode *>> Command;
llvm::yaml::ScalarNode *File = nullptr;
llvm::yaml::ScalarNode *Output = nullptr;
for (auto& NextKeyValue : *Object) {
@@ -419,14 +419,13 @@ bool JSONCompilationDatabase::parse(std::string &ErrorMessage) {
SmallString<128> NativeFilePath;
if (llvm::sys::path::is_relative(FileName)) {
SmallString<8> DirectoryStorage;
- SmallString<128> AbsolutePath(
- Directory->getValue(DirectoryStorage));
+ SmallString<128> AbsolutePath(Directory->getValue(DirectoryStorage));
llvm::sys::path::append(AbsolutePath, FileName);
- llvm::sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/ true);
llvm::sys::path::native(AbsolutePath, NativeFilePath);
} else {
llvm::sys::path::native(FileName, NativeFilePath);
}
+ llvm::sys::path::remove_dots(NativeFilePath, /*remove_dot_dot=*/true);
auto Cmd = CompileCommandRef(Directory, File, *Command, Output);
IndexByFile[NativeFilePath].push_back(Cmd);
AllCommands.push_back(Cmd);
diff --git a/clang/lib/Tooling/Refactoring/ASTSelection.cpp b/clang/lib/Tooling/Refactoring/ASTSelection.cpp
index 9485c8bc04ad..058574d8ec1a 100644
--- a/clang/lib/Tooling/Refactoring/ASTSelection.cpp
+++ b/clang/lib/Tooling/Refactoring/ASTSelection.cpp
@@ -10,6 +10,7 @@
#include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h"
#include "clang/Lex/Lexer.h"
#include "llvm/Support/SaveAndRestore.h"
+#include <optional>
using namespace clang;
using namespace tooling;
@@ -50,12 +51,12 @@ public:
SourceSelectionKind::None));
}
- Optional<SelectedASTNode> getSelectedASTNode() {
+ std::optional<SelectedASTNode> getSelectedASTNode() {
assert(SelectionStack.size() == 1 && "stack was not popped");
SelectedASTNode Result = std::move(SelectionStack.back());
SelectionStack.pop_back();
if (Result.Children.empty())
- return None;
+ return std::nullopt;
return std::move(Result);
}
@@ -63,14 +64,14 @@ public:
// Avoid traversing the semantic expressions. They should be handled by
// looking through the appropriate opaque expressions in order to build
// a meaningful selection tree.
- llvm::SaveAndRestore<bool> LookThrough(LookThroughOpaqueValueExprs, true);
+ llvm::SaveAndRestore LookThrough(LookThroughOpaqueValueExprs, true);
return TraverseStmt(E->getSyntacticForm());
}
bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) {
if (!LookThroughOpaqueValueExprs)
return true;
- llvm::SaveAndRestore<bool> LookThrough(LookThroughOpaqueValueExprs, false);
+ llvm::SaveAndRestore LookThrough(LookThroughOpaqueValueExprs, false);
return TraverseStmt(E->getSourceExpr());
}
@@ -178,7 +179,7 @@ private:
} // end anonymous namespace
-Optional<SelectedASTNode>
+std::optional<SelectedASTNode>
clang::tooling::findSelectedASTNodes(const ASTContext &Context,
SourceRange SelectionRange) {
assert(SelectionRange.isValid() &&
@@ -375,22 +376,22 @@ static void findDeepestWithKind(
findDeepestWithKind(ASTSelection, MatchingNodes, Kind, ParentStack);
}
-Optional<CodeRangeASTSelection>
+std::optional<CodeRangeASTSelection>
CodeRangeASTSelection::create(SourceRange SelectionRange,
const SelectedASTNode &ASTSelection) {
// Code range is selected when the selection range is not empty.
if (SelectionRange.getBegin() == SelectionRange.getEnd())
- return None;
+ return std::nullopt;
llvm::SmallVector<SelectedNodeWithParents, 4> ContainSelection;
findDeepestWithKind(ASTSelection, ContainSelection,
SourceSelectionKind::ContainsSelection);
// We are looking for a selection in one body of code, so let's focus on
// one matching result.
if (ContainSelection.size() != 1)
- return None;
+ return std::nullopt;
SelectedNodeWithParents &Selected = ContainSelection[0];
if (!Selected.Node.get().Node.get<Stmt>())
- return None;
+ return std::nullopt;
const Stmt *CodeRangeStmt = Selected.Node.get().Node.get<Stmt>();
if (!isa<CompoundStmt>(CodeRangeStmt)) {
Selected.canonicalize();
diff --git a/clang/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp b/clang/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
index 70a4df07ea67..0e052bb19768 100644
--- a/clang/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
+++ b/clang/lib/Tooling/Refactoring/ASTSelectionRequirements.cpp
@@ -8,6 +8,7 @@
#include "clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h"
#include "clang/AST/Attr.h"
+#include <optional>
using namespace clang;
using namespace tooling;
@@ -20,7 +21,7 @@ ASTSelectionRequirement::evaluate(RefactoringRuleContext &Context) const {
if (!Range)
return Range.takeError();
- Optional<SelectedASTNode> Selection =
+ std::optional<SelectedASTNode> Selection =
findSelectedASTNodes(Context.getASTContext(), *Range);
if (!Selection)
return Context.createDiagnosticError(
@@ -37,8 +38,9 @@ Expected<CodeRangeASTSelection> CodeRangeASTSelectionRequirement::evaluate(
return ASTSelection.takeError();
std::unique_ptr<SelectedASTNode> StoredSelection =
std::make_unique<SelectedASTNode>(std::move(*ASTSelection));
- Optional<CodeRangeASTSelection> CodeRange = CodeRangeASTSelection::create(
- Context.getSelectionRange(), *StoredSelection);
+ std::optional<CodeRangeASTSelection> CodeRange =
+ CodeRangeASTSelection::create(Context.getSelectionRange(),
+ *StoredSelection);
if (!CodeRange)
return Context.createDiagnosticError(
Context.getSelectionRange().getBegin(),
diff --git a/clang/lib/Tooling/Refactoring/Extract/Extract.cpp b/clang/lib/Tooling/Refactoring/Extract/Extract.cpp
index 402b56109052..d437f4c21f47 100644
--- a/clang/lib/Tooling/Refactoring/Extract/Extract.cpp
+++ b/clang/lib/Tooling/Refactoring/Extract/Extract.cpp
@@ -19,6 +19,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/Refactoring/Extract/SourceExtraction.h"
+#include <optional>
namespace clang {
namespace tooling {
@@ -68,7 +69,7 @@ const RefactoringDescriptor &ExtractFunction::describe() {
Expected<ExtractFunction>
ExtractFunction::initiate(RefactoringRuleContext &Context,
CodeRangeASTSelection Code,
- Optional<std::string> DeclName) {
+ std::optional<std::string> DeclName) {
// We would like to extract code out of functions/methods/blocks.
// Prohibit extraction from things like global variable / field
// initializers and other top-level expressions.
diff --git a/clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp b/clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
index 5d57ecf90a96..5e69fb805150 100644
--- a/clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
+++ b/clang/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/StmtObjC.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
+#include <optional>
using namespace clang;
@@ -100,7 +101,7 @@ ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange,
/// Other statements should generally have a trailing ';'. We can try to find
/// it and move it together it with the extracted code.
- Optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts);
+ std::optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts);
if (NextToken && NextToken->is(tok::semi) &&
areOnSameLine(NextToken->getLocation(), End, SM)) {
ExtractedRange.setEnd(NextToken->getLocation());
diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp
index 041cc4f939d9..cd0261989495 100644
--- a/clang/lib/Tooling/Syntax/BuildTree.cpp
+++ b/clang/lib/Tooling/Syntax/BuildTree.cpp
@@ -469,7 +469,7 @@ public:
assert(Last.isValid());
assert(First == Last ||
TBTM.sourceManager().isBeforeInTranslationUnit(First, Last));
- return llvm::makeArrayRef(findToken(First), std::next(findToken(Last)));
+ return llvm::ArrayRef(findToken(First), std::next(findToken(Last)));
}
ArrayRef<syntax::Token>
@@ -552,7 +552,7 @@ private:
assert(Tokens.back().kind() != tok::eof);
// We never consume 'eof', so looking at the next token is ok.
if (Tokens.back().kind() != tok::semi && Tokens.end()->kind() == tok::semi)
- return llvm::makeArrayRef(Tokens.begin(), Tokens.end() + 1);
+ return llvm::ArrayRef(Tokens.begin(), Tokens.end() + 1);
return Tokens;
}
@@ -768,7 +768,7 @@ public:
// Build TemplateDeclaration nodes if we had template parameters.
auto ConsumeTemplateParameters = [&](const TemplateParameterList &L) {
const auto *TemplateKW = Builder.findToken(L.getTemplateLoc());
- auto R = llvm::makeArrayRef(TemplateKW, DeclarationRange.end());
+ auto R = llvm::ArrayRef(TemplateKW, DeclarationRange.end());
Result =
foldTemplateDeclaration(R, TemplateKW, DeclarationRange, nullptr);
DeclarationRange = R;
@@ -1639,7 +1639,7 @@ private:
auto Return = Builder.getRange(ReturnedType.getSourceRange());
const auto *Arrow = Return.begin() - 1;
assert(Arrow->kind() == tok::arrow);
- auto Tokens = llvm::makeArrayRef(Arrow, Return.end());
+ auto Tokens = llvm::ArrayRef(Arrow, Return.end());
Builder.markChildToken(Arrow, syntax::NodeRole::ArrowToken);
if (ReturnDeclarator)
Builder.markChild(ReturnDeclarator, syntax::NodeRole::Declarator);
diff --git a/clang/lib/Tooling/Syntax/ComputeReplacements.cpp b/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
index 08e09e4ebdbf..fe9a9df73cb3 100644
--- a/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
+++ b/clang/lib/Tooling/Syntax/ComputeReplacements.cpp
@@ -31,7 +31,7 @@ void enumerateTokenSpans(const syntax::Tree *Root,
process(Root);
// Report the last span to the user.
if (SpanBegin)
- Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
+ Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
}
private:
@@ -52,7 +52,7 @@ void enumerateTokenSpans(const syntax::Tree *Root,
}
// Report the current span to the user.
if (SpanBegin)
- Callback(llvm::makeArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
+ Callback(llvm::ArrayRef(SpanBegin, SpanEnd), SpanIsOriginal);
// Start recording a new span.
SpanBegin = STM.getToken(L->getTokenKey());
SpanEnd = SpanBegin + 1;
@@ -118,17 +118,17 @@ syntax::computeReplacements(const TokenBufferTokenManager &TBTM,
// We are looking at a span of original tokens.
if (NextOriginal != Tokens.begin()) {
// There is a gap, record a replacement or deletion.
- emitReplacement(llvm::makeArrayRef(NextOriginal, Tokens.begin()));
+ emitReplacement(llvm::ArrayRef(NextOriginal, Tokens.begin()));
} else {
// No gap, but we may have pending insertions. Emit them now.
- emitReplacement(llvm::makeArrayRef(NextOriginal, /*Length=*/0));
+ emitReplacement(llvm::ArrayRef(NextOriginal, /*Length=*/(size_t)0));
}
NextOriginal = Tokens.end();
});
// We might have pending replacements at the end of file. If so, emit them.
- emitReplacement(llvm::makeArrayRef(
- NextOriginal, Buffer.expandedTokens().drop_back().end()));
+ emitReplacement(
+ llvm::ArrayRef(NextOriginal, Buffer.expandedTokens().drop_back().end()));
return Replacements;
}
diff --git a/clang/lib/Tooling/Syntax/Mutations.cpp b/clang/lib/Tooling/Syntax/Mutations.cpp
index 824f1942532d..0f04acea3d04 100644
--- a/clang/lib/Tooling/Syntax/Mutations.cpp
+++ b/clang/lib/Tooling/Syntax/Mutations.cpp
@@ -15,7 +15,6 @@
#include "clang/Tooling/Syntax/Tokens.h"
#include "clang/Tooling/Syntax/Tree.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include <cassert>
diff --git a/clang/lib/Tooling/Syntax/Tokens.cpp b/clang/lib/Tooling/Syntax/Tokens.cpp
index e2014f965c90..b13dc9ef4aee 100644
--- a/clang/lib/Tooling/Syntax/Tokens.cpp
+++ b/clang/lib/Tooling/Syntax/Tokens.cpp
@@ -18,8 +18,6 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/Token.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
@@ -28,6 +26,7 @@
#include <algorithm>
#include <cassert>
#include <iterator>
+#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -55,45 +54,140 @@ getTokensCovering(llvm::ArrayRef<syntax::Token> Toks, SourceRange R,
return {Begin, End};
}
-// Finds the smallest expansion range that contains expanded tokens First and
-// Last, e.g.:
+// Finds the range within FID corresponding to expanded tokens [First, Last].
+// Prev precedes First and Next follows Last, these must *not* be included.
+// If no range satisfies the criteria, returns an invalid range.
+//
// #define ID(x) x
// ID(ID(ID(a1) a2))
// ~~ -> a1
// ~~ -> a2
// ~~~~~~~~~ -> a1 a2
-SourceRange findCommonRangeForMacroArgs(const syntax::Token &First,
- const syntax::Token &Last,
- const SourceManager &SM) {
- SourceRange Res;
- auto FirstLoc = First.location(), LastLoc = Last.location();
- // Keep traversing up the spelling chain as longs as tokens are part of the
- // same expansion.
- while (!FirstLoc.isFileID() && !LastLoc.isFileID()) {
- auto ExpInfoFirst = SM.getSLocEntry(SM.getFileID(FirstLoc)).getExpansion();
- auto ExpInfoLast = SM.getSLocEntry(SM.getFileID(LastLoc)).getExpansion();
- // Stop if expansions have diverged.
- if (ExpInfoFirst.getExpansionLocStart() !=
- ExpInfoLast.getExpansionLocStart())
+SourceRange spelledForExpandedSlow(SourceLocation First, SourceLocation Last,
+ SourceLocation Prev, SourceLocation Next,
+ FileID TargetFile,
+ const SourceManager &SM) {
+ // There are two main parts to this algorithm:
+ // - identifying which spelled range covers the expanded tokens
+ // - validating that this range doesn't cover any extra tokens (First/Last)
+ //
+ // We do these in order. However as we transform the expanded range into the
+ // spelled one, we adjust First/Last so the validation remains simple.
+
+ assert(SM.getSLocEntry(TargetFile).isFile());
+ // In most cases, to select First and Last we must return their expansion
+ // range, i.e. the whole of any macros they are included in.
+ //
+ // When First and Last are part of the *same macro arg* of a macro written
+ // in TargetFile, we that slice of the arg, i.e. their spelling range.
+ //
+ // Unwrap such macro calls. If the target file has A(B(C)), the
+ // SourceLocation stack of a token inside C shows us the expansion of A first,
+ // then B, then any macros inside C's body, then C itself.
+ // (This is the reverse of the order the PP applies the expansions in).
+ while (First.isMacroID() && Last.isMacroID()) {
+ auto DecFirst = SM.getDecomposedLoc(First);
+ auto DecLast = SM.getDecomposedLoc(Last);
+ auto &ExpFirst = SM.getSLocEntry(DecFirst.first).getExpansion();
+ auto &ExpLast = SM.getSLocEntry(DecLast.first).getExpansion();
+
+ if (!ExpFirst.isMacroArgExpansion() || !ExpLast.isMacroArgExpansion())
+ break;
+ // Locations are in the same macro arg if they expand to the same place.
+ // (They may still have different FileIDs - an arg can have >1 chunks!)
+ if (ExpFirst.getExpansionLocStart() != ExpLast.getExpansionLocStart())
break;
- // Do not continue into macro bodies.
- if (!ExpInfoFirst.isMacroArgExpansion() ||
- !ExpInfoLast.isMacroArgExpansion())
+ // Careful, given:
+ // #define HIDE ID(ID(a))
+ // ID(ID(HIDE))
+ // The token `a` is wrapped in 4 arg-expansions, we only want to unwrap 2.
+ // We distinguish them by whether the macro expands into the target file.
+ // Fortunately, the target file ones will always appear first.
+ auto &ExpMacro =
+ SM.getSLocEntry(SM.getFileID(ExpFirst.getExpansionLocStart()))
+ .getExpansion();
+ if (ExpMacro.getExpansionLocStart().isMacroID())
break;
- FirstLoc = SM.getImmediateSpellingLoc(FirstLoc);
- LastLoc = SM.getImmediateSpellingLoc(LastLoc);
- // Update the result afterwards, as we want the tokens that triggered the
- // expansion.
- Res = {FirstLoc, LastLoc};
+ // Replace each endpoint with its spelling inside the macro arg.
+ // (This is getImmediateSpellingLoc without repeating lookups).
+ First = ExpFirst.getSpellingLoc().getLocWithOffset(DecFirst.second);
+ Last = ExpLast.getSpellingLoc().getLocWithOffset(DecLast.second);
+
+ // Now: how do we adjust the previous/next bounds? Three cases:
+ // A) If they are also part of the same macro arg, we translate them too.
+ // This will ensure that we don't select any macros nested within the
+ // macro arg that cover extra tokens. Critical case:
+ // #define ID(X) X
+ // ID(prev target) // selecting 'target' succeeds
+ // #define LARGE ID(prev target)
+ // LARGE // selecting 'target' fails.
+ // B) They are not in the macro at all, then their expansion range is a
+ // sibling to it, and we can safely substitute that.
+ // #define PREV prev
+ // #define ID(X) X
+ // PREV ID(target) // selecting 'target' succeeds.
+ // #define LARGE PREV ID(target)
+ // LARGE // selecting 'target' fails.
+ // C) They are in a different arg of this macro, or the macro body.
+ // Now selecting the whole macro arg is fine, but the whole macro is not.
+ // Model this by setting using the edge of the macro call as the bound.
+ // #define ID2(X, Y) X Y
+ // ID2(prev, target) // selecting 'target' succeeds
+ // #define LARGE ID2(prev, target)
+ // LARGE // selecting 'target' fails
+ auto AdjustBound = [&](SourceLocation &Bound) {
+ if (Bound.isInvalid() || !Bound.isMacroID()) // Non-macro must be case B.
+ return;
+ auto DecBound = SM.getDecomposedLoc(Bound);
+ auto &ExpBound = SM.getSLocEntry(DecBound.first).getExpansion();
+ if (ExpBound.isMacroArgExpansion() &&
+ ExpBound.getExpansionLocStart() == ExpFirst.getExpansionLocStart()) {
+ // Case A: translate to (spelling) loc within the macro arg.
+ Bound = ExpBound.getSpellingLoc().getLocWithOffset(DecBound.second);
+ return;
+ }
+ while (Bound.isMacroID()) {
+ SourceRange Exp = SM.getImmediateExpansionRange(Bound).getAsRange();
+ if (Exp.getBegin() == ExpMacro.getExpansionLocStart()) {
+ // Case B: bounds become the macro call itself.
+ Bound = (&Bound == &Prev) ? Exp.getBegin() : Exp.getEnd();
+ return;
+ }
+ // Either case C, or expansion location will later find case B.
+ // We choose the upper bound for Prev and the lower one for Next:
+ // ID(prev) target ID(next)
+ // ^ ^
+ // new-prev new-next
+ Bound = (&Bound == &Prev) ? Exp.getEnd() : Exp.getBegin();
+ }
+ };
+ AdjustBound(Prev);
+ AdjustBound(Next);
}
- // Normally mapping back to expansion location here only changes FileID, as
- // we've already found some tokens expanded from the same macro argument, and
- // they should map to a consecutive subset of spelled tokens. Unfortunately
- // SourceManager::isBeforeInTranslationUnit discriminates sourcelocations
- // based on their FileID in addition to offsets. So even though we are
- // referring to same tokens, SourceManager might tell us that one is before
- // the other if they've got different FileIDs.
- return SM.getExpansionRange(CharSourceRange(Res, true)).getAsRange();
+
+ // In all remaining cases we need the full containing macros.
+ // If this overlaps Prev or Next, then no range is possible.
+ SourceRange Candidate =
+ SM.getExpansionRange(SourceRange(First, Last)).getAsRange();
+ auto DecFirst = SM.getDecomposedExpansionLoc(Candidate.getBegin());
+ auto DecLast = SM.getDecomposedLoc(Candidate.getEnd());
+ // Can end up in the wrong file due to bad input or token-pasting shenanigans.
+ if (Candidate.isInvalid() || DecFirst.first != TargetFile || DecLast.first != TargetFile)
+ return SourceRange();
+ // Check bounds, which may still be inside macros.
+ if (Prev.isValid()) {
+ auto Dec = SM.getDecomposedLoc(SM.getExpansionRange(Prev).getBegin());
+ if (Dec.first != DecFirst.first || Dec.second >= DecFirst.second)
+ return SourceRange();
+ }
+ if (Next.isValid()) {
+ auto Dec = SM.getDecomposedLoc(SM.getExpansionRange(Next).getEnd());
+ if (Dec.first != DecLast.first || Dec.second <= DecLast.second)
+ return SourceRange();
+ }
+ // Now we know that Candidate is a file range that covers [First, Last]
+ // without encroaching on {Prev, Next}. Ship it!
+ return Candidate;
}
} // namespace
@@ -331,8 +425,8 @@ TokenBuffer::expandedForSpelled(llvm::ArrayRef<syntax::Token> Spelled) const {
// Avoid returning empty ranges.
if (ExpandedBegin == ExpandedEnd)
return {};
- return {llvm::makeArrayRef(ExpandedTokens.data() + ExpandedBegin,
- ExpandedTokens.data() + ExpandedEnd)};
+ return {llvm::ArrayRef(ExpandedTokens.data() + ExpandedBegin,
+ ExpandedTokens.data() + ExpandedEnd)};
}
llvm::ArrayRef<syntax::Token> TokenBuffer::spelledTokens(FileID FID) const {
@@ -357,57 +451,54 @@ std::string TokenBuffer::Mapping::str() const {
BeginSpelled, EndSpelled, BeginExpanded, EndExpanded));
}
-llvm::Optional<llvm::ArrayRef<syntax::Token>>
+std::optional<llvm::ArrayRef<syntax::Token>>
TokenBuffer::spelledForExpanded(llvm::ArrayRef<syntax::Token> Expanded) const {
// Mapping an empty range is ambiguous in case of empty mappings at either end
// of the range, bail out in that case.
if (Expanded.empty())
- return llvm::None;
-
- const syntax::Token *BeginSpelled;
- const Mapping *BeginMapping;
- std::tie(BeginSpelled, BeginMapping) =
- spelledForExpandedToken(&Expanded.front());
-
- const syntax::Token *LastSpelled;
- const Mapping *LastMapping;
- std::tie(LastSpelled, LastMapping) =
- spelledForExpandedToken(&Expanded.back());
+ return std::nullopt;
+ const syntax::Token *First = &Expanded.front();
+ const syntax::Token *Last = &Expanded.back();
+ auto [FirstSpelled, FirstMapping] = spelledForExpandedToken(First);
+ auto [LastSpelled, LastMapping] = spelledForExpandedToken(Last);
- FileID FID = SourceMgr->getFileID(BeginSpelled->location());
+ FileID FID = SourceMgr->getFileID(FirstSpelled->location());
// FIXME: Handle multi-file changes by trying to map onto a common root.
if (FID != SourceMgr->getFileID(LastSpelled->location()))
- return llvm::None;
+ return std::nullopt;
const MarkedFile &File = Files.find(FID)->second;
- // If both tokens are coming from a macro argument expansion, try and map to
- // smallest part of the macro argument. BeginMapping && LastMapping check is
- // only for performance, they are a prerequisite for Expanded.front() and
- // Expanded.back() being part of a macro arg expansion.
- if (BeginMapping && LastMapping &&
- SourceMgr->isMacroArgExpansion(Expanded.front().location()) &&
- SourceMgr->isMacroArgExpansion(Expanded.back().location())) {
- auto CommonRange = findCommonRangeForMacroArgs(Expanded.front(),
- Expanded.back(), *SourceMgr);
- // It might be the case that tokens are arguments of different macro calls,
- // in that case we should continue with the logic below instead of returning
- // an empty range.
- if (CommonRange.isValid())
- return getTokensCovering(File.SpelledTokens, CommonRange, *SourceMgr);
+ // If the range is within one macro argument, the result may be only part of a
+ // Mapping. We must use the general (SourceManager-based) algorithm.
+ if (FirstMapping && FirstMapping == LastMapping &&
+ SourceMgr->isMacroArgExpansion(First->location()) &&
+ SourceMgr->isMacroArgExpansion(Last->location())) {
+ // We use excluded Prev/Next token for bounds checking.
+ SourceLocation Prev = (First == &ExpandedTokens.front())
+ ? SourceLocation()
+ : (First - 1)->location();
+ SourceLocation Next = (Last == &ExpandedTokens.back())
+ ? SourceLocation()
+ : (Last + 1)->location();
+ SourceRange Range = spelledForExpandedSlow(
+ First->location(), Last->location(), Prev, Next, FID, *SourceMgr);
+ if (Range.isInvalid())
+ return std::nullopt;
+ return getTokensCovering(File.SpelledTokens, Range, *SourceMgr);
}
+ // Otherwise, use the fast version based on Mappings.
// Do not allow changes that doesn't cover full expansion.
- unsigned BeginExpanded = Expanded.begin() - ExpandedTokens.data();
- unsigned EndExpanded = Expanded.end() - ExpandedTokens.data();
- if (BeginMapping && BeginExpanded != BeginMapping->BeginExpanded)
- return llvm::None;
- if (LastMapping && LastMapping->EndExpanded != EndExpanded)
- return llvm::None;
- // All is good, return the result.
- return llvm::makeArrayRef(
- BeginMapping ? File.SpelledTokens.data() + BeginMapping->BeginSpelled
- : BeginSpelled,
+ unsigned FirstExpanded = Expanded.begin() - ExpandedTokens.data();
+ unsigned LastExpanded = Expanded.end() - ExpandedTokens.data();
+ if (FirstMapping && FirstExpanded != FirstMapping->BeginExpanded)
+ return std::nullopt;
+ if (LastMapping && LastMapping->EndExpanded != LastExpanded)
+ return std::nullopt;
+ return llvm::ArrayRef(
+ FirstMapping ? File.SpelledTokens.data() + FirstMapping->BeginSpelled
+ : FirstSpelled,
LastMapping ? File.SpelledTokens.data() + LastMapping->EndSpelled
: LastSpelled + 1);
}
@@ -415,10 +506,10 @@ TokenBuffer::spelledForExpanded(llvm::ArrayRef<syntax::Token> Expanded) const {
TokenBuffer::Expansion TokenBuffer::makeExpansion(const MarkedFile &F,
const Mapping &M) const {
Expansion E;
- E.Spelled = llvm::makeArrayRef(F.SpelledTokens.data() + M.BeginSpelled,
- F.SpelledTokens.data() + M.EndSpelled);
- E.Expanded = llvm::makeArrayRef(ExpandedTokens.data() + M.BeginExpanded,
- ExpandedTokens.data() + M.EndExpanded);
+ E.Spelled = llvm::ArrayRef(F.SpelledTokens.data() + M.BeginSpelled,
+ F.SpelledTokens.data() + M.EndSpelled);
+ E.Expanded = llvm::ArrayRef(ExpandedTokens.data() + M.BeginExpanded,
+ ExpandedTokens.data() + M.EndExpanded);
return E;
}
@@ -441,7 +532,7 @@ TokenBuffer::fileForSpelled(llvm::ArrayRef<syntax::Token> Spelled) const {
return File;
}
-llvm::Optional<TokenBuffer::Expansion>
+std::optional<TokenBuffer::Expansion>
TokenBuffer::expansionStartingAt(const syntax::Token *Spelled) const {
assert(Spelled);
const auto &File = fileForSpelled(*Spelled);
@@ -451,7 +542,7 @@ TokenBuffer::expansionStartingAt(const syntax::Token *Spelled) const {
return M.BeginSpelled < SpelledIndex;
});
if (M == File.Mappings.end() || M->BeginSpelled != SpelledIndex)
- return llvm::None;
+ return std::nullopt;
return makeExpansion(File, *M);
}
@@ -483,8 +574,8 @@ syntax::spelledTokensTouching(SourceLocation Loc,
bool AcceptRight = Right != Tokens.end() && Right->location() <= Loc;
bool AcceptLeft =
Right != Tokens.begin() && (Right - 1)->endLocation() >= Loc;
- return llvm::makeArrayRef(Right - (AcceptLeft ? 1 : 0),
- Right + (AcceptRight ? 1 : 0));
+ return llvm::ArrayRef(Right - (AcceptLeft ? 1 : 0),
+ Right + (AcceptRight ? 1 : 0));
}
llvm::ArrayRef<syntax::Token>
@@ -714,7 +805,7 @@ private:
// In the simplest case, skips spelled tokens until finding one that produced
// the NextExpanded token, and creates an empty mapping for them.
// If Drain is provided, skips remaining tokens from that file instead.
- void discard(llvm::Optional<FileID> Drain = llvm::None) {
+ void discard(std::optional<FileID> Drain = std::nullopt) {
SourceLocation Target =
Drain ? SM.getLocForEndOfFile(*Drain)
: SM.getExpansionLoc(
@@ -751,7 +842,7 @@ private:
SpelledTokens[NextSpelled].location() <= KnownEnd)
++NextSpelled;
FlushMapping(); // Emits [NextSpelled, KnownEnd]
- // Now the loop contitues and will emit (KnownEnd, Target).
+ // Now the loop continues and will emit (KnownEnd, Target).
} else {
++NextSpelled;
}
@@ -891,7 +982,7 @@ std::string TokenBuffer::dumpForTests() const {
OS << "expanded tokens:\n"
<< " ";
// (!) we do not show '<eof>'.
- DumpTokens(OS, llvm::makeArrayRef(ExpandedTokens).drop_back());
+ DumpTokens(OS, llvm::ArrayRef(ExpandedTokens).drop_back());
OS << "\n";
std::vector<FileID> Keys;
diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp
index 6314615f83c8..8966c12ef7c1 100644
--- a/clang/lib/Tooling/Tooling.cpp
+++ b/clang/lib/Tooling/Tooling.cpp
@@ -98,7 +98,7 @@ static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) {
OffloadCompilation = true;
if (Jobs.size() > 1) {
- for (auto A : Actions){
+ for (auto *A : Actions){
// On MacOSX real actions may end up being wrapped in BindArchAction
if (isa<driver::BindArchAction>(A))
A = *A->input_begin();
@@ -161,7 +161,7 @@ getCC1Arguments(DiagnosticsEngine *Diagnostics,
/// Returns a clang build invocation initialized from the CC1 flags.
CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,
- const llvm::opt::ArgStringList &CC1Args,
+ ArrayRef<const char *> CC1Args,
const char *const BinaryName) {
assert(!CC1Args.empty() && "Must at least contain the program name!");
CompilerInvocation *Invocation = new CompilerInvocation;
@@ -339,7 +339,7 @@ ToolInvocation::~ToolInvocation() {
}
bool ToolInvocation::run() {
- std::vector<const char*> Argv;
+ llvm::opt::ArgStringList Argv;
for (const std::string &Str : CommandLine)
Argv.push_back(Str.c_str());
const char *const BinaryName = Argv[0];
@@ -362,6 +362,17 @@ bool ToolInvocation::run() {
SourceManager SrcMgr(*Diagnostics, *Files);
Diagnostics->setSourceManager(&SrcMgr);
+ // We already have a cc1, just create an invocation.
+ if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
+ ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
+ std::unique_ptr<CompilerInvocation> Invocation(
+ newInvocation(&*Diagnostics, CC1Args, BinaryName));
+ if (Diagnostics->hasErrorOccurred())
+ return false;
+ return Action->runInvocation(std::move(Invocation), Files,
+ std::move(PCHContainerOps), DiagConsumer);
+ }
+
const std::unique_ptr<driver::Driver> Driver(
newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
// The "input file not found" diagnostics from the driver are useful.
@@ -371,7 +382,7 @@ bool ToolInvocation::run() {
if (!Files->getFileSystemOpts().WorkingDir.empty())
Driver->setCheckInputsExist(false);
const std::unique_ptr<driver::Compilation> Compilation(
- Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
+ Driver->BuildCompilation(llvm::ArrayRef(Argv)));
if (!Compilation)
return false;
const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
diff --git a/clang/lib/Tooling/Transformer/Parsing.cpp b/clang/lib/Tooling/Transformer/Parsing.cpp
index 4f41e2e90def..53a78e8df22a 100644
--- a/clang/lib/Tooling/Transformer/Parsing.cpp
+++ b/clang/lib/Tooling/Transformer/Parsing.cpp
@@ -14,11 +14,11 @@
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Transformer/RangeSelector.h"
#include "clang/Tooling/Transformer/SourceCode.h"
-#include "llvm/ADT/None.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
+#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -46,7 +46,7 @@ struct ParseState {
};
// Represents an intermediate result returned by a parsing function. Functions
-// that don't generate values should use `llvm::None`
+// that don't generate values should use `std::nullopt`
template <typename ResultType> struct ParseProgress {
ParseState State;
// Intermediate result generated by the Parser.
@@ -120,11 +120,11 @@ getBinaryRangeSelectors() {
}
template <typename Element>
-llvm::Optional<Element> findOptional(const llvm::StringMap<Element> &Map,
- llvm::StringRef Key) {
+std::optional<Element> findOptional(const llvm::StringMap<Element> &Map,
+ llvm::StringRef Key) {
auto it = Map.find(Key);
if (it == Map.end())
- return llvm::None;
+ return std::nullopt;
return it->second;
}
@@ -152,12 +152,12 @@ static StringRef consumeWhitespace(StringRef S) {
// Parses a single expected character \c c from \c State, skipping preceding
// whitespace. Error if the expected character isn't found.
-static ExpectedProgress<llvm::NoneType> parseChar(char c, ParseState State) {
+static ExpectedProgress<std::nullopt_t> parseChar(char c, ParseState State) {
State.Input = consumeWhitespace(State.Input);
if (State.Input.empty() || State.Input.front() != c)
return makeParseError(State,
("expected char not found: " + llvm::Twine(c)).str());
- return makeParseProgress(advance(State, 1), llvm::None);
+ return makeParseProgress(advance(State, 1), std::nullopt);
}
// Parses an identitifer "token" -- handles preceding whitespace.
diff --git a/clang/lib/Tooling/Transformer/RewriteRule.cpp b/clang/lib/Tooling/Transformer/RewriteRule.cpp
index 3e76489782f3..eefddc349404 100644
--- a/clang/lib/Tooling/Transformer/RewriteRule.cpp
+++ b/clang/lib/Tooling/Transformer/RewriteRule.cpp
@@ -13,7 +13,6 @@
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Transformer/SourceCode.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
@@ -39,8 +38,8 @@ translateEdits(const MatchResult &Result, ArrayRef<ASTEdit> ASTEdits) {
Expected<CharSourceRange> Range = E.TargetRange(Result);
if (!Range)
return Range.takeError();
- llvm::Optional<CharSourceRange> EditRange =
- tooling::getRangeForEdit(*Range, *Result.Context);
+ std::optional<CharSourceRange> EditRange =
+ tooling::getFileRangeForEdit(*Range, *Result.Context);
// FIXME: let user specify whether to treat this case as an error or ignore
// it as is currently done. This behavior is problematic in that it hides
// failures from bad ranges. Also, the behavior here differs from
@@ -50,17 +49,27 @@ translateEdits(const MatchResult &Result, ArrayRef<ASTEdit> ASTEdits) {
// produces a bad range, whereas the latter will simply ignore A.
if (!EditRange)
return SmallVector<Edit, 0>();
- auto Replacement = E.Replacement->eval(Result);
- if (!Replacement)
- return Replacement.takeError();
- auto Metadata = E.Metadata(Result);
- if (!Metadata)
- return Metadata.takeError();
transformer::Edit T;
T.Kind = E.Kind;
T.Range = *EditRange;
- T.Replacement = std::move(*Replacement);
- T.Metadata = std::move(*Metadata);
+ if (E.Replacement) {
+ auto Replacement = E.Replacement->eval(Result);
+ if (!Replacement)
+ return Replacement.takeError();
+ T.Replacement = std::move(*Replacement);
+ }
+ if (E.Note) {
+ auto Note = E.Note->eval(Result);
+ if (!Note)
+ return Note.takeError();
+ T.Note = std::move(*Note);
+ }
+ if (E.Metadata) {
+ auto Metadata = E.Metadata(Result);
+ if (!Metadata)
+ return Metadata.takeError();
+ T.Metadata = std::move(*Metadata);
+ }
Edits.push_back(std::move(T));
}
return Edits;
@@ -121,6 +130,13 @@ ASTEdit transformer::changeTo(RangeSelector Target, TextGenerator Replacement) {
return E;
}
+ASTEdit transformer::note(RangeSelector Anchor, TextGenerator Note) {
+ ASTEdit E;
+ E.TargetRange = transformer::before(Anchor);
+ E.Note = std::move(Note);
+ return E;
+}
+
namespace {
/// A \c TextGenerator that always returns a fixed string.
class SimpleTextGenerator : public MatchComputation<std::string> {
@@ -433,7 +449,7 @@ SourceLocation transformer::detail::getRuleMatchLoc(const MatchResult &Result) {
auto &NodesMap = Result.Nodes.getMap();
auto Root = NodesMap.find(RootID);
assert(Root != NodesMap.end() && "Transformation failed: missing root node.");
- llvm::Optional<CharSourceRange> RootRange = tooling::getRangeForEdit(
+ std::optional<CharSourceRange> RootRange = tooling::getFileRangeForEdit(
CharSourceRange::getTokenRange(Root->second.getSourceRange()),
*Result.Context);
if (RootRange)
diff --git a/clang/lib/Tooling/Transformer/SourceCode.cpp b/clang/lib/Tooling/Transformer/SourceCode.cpp
index 26b204851f05..35edc261ef09 100644
--- a/clang/lib/Tooling/Transformer/SourceCode.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCode.cpp
@@ -50,8 +50,9 @@ CharSourceRange clang::tooling::maybeExtendRange(CharSourceRange Range,
return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation());
}
-llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
- const SourceManager &SM) {
+static llvm::Error validateRange(const CharSourceRange &Range,
+ const SourceManager &SM,
+ bool AllowSystemHeaders) {
if (Range.isInvalid())
return llvm::make_error<StringError>(errc::invalid_argument,
"Invalid range");
@@ -60,10 +61,12 @@ llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
return llvm::make_error<StringError>(
errc::invalid_argument, "Range starts or ends in a macro expansion");
- if (SM.isInSystemHeader(Range.getBegin()) ||
- SM.isInSystemHeader(Range.getEnd()))
- return llvm::make_error<StringError>(errc::invalid_argument,
- "Range is in system header");
+ if (!AllowSystemHeaders) {
+ if (SM.isInSystemHeader(Range.getBegin()) ||
+ SM.isInSystemHeader(Range.getEnd()))
+ return llvm::make_error<StringError>(errc::invalid_argument,
+ "Range is in system header");
+ }
std::pair<FileID, unsigned> BeginInfo = SM.getDecomposedLoc(Range.getBegin());
std::pair<FileID, unsigned> EndInfo = SM.getDecomposedLoc(Range.getEnd());
@@ -72,33 +75,74 @@ llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
errc::invalid_argument, "Range begins and ends in different files");
if (BeginInfo.second > EndInfo.second)
- return llvm::make_error<StringError>(
- errc::invalid_argument, "Range's begin is past its end");
+ return llvm::make_error<StringError>(errc::invalid_argument,
+ "Range's begin is past its end");
return llvm::Error::success();
}
-llvm::Optional<CharSourceRange>
-clang::tooling::getRangeForEdit(const CharSourceRange &EditRange,
+llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range,
+ const SourceManager &SM) {
+ return validateRange(Range, SM, /*AllowSystemHeaders=*/false);
+}
+
+static bool spelledInMacroDefinition(SourceLocation Loc,
+ const SourceManager &SM) {
+ while (Loc.isMacroID()) {
+ const auto &Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion();
+ if (Expansion.isMacroArgExpansion()) {
+ // Check the spelling location of the macro arg, in case the arg itself is
+ // in a macro expansion.
+ Loc = Expansion.getSpellingLoc();
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
+
+static CharSourceRange getRange(const CharSourceRange &EditRange,
const SourceManager &SM,
- const LangOptions &LangOpts) {
- // FIXME: makeFileCharRange() has the disadvantage of stripping off "identity"
- // macros. For example, if we're looking to rewrite the int literal 3 to 6,
- // and we have the following definition:
- // #define DO_NOTHING(x) x
- // then
- // foo(DO_NOTHING(3))
- // will be rewritten to
- // foo(6)
- // rather than the arguably better
- // foo(DO_NOTHING(6))
- // Decide whether the current behavior is desirable and modify if not.
- CharSourceRange Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
+ const LangOptions &LangOpts,
+ bool IncludeMacroExpansion) {
+ CharSourceRange Range;
+ if (IncludeMacroExpansion) {
+ Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts);
+ } else {
+ if (spelledInMacroDefinition(EditRange.getBegin(), SM) ||
+ spelledInMacroDefinition(EditRange.getEnd(), SM))
+ return {};
+
+ auto B = SM.getSpellingLoc(EditRange.getBegin());
+ auto E = SM.getSpellingLoc(EditRange.getEnd());
+ if (EditRange.isTokenRange())
+ E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts);
+ Range = CharSourceRange::getCharRange(B, E);
+ }
+ return Range;
+}
+
+std::optional<CharSourceRange> clang::tooling::getFileRangeForEdit(
+ const CharSourceRange &EditRange, const SourceManager &SM,
+ const LangOptions &LangOpts, bool IncludeMacroExpansion) {
+ CharSourceRange Range =
+ getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
bool IsInvalid = llvm::errorToBool(validateEditRange(Range, SM));
if (IsInvalid)
- return llvm::None;
+ return std::nullopt;
return Range;
+}
+std::optional<CharSourceRange> clang::tooling::getFileRange(
+ const CharSourceRange &EditRange, const SourceManager &SM,
+ const LangOptions &LangOpts, bool IncludeMacroExpansion) {
+ CharSourceRange Range =
+ getRange(EditRange, SM, LangOpts, IncludeMacroExpansion);
+ bool IsInvalid =
+ llvm::errorToBool(validateRange(Range, SM, /*AllowSystemHeaders=*/true));
+ if (IsInvalid)
+ return std::nullopt;
+ return Range;
}
static bool startsWithNewline(const SourceManager &SM, const Token &Tok) {
diff --git a/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp b/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
index 7496e968469c..10588a383da0 100644
--- a/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
+++ b/clang/lib/Tooling/Transformer/SourceCodeBuilders.cpp
@@ -72,17 +72,17 @@ bool tooling::isKnownPointerLikeType(QualType Ty, ASTContext &Context) {
return match(PointerLikeTy, Ty, Context).size() > 0;
}
-llvm::Optional<std::string> tooling::buildParens(const Expr &E,
- const ASTContext &Context) {
+std::optional<std::string> tooling::buildParens(const Expr &E,
+ const ASTContext &Context) {
StringRef Text = getText(E, Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
if (mayEverNeedParens(E))
return ("(" + Text + ")").str();
return Text.str();
}
-llvm::Optional<std::string>
+std::optional<std::string>
tooling::buildDereference(const Expr &E, const ASTContext &Context) {
if (const auto *Op = dyn_cast<UnaryOperator>(&E))
if (Op->getOpcode() == UO_AddrOf) {
@@ -90,21 +90,21 @@ tooling::buildDereference(const Expr &E, const ASTContext &Context) {
StringRef Text =
getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
return Text.str();
}
StringRef Text = getText(E, Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
// Add leading '*'.
if (needParensAfterUnaryOperator(E))
return ("*(" + Text + ")").str();
return ("*" + Text).str();
}
-llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E,
- const ASTContext &Context) {
+std::optional<std::string> tooling::buildAddressOf(const Expr &E,
+ const ASTContext &Context) {
if (E.isImplicitCXXThis())
return std::string("this");
if (const auto *Op = dyn_cast<UnaryOperator>(&E))
@@ -113,13 +113,13 @@ llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E,
StringRef Text =
getText(*Op->getSubExpr()->IgnoreParenImpCasts(), Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
return Text.str();
}
// Add leading '&'.
StringRef Text = getText(E, Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
if (needParensAfterUnaryOperator(E)) {
return ("&(" + Text + ")").str();
}
@@ -128,7 +128,7 @@ llvm::Optional<std::string> tooling::buildAddressOf(const Expr &E,
// Append the appropriate access operation (syntactically) to `E`, assuming `E`
// is a non-pointer value.
-static llvm::Optional<std::string>
+static std::optional<std::string>
buildAccessForValue(const Expr &E, const ASTContext &Context) {
if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
if (Op->getOpcode() == UO_Deref) {
@@ -136,7 +136,7 @@ buildAccessForValue(const Expr &E, const ASTContext &Context) {
const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
StringRef DerefText = getText(*SubExpr, Context);
if (DerefText.empty())
- return llvm::None;
+ return std::nullopt;
if (needParensBeforeDotOrArrow(*SubExpr))
return ("(" + DerefText + ")->").str();
return (DerefText + "->").str();
@@ -145,7 +145,7 @@ buildAccessForValue(const Expr &E, const ASTContext &Context) {
// Add following '.'.
StringRef Text = getText(E, Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
if (needParensBeforeDotOrArrow(E)) {
return ("(" + Text + ").").str();
}
@@ -154,7 +154,7 @@ buildAccessForValue(const Expr &E, const ASTContext &Context) {
// Append the appropriate access operation (syntactically) to `E`, assuming `E`
// is a pointer value.
-static llvm::Optional<std::string>
+static std::optional<std::string>
buildAccessForPointer(const Expr &E, const ASTContext &Context) {
if (const auto *Op = llvm::dyn_cast<UnaryOperator>(&E))
if (Op->getOpcode() == UO_AddrOf) {
@@ -162,7 +162,7 @@ buildAccessForPointer(const Expr &E, const ASTContext &Context) {
const Expr *SubExpr = Op->getSubExpr()->IgnoreParenImpCasts();
StringRef DerefText = getText(*SubExpr, Context);
if (DerefText.empty())
- return llvm::None;
+ return std::nullopt;
if (needParensBeforeDotOrArrow(*SubExpr))
return ("(" + DerefText + ").").str();
return (DerefText + ".").str();
@@ -171,19 +171,19 @@ buildAccessForPointer(const Expr &E, const ASTContext &Context) {
// Add following '->'.
StringRef Text = getText(E, Context);
if (Text.empty())
- return llvm::None;
+ return std::nullopt;
if (needParensBeforeDotOrArrow(E))
return ("(" + Text + ")->").str();
return (Text + "->").str();
}
-llvm::Optional<std::string> tooling::buildDot(const Expr &E,
- const ASTContext &Context) {
+std::optional<std::string> tooling::buildDot(const Expr &E,
+ const ASTContext &Context) {
return buildAccessForValue(E, Context);
}
-llvm::Optional<std::string> tooling::buildArrow(const Expr &E,
- const ASTContext &Context) {
+std::optional<std::string> tooling::buildArrow(const Expr &E,
+ const ASTContext &Context) {
return buildAccessForPointer(E, Context);
}
@@ -210,11 +210,12 @@ static bool treatLikePointer(QualType Ty, PLTClass C, ASTContext &Context) {
// FIXME: move over the other `maybe` functionality from Stencil. Should all be
// in one place.
-llvm::Optional<std::string> tooling::buildAccess(const Expr &RawExpression,
- ASTContext &Context,
- PLTClass Classification) {
+std::optional<std::string> tooling::buildAccess(const Expr &RawExpression,
+ ASTContext &Context,
+ PLTClass Classification) {
if (RawExpression.isImplicitCXXThis())
- // Return the empty string, because `None` signifies some sort of failure.
+ // Return the empty string, because `std::nullopt` signifies some sort of
+ // failure.
return std::string();
const Expr *E = RawExpression.IgnoreImplicitAsWritten();
diff --git a/clang/lib/Tooling/Transformer/Stencil.cpp b/clang/lib/Tooling/Transformer/Stencil.cpp
index 82dd4a2587b5..2198aefddc9f 100644
--- a/clang/lib/Tooling/Transformer/Stencil.cpp
+++ b/clang/lib/Tooling/Transformer/Stencil.cpp
@@ -152,7 +152,7 @@ public:
if (E == nullptr)
return llvm::make_error<StringError>(errc::invalid_argument,
"Id not bound or not Expr: " + Id);
- llvm::Optional<std::string> Source;
+ std::optional<std::string> Source;
switch (Op) {
case UnaryNodeOperator::Parens:
Source = tooling::buildParens(*E, *Match.Context);
@@ -277,7 +277,7 @@ public:
if (E == nullptr)
return llvm::make_error<StringError>(errc::invalid_argument,
"Id not bound: " + BaseId);
- llvm::Optional<std::string> S = tooling::buildAccess(*E, *Match.Context);
+ std::optional<std::string> S = tooling::buildAccess(*E, *Match.Context);
if (!S)
return llvm::make_error<StringError>(
errc::invalid_argument,