Skip to content
This repository has been archived by the owner on Jan 19, 2025. It is now read-only.

Prokofev Kirill Lab1 Var4 #49

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions clang/labs/lab1/prokofev_kirill/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
add_llvm_library(ProkofevRenamePlugin MODULE RenameIdentificator.cpp PLUGIN_TOOL clang)

if(WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS
Support
)
clang_target_link_libraries(ProkofevRenamePlugin PRIVATE
clangAST
clangBasic
clangFrontend
)
endif()

set(CLANG_TEST_DEPS "ProkofevRenamePlugin" ${CLANG_TEST_DEPS} PARENT_SCOPE)
198 changes: 198 additions & 0 deletions clang/labs/lab1/prokofev_kirill/RenameIdentificator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Rewrite/Core/Rewriter.h"

enum class IdType { Var, Func, Class };

class RenameVisitor : public clang::RecursiveASTVisitor<RenameVisitor> {
public:
explicit RenameVisitor(clang::Rewriter rewriter, IdType type,
clang::StringRef oldName, clang::StringRef newName)
: rewriter(rewriter), type(type), oldName(oldName), newName(newName) {}

bool VisitFunctionDecl(clang::FunctionDecl *func) {
if (type == IdType::Func && func->getName() == oldName) {
rewriter.ReplaceText(func->getNameInfo().getSourceRange(), newName);
}
return true;
}

bool VisitCallExpr(clang::CallExpr *call) {
if (type == IdType::Func) {
clang::FunctionDecl *callee = call->getDirectCallee();
if (callee && callee->getName() == oldName) {
rewriter.ReplaceText(call->getCallee()->getSourceRange(), newName);
}
}
return true;
}

bool VisitVarDecl(clang::VarDecl *var) {
if ((type == IdType::Var && var->getName() == oldName) ||
(type == IdType::Class && var->getType().getAsString() == oldName) ||
(type == IdType::Class &&
var->getType().getAsString() == oldName + " *")) {
if (type == IdType::Var) {
rewriter.ReplaceText(var->getLocation(), oldName.size(), newName);
} else {
if (var->getType().getAsString() == oldName + " *") {
rewriter.ReplaceText(
var->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
oldName.size(), newName);
} else {
rewriter.ReplaceText(
var->getTypeSourceInfo()->getTypeLoc().getSourceRange(), newName);
}
}
}
return true;
}

bool VisitDeclRefExpr(clang::DeclRefExpr *expr) {
clang::VarDecl *var = clang::dyn_cast<clang::VarDecl>(expr->getDecl());
if (type == IdType::Var && var && var->getName() == oldName) {
rewriter.ReplaceText(expr->getSourceRange(), newName);
}
return true;
}

bool VisitCXXRecordDecl(clang::CXXRecordDecl *record) {
if (type == IdType::Class && record->getName() == oldName) {
rewriter.ReplaceText(record->getLocation(), newName);
const auto *destructor = record->getDestructor();
if (destructor) {
rewriter.ReplaceText(destructor->getLocation(), oldName.size() + 1,
'~' + newName);
}
}
return true;
}

bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *constructor) {
if (type == IdType::Class) {
if (constructor->getNameAsString() == oldName) {
rewriter.ReplaceText(constructor->getLocation(), oldName.size(),
newName);
}
}
return true;
}

bool VisitCXXNewExpr(clang::CXXNewExpr *newExpr) {
if (type == IdType::Class) {
if (newExpr->getConstructExpr()->getType().getAsString() == oldName) {
rewriter.ReplaceText(newExpr->getExprLoc(), oldName.size() + 4,
"new " + newName);
}
}
return true;
}

bool save_changes() { return rewriter.overwriteChangedFiles(); }

private:
clang::Rewriter rewriter;
IdType type;
std::string oldName;
std::string newName;
};

class RenameASTConsumer : public clang::ASTConsumer {
public:
explicit RenameASTConsumer(clang::CompilerInstance &CI, IdType type,
clang::StringRef cur_name,
clang::StringRef new_name)
: Visitor(clang::Rewriter(CI.getSourceManager(), CI.getLangOpts()), type,
cur_name, new_name) {}

void HandleTranslationUnit(clang::ASTContext &context) override {
Visitor.TraverseDecl(context.getTranslationUnitDecl());
if (Visitor.save_changes()) {
llvm::errs() << "An error occurred while saving changes to a file!\n";
}
}

private:
RenameVisitor Visitor;
};

class RenamePlugin : public clang::PluginASTAction {
public:
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &CI,
clang::StringRef InFile) override {
return std::make_unique<RenameASTConsumer>(CI, type, oldName, newName);
}

