diff --git a/clang/labs/lab1/zakharov_artem/CMakeLists.txt b/clang/labs/lab1/zakharov_artem/CMakeLists.txt new file mode 100644 index 0000000000000..72bda7ecd1f97 --- /dev/null +++ b/clang/labs/lab1/zakharov_artem/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_library(RenamePlugin MODULE RenameIds.cpp PLUGIN_TOOL clang) + +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS + Support + ) + clang_target_link_libraries(RenamePlugin PRIVATE + clangAST + clangBasic + clangFrontend + ) +endif() + +set(CLANG_TEST_DEPS "RenamePlugin" ${CLANG_TEST_DEPS} PARENT_SCOPE) diff --git a/clang/labs/lab1/zakharov_artem/RenameIds.cpp b/clang/labs/lab1/zakharov_artem/RenameIds.cpp new file mode 100644 index 0000000000000..ee465535aed45 --- /dev/null +++ b/clang/labs/lab1/zakharov_artem/RenameIds.cpp @@ -0,0 +1,197 @@ +#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 { +public: + explicit RenameVisitor(clang::Rewriter rewriter, IdType type, + clang::StringRef cur_name, clang::StringRef new_name) + : rewriter(rewriter), type(type), cur_name(cur_name), new_name(new_name) { + } + + bool VisitFunctionDecl(clang::FunctionDecl *func) { + if (type == IdType::Func && func->getName() == cur_name) { + rewriter.ReplaceText(func->getNameInfo().getSourceRange(), new_name); + } + return true; + } + + bool VisitCallExpr(clang::CallExpr *call) { + if (type == IdType::Func) { + clang::FunctionDecl *callee = call->getDirectCallee(); + if (callee && callee->getName() == cur_name) { + rewriter.ReplaceText(call->getCallee()->getSourceRange(), new_name); + } + } + return true; + } + + bool VisitVarDecl(clang::VarDecl *var) { + if (type == IdType::Var && var->getName() == cur_name) { + rewriter.ReplaceText(var->getLocation(), cur_name.size(), new_name); + } + if (type == IdType::Class && + var->getType().getAsString() == cur_name + " *") { + rewriter.ReplaceText(var->getTypeSourceInfo()->getTypeLoc().getBeginLoc(), + cur_name.size(), new_name); + } + if (type == IdType::Class && var->getType().getAsString() == cur_name) { + rewriter.ReplaceText( + var->getTypeSourceInfo()->getTypeLoc().getSourceRange(), new_name); + } + return true; + } + + bool VisitDeclRefExpr(clang::DeclRefExpr *expr) { + clang::VarDecl *var = clang::dyn_cast(expr->getDecl()); + if (type == IdType::Var && var && var->getName() == cur_name) { + rewriter.ReplaceText(expr->getSourceRange(), new_name); + } + return true; + } + + bool VisitCXXRecordDecl(clang::CXXRecordDecl *record) { + if (type == IdType::Class && record->getName() == cur_name) { + rewriter.ReplaceText(record->getLocation(), new_name); + const auto *destructor = record->getDestructor(); + if (destructor) { + rewriter.ReplaceText(destructor->getLocation(), cur_name.size() + 1, + '~' + new_name); + } + } + return true; + } + + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *constructor) { + if (type == IdType::Class) { + if (constructor->getNameAsString() == cur_name) { + rewriter.ReplaceText(constructor->getLocation(), cur_name.size(), + new_name); + } + } + return true; + } + + bool VisitCXXNewExpr(clang::CXXNewExpr *newExpr) { + if (type == IdType::Class) { + if (newExpr->getConstructExpr()->getType().getAsString() == cur_name) { + rewriter.ReplaceText(newExpr->getExprLoc(), cur_name.size() + 4, + "new " + new_name); + } + } + return true; + } + + bool save_changes() { return rewriter.overwriteChangedFiles(); } + +private: + clang::Rewriter rewriter; + IdType type; + std::string cur_name; + std::string new_name; +}; + +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 + CreateASTConsumer(clang::CompilerInstance &CI, + clang::StringRef InFile) override { + return std::make_unique(CI, type, cur_name, new_name); + } + +protected: + bool ParseArgs(const clang::CompilerInstance &CI, + const std::vector &args) override { + std::vector> params = { + {"type=", ""}, {"cur-name=", ""}, {"new-name=", ""}}; + + if (!args.empty() && args[0] == "help") { + PrintHelp(llvm::errs()); + return true; + } + + if (args.size() < 3) { + PrintParamsError(CI); + return false; + } + + for (const auto &arg : args) { + bool is_found = false; + for (auto ¶m : 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> 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; + } + cur_name = params[1].second; + new_name = 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 cur-name=\"Current identifier name\"\n" + "-plugin-arg-rename new-name=\"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 cur_name; + std::string new_name; +}; + +static clang::FrontendPluginRegistry::Add + X("rename", "Rename variable, function or class"); diff --git a/clang/test/lab1/zakharov_artem/test.cpp b/clang/test/lab1/zakharov_artem/test.cpp new file mode 100644 index 0000000000000..544ab72cecf9d --- /dev/null +++ b/clang/test/lab1/zakharov_artem/test.cpp @@ -0,0 +1,212 @@ +// RUN: split-file %s %t + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=var\ +// RUN: -plugin-arg-rename cur-name=a\ +// RUN: -plugin-arg-rename new-name=new_var %t/rename_var.cpp +// RUN: FileCheck %s < %t/rename_var.cpp --check-prefix=VAR + +// VAR: int func() { +// VAR-NEXT: int new_var = 2, b = 2; +// VAR-NEXT: new_var = b + new_var; +// VAR-NEXT: new_var++; +// VAR-NEXT: return new_var; +// VAR-NEXT: } + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=var\ +// RUN: -plugin-arg-rename cur-name=c\ +// RUN: -plugin-arg-rename new-name=new_var %t/rename_non_existent_var.cpp +// RUN: FileCheck %s < %t/rename_non_existent_var.cpp --check-prefix=NON_EXIST_VAR + +// NON_EXIST_VAR: int func() { +// NON_EXIST_VAR-NEXT: int a = 2; +// NON_EXIST_VAR-NEXT: int b = 3; +// NON_EXIST_VAR-NEXT: b += a; +// NON_EXIST_VAR-NEXT: a++; +// NON_EXIST_VAR-NEXT: return b - a; +// NON_EXIST_VAR-NEXT: } + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=func\ +// RUN: -plugin-arg-rename cur-name=function\ +// RUN: -plugin-arg-rename new-name=new_func %t/rename_func.cpp +// RUN: FileCheck %s < %t/rename_func.cpp --check-prefix=FUNC + +// FUNC: int new_func(int param) { +// FUNC-NEXT: int a; +// FUNC-NEXT: a = 2; +// FUNC-NEXT: return param + a; +// FUNC-NEXT: } +// FUNC-NEXT: int other_func(){ +// FUNC-NEXT: new_func(3); +// FUNC-NEXT: int a = new_func(2) + 3; +// FUNC-NEXT: return a; +// FUNC-NEXT: } + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=func\ +// RUN: -plugin-arg-rename cur-name=function\ +// RUN: -plugin-arg-rename new-name=f %t/rename_non_existent_func.cpp +// RUN: FileCheck %s < %t/rename_non_existent_func.cpp --check-prefix=NON_EXIST_FUNC + +// NON_EXIST_FUNC: int func(int a) { +// NON_EXIST_FUNC-NEXT: int b = 2; +// NON_EXIST_FUNC-NEXT: return a + b; +// NON_EXIST_FUNC-NEXT: } +// NON_EXIST_FUNC: void func2() { +// NON_EXIST_FUNC-NEXT: int c = func(2); +// NON_EXIST_FUNC-NEXT: int b = func(c) + func(3); +// NON_EXIST_FUNC-NEXT: } + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=class\ +// RUN: -plugin-arg-rename cur-name=Base\ +// RUN: -plugin-arg-rename new-name=SimpleClass %t/rename_class.cpp +// RUN: FileCheck %s < %t/rename_class.cpp --check-prefix=CLASS + +// CLASS: class SimpleClass{ +// CLASS-NEXT: private: +// CLASS-NEXT: int a; +// CLASS-NEXT: int b; +// CLASS-NEXT: public: +// CLASS-NEXT: SimpleClass() {} +// CLASS-NEXT: SimpleClass(int a, int b): a(a), b(b) {} +// CLASS-NEXT: ~SimpleClass(); +// CLASS-NEXT: }; +// CLASS: void func() { +// CLASS-NEXT: SimpleClass a; +// CLASS-NEXT: SimpleClass* var = new SimpleClass(1, 2); +// CLASS-NEXT: delete var; +// CLASS-NEXT: } + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=class\ +// RUN: -plugin-arg-rename cur-name=B\ +// RUN: -plugin-arg-rename new-name=C %t/rename_non_existent_class.cpp +// RUN: FileCheck %s < %t/rename_non_existent_class.cpp --check-prefix=NON_EXIST_CLASS + +// NON_EXIST_CLASS: class A{ +// NON_EXIST_CLASS-NEXT: private: +// NON_EXIST_CLASS-NEXT: int var1; +// NON_EXIST_CLASS-NEXT: double var2; +// NON_EXIST_CLASS-NEXT: public: +// NON_EXIST_CLASS-NEXT: A() {}; +// NON_EXIST_CLASS-NEXT: ~A() {}; +// NON_EXIST_CLASS-NEXT: }; +// NON_EXIST_CLASS: void func() { +// NON_EXIST_CLASS-NEXT: A var1; +// NON_EXIST_CLASS-NEXT: A* var2 = new A; +// NON_EXIST_CLASS-NEXT: delete var2; +// NON_EXIST_CLASS-NEXT: } + +// RUN: %clang_cc1 -load %llvmshlibdir/RenamePlugin%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 cur-name="Current identifier name" +// HELP-NEXT: -plugin-arg-rename new-name="New identifier name" + +// RUN: not %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename cur-name=B\ +// RUN: -plugin-arg-rename new-name=C\ +// RUN: 2>&1 | FileCheck %s --check-prefix=ERROR + +// RUN: not %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename cur-name=B\ +// RUN: -plugin-arg-rename new-name=C\ +// RUN: -plugin-arg-rename param=val\ +// RUN: 2>&1 | FileCheck %s --check-prefix=ERROR + +// RUN: not %clang_cc1 -load %llvmshlibdir/RenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=undefined\ +// RUN: -plugin-arg-rename cur-name=B\ +// RUN: -plugin-arg-rename new-name=C\ +// RUN: 2>&1 | FileCheck %s --check-prefix=ERROR + +// RUN: not %clang_cc1 -load %llvmshlibdir/RenamePlugin%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 + +//--- rename_var.cpp +int func() { + int a = 2, b = 2; + a = b + a; + a++; + return a; +} +//--- rename_non_existent_var.cpp +int func() { + int a = 2; + int b = 3; + b += a; + a++; + return b - a; +} +//--- rename_func.cpp +int function(int param) { + int a; + a = 2; + return param + a; +} +int other_func(){ + function(3); + int a = function(2) + 3; + return a; +} +//--- rename_non_existent_func.cpp +int func(int a) { + int b = 2; + return a + b; +} + +void func2() { + int c = func(2); + int b = func(c) + func(3); +} +//--- rename_class.cpp +class Base{ + private: + int a; + int b; + public: + Base() {} + Base(int a, int b): a(a), b(b) {} + ~Base(); +}; + +void func() { + Base a; + Base* var = new Base(1, 2); + delete var; +} +//--- rename_non_existent_class.cpp +class A{ + private: + int var1; + double var2; + public: + A() {}; + ~A() {}; +}; + +void func() { + A var1; + A* var2 = new A; + delete var2; +}