From 0fcddd9d7687e1a96d0c8711dc6684760d4e7427 Mon Sep 17 00:00:00 2001 From: Dennis Klein Date: Tue, 15 Jun 2021 13:56:40 +0200 Subject: [PATCH] feat(tidy): Add new fairmq-tidy tool --- .gitignore | 5 +- CMakeLists.txt | 9 +++ cmake/FairMQDependencies.cmake | 9 ++- cmake/FairMQSummary.cmake | 6 ++ fairmq/tidy/CMakeLists.txt | 24 +++++++ fairmq/tidy/ModernizeNonNamespacedTypes.h | 81 +++++++++++++++++++++++ fairmq/tidy/Tool.h | 76 +++++++++++++++++++++ fairmq/tidy/runTool.cpp | 29 ++++++++ 8 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 fairmq/tidy/CMakeLists.txt create mode 100644 fairmq/tidy/ModernizeNonNamespacedTypes.h create mode 100644 fairmq/tidy/Tool.h create mode 100644 fairmq/tidy/runTool.cpp diff --git a/.gitignore b/.gitignore index cc76bca6..f0a60098 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build - +install .DS_Store - .vscode +/compile_commands.json +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt index d151af28..e04a90e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,8 @@ fairmq_build_option(BUILD_EXAMPLES "Build FairMQ examples." DEFAULT ON REQUIRES "BUILD_FAIRMQ") fairmq_build_option(BUILD_SDK "Build the FairMQ controller SDK." DEFAULT OFF REQUIRES "BUILD_DDS_PLUGIN;BUILD_SDK_COMMANDS") +fairmq_build_option(BUILD_TIDY_TOOL "Build the fairmq-tidy tool." + DEFAULT OFF) fairmq_build_option(BUILD_DOCS "Build FairMQ documentation." DEFAULT OFF) fairmq_build_option(USE_EXTERNAL_GTEST "Do not use bundled GTest. Not recommended." @@ -76,6 +78,10 @@ if(BUILD_DOCS) doxygen_add_docs(doxygen README.md fairmq) add_custom_target(docs ALL DEPENDS doxygen) endif() + +if(BUILD_TIDY_TOOL) + add_subdirectory(fairmq/tidy) +endif() ################################################################################ @@ -107,6 +113,9 @@ endif() if(BUILD_SDK_COMMANDS) list(APPEND PROJECT_PACKAGE_COMPONENTS sdk_commands) endif() +if(BUILD_TIDY_TOOL) + list(APPEND PROJECT_PACKAGE_COMPONENTS tidy_tool) +endif() ################################################################################ diff --git a/cmake/FairMQDependencies.cmake b/cmake/FairMQDependencies.cmake index 56cb07f9..bcab5c7a 100644 --- a/cmake/FairMQDependencies.cmake +++ b/cmake/FairMQDependencies.cmake @@ -37,7 +37,7 @@ if(BUILD_PMIX_PLUGIN) find_package2(PRIVATE PMIx REQUIRED VERSION 2.1.4) endif() -if(BUILD_FAIRMQ OR BUILD_SDK) +if(BUILD_FAIRMQ OR BUILD_SDK OR BUILD_TIDY_TOOL) find_package2(PUBLIC FairLogger REQUIRED VERSION 1.6.0) find_package2(PUBLIC Boost REQUIRED VERSION 1.66 COMPONENTS container program_options filesystem date_time regex @@ -73,6 +73,13 @@ if(BUILD_DOCS) ) endif() +if(BUILD_TIDY_TOOL) + find_package2(PRIVATE LLVM REQUIRED) + find_package2(PRIVATE Clang REQUIRED) + set(Clang_VERSION ${LLVM_VERSION}) + find_package2(PRIVATE CLI11 REQUIRED) +endif() + find_package2_implicit_dependencies() # Always call last after latest find_package2 if(PROJECT_PACKAGE_DEPENDENCIES) diff --git a/cmake/FairMQSummary.cmake b/cmake/FairMQSummary.cmake index 7b501f8b..b1dc78ba 100644 --- a/cmake/FairMQSummary.cmake +++ b/cmake/FairMQSummary.cmake @@ -69,6 +69,12 @@ macro(fairmq_summary_components) set(sdk_commands_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_SDK_COMMANDS=ON${CR})") endif() message(STATUS " ${BWhite}sdk_commands${CR} ${sdk_commands_summary}") + if(BUILD_TIDY_TOOL) + set(sdk_tidy_summary "${BGreen}YES${CR} (disable with ${BMagenta}-DBUILD_TIDY_TOOL=OFF${CR})") + else() + set(sdk_tidy_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_TIDY_TOOL=ON${CR})") + endif() + message(STATUS " ${BWhite}tidy_tool${CR} ${sdk_tidy_summary}") endmacro() macro(fairmq_summary_static_analysis) diff --git a/fairmq/tidy/CMakeLists.txt b/fairmq/tidy/CMakeLists.txt new file mode 100644 index 00000000..80452496 --- /dev/null +++ b/fairmq/tidy/CMakeLists.txt @@ -0,0 +1,24 @@ +################################################################################ +# Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# # +# This software is distributed under the terms of the # +# GNU Lesser General Public Licence (LGPL) version 3, # +# copied verbatim in the file "LICENSE" # +################################################################################ + +set(target fairmq-tidy) +add_executable(${target} + ModernizeNonNamespacedTypes.h + Tool.h + runTool.cpp +) +target_compile_features(${target} PRIVATE cxx_std_17) +target_link_libraries(${target} PRIVATE clang-cpp LLVM) +target_include_directories(${target} PRIVATE + $ + $ +) +install(TARGETS ${target} + EXPORT ${PROJECT_EXPORT_SET} + RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR} +) diff --git a/fairmq/tidy/ModernizeNonNamespacedTypes.h b/fairmq/tidy/ModernizeNonNamespacedTypes.h new file mode 100644 index 00000000..940b5ffd --- /dev/null +++ b/fairmq/tidy/ModernizeNonNamespacedTypes.h @@ -0,0 +1,81 @@ +/******************************************************************************** + * Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#ifndef FAIR_MQ_TIDY_MODERNIZENONNAMESPACEDTYPES +#define FAIR_MQ_TIDY_MODERNIZENONNAMESPACEDTYPES + +#include +#include +#include +#include +#include +#include + +namespace fair::mq::tidy { + +struct ModernizeNonNamespacedTypes +{ + ModernizeNonNamespacedTypes() = delete; + ModernizeNonNamespacedTypes(clang::ast_matchers::MatchFinder& finder) + { + using namespace clang::ast_matchers; + + // https://clang.llvm.org/docs/LibASTMatchersReference.html + finder.addMatcher( + typeLoc(loc(qualType(hasDeclaration(namedDecl(matchesName("FairMQ.*")).bind("decl"))))) + .bind("loc"), + &fCallback); + } + + struct Callback : clang::ast_matchers::MatchFinder::MatchCallback + { + auto run(clang::ast_matchers::MatchFinder::MatchResult const& m) -> void final + { + using namespace clang; + + auto const type_loc(m.Nodes.getNodeAs("loc")); + auto const named_decl(m.Nodes.getNodeAs("decl")); + + if (auto const type_alias_decl = m.Nodes.getNodeAs("decl")) { + auto const underlying_type(type_alias_decl->getUnderlyingType()); + + // auto ldecl_ctx(type_loc->getType()getLexicalDeclContext()); + // std::stringstream s; + // while (ldecl_ctx) { + // s << "." << ldecl_ctx->getDeclKindName(); + // if (ldecl_ctx->isNamespace()) { + // s << dyn_cast(ldecl_ctx)->getNameAsString(); + // } + // ldecl_ctx = ldecl_ctx->getLexicalParent(); + // } + + if (underlying_type.getAsString().rfind("fair::mq::", 0) == 0) { + auto& diag_engine(m.Context->getDiagnostics()); + auto builder( + diag_engine.Report(type_loc->getBeginLoc(), + diag_engine.getCustomDiagID( + DiagnosticsEngine::Warning, + "Modernize non-namespaced type %0 with %1. [%2]"))); + builder << named_decl; + builder << underlying_type; + builder << "fairmq-modernize-nonnamespaced-types"; + + builder.AddFixItHint(FixItHint::CreateReplacement( + type_loc->getSourceRange(), underlying_type.getAsString())); + } + } + } + }; + + private: + Callback fCallback; +}; + +} // namespace fair::mq::tidy + +#endif /* FAIR_MQ_TIDY_MODERNIZENONNAMESPACEDTYPES */ diff --git a/fairmq/tidy/Tool.h b/fairmq/tidy/Tool.h new file mode 100644 index 00000000..00308162 --- /dev/null +++ b/fairmq/tidy/Tool.h @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#ifndef FAIR_MQ_TIDY_TOOL +#define FAIR_MQ_TIDY_TOOL + +#include +#include +#include +#include +#include +#include + +namespace fair::mq::tidy { + +// Getting up to speed, read this: +// https://clang.llvm.org/docs/LibTooling.html +// https://clang.llvm.org/docs/IntroductionToTheClangAST.html +// Watch https://www.youtube.com/watch?v=VqCkCDFLSsc !!! +// +// optional, but helpful: +// https://static.linaro.org/connect/yvr18/presentations/yvr18-223.pdf +// https://steveire.wordpress.com/2018/11/20/composing-ast-matchers-in-clang-tidy/ +// https://www.goldsborough.me/c++/clang/llvm/tools/2017/02/24/00-00-06-emitting_diagnostics_and_fixithints_in_clang_tools/ +// https://steveire.com/CodeDive2018Presentation.pdf +// +// reference (no built-in search, however google will find things): +// https://clang.llvm.org/doxygen/ +// +// Note: Implementing a standalone tool will impose double PP and parsing cost if one also +// runs clang-tidy. At the moment, one cannot extend clang-tidy with new checks without +// recompiling clang-tidy. In principle llvm-project/clang-extra-tools/clang-tidy +// has install rules, but Fedora is not packaging them which is likely due to their +// unstable state (lots of comments aka todo, fixme, refactor etc). Also, it seems +// focus has shifted towards implementing the +// https://microsoft.github.io/language-server-protocol/ +// via https://clangd.llvm.org/ which includes clang-tidy, but also does not have +// an extension/plugin interface for third-party checks yet AFAICS. + +struct Tool +{ + static auto run(clang::tooling::CompilationDatabase const &compilations, + clang::ArrayRef sources) -> int + { + using namespace clang; + + // compose all checks in a single match finder + ast_matchers::MatchFinder finder; + ModernizeNonNamespacedTypes check1(finder); + + // configure the clang tool + tooling::ClangTool tool(compilations, sources); + tool.appendArgumentsAdjuster( + [](tooling::CommandLineArguments const &_args, StringRef /*file*/) { + tooling::CommandLineArguments args(_args); + // TODO add only if cdb was generated with GCC + args.emplace(args.begin() + 1, "-I/usr/lib64/clang/12.0.0/include"); + // TODO add only if missing + args.emplace(args.begin() + 1, "-std=c++17"); + args.emplace_back("-Wno-everything"); + return args; + }); + + // run checks on given files + return tool.run(clang::tooling::newFrontendActionFactory(&finder).get()); + } +}; + +} // namespace fair::mq::tidy + +#endif /* FAIR_MQ_TIDY_TOOL */ diff --git a/fairmq/tidy/runTool.cpp b/fairmq/tidy/runTool.cpp new file mode 100644 index 00000000..d30a6351 --- /dev/null +++ b/fairmq/tidy/runTool.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#include +#include +#include +#include +#include + +static llvm::cl::OptionCategory ToolCategory("fairmq-tidy options"); +static llvm::cl::extrahelp CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage); + +int main(int argc, const char** argv) +{ + // TODO Replace command line parser with CLI11 + auto parser(clang::tooling::CommonOptionsParser::create( + argc, argv, ToolCategory, llvm::cl::NumOccurrencesFlag::Optional, "")); + if (!parser) { + llvm::errs() << parser.takeError(); + return EXIT_FAILURE; + } + + return fair::mq::tidy::Tool::run(parser->getCompilations(), parser->getSourcePathList()); +}