protected:
bool ParseArgs(const clang::CompilerInstance &CI,
const std::vector<std::string> &args) override {
std::vector<std::pair<std::string, std::string>> params = {
{"type=", ""}, {"oldName=", ""}, {"newName=", ""}};

if (!args.empty() && args[0] == "help") {
PrintHelp(llvm::errs());
return false;
}

if (args.size() < 3) {
PrintParamsError(CI);
return false;
}
for (const auto &arg : args) {
bool is_found = false;
for (auto &param : params) {
if (arg.find(param.first) == 0 && param.second.empty()) {
param.second = arg.substr(param.first.size());
is_found = true;
break;
}
}
if (!is_found) {
PrintParamsError(CI);
return false;
}
}
std::vector<std::pair<std::string, IdType>> id_type = {
{"var", IdType::Var}, {"func", IdType::Func}, {"class", IdType::Class}};
size_t i;
for (i = 0; i < id_type.size(); i++) {
if (params[0].second == id_type[i].first) {
type = id_type[i].second;
break;
}
}
if (i == id_type.size()) {
PrintParamsError(CI);
return false;
}
oldName = params[1].second;
newName = params[2].second;
return true;
}

void PrintHelp(llvm::raw_ostream &ros) {
ros << "Specify three required arguments:\n"
"-plugin-arg-rename type=[\"var\", \"func\", \"class\"]\n"
"-plugin-arg-rename oldName=\"Current identifier name\"\n"
"-plugin-arg-rename newName=\"New identifier name\"\n";
}
void PrintParamsError(const clang::CompilerInstance &CI) {
clang::DiagnosticsEngine &D = CI.getDiagnostics();

D.Report(
D.getCustomDiagID(clang::DiagnosticsEngine::Error,
"Invalid arguments\n"
"Specify \"-plugin-arg-rename help\" for usage\n"));
}

private:
IdType type;
std::string oldName;
std::string newName;
};

static clang::FrontendPluginRegistry::Add<RenamePlugin>
X("rename", "Rename variable, function or class");
71 changes: 71 additions & 0 deletions clang/test/lab1/prokofev_kirill/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RUN: split-file %s %t
// RUN: %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext -add-plugin rename\
// RUN: -plugin-arg-rename type=class\
// RUN: -plugin-arg-rename oldName=Alpha\
// RUN: -plugin-arg-rename newName=Beta %t/class_test.cpp
// RUN: FileCheck %s < %t/class_test.cpp --check-prefix=CLASS

// CLASS: class Beta {
// CLASS-NEXT: public:
// CLASS-NEXT: Beta(){};
// CLASS-NEXT: ~Beta();
// CLASS-NEXT:};

//--- class_test.cpp
class Alpha {
public:
Alpha(){};
~Alpha();
};

// RUN: %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext -add-plugin rename\
// RUN: -plugin-arg-rename type=func\
// RUN: -plugin-arg-rename oldName=Foo\
// RUN: -plugin-arg-rename newName=Function %t/function_test.cpp
// RUN: FileCheck %s < %t/function_test.cpp --check-prefix=FUNCTION

// FUNCTION: int Function(int digit) { return digit * 2; };

//--- function_test.cpp
int Foo(int digit) { return digit * 2; };

// RUN: %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext -add-plugin rename\
// RUN: -plugin-arg-rename type=var\
// RUN: -plugin-arg-rename oldName=oldVar\
// RUN: -plugin-arg-rename newName=newVar %t/variable_test.cpp
// RUN: FileCheck %s < %t/variable_test.cpp --check-prefix=VARIABLE

// VARIABLE: Foo(int newVar) { return newVar * 2; }

//--- variable_test.cpp
int Foo(int oldVar) { return oldVar * 2; }

// RUN: %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext\
// RUN: -add-plugin rename\
// RUN: -plugin-arg-rename help\
// RUN: 2>&1 | FileCheck %s --check-prefix=HELP

// HELP: Specify three required arguments:
// HELP-NEXT: -plugin-arg-rename type=["var", "func", "class"]
// HELP-NEXT: -plugin-arg-rename oldName="Current identifier name"
// HELP-NEXT: -plugin-arg-rename newName="New identifier name"

// RUN: not %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext\
// RUN: -add-plugin rename\
// RUN: -plugin-arg-rename oldName=var1\
// RUN: -plugin-arg-rename newName=variable\
// RUN: 2>&1 | FileCheck %s --check-prefix=ERROR

// RUN: not %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext\
// RUN: -add-plugin rename\
// RUN: -plugin-arg-rename type=undefined\
// RUN: -plugin-arg-rename oldName=var1\
// RUN: -plugin-arg-rename newName=variable\
// RUN: 2>&1 | FileCheck %s --check-prefix=ERROR

// RUN: not %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext\
// RUN: -add-plugin rename\
// RUN: 2>&1 | FileCheck %s --check-prefix=ERROR

//ERROR: Invalid arguments
//ERROR-NEXT: Specify "-plugin-arg-rename help" for usage
Loading