diff --git a/clang/labs/lab1/prokofev_kirill/CMakeLists.txt b/clang/labs/lab1/prokofev_kirill/CMakeLists.txt new file mode 100644 index 0000000000000..0d2cc22339580 --- /dev/null +++ b/clang/labs/lab1/prokofev_kirill/CMakeLists.txt @@ -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) diff --git a/clang/labs/lab1/prokofev_kirill/RenameIdentificator.cpp b/clang/labs/lab1/prokofev_kirill/RenameIdentificator.cpp new file mode 100644 index 0000000000000..6a4603dd7421b --- /dev/null +++ b/clang/labs/lab1/prokofev_kirill/RenameIdentificator.cpp @@ -0,0 +1,200 @@ +#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 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(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 + CreateASTConsumer(clang::CompilerInstance &CI, + clang::StringRef InFile) override { + return std::make_unique(CI, type, oldName, newName); + } + +protected: + bool ParseArgs(const clang::CompilerInstance &CI, + const std::vector &args) override { + std::vector> params = { + {"type=", ""}, {"oldName=", ""}, {"newName=", ""}}; + if (!args.empty()) { + for (int i = 0; i < args.size(); i++) { + if (args[i] == "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 ¶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; + } + 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 + X("rename", "Rename variable, function or class"); diff --git a/clang/test/lab1/prokofev_kirill/test.cpp b/clang/test/lab1/prokofev_kirill/test.cpp new file mode 100644 index 0000000000000..9baa73ecd71ad --- /dev/null +++ b/clang/test/lab1/prokofev_kirill/test.cpp @@ -0,0 +1,101 @@ +// 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(int a, int b): var1(a), var2(b) {} +// CLASS-NEXT: ~Beta(); +// CLASS-NEXT:protected: +// CLASS-NEXT: int var1; +// CLASS-NEXT: int var2; +// CLASS-NEXT: }; +// CLASS-NEXT: void SomeFunc(){ +// CLASS-NEXT: Beta obj1; +// CLASS-NEXT: Beta *obj2 = new Beta(5,6); +// CLASS-NEXT: Beta AplphaVar; +// CLASS-NEXT: delete obj2; +// CLASS-NEXT: } + +//--- class_test.cpp +class Alpha { +public: + Alpha(){}; + Alpha(int a, int b): var1(a), var2(b) {} + ~Alpha(); +protected: + int var1; + int var2; +}; +void SomeFunc(){ + Alpha obj1; + Alpha *obj2 = new Alpha(5,6); + Alpha AplphaVar; + delete obj2; +} + +// 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 + +// RUN: %clang_cc1 -load %llvmshlibdir/ProkofevRenamePlugin%pluginext\ +// RUN: -add-plugin rename\ +// RUN: -plugin-arg-rename type=func\ +// RUN: -plugin-arg-rename oldName=Foo\ +// RUN: -plugin-arg-rename newName=Function\ +// 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 + +