Advertisement
Guest User

Untitled

a guest
Mar 15th, 2024
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.81 KB | None | 0 0
  1. #include "clang/AST/AST.h"
  2. #include "clang/AST/ASTConsumer.h"
  3. #include "clang/AST/RecursiveASTVisitor.h"
  4. #include "clang/Format/Format.h"
  5. #include "clang/Frontend/CompilerInstance.h"
  6. #include "clang/Frontend/FrontendActions.h"
  7. #include "clang/Rewrite/Core/Rewriter.h"
  8. #include "clang/Tooling/CommonOptionsParser.h"
  9. #include "clang/Tooling/Core/Replacement.h"
  10. #include "clang/Tooling/Tooling.h"
  11. #include "llvm/Support/CommandLine.h"
  12. #include "llvm/Support/Signals.h"
  13. #include "clang/Tooling/Inclusions/HeaderIncludes.h"
  14. #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
  15. #include "clang/Basic/SourceLocation.h"
  16.  
  17. #include <codecvt>
  18. #include <filesystem>
  19. #include <iostream>
  20. #include <locale>
  21. #include <numeric>
  22. #include <windows.h>
  23. #include <unordered_set>
  24.  
  25. using namespace llvm;
  26. using namespace clang;
  27. using namespace clang::tooling;
  28. using namespace clang::format;
  29. using namespace cl;
  30.  
  31. static OptionCategory EmbeddedCoverageCode("Embedded coverage code");
  32.  
  33. static cl::opt<bool> RecursiveDirectoryTraversal(
  34.     "recursive",
  35.     desc(
  36.         R"(The presence of the flag indicates that one of the command arguments
  37.        is passed a directory that must be traversed recursively by
  38.        processing files with the specified extensions --extensions. Used
  39.        together with --extensions.)"),
  40.     cl::init(false), cat(EmbeddedCoverageCode));
  41.  
  42. static cl::opt<bool>
  43.     ShowMacrosName("show-macros-name",
  44.                    desc("Show macros to insert into the source code."),
  45.                    cl::init(false), cat(EmbeddedCoverageCode));
  46.  
  47. static list<std::string>
  48.     Extensions("extensions",
  49.                desc(R"(A set of strings with file extensions that need to be
  50.                    processed when recursively traversing a directory. Used
  51.                    in conjunction with --recursive.)"),
  52.                cat(EmbeddedCoverageCode));
  53.  
  54. static list<std::string> ExcludeFiles(
  55.     "exclude-files",
  56.     desc(R"(Allows you to specify files that need to be excluded when
  57.         recursively traversing the directory. Used together with
  58.         --recursive.)"),
  59.     cat(EmbeddedCoverageCode));
  60.  
  61. static cl::list<std::string> FileNames(cl::Positional,
  62.                                        cl::desc("[@<file>] [<file> ...]"),
  63.                                        cl::cat(EmbeddedCoverageCode));
  64.  
  65. static cl::opt<std::string> IncludeFile(
  66.     "include",
  67.     desc("The path to the attached header file with the code coverage logger."),
  68.     cl::init(""), cat(EmbeddedCoverageCode));
  69. static std::string IncludeFileSave("");
  70.  
  71. static std::set<std::string> SourceFilesSet;
  72.  
  73. static std::map<std::string, std::string> MacrosName{
  74.     {"if", "COV_LOG_IF();"},         {"else", "COV_LOG_ELSE();"},
  75.     {"for", "COV_LOG_FOR();"},       {"while", "COV_LOG_WHILE();"},
  76.     {"do while", "COV_LOG_DO();"},   {"switch case", "COV_LOG_CASE();"},
  77.     {"function", "COV_LOG_FUNC();"}, {"lambda", "COV_LOG_LAMBDA();"},
  78. };
  79.  
  80. class StatisticsCollector {
  81. public:
  82.   StatisticsCollector() = default;
  83.   void addMacro(const std::string &macroName) { Statistics[macroName]++; }
  84.   std::map<std::string, int> getStatistics() const { return Statistics; }
  85.  
  86. private:
  87.   std::map<std::string, int> Statistics;
  88. };
  89.  
  90. class StatementDeclarationVisitor
  91.     : public RecursiveASTVisitor<StatementDeclarationVisitor> {
  92. public:
  93.   explicit StatementDeclarationVisitor(ASTContext *Context, Rewriter &Rewrite)
  94.       : Context(Context), Rewrite(Rewrite) {}
  95.  
  96.   bool VisitIfStmt(IfStmt *If) {
  97.     auto IfBeginLoc = If->getThen()->getBeginLoc().getLocWithOffset(1);
  98.     if (!isInSourceFile(IfBeginLoc))
  99.       return true;
  100.     if (Rewrite.InsertText(IfBeginLoc, MacrosName["if"], true, true) == false)
  101.       Statistics.addMacro(MacrosName["if"]);
  102.     if (If->hasElseStorage()) {
  103.       if (const auto *ElseIf = dyn_cast_or_null<IfStmt>(If->getElse())) {
  104.         return true;
  105.       } else {
  106.         auto ElseBeginLoc = If->getElse()->getBeginLoc().getLocWithOffset(1);
  107.         if (Rewrite.InsertText(ElseBeginLoc, MacrosName["else"], true, true) ==
  108.             false)
  109.           Statistics.addMacro(MacrosName["else"]);
  110.       }
  111.     }
  112.     return true;
  113.   }
  114.  
  115.   bool VisitForStmt(ForStmt *For) {
  116.     auto ForBeginLoc = For->getBody()->getBeginLoc().getLocWithOffset(1);
  117.     if (!isInSourceFile(ForBeginLoc))
  118.       return true;
  119.     if (Rewrite.InsertText(ForBeginLoc, MacrosName["for"], true, true) == false)
  120.       Statistics.addMacro(MacrosName["for"]);
  121.     return true;
  122.   }
  123.  
  124.   bool VisitCXXForRangeStmt(CXXForRangeStmt *For) {
  125.     auto ForBeginLoc = For->getBody()->getBeginLoc().getLocWithOffset(1);
  126.     if (!isInSourceFile(ForBeginLoc))
  127.       return true;
  128.     if (Rewrite.InsertText(ForBeginLoc, MacrosName["for"], true, true) == false)
  129.       Statistics.addMacro(MacrosName["for"]);
  130.     return true;
  131.   }
  132.  
  133.   bool VisitWhileStmt(WhileStmt *While) {
  134.     auto WhileBeginLoc = While->getBody()->getBeginLoc().getLocWithOffset(1);
  135.     if (!isInSourceFile(WhileBeginLoc))
  136.       return true;
  137.     if (Rewrite.InsertText(WhileBeginLoc, MacrosName["while"], true, true) ==
  138.         false)
  139.       Statistics.addMacro(MacrosName["while"]);
  140.     return true;
  141.   }
  142.  
  143.   bool VisitDoStmt(DoStmt *Do) {
  144.     auto DoBeginLoc = Do->getBody()->getBeginLoc().getLocWithOffset(1);
  145.     if (!isInSourceFile(DoBeginLoc))
  146.       return true;
  147.     if (Rewrite.InsertText(DoBeginLoc, MacrosName["do while"], true, true) == false)
  148.       Statistics.addMacro(MacrosName["do while"]);
  149.     return true;
  150.   }
  151.  
  152.   bool VisitSwitchStmt(SwitchStmt *Switch) {
  153.     auto CurrentCase = Switch->getSwitchCaseList();
  154.     while (CurrentCase) {
  155.       auto CaseBeginLoc = CurrentCase->getSubStmt()->getBeginLoc();
  156.       if (!isInSourceFile(CaseBeginLoc))
  157.         return true;
  158.       if (Rewrite.InsertText(CaseBeginLoc, MacrosName["switch case"], true, true) ==
  159.           false)
  160.         Statistics.addMacro(MacrosName["switch case"]);
  161.       CurrentCase = CurrentCase->getNextSwitchCase();
  162.     }
  163.     return true;
  164.   }
  165.  
  166.   bool VisitFunctionDecl(FunctionDecl *Func) {
  167.     if (!Func->isThisDeclarationADefinition())
  168.       return true;
  169.     if (Func->getBody()) {
  170.       auto FuncBeginLoc = Func->getBody()->getBeginLoc().getLocWithOffset(1);
  171.       if (!isInSourceFile(FuncBeginLoc))
  172.         return true;
  173.       if (Rewrite.InsertText(FuncBeginLoc, MacrosName["function"], true, true) ==
  174.           false)
  175.         Statistics.addMacro(MacrosName["function"]);
  176.     }
  177.     return true;
  178.   }
  179.  
  180.   bool VisitLambdaExpr(LambdaExpr *Lambda) {
  181.     auto LambdaBeginLoc = Lambda->getBody()->getBeginLoc().getLocWithOffset(1);
  182.     if (!isInSourceFile(LambdaBeginLoc))
  183.       return true;
  184.     if (Rewrite.InsertText(LambdaBeginLoc, MacrosName["lambda"], true, true) ==
  185.         false)
  186.       Statistics.addMacro(MacrosName["lambda"]);
  187.     return true;
  188.   }
  189.  
  190.   bool VisitFunctionTemplateDecl(FunctionTemplateDecl *FuncTemplate) {
  191.     FunctionDecl *FuncDecl = FuncTemplate->getTemplatedDecl();
  192.     if (FuncDecl->isThisDeclarationADefinition()) {
  193.       if (!isInSourceFile(FuncDecl->getBeginLoc()))
  194.         return true;
  195.       if (Stmt *FuncBody = FuncDecl->getBody()) {
  196.         SourceLocation FuncBeginLoc =
  197.             FuncBody->getBeginLoc().getLocWithOffset(3);
  198.         if (Rewrite.InsertText(FuncBeginLoc, MacrosName["function"], true, true) ==
  199.             false)
  200.           Statistics.addMacro(MacrosName["function"]);
  201.       } else {
  202.         errs() << "Warning: Template function "
  203.                << FuncDecl->getDeclName().getAsString() << " in file "
  204.                << FuncDecl->getLocation().printToString(
  205.                       Context->getSourceManager())
  206.                << " not instantiated.\n";
  207.         return true;
  208.       }
  209.     }
  210.     return true;
  211.   }
  212.  
  213.   StatisticsCollector Statistics;
  214.  
  215. private:
  216.   bool isInSourceFile(const SourceLocation &loc) const {
  217.     SourceManager &SM = Context->getSourceManager();
  218.     std::string fileName = SM.getFilename(loc).str();
  219.     return SourceFilesSet.find(fileName) != SourceFilesSet.end();
  220.   }
  221.  
  222.   ASTContext *Context;
  223.   Rewriter &Rewrite;
  224. };
  225.  
  226. static std::optional<Replacement>
  227. insertInclude(const StringRef &PathFileToInsert,
  228.               const StringRef &PathIncludeFile) {
  229.   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
  230.       MemoryBuffer::getFile(PathFileToInsert);
  231.  
  232.   if (std::error_code EC = CodeOrErr.getError()) {
  233.     errs() << EC.message() << "\n";
  234.     return std::optional<Replacement>();
  235.   }
  236.  
  237.   std::unique_ptr<MemoryBuffer> Code = std::move(CodeOrErr.get());
  238.  
  239.   if (Code->getBufferSize() == 0)
  240.     return std::optional<Replacement>();
  241.   StringRef BufStr = Code->getBuffer();
  242.  
  243.   FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LanguageKind::LK_Cpp);
  244.   IncludeStyle &GoogleIncludeStyle = GoogleStyle.IncludeStyle;
  245.  
  246.   HeaderIncludes HeaderInclusions(PathFileToInsert, BufStr, GoogleIncludeStyle);
  247.   return HeaderInclusions.insert(PathIncludeFile, false,
  248.                                  IncludeDirective::Include);
  249. }
  250.  
  251. class CoverageConsumer : public ASTConsumer {
  252. public:
  253.   explicit CoverageConsumer(ASTContext *Context)
  254.       : Rewrite(Context->getSourceManager(), Context->getLangOpts()),
  255.         Visitor(Context, Rewrite) {}
  256.  
  257.   void HandleTranslationUnit(ASTContext &Context) override {
  258.     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
  259.     SourceManager &SM = Context.getSourceManager();
  260.     FileID MainFileID = SM.getMainFileID();
  261.     StringRef FileName = SM.getFilename(SM.getLocForStartOfFile(MainFileID));
  262.     if (!IncludeFileSave.empty()) {
  263.       auto OptionalReplace =
  264.           insertInclude(FileName, IncludeFileSave);
  265.       if (OptionalReplace.has_value())
  266.         applyAllReplacements(Replacements(OptionalReplace.value()), Rewrite);
  267.     }
  268.     Rewrite.overwriteChangedFiles();
  269.     if (!Visitor.Statistics.getStatistics().empty()) {
  270.       for (auto const &[macros, count] : Visitor.Statistics.getStatistics())
  271.         std::cout << macros << " - " << count << std::endl;
  272.     }
  273.   }
  274.  
  275. private:
  276.   Rewriter Rewrite;
  277.   StatementDeclarationVisitor Visitor;
  278. };
  279.  
  280. class ASTCoverageAction : public ASTFrontendAction {
  281. public:
  282.   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
  283.                                                  StringRef file) override {
  284.     CI.getDiagnostics().setClient(new IgnoringDiagConsumer, true);
  285.     return std::make_unique<CoverageConsumer>(&CI.getASTContext());
  286.   }
  287. };
  288.  
  289. class IgnoreDiagnosticConsumer : public DiagnosticConsumer {
  290. public:
  291.   void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
  292.                         const Diagnostic &Info) override {}
  293. };
  294.  
  295. static void formatCode(const StringRef& FileName) {
  296.   if (FileName.empty()) {
  297.     errs() << "Error: Empty file names are not allowed.\n";
  298.     return;
  299.   }
  300.  
  301.   ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
  302.       MemoryBuffer::getFile(FileName);
  303.  
  304.   if (std::error_code EC = CodeOrErr.getError()) {
  305.     errs() << EC.message() << "\n";
  306.     return;
  307.   }
  308.  
  309.   std::unique_ptr<MemoryBuffer> Code = std::move(CodeOrErr.get());
  310.  
  311.   if (Code->getBufferSize() == 0)
  312.     return;
  313.  
  314.   StringRef BufStr = Code->getBuffer();
  315.  
  316.   const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
  317.   if (InvalidBOM) {
  318.     errs() << "Error: Encoding with unsupported byte order sign found \""
  319.            << InvalidBOM << "\" in file '" << FileName << "'"
  320.            << ".\n";
  321.     return;
  322.   }
  323.  
  324.   std::vector<Range> Ranges;
  325.   Ranges.push_back(Range(0, Code->getBuffer().size()));
  326.  
  327.   FormatStyle Style = getGoogleStyle(FormatStyle::LanguageKind::LK_Cpp);
  328.   Style.AllowShortBlocksOnASingleLine = FormatStyle::ShortBlockStyle::SBS_Never;
  329.   Style.AllowShortFunctionsOnASingleLine =
  330.       FormatStyle::ShortFunctionStyle::SFS_None;
  331.   Style.AllowShortIfStatementsOnASingleLine =
  332.       FormatStyle::ShortIfStyle::SIS_Never;
  333.   Style.AllowShortLambdasOnASingleLine =
  334.       FormatStyle::ShortLambdaStyle::SLS_None;
  335.   Style.AllowShortLoopsOnASingleLine = false;
  336.   Style.BraceWrapping.AfterCaseLabel = true;
  337.   Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always;
  338.   Style.BraceWrapping.AfterFunction = true;
  339.   Style.BraceWrapping.BeforeElse = true;
  340.   Style.BraceWrapping.BeforeLambdaBody = true;
  341.   Style.BraceWrapping.BeforeWhile = false;
  342.   Style.BraceWrapping.SplitEmptyFunction = true;
  343.   Style.InsertBraces = true;
  344.   Style.RemoveBracesLLVM = false;
  345.   Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
  346.   Style.SortIncludes = FormatStyle::SI_CaseSensitive;
  347.   Style.IndentWidth = 4;
  348.   Style.ColumnLimit = 0;
  349.  
  350.   unsigned CursorPosition{0};
  351.   Replacements Replaces =
  352.       sortIncludes(Style, Code->getBuffer(), Ranges, FileName, &CursorPosition);
  353.  
  354.   auto ChangedCode = applyAllReplacements(Code->getBuffer(), Replaces);
  355.   if (!ChangedCode) {
  356.     errs() << toString(ChangedCode.takeError()) << "\n";
  357.     return;
  358.   }
  359.  
  360.   Ranges = calculateRangesAfterReplacements(Replaces, Ranges);
  361.  
  362.   FormattingAttemptStatus Status;
  363.   Replacements FormatChanges =
  364.       reformat(Style, *ChangedCode, Ranges, FileName, &Status);
  365.  
  366.   Replaces = Replaces.merge(FormatChanges);
  367.  
  368.   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
  369.   IgnoreDiagnosticConsumer IgnoreDiagnostics;
  370.   DiagnosticsEngine Diagnostics(
  371.       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
  372.       &IgnoreDiagnostics, false);
  373.  
  374.   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
  375.       new vfs::InMemoryFileSystem);
  376.   FileManager Files(FileSystemOptions(), InMemoryFileSystem);
  377.   SourceManager Sources(Diagnostics, Files);
  378.  
  379.   InMemoryFileSystem.get()->addFileNoOwn(FileName, 0, *Code);
  380.   auto File = Files.getOptionalFileRef(FileName);
  381.   assert(File && "The file has not been added to MemFS");
  382.   FileID ID = Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
  383.  
  384.   Rewriter Rewrite(Sources, LangOptions());
  385.   applyAllReplacements(Replaces, Rewrite);
  386.  
  387.   if (Rewrite.overwriteChangedFiles())
  388.     return;
  389. }
  390.  
  391. namespace fs = std::filesystem;
  392. static void traverseDirectory(const fs::path &directory,
  393.                               const std::vector<std::string> &extensions,
  394.                               std::set<std::string> &fileSet) {
  395.   for (const auto &entry : fs::directory_iterator(directory)) {
  396.     if (fs::is_directory(entry.status())) {
  397.       traverseDirectory(entry.path(), extensions, fileSet);
  398.     } else if (fs::is_regular_file(entry.status())) {
  399.       for (const auto &ext : extensions) {
  400.         if (entry.path().extension() == ext) {
  401.           fileSet.insert(entry.path().string());
  402.         }
  403.       }
  404.     }
  405.   }
  406. }
  407.  
  408. int main(int argc, const char **argv) {
  409.   setlocale(LC_ALL, "Ru");
  410.   sys::PrintStackTraceOnErrorSignal(argv[0]);
  411.   cl::ParseCommandLineOptions(argc, argv);
  412.   IncludeFileSave = IncludeFile.getValue();
  413.  
  414.   if (ShowMacrosName) {
  415.     for (const auto &[key, value] : MacrosName)
  416.       std::cout << key << " has value " << value << std::endl;
  417.     return 0;
  418.   }
  419.  
  420.   if (RecursiveDirectoryTraversal == true && !FileNames.empty())
  421.   {
  422.     if (Extensions.empty()) {
  423.       errs() << "Error: File extensions are not specified.\n";
  424.       return 1;
  425.     }
  426.     std::set<std::string> files;
  427.     fs::path directoryPath = FileNames.front();
  428.     FileNames.erase(FileNames.begin());
  429.     if (!FileNames.empty())
  430.       SourceFilesSet.insert(FileNames.begin(), FileNames.end());
  431.     if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) {
  432.       errs() << "Error: Invalid directory path.\n";
  433.       return 1;
  434.     }
  435.     traverseDirectory(directoryPath, Extensions, files);
  436.     std::set<std::string> ExcludeFilesSet(ExcludeFiles.begin(),
  437.                                           ExcludeFiles.end());
  438.     for (auto fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
  439.       if (ExcludeFilesSet.find(*fileIt) == ExcludeFilesSet.end())
  440.         SourceFilesSet.insert(*fileIt);
  441.     }
  442.   } else {
  443.     SourceFilesSet.insert(FileNames.begin(), FileNames.end());
  444.   }
  445.   std::vector<const char *> args;
  446.   args.reserve(1 + SourceFilesSet.size());
  447.   args.push_back(argv[0]);
  448.   std::transform(SourceFilesSet.begin(), SourceFilesSet.end(),
  449.                  std::back_inserter(args),
  450.                  [](const std::string &file) { return file.c_str(); });
  451.   int countArgs = args.size();
  452.  
  453.   auto ExpectedParser =
  454.       CommonOptionsParser::create(countArgs, args.data(), EmbeddedCoverageCode);
  455.   if (!ExpectedParser) {
  456.     errs() << ExpectedParser.takeError();
  457.     return 1;
  458.   }
  459.   auto &OptionsParser = ExpectedParser.get();
  460.  
  461.   unsigned FileNo = 1;
  462.   for (const auto &FileName : SourceFilesSet) {
  463.     std::cout << "[" << FileNo++ << "/" << SourceFilesSet.size()
  464.               << "] file " << FileName << std::endl;
  465.     formatCode(FileName);
  466.     ClangTool Tool(OptionsParser.getCompilations(), std::vector({FileName}));
  467.     int Result = Tool.run(newFrontendActionFactory<ASTCoverageAction>().get());
  468.     if (Result != 0)
  469.       errs() << "An error in the operation of the toolkit.\n";
  470.   }
  471. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement