Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "clang/AST/AST.h"
- #include "clang/AST/ASTConsumer.h"
- #include "clang/AST/RecursiveASTVisitor.h"
- #include "clang/Format/Format.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Frontend/FrontendActions.h"
- #include "clang/Rewrite/Core/Rewriter.h"
- #include "clang/Tooling/CommonOptionsParser.h"
- #include "clang/Tooling/Core/Replacement.h"
- #include "clang/Tooling/Tooling.h"
- #include "llvm/Support/CommandLine.h"
- #include "llvm/Support/Signals.h"
- #include "clang/Tooling/Inclusions/HeaderIncludes.h"
- #include "clang/Tooling/Inclusions/HeaderAnalysis.h"
- #include "clang/Basic/SourceLocation.h"
- #include <codecvt>
- #include <filesystem>
- #include <iostream>
- #include <locale>
- #include <numeric>
- #include <windows.h>
- #include <unordered_set>
- using namespace llvm;
- using namespace clang;
- using namespace clang::tooling;
- using namespace clang::format;
- using namespace cl;
- static OptionCategory EmbeddedCoverageCode("Embedded coverage code");
- static cl::opt<bool> RecursiveDirectoryTraversal(
- "recursive",
- desc(
- R"(The presence of the flag indicates that one of the command arguments
- is passed a directory that must be traversed recursively by
- processing files with the specified extensions --extensions. Used
- together with --extensions.)"),
- cl::init(false), cat(EmbeddedCoverageCode));
- static cl::opt<bool>
- ShowMacrosName("show-macros-name",
- desc("Show macros to insert into the source code."),
- cl::init(false), cat(EmbeddedCoverageCode));
- static list<std::string>
- Extensions("extensions",
- desc(R"(A set of strings with file extensions that need to be
- processed when recursively traversing a directory. Used
- in conjunction with --recursive.)"),
- cat(EmbeddedCoverageCode));
- static list<std::string> ExcludeFiles(
- "exclude-files",
- desc(R"(Allows you to specify files that need to be excluded when
- recursively traversing the directory. Used together with
- --recursive.)"),
- cat(EmbeddedCoverageCode));
- static cl::list<std::string> FileNames(cl::Positional,
- cl::desc("[@<file>] [<file> ...]"),
- cl::cat(EmbeddedCoverageCode));
- static cl::opt<std::string> IncludeFile(
- "include",
- desc("The path to the attached header file with the code coverage logger."),
- cl::init(""), cat(EmbeddedCoverageCode));
- static std::string IncludeFileSave("");
- static std::set<std::string> SourceFilesSet;
- static std::map<std::string, std::string> MacrosName{
- {"if", "COV_LOG_IF();"}, {"else", "COV_LOG_ELSE();"},
- {"for", "COV_LOG_FOR();"}, {"while", "COV_LOG_WHILE();"},
- {"do while", "COV_LOG_DO();"}, {"switch case", "COV_LOG_CASE();"},
- {"function", "COV_LOG_FUNC();"}, {"lambda", "COV_LOG_LAMBDA();"},
- };
- class StatisticsCollector {
- public:
- StatisticsCollector() = default;
- void addMacro(const std::string ¯oName) { Statistics[macroName]++; }
- std::map<std::string, int> getStatistics() const { return Statistics; }
- private:
- std::map<std::string, int> Statistics;
- };
- class StatementDeclarationVisitor
- : public RecursiveASTVisitor<StatementDeclarationVisitor> {
- public:
- explicit StatementDeclarationVisitor(ASTContext *Context, Rewriter &Rewrite)
- : Context(Context), Rewrite(Rewrite) {}
- bool VisitIfStmt(IfStmt *If) {
- auto IfBeginLoc = If->getThen()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(IfBeginLoc))
- return true;
- if (Rewrite.InsertText(IfBeginLoc, MacrosName["if"], true, true) == false)
- Statistics.addMacro(MacrosName["if"]);
- if (If->hasElseStorage()) {
- if (const auto *ElseIf = dyn_cast_or_null<IfStmt>(If->getElse())) {
- return true;
- } else {
- auto ElseBeginLoc = If->getElse()->getBeginLoc().getLocWithOffset(1);
- if (Rewrite.InsertText(ElseBeginLoc, MacrosName["else"], true, true) ==
- false)
- Statistics.addMacro(MacrosName["else"]);
- }
- }
- return true;
- }
- bool VisitForStmt(ForStmt *For) {
- auto ForBeginLoc = For->getBody()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(ForBeginLoc))
- return true;
- if (Rewrite.InsertText(ForBeginLoc, MacrosName["for"], true, true) == false)
- Statistics.addMacro(MacrosName["for"]);
- return true;
- }
- bool VisitCXXForRangeStmt(CXXForRangeStmt *For) {
- auto ForBeginLoc = For->getBody()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(ForBeginLoc))
- return true;
- if (Rewrite.InsertText(ForBeginLoc, MacrosName["for"], true, true) == false)
- Statistics.addMacro(MacrosName["for"]);
- return true;
- }
- bool VisitWhileStmt(WhileStmt *While) {
- auto WhileBeginLoc = While->getBody()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(WhileBeginLoc))
- return true;
- if (Rewrite.InsertText(WhileBeginLoc, MacrosName["while"], true, true) ==
- false)
- Statistics.addMacro(MacrosName["while"]);
- return true;
- }
- bool VisitDoStmt(DoStmt *Do) {
- auto DoBeginLoc = Do->getBody()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(DoBeginLoc))
- return true;
- if (Rewrite.InsertText(DoBeginLoc, MacrosName["do while"], true, true) == false)
- Statistics.addMacro(MacrosName["do while"]);
- return true;
- }
- bool VisitSwitchStmt(SwitchStmt *Switch) {
- auto CurrentCase = Switch->getSwitchCaseList();
- while (CurrentCase) {
- auto CaseBeginLoc = CurrentCase->getSubStmt()->getBeginLoc();
- if (!isInSourceFile(CaseBeginLoc))
- return true;
- if (Rewrite.InsertText(CaseBeginLoc, MacrosName["switch case"], true, true) ==
- false)
- Statistics.addMacro(MacrosName["switch case"]);
- CurrentCase = CurrentCase->getNextSwitchCase();
- }
- return true;
- }
- bool VisitFunctionDecl(FunctionDecl *Func) {
- if (!Func->isThisDeclarationADefinition())
- return true;
- if (Func->getBody()) {
- auto FuncBeginLoc = Func->getBody()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(FuncBeginLoc))
- return true;
- if (Rewrite.InsertText(FuncBeginLoc, MacrosName["function"], true, true) ==
- false)
- Statistics.addMacro(MacrosName["function"]);
- }
- return true;
- }
- bool VisitLambdaExpr(LambdaExpr *Lambda) {
- auto LambdaBeginLoc = Lambda->getBody()->getBeginLoc().getLocWithOffset(1);
- if (!isInSourceFile(LambdaBeginLoc))
- return true;
- if (Rewrite.InsertText(LambdaBeginLoc, MacrosName["lambda"], true, true) ==
- false)
- Statistics.addMacro(MacrosName["lambda"]);
- return true;
- }
- bool VisitFunctionTemplateDecl(FunctionTemplateDecl *FuncTemplate) {
- FunctionDecl *FuncDecl = FuncTemplate->getTemplatedDecl();
- if (FuncDecl->isThisDeclarationADefinition()) {
- if (!isInSourceFile(FuncDecl->getBeginLoc()))
- return true;
- if (Stmt *FuncBody = FuncDecl->getBody()) {
- SourceLocation FuncBeginLoc =
- FuncBody->getBeginLoc().getLocWithOffset(3);
- if (Rewrite.InsertText(FuncBeginLoc, MacrosName["function"], true, true) ==
- false)
- Statistics.addMacro(MacrosName["function"]);
- } else {
- errs() << "Warning: Template function "
- << FuncDecl->getDeclName().getAsString() << " in file "
- << FuncDecl->getLocation().printToString(
- Context->getSourceManager())
- << " not instantiated.\n";
- return true;
- }
- }
- return true;
- }
- StatisticsCollector Statistics;
- private:
- bool isInSourceFile(const SourceLocation &loc) const {
- SourceManager &SM = Context->getSourceManager();
- std::string fileName = SM.getFilename(loc).str();
- return SourceFilesSet.find(fileName) != SourceFilesSet.end();
- }
- ASTContext *Context;
- Rewriter &Rewrite;
- };
- static std::optional<Replacement>
- insertInclude(const StringRef &PathFileToInsert,
- const StringRef &PathIncludeFile) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
- MemoryBuffer::getFile(PathFileToInsert);
- if (std::error_code EC = CodeOrErr.getError()) {
- errs() << EC.message() << "\n";
- return std::optional<Replacement>();
- }
- std::unique_ptr<MemoryBuffer> Code = std::move(CodeOrErr.get());
- if (Code->getBufferSize() == 0)
- return std::optional<Replacement>();
- StringRef BufStr = Code->getBuffer();
- FormatStyle GoogleStyle = getGoogleStyle(FormatStyle::LanguageKind::LK_Cpp);
- IncludeStyle &GoogleIncludeStyle = GoogleStyle.IncludeStyle;
- HeaderIncludes HeaderInclusions(PathFileToInsert, BufStr, GoogleIncludeStyle);
- return HeaderInclusions.insert(PathIncludeFile, false,
- IncludeDirective::Include);
- }
- class CoverageConsumer : public ASTConsumer {
- public:
- explicit CoverageConsumer(ASTContext *Context)
- : Rewrite(Context->getSourceManager(), Context->getLangOpts()),
- Visitor(Context, Rewrite) {}
- void HandleTranslationUnit(ASTContext &Context) override {
- Visitor.TraverseDecl(Context.getTranslationUnitDecl());
- SourceManager &SM = Context.getSourceManager();
- FileID MainFileID = SM.getMainFileID();
- StringRef FileName = SM.getFilename(SM.getLocForStartOfFile(MainFileID));
- if (!IncludeFileSave.empty()) {
- auto OptionalReplace =
- insertInclude(FileName, IncludeFileSave);
- if (OptionalReplace.has_value())
- applyAllReplacements(Replacements(OptionalReplace.value()), Rewrite);
- }
- Rewrite.overwriteChangedFiles();
- if (!Visitor.Statistics.getStatistics().empty()) {
- for (auto const &[macros, count] : Visitor.Statistics.getStatistics())
- std::cout << macros << " - " << count << std::endl;
- }
- }
- private:
- Rewriter Rewrite;
- StatementDeclarationVisitor Visitor;
- };
- class ASTCoverageAction : public ASTFrontendAction {
- public:
- std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
- StringRef file) override {
- CI.getDiagnostics().setClient(new IgnoringDiagConsumer, true);
- return std::make_unique<CoverageConsumer>(&CI.getASTContext());
- }
- };
- class IgnoreDiagnosticConsumer : public DiagnosticConsumer {
- public:
- void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
- const Diagnostic &Info) override {}
- };
- static void formatCode(const StringRef& FileName) {
- if (FileName.empty()) {
- errs() << "Error: Empty file names are not allowed.\n";
- return;
- }
- ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
- MemoryBuffer::getFile(FileName);
- if (std::error_code EC = CodeOrErr.getError()) {
- errs() << EC.message() << "\n";
- return;
- }
- std::unique_ptr<MemoryBuffer> Code = std::move(CodeOrErr.get());
- if (Code->getBufferSize() == 0)
- return;
- StringRef BufStr = Code->getBuffer();
- const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
- if (InvalidBOM) {
- errs() << "Error: Encoding with unsupported byte order sign found \""
- << InvalidBOM << "\" in file '" << FileName << "'"
- << ".\n";
- return;
- }
- std::vector<Range> Ranges;
- Ranges.push_back(Range(0, Code->getBuffer().size()));
- FormatStyle Style = getGoogleStyle(FormatStyle::LanguageKind::LK_Cpp);
- Style.AllowShortBlocksOnASingleLine = FormatStyle::ShortBlockStyle::SBS_Never;
- Style.AllowShortFunctionsOnASingleLine =
- FormatStyle::ShortFunctionStyle::SFS_None;
- Style.AllowShortIfStatementsOnASingleLine =
- FormatStyle::ShortIfStyle::SIS_Never;
- Style.AllowShortLambdasOnASingleLine =
- FormatStyle::ShortLambdaStyle::SLS_None;
- Style.AllowShortLoopsOnASingleLine = false;
- Style.BraceWrapping.AfterCaseLabel = true;
- Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always;
- Style.BraceWrapping.AfterFunction = true;
- Style.BraceWrapping.BeforeElse = true;
- Style.BraceWrapping.BeforeLambdaBody = true;
- Style.BraceWrapping.BeforeWhile = false;
- Style.BraceWrapping.SplitEmptyFunction = true;
- Style.InsertBraces = true;
- Style.RemoveBracesLLVM = false;
- Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always;
- Style.SortIncludes = FormatStyle::SI_CaseSensitive;
- Style.IndentWidth = 4;
- Style.ColumnLimit = 0;
- unsigned CursorPosition{0};
- Replacements Replaces =
- sortIncludes(Style, Code->getBuffer(), Ranges, FileName, &CursorPosition);
- auto ChangedCode = applyAllReplacements(Code->getBuffer(), Replaces);
- if (!ChangedCode) {
- errs() << toString(ChangedCode.takeError()) << "\n";
- return;
- }
- Ranges = calculateRangesAfterReplacements(Replaces, Ranges);
- FormattingAttemptStatus Status;
- Replacements FormatChanges =
- reformat(Style, *ChangedCode, Ranges, FileName, &Status);
- Replaces = Replaces.merge(FormatChanges);
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
- IgnoreDiagnosticConsumer IgnoreDiagnostics;
- DiagnosticsEngine Diagnostics(
- IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
- &IgnoreDiagnostics, false);
- IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
- new vfs::InMemoryFileSystem);
- FileManager Files(FileSystemOptions(), InMemoryFileSystem);
- SourceManager Sources(Diagnostics, Files);
- InMemoryFileSystem.get()->addFileNoOwn(FileName, 0, *Code);
- auto File = Files.getOptionalFileRef(FileName);
- assert(File && "The file has not been added to MemFS");
- FileID ID = Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
- Rewriter Rewrite(Sources, LangOptions());
- applyAllReplacements(Replaces, Rewrite);
- if (Rewrite.overwriteChangedFiles())
- return;
- }
- namespace fs = std::filesystem;
- static void traverseDirectory(const fs::path &directory,
- const std::vector<std::string> &extensions,
- std::set<std::string> &fileSet) {
- for (const auto &entry : fs::directory_iterator(directory)) {
- if (fs::is_directory(entry.status())) {
- traverseDirectory(entry.path(), extensions, fileSet);
- } else if (fs::is_regular_file(entry.status())) {
- for (const auto &ext : extensions) {
- if (entry.path().extension() == ext) {
- fileSet.insert(entry.path().string());
- }
- }
- }
- }
- }
- int main(int argc, const char **argv) {
- setlocale(LC_ALL, "Ru");
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- cl::ParseCommandLineOptions(argc, argv);
- IncludeFileSave = IncludeFile.getValue();
- if (ShowMacrosName) {
- for (const auto &[key, value] : MacrosName)
- std::cout << key << " has value " << value << std::endl;
- return 0;
- }
- if (RecursiveDirectoryTraversal == true && !FileNames.empty())
- {
- if (Extensions.empty()) {
- errs() << "Error: File extensions are not specified.\n";
- return 1;
- }
- std::set<std::string> files;
- fs::path directoryPath = FileNames.front();
- FileNames.erase(FileNames.begin());
- if (!FileNames.empty())
- SourceFilesSet.insert(FileNames.begin(), FileNames.end());
- if (!fs::exists(directoryPath) || !fs::is_directory(directoryPath)) {
- errs() << "Error: Invalid directory path.\n";
- return 1;
- }
- traverseDirectory(directoryPath, Extensions, files);
- std::set<std::string> ExcludeFilesSet(ExcludeFiles.begin(),
- ExcludeFiles.end());
- for (auto fileIt = files.begin(); fileIt != files.end(); ++fileIt) {
- if (ExcludeFilesSet.find(*fileIt) == ExcludeFilesSet.end())
- SourceFilesSet.insert(*fileIt);
- }
- } else {
- SourceFilesSet.insert(FileNames.begin(), FileNames.end());
- }
- std::vector<const char *> args;
- args.reserve(1 + SourceFilesSet.size());
- args.push_back(argv[0]);
- std::transform(SourceFilesSet.begin(), SourceFilesSet.end(),
- std::back_inserter(args),
- [](const std::string &file) { return file.c_str(); });
- int countArgs = args.size();
- auto ExpectedParser =
- CommonOptionsParser::create(countArgs, args.data(), EmbeddedCoverageCode);
- if (!ExpectedParser) {
- errs() << ExpectedParser.takeError();
- return 1;
- }
- auto &OptionsParser = ExpectedParser.get();
- unsigned FileNo = 1;
- for (const auto &FileName : SourceFilesSet) {
- std::cout << "[" << FileNo++ << "/" << SourceFilesSet.size()
- << "] file " << FileName << std::endl;
- formatCode(FileName);
- ClangTool Tool(OptionsParser.getCompilations(), std::vector({FileName}));
- int Result = Tool.run(newFrontendActionFactory<ASTCoverageAction>().get());
- if (Result != 0)
- errs() << "An error in the operation of the toolkit.\n";
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement