[clang] Implement PrimitivesInit refactoring (PR #95867)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 17 16:47:01 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Pavel Desyatnikov (desyatok)
<details>
<summary>Changes</summary>
This is a gitlab mirror (LLVM repo is too large to directly create one)
---
Patch is 21.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/95867.diff
10 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticRefactoringKinds.td (+6)
- (modified) clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h (+17)
- (added) clang/include/clang/Tooling/Refactoring/VarInits/PrimitiveVarDecl.h (+26)
- (added) clang/include/clang/Tooling/Refactoring/VarInits/PrimitivesInit.h (+40)
- (modified) clang/lib/Tooling/Refactoring/CMakeLists.txt (+3)
- (modified) clang/lib/Tooling/Refactoring/RefactoringActions.cpp (+18)
- (added) clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDecl.cpp (+55)
- (added) clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDeclRequirement.cpp (+32)
- (added) clang/lib/Tooling/Refactoring/VarInits/PrimitivesInit.cpp (+65)
- (modified) clang/tools/clang-refactor/ClangRefactor.cpp (+143-6)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
index 5446b32efbdd4..39366bd1492bb 100644
--- a/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
+++ b/clang/include/clang/Basic/DiagnosticRefactoringKinds.td
@@ -28,6 +28,12 @@ def err_refactor_extract_simple_expression : Error<"the selected expression "
def err_refactor_extract_prohibited_expression : Error<"the selected "
"expression can't be extracted">;
+def err_refactor_no_vardecl : Error<"refactoring action can't be initiated "
+ "without a vardecl">;
+def err_refactor_initialized_variable : Error<"the provided vardecl is already initialized">;
+def err_refactor_global_variable_init : Error<"no need to initialize global variable">;
+def err_refactor_non_primitive_variable : Error<"refactoring action can't be initiated "
+ "with non-primitive variable">;
}
} // end of Refactoring diagnostics
diff --git a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
index 1a318da3acca1..bd410d43abd12 100644
--- a/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
+++ b/clang/include/clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_TOOLING_REFACTORING_REFACTORINGACTIONRULEREQUIREMENTS_H
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Tooling/Refactoring/ASTSelection.h"
#include "clang/Tooling/Refactoring/RefactoringDiagnostic.h"
#include "clang/Tooling/Refactoring/RefactoringOption.h"
@@ -77,6 +78,17 @@ class CodeRangeASTSelectionRequirement : public ASTSelectionRequirement {
evaluate(RefactoringRuleContext &Context) const;
};
+/// A base class for any requirement that expects source code position
+/// (or the refactoring tool with the -location option).
+class SourceLocationRequirement : public RefactoringActionRuleRequirement {
+public:
+ Expected<SourceLocation> evaluate(RefactoringRuleContext &Context) const {
+ if (Context.getLocation().isValid())
+ return Context.getLocation();
+ return Context.createDiagnosticError(diag::err_refactor_no_location);
+ }
+};
+
/// A base class for any requirement that requires some refactoring options.
class RefactoringOptionsRequirement : public RefactoringActionRuleRequirement {
public:
@@ -116,6 +128,11 @@ class OptionRequirement : public RefactoringOptionsRequirement {
std::shared_ptr<RefactoringOption> Opt;
};
+class PrimitiveVarDeclRequirement : public SourceLocationRequirement {
+public:
+ Expected<VarDecl *> evaluate(RefactoringRuleContext &Context) const;
+};
+
} // end namespace tooling
} // end namespace clang
diff --git a/clang/include/clang/Tooling/Refactoring/VarInits/PrimitiveVarDecl.h b/clang/include/clang/Tooling/Refactoring/VarInits/PrimitiveVarDecl.h
new file mode 100644
index 0000000000000..dd9ec755d30c7
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/VarInits/PrimitiveVarDecl.h
@@ -0,0 +1,26 @@
+//===--- PrimitiveVarDecl.h - Clang refactoring library ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTORING_VARINITS_PRIMITIVEVARDECL_H
+#define LLVM_CLANG_TOOLING_REFACTORING_VARINITS_PRIMITIVEVARDECL_H
+
+
+namespace clang {
+namespace tooling {
+/// \returns a ptr to concrete DeclRefExpr (it is basically a pointer to
+/// VarDecl) if given SourceLocation is in between
+/// a DeclRefExpr start location and end location
+/// and nullptr otherwise
+DeclRefExpr *getDeclRefExprFromSourceLocation(ASTContext &AST,
+ SourceLocation Location);
+
+} // namespace tooling
+} // namespace clang
+
+
+#endif // LLVM_CLANG_TOOLING_REFACTORING_VARINITS_PRIMITIVEVARDECL_H
diff --git a/clang/include/clang/Tooling/Refactoring/VarInits/PrimitivesInit.h b/clang/include/clang/Tooling/Refactoring/VarInits/PrimitivesInit.h
new file mode 100644
index 0000000000000..744422b396459
--- /dev/null
+++ b/clang/include/clang/Tooling/Refactoring/VarInits/PrimitivesInit.h
@@ -0,0 +1,40 @@
+//===--- PrimitivesInit.h - Clang refactoring library ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTORING_VARINITS_PRIMITIVES_INIT_H
+#define LLVM_CLANG_TOOLING_REFACTORING_VARINITS_PRIMITIVES_INIT_H
+
+#include "clang/Tooling/Refactoring/RefactoringActionRules.h"
+
+namespace clang {
+namespace tooling {
+
+/// \c PrimitivesInit performs the initialization of
+/// a selected primitive variable with a default value
+class PrimitivesInit final : public SourceChangeRefactoringRule {
+public:
+ /// \param Variable declaration-only VarDecl
+ static Expected<PrimitivesInit>
+ initiate(RefactoringRuleContext &Context, VarDecl *Variable);
+
+ static const RefactoringDescriptor &describe();
+
+private:
+ PrimitivesInit(VarDecl *Variable)
+ : Variable(std::move(Variable)) {}
+
+ Expected<AtomicChanges>
+ createSourceReplacements(RefactoringRuleContext &Context) override;
+
+ VarDecl *Variable;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTORING_VARINITS_PRIMITIVES_INIT_H
diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt
index d3077be8810aa..63b9dd700ba35 100644
--- a/clang/lib/Tooling/Refactoring/CMakeLists.txt
+++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt
@@ -13,6 +13,9 @@ add_clang_library(clangToolingRefactoring
Rename/USRFinder.cpp
Rename/USRFindingAction.cpp
Rename/USRLocFinder.cpp
+ VarInits/PrimitivesInit.cpp
+ VarInits/PrimitiveVarDeclRequirement.cpp
+ VarInits/PrimitiveVarDecl.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/Tooling/Refactoring/RefactoringActions.cpp b/clang/lib/Tooling/Refactoring/RefactoringActions.cpp
index bf98941f568b3..f68b4daf346ff 100644
--- a/clang/lib/Tooling/Refactoring/RefactoringActions.cpp
+++ b/clang/lib/Tooling/Refactoring/RefactoringActions.cpp
@@ -10,6 +10,7 @@
#include "clang/Tooling/Refactoring/RefactoringAction.h"
#include "clang/Tooling/Refactoring/RefactoringOptions.h"
#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/VarInits/PrimitivesInit.h"
namespace clang {
namespace tooling {
@@ -93,6 +94,22 @@ class LocalRename final : public RefactoringAction {
}
};
+class PrimitivesInitAction : public RefactoringAction {
+public:
+ StringRef getCommand() const override { return "init-primitives"; }
+
+ StringRef getDescription() const override {
+ return "Initialization of declared-only primitives";
+ }
+
+ RefactoringActionRules createActionRules() const override {
+ RefactoringActionRules Rules;
+ Rules.push_back(createRefactoringActionRule<PrimitivesInit>(
+ PrimitiveVarDeclRequirement()));
+ return Rules;
+ }
+};
+
} // end anonymous namespace
std::vector<std::unique_ptr<RefactoringAction>> createRefactoringActions() {
@@ -100,6 +117,7 @@ std::vector<std::unique_ptr<RefactoringAction>> createRefactoringActions() {
Actions.push_back(std::make_unique<LocalRename>());
Actions.push_back(std::make_unique<ExtractRefactoring>());
+ Actions.push_back(std::make_unique<PrimitivesInitAction>());
return Actions;
}
diff --git a/clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDecl.cpp b/clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDecl.cpp
new file mode 100644
index 0000000000000..1d416e9bf944e
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDecl.cpp
@@ -0,0 +1,55 @@
+//===--- PrimitiveVarDecl.cpp - Clang refactoring library ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h"
+#include "clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h"
+#include "clang/Tooling/Refactoring/VarInits/PrimitiveVarDecl.h"
+
+using namespace clang;
+using namespace tooling;
+
+namespace {
+
+class VarDeclFinder
+ : public LexicallyOrderedRecursiveASTVisitor<VarDeclFinder> {
+public:
+ VarDeclFinder(SourceLocation Location, FileID TargetFile,
+ const ASTContext &AST)
+ : LexicallyOrderedRecursiveASTVisitor(AST.getSourceManager()),
+ Location(Location), TargetFile(TargetFile), AST(AST) {}
+
+ bool VisitDeclRefExpr(DeclRefExpr *Ref) {
+ const SourceManager &SM = AST.getSourceManager();
+ if (SM.isPointWithin(Location, Ref->getBeginLoc(),
+ Ref->getEndLoc())) {
+ this->VariableReference = Ref;
+ return false;
+ }
+ return true;
+ }
+
+ DeclRefExpr *getDeclRefExpr() { return VariableReference; }
+
+private:
+ const SourceLocation Location;
+ FileID TargetFile;
+ const ASTContext &AST;
+ DeclRefExpr *VariableReference = nullptr;
+};
+
+} // end anonymous namespace
+
+DeclRefExpr *
+clang::tooling::getDeclRefExprFromSourceLocation(ASTContext &AST,
+ SourceLocation Location) {
+
+ FileID TargetFile = AST.getSourceManager().getFileID(Location);
+
+ VarDeclFinder Visitor(Location, TargetFile, AST);
+ Visitor.TraverseAST(AST);
+ return Visitor.getDeclRefExpr();
+}
diff --git a/clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDeclRequirement.cpp b/clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDeclRequirement.cpp
new file mode 100644
index 0000000000000..68e31ba147060
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/VarInits/PrimitiveVarDeclRequirement.cpp
@@ -0,0 +1,32 @@
+//===--- PrimitiveVarDeclRequirement.cpp - Clang refactoring library ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Tooling/Refactoring/RefactoringActionRuleRequirements.h"
+#include "clang/Tooling/Refactoring/VarInits/PrimitiveVarDecl.h"
+#include <optional>
+
+using namespace clang;
+using namespace tooling;
+
+Expected<VarDecl *>
+PrimitiveVarDeclRequirement::evaluate(RefactoringRuleContext &Context) const {
+ Expected<SourceLocation> Location =
+ SourceLocationRequirement::evaluate(Context);
+ if (!Location)
+ return Location.takeError();
+
+ DeclRefExpr *VariableReference = getDeclRefExprFromSourceLocation(
+ Context.getASTContext(), Location.get());
+
+ if (!VariableReference)
+ return Context.createDiagnosticError(
+ Location.get(), diag::err_refactor_no_vardecl);
+
+ VarDecl *Variable = VariableReference->getDecl()->
+ getPotentiallyDecomposedVarDecl();
+ return std::move(Variable);
+}
diff --git a/clang/lib/Tooling/Refactoring/VarInits/PrimitivesInit.cpp b/clang/lib/Tooling/Refactoring/VarInits/PrimitivesInit.cpp
new file mode 100644
index 0000000000000..76926281794f4
--- /dev/null
+++ b/clang/lib/Tooling/Refactoring/VarInits/PrimitivesInit.cpp
@@ -0,0 +1,65 @@
+//===--- PrimitivesInit.cpp - Clang refactoring library -------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/VarInits/PrimitivesInit.h"
+#include "clang/AST/ASTContext.h"
+
+using namespace clang;
+using namespace tooling;
+
+static inline bool VariableHasPrimitiveType(VarDecl *Variable) {
+ return Variable->getType()->isCharType() ||
+ Variable->getType()->isIntegerType() ||
+ Variable->getType()->isFloatingType();
+}
+
+Expected<PrimitivesInit>
+PrimitivesInit::initiate(RefactoringRuleContext &Context,
+ VarDecl *Variable) {
+ // Checks whether provided VarDecl is valid
+ if (!Variable->isLocalVarDecl()) {
+ return Context.createDiagnosticError(
+ diag::err_refactor_global_variable_init);
+ }
+
+ if (!VariableHasPrimitiveType(Variable)) {
+ return Context.createDiagnosticError(
+ diag::err_refactor_non_primitive_variable);
+ }
+
+ if (Variable->hasInit()) {
+ return Context.createDiagnosticError(
+ diag::err_refactor_initialized_variable);
+ }
+
+ return PrimitivesInit(std::move(Variable));
+}
+const RefactoringDescriptor &PrimitivesInit::describe() {
+ static const RefactoringDescriptor Descriptor = {
+ "primitives-init",
+ "Primitives Initialization",
+ "Initializes a primitive variable with default value",
+ };
+ return Descriptor;
+}
+
+Expected<AtomicChanges>
+PrimitivesInit::createSourceReplacements(RefactoringRuleContext &Context) {
+ ASTContext &AST = Context.getASTContext();
+ SourceManager &SM = AST.getSourceManager();
+ AtomicChange Replacement(SM, Variable->getLocation());
+ std::string VarName = Variable->getNameAsString();
+ std::string InitWithDefaultValue = (Variable->getType()->isCharType() ?
+ " = \'\\0\'" : " = 0");
+
+ auto Error = Replacement.replace(SM, Variable->getEndLoc(),
+ VarName.length(),
+ VarName + InitWithDefaultValue);
+ if (Error) return std::move(Error);
+ return AtomicChanges{std::move(Replacement)};
+}
diff --git a/clang/tools/clang-refactor/ClangRefactor.cpp b/clang/tools/clang-refactor/ClangRefactor.cpp
index 175a2b8234e9a..c42baba3ef9cd 100644
--- a/clang/tools/clang-refactor/ClangRefactor.cpp
+++ b/clang/tools/clang-refactor/ClangRefactor.cpp
@@ -164,6 +164,83 @@ SourceSelectionArgument::fromString(StringRef Value) {
return nullptr;
}
+/// Stores the parsed `-location` argument.
+class SourceLocationArgument {
+public:
+ virtual ~SourceLocationArgument() {}
+
+ /// Parse the `-location` argument.
+ ///
+ /// \returns A valid argument when the parse succedeed, null otherwise.
+ static std::unique_ptr<SourceLocationArgument> fromString(StringRef Value);
+
+ /// Prints any additional state associated with the location argument to
+ /// the given output stream.
+ virtual void print(raw_ostream &OS) {}
+
+ /// Returns a replacement refactoring result consumer (if any) that should
+ /// consume the results of a refactoring operation.
+ ///
+ /// The replacement refactoring result consumer is used by \c
+ /// TestSourceLocationArgument to inject a test-specific result handling
+ /// logic into the refactoring operation. The test-specific consumer
+ /// ensures that the individual results in a particular test group are
+ /// identical.
+ virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
+ createCustomConsumer() {
+ return nullptr;
+ }
+
+ /// Runs the given refactoring function for each specified location.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ virtual bool
+ forAllLocations(const SourceManager &SM,
+ llvm::function_ref<void(SourceLocation L)> Callback) = 0;
+};
+
+/// Stores the parsed -location=filename:line:column option.
+class SourceLocLocationArgument final : public SourceLocationArgument {
+public:
+ SourceLocLocationArgument(ParsedSourceLocation Location)
+ : Location(std::move(Location)) {}
+
+ bool forAllLocations(const SourceManager &SM,
+ llvm::function_ref<void(SourceLocation L)> Callback) override {
+ auto FE = SM.getFileManager().getFile(Location.FileName);
+ FileID FID = FE ? SM.translateFile(*FE) : FileID();
+ if (!FE || FID.isInvalid()) {
+ llvm::errs() << "error: -location=" << Location.FileName
+ << ":... : given file is not in the target TU\n";
+ return true;
+ }
+
+ SourceLocation Loc = SM.getMacroArgExpandedLocation(
+ SM.translateLineCol(FID, Location.Line, Location.Column));
+ if (Loc.isInvalid()) {
+ llvm::errs() << "error: -location=" << Location.FileName << ':'
+ << Location.Line << ':' << Location.Column
+ << " : invalid source location\n";
+ return true;
+ }
+ Callback(Loc);
+ return false;
+ }
+
+private:
+ ParsedSourceLocation Location;
+};
+
+std::unique_ptr<SourceLocationArgument>
+SourceLocationArgument::fromString(StringRef Value) {
+ std::optional<ParsedSourceLocation> Location = ParsedSourceLocation::FromString(Value);
+ if (Location.value().FileName != "")
+ return std::make_unique<SourceLocLocationArgument>(std::move(*Location));
+ llvm::errs() << "error: '-location' option must be specified using "
+ "<file>:<line>:<column>\n";
+ return nullptr;
+}
+
/// A container that stores the command-line options used by a single
/// refactoring option.
class RefactoringActionCommandLineOptions {
@@ -272,6 +349,18 @@ class RefactoringActionSubcommand : public cl::SubCommand {
break;
}
}
+ // Check if the location option is supported.
+ for (const auto &Rule : this->ActionRules) {
+ if (Rule->hasLocationRequirement()) {
+ Location = std::make_unique<cl::opt<std::string>>(
+ "location",
+ cl::desc(
+ "Location where refactoring should "
+ "be initiated (<file>:<line>:<column>)"),
+ cl::cat(Category), cl::sub(*this));
+ break;
+ }
+ }
// Create the refactoring options.
for (const auto &Rule : this->ActionRules) {
CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
@@ -296,11 +385,28 @@ class RefactoringActionSubcommand : public cl::SubCommand {
return false;
}
+ /// Parses the "-location" command-line argument.
+ ///
+ /// \returns true on error, false otherwise.
+ bool parseLocationArgument() {
+ if (Location) {
+ ParsedLocation = SourceLocationArgument::fromString(*Location);
+ if (!ParsedLocation)
+ return true;
+ }
+ return false;
+ }
+
SourceSelectionArgument *getSelection() const {
assert(Selection && "selection not supported!");
return ParsedSelection.get();
}
+ SourceLocationArgument *getLocation() const {
+ assert(Location && "location not supported!");
+ return ParsedLocation.get();
+ }
+
const RefactoringActionCommandLineOptions &getOptions() const {
return Options;
}
@@ -309,7 +415,9 @@ class RefactoringActionSubcommand : public cl::SubCommand {
std::unique_ptr<RefactoringAction> Action;
RefactoringActionRules ActionRules;
std::unique_ptr<cl::opt<std::string>> Selection;
+ std::unique_ptr<cl::opt<std::string>> Location;
std::unique_ptr<SourceSelectionArgument> ParsedSelection;
+ std::unique_ptr<SourceLocationArgument> ParsedLocation;
RefactoringActionCommandLineOptions Options;
};
@@ -399,6 +507,7 @@ class ClangRefactorTool {
// consumer.
std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer;
bool HasSelection = MatchingRule->hasSelectionRequirement();
+ bool HasLocation = MatchingRule->hasLocationRequirement();
if (HasSelection)
TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer();
ClangRefactorToolConsumerInterface *ActiveConsumer =
@@ -424,6 +533,20 @@ class ClangRefactorTool {
ActiveConsumer->endTU();
return;
}
+ if (HasLocation) {
+ assert(SelectedSubcommand->getLocation() &&
+ "Missing location argument?");
+ if (opts::Verbose)
+ SelectedSubcommand->getLocation()->print(llvm::outs());
+ if (SelectedSubco...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/95867
More information about the cfe-commits
mailing list