diff --git a/.gitignore b/.gitignore index d59b1b7..def3363 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,32 @@ CMakeFiles/ CMakeCache.txt CMakeLists.txt.user -ClangLazy.cbp +ClazyPlugin.cbp cmake_install.cmake lib/ compile.output test.output Makefile *.o dump.ast install_manifest.txt *_fixed.cpp empty.o* compile_fixed.output *.o-* *.compile_output *.out *.result /tests/clazy/test_requested_checks.sh.output *.ast /clazy clazylib_export.h output.log /clazy.bat /bin/* /build/ clazy.1 /dev-scripts/*.log clanglazy_export.h CPackConfig.cmake CPackSourceConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f71198..8483946 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,242 +1,242 @@ # This is the top-level CMakeLists.txt file for the Clazy project. # # To build the man page from POD, run 'make man' after CMake (assumes perl is available) # To install the resulting man page, run 'make install' # The man page is not available on Windows. # -project(ClangLazy) +project(clazy) cmake_minimum_required(VERSION 3.3) include(FeatureSummary) include(GenerateExportHeader) include("GNUInstallDirs") # Version setup set(CLAZY_VERSION_MAJOR "1") set(CLAZY_VERSION_MINOR "5") set(CLAZY_VERSION_PATCH "0") set(CLAZY_VERSION "${CLAZY_VERSION_MAJOR}.${CLAZY_VERSION_MINOR}.${CLAZY_VERSION_PATCH}") set(CLAZY_PRINT_VERSION "${CLAZY_VERSION_MAJOR}.${CLAZY_VERSION_MINOR}") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR}/cmake) if (NOT CLAZY_BUILD_WITH_CLANG) find_package(Clang 3.9 MODULE REQUIRED) endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) add_definitions(-D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS) add_definitions(-D_GNU_SOURCE -DHAVE_CLANG_CONFIG_H) option(CLAZY_AST_MATCHERS_CRASH_WORKAROUND "Disable AST Matchers if being built with clang. See bug #392223" ON) if (CLAZY_AST_MATCHERS_CRASH_WORKAROUND AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") message("Enabling AST Matchers workaround. Consider building with gcc instead. See bug #392223.") add_definitions(-DCLAZY_DISABLE_AST_MATCHERS) endif() if(NOT CLAZY_BUILD_WITH_CLANG AND MSVC AND NOT CLANG_LIBRARY_IMPORT) message(FATAL_ERROR "\nOn MSVC you need to pass -DCLANG_LIBRARY_IMPORT=C:/path/to/llvm-build/lib/clang.lib to cmake when building Clazy.\nAlso make sure you've built LLVM with -DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON") endif() if(MSVC) # disable trigger-happy warnings from Clang/LLVM headers set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244 /wd4291 /wd4800 /wd4141 /wd4146 /wd4251") elseif(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-common -Woverloaded-virtual -Wcast-qual -fno-strict-aliasing -pedantic -Wno-long-long -Wall -W -Wno-unused-parameter -Wwrite-strings -fno-exceptions -fno-rtti") endif() set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-flat_namespace -Wl,-undefined -Wl,suppress") if(WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") endif() # Look for std::regex support message("Looking for std::regex support...") try_run(RUN_RESULT COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/.cmake_has_regex_test.cpp) if(RUN_RESULT EQUAL 0) set(HAS_STD_REGEX TRUE) else() set(HAS_STD_REGEX FALSE) add_definitions(-DNO_STD_REGEX) message("old-style-connect check is disabled due to missing std::regex support") message("Suppressions are disabled due to missing std::regex support") endif() include(ClazySources.cmake) include_directories(${CMAKE_BINARY_DIR}) include_directories(${CLANG_INCLUDE_DIRS} ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/src) link_directories("${LLVM_INSTALL_PREFIX}/lib" ${LLVM_LIBRARY_DIRS}) macro(link_to_llvm name is_standalone) foreach(clang_lib ${CLANG_LIBS}) if(MSVC) get_filename_component(LIB_FILENAME ${clang_lib} NAME) if(LIB_FILENAME STREQUAL "clangFrontend.lib") # On MSVC we don't link against clangFrontend.lib, instead we link against clang.exe (via clang.lib) # Otherwise the clazy plugin would have it's own plugin registry and clang wouldn't see it. # This way clazy registers with clang. continue() endif() endif() target_link_libraries(${name} ${clang_lib}) endforeach() foreach(llvm_lib ${LLVM_LIBS}) if(NOT ${is_standalone} AND NOT APPLE AND NOT MINGW AND NOT MSVC) ## Don't link against LLVMSupport, causes: CommandLine Error: Option 'view-background' registered more than once! if (NOT llvm_lib MATCHES ".*LLVMSupport.*") target_link_libraries(${name} ${llvm_lib}) endif() else() target_link_libraries(${name} ${llvm_lib}) endif() endforeach() foreach(user_lib ${USER_LIBS}) target_link_libraries(${name} ${user_lib}) endforeach() foreach(llvm_system_lib ${LLVM_SYSTEM_LIBS}) target_link_libraries(${name} ${llvm_system_lib}) endforeach() if(WIN32) target_link_libraries(${name} version.lib) endif() endmacro() macro(add_clang_plugin name) set(srcs ${ARGN}) add_library(${name} SHARED ${srcs}) if(SYMBOL_FILE) set_target_properties(${name} PROPERTIES LINK_FlAGS "-exported_symbols_list ${SYMBOL_FILE}") endif() link_to_llvm(${name} FALSE) if(MSVC) target_link_libraries(${name} ${CLANG_LIBRARY_IMPORT}) # Link against clang.exe to share the plugin registry endif() endmacro() set(SYMBOL_FILE Lazy.exports) if (NOT CLAZY_BUILD_WITH_CLANG) - add_clang_plugin(ClangLazy ${CLAZY_PLUGIN_SRCS}) - set_target_properties(ClangLazy PROPERTIES + add_clang_plugin(ClazyPlugin ${CLAZY_PLUGIN_SRCS}) + set_target_properties(ClazyPlugin PROPERTIES LINKER_LANGUAGE CXX PREFIX "" ) - install(TARGETS ClangLazy + install(TARGETS ClazyPlugin RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_DATAROOTDIR} CACHE STRING "Share directory name") if(NOT WIN32) configure_file(${CMAKE_CURRENT_LIST_DIR}/clazy.cmake ${CMAKE_BINARY_DIR}/clazy @ONLY) install(FILES ${CMAKE_BINARY_DIR}/clazy DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) else() install(FILES ${CMAKE_CURRENT_LIST_DIR}/clazy.bat DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) if(MSVC) install(FILES ${CMAKE_CURRENT_LIST_DIR}/clazy-cl.bat DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) endif() endif() # Install the explanation README's set(DOC_INSTALL_DIR ${SHARE_INSTALL_DIR}/doc/clazy) include(${CMAKE_CURRENT_LIST_DIR}/readmes.cmake) install(FILES ${README_LEVEL0_FILES} DESTINATION ${DOC_INSTALL_DIR}/level0) install(FILES ${README_LEVEL1_FILES} DESTINATION ${DOC_INSTALL_DIR}/level1) install(FILES ${README_LEVEL2_FILES} DESTINATION ${DOC_INSTALL_DIR}/level2) install(FILES ${README_LEVEL3_FILES} DESTINATION ${DOC_INSTALL_DIR}/level3) install(FILES ${README_manuallevel_FILES} DESTINATION ${DOC_INSTALL_DIR}/manuallevel) # Install more doc files install(FILES README.md COPYING-LGPL2.txt checks.json DESTINATION ${DOC_INSTALL_DIR}) # Build docs set(MAN_INSTALL_DIR "${SHARE_INSTALL_DIR}/man/man1") add_subdirectory(docs) # rpath set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" isSystemDir) if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif("${isSystemDir}" STREQUAL "-1") # Build clazy-standalone add_executable(clazy-standalone ${CLAZY_STANDALONE_SRCS}) if(MSVC) - # On MSVC clang-standalone crashes with a meaningless backtrace if linked to ClangLazy.dll + # On MSVC clang-standalone crashes with a meaningless backtrace if linked to ClazyPlugin.dll target_link_libraries(clazy-standalone clangFrontend) else() - target_link_libraries(clazy-standalone ClangLazy) + target_link_libraries(clazy-standalone ClazyPlugin) endif() link_to_llvm(clazy-standalone TRUE) install(TARGETS clazy-standalone DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) set(CPACK_PACKAGE_VERSION_MAJOR ${CLAZY_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${CLAZY_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${CLAZY_VERSION_PATCH}) include(CPack) else() set(LLVM_LINK_COMPONENTS Support ) add_clang_library(clazyPlugin ${CLAZY_PLUGIN_SRCS} LINK_LIBS clangFrontend clangDriver clangCodeGen clangSema clangAnalysis clangRewriteFrontend clangRewrite clangAST clangASTMatchers clangParse clangLex clangBasic clangARCMigrate clangEdit clangFrontendTool clangRewrite clangSerialization clangTooling clangStaticAnalyzerCheckers clangStaticAnalyzerCore clangStaticAnalyzerFrontend ) add_executable(clazy-standalone ${CLAZY_STANDALONE_SRCS}) target_link_libraries(clazy-standalone clazyPlugin) install(TARGETS clazy-standalone DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) endif() diff --git a/Changelog b/Changelog index 6a9e64d..b48b1f4 100644 --- a/Changelog +++ b/Changelog @@ -1,97 +1,98 @@ * v0.0.1 (June 10th, 2015) - (...) * v1.0 (September 12th, 2016) - (...) * v1.1 (February 20th, 2017) - macOS and Windows support - New checks: child-event-qobject-cast ctor-missing-parent-argument returning-data-from-temporary qt-macros base-class-event connect-non-signal incorrect-emit tr-non-literal - Fixes against clang 4.0 - Fixes against Qt 5.9 - 60% performance improvement - Fixed many false positives * v1.2 (July 8th, 2017) - clazy-standalone executable. Allows to run clazy against a JSON compilation database instead of as a plugin. clang-tidy doesn't support loading external modules (https://bugs.llvm.org/show_bug.cgi?id=32739) so this is a good workaround. - - qt-compat mode. Allows to disable Qt5 specific checks by passing -Xclang -plugin-arg-clang-lazy -Xclang qt4-compat + - qt-compat mode. Allows to disable Qt5 specific checks by passing -Xclang -plugin-arg-clazy -Xclang qt4-compat - New checks: install-event-filter qcolor-from-literal strict-iterators connect-not-normalized - returning-data-from-temporary now checks for temporary QByteArrays casting to char* when returned - returning-data-from-temporary now checks for assignment too, not only return statements - unused-non-trivial-variable now warns for unused QList, QVector and many more types - ASTMatchers based checks are now supported - clang 3.7 was dropped due to ASTMatchers source incompatibilities. Use clazy v1.1 for clang >= 3.6 support - clazylib.so no longer gets built by default, only the plugin (ClangLazy.so) gets built. Pass -DCLAZY_BUILD_UTILS_LIB=ON to enable the utils library if you're developing tools using clazy's convenience functions, which you're probably not. - CLAZY_INSTALL_NO_HEADERS option was removed. Either install the utils library and headers or nothing at all. By default nothing is installed, except the plugin and man pages. * v1.3 (November 26th, 2017) - New checks: thread-with-slots connect-3arg-lambda qproperty-without-notify virtual-signal overridden-signal qhash-namespace const-signal-or-slot lambda-unique-connection - missing-qobject-macro is now a level2 check, instead of level1. Because, people can omit Q_OBJECT intentionally. - Added -only-qt option, which will make clazy bailout early on non-Qt files. For this purpose, the definition of a Qt file is whenever -DQT_CORE_LIB is passed, which is usually the case in most build systems. - Added -qt-developer option, when building Qt with clazy it will honour specific guidelines for Qt, which are not many right now but the list will grow. * v1.4 (September 23rd, 2018) - New Checks: connect-by-name skipped-base-method qstring-varargs fully-qualified-moc-types qt-keywords, with fixit included qhash-with-char-pointer-key wrong-qevent-cast static-pmf raw-environment-function empty-qstringliteral - auto-unexpected-qstringbuilder now also warns for lambdas returning QStringBuilder - performance optimizations - Added -header-filter= option to clazy-standalone. Only headers matching the regexp will have warnings, besides the .cpp file from the translation unit, which is never filtered out. - Added -ignore-dirs= option to clazy-standalone, and its CLAZY_IGNORE_DIRS env variable equivalent. - Added CLAZY_HEADER_FILTER env variable which adds above functionality to both clazy and clazy-standalone - unused-non-trivial-variable got unused-non-trivial-variable-no-whitelist option - unused-non-trivial-variable got user-blacklist and user-whitelist support - container-inside-loop is now a manual check instead of level2 - HiddenLevel was renamed to ManualLevel - connect-3arg-lambda now warns when passing a lambda to QTimer::singleShot() or QMenu::addAction() without a context object - old-style-connect warns for QMenu::addAction() and QMessageBox::open() too now * v1.5 (, 2019) - New Checks: ifndef-define-typo - - Removed support for the obscure -DCLAZY_BUILD_UTILS_LIB to simplify the CMakeLists.txt. + - Removed support for the obscure -DCLAZY_BUILD_UTILS_LIB to simplify the CMakeLists.txt + - Renamed the clazy plugin from ClangLazy.so to ClazyPlugin.so diff --git a/README.md b/README.md index afd98b7..8785dcb 100644 --- a/README.md +++ b/README.md @@ -1,469 +1,469 @@ *WARNING:* master is the development branch. Please use the v1.4 tag. clazy v1.5 =========== clazy is a compiler plugin which allows clang to understand Qt semantics. You get more than 50 Qt related compiler warnings, ranging from unneeded memory allocations to misusage of API, including fix-its for automatic refactoring. Table of contents ================= * [Source Code](#source-code) * [Build Instructions](#build-instructions) * [Linux](#linux) * [Install dependencies](#install-dependencies) * [Build and install clang](#build-and-install-clang) * [Build clazy](#build-clazy) * [Windows](#windows) * [3rdparty pre-built msvc2015 clang and clazy binaries](#3rdparty-pre-built-msvc2015-clang-and-clazy-binaries) * [Build and install clang](#build-and-install-clang-1) * [Build clazy](#build-clazy-1) * [macOS with MacPorts](#macos-with-macports) * [Install clang](#install-clang) * [Build clazy](#build-clazy-2) * [macOS with Homebrew](#macos-with-homebrew) * [Install clang](#install-clang-1) * [Build clazy](#build-clazy-3) * [Setting up your project to build with clazy](#setting-up-your-project-to-build-with-clazy) * [List of checks](#list-of-checks) * [Selecting which checks to enable](#selecting-which-checks-to-enable) * [Example via env variable](#example-via-env-variable) * [Example via compiler argument](#example-via-compiler-argument) * [clazy-standalone and JSON database support](#clazy-standalone-and-json-database-support) * [Enabling Fixits](#enabling-fixits) * [Troubleshooting](#troubleshooting) * [Qt4 compatibility mode](#qt4-compatibility-mode) * [Reducing warning noise](#reducing-warning-noise) * [Reporting bugs and wishes](#reporting-bugs-and-wishes) * [Authors](#authors) * [Contributing patches](#contributing-patches) # Source Code You can get clazy from: - - git@git.kde.org:clazy - git://anongit.kde.org/clazy # Build Instructions ## Linux ### Install dependencies - OpenSUSE tumbleweed: `zypper install cmake git-core llvm llvm-devel llvm-clang llvm-clang-devel` - Ubuntu: `apt install g++ cmake clang llvm-dev git-core libclang-dev` - Archlinux: `pacman -S make llvm clang python2 cmake git gcc` - Fedora: be sure to *remove* the llvm-static package and only install the one with dynamic libraries - Other distros: Check llvm/clang build docs. ### Build and install clang clang and LLVM >= 4.0 are required. Use clazy v1.1 if you need 3.7 support, or v1.3 for 3.8 support. If your distro provides clang then you can skip this step. ``` $ git clone https://github.com/llvm-mirror/llvm.git $ cd /tools/ && git clone https://github.com/llvm-mirror/clang.git $ cd /projects && git clone https://github.com/llvm-mirror/compiler-rt.git $ mkdir /build && cd /build $ cmake -DCMAKE_INSTALL_PREFIX= -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_BUILD_TYPE=Release .. $ make -jX && make install ``` ### Build clazy ``` $ cd clazy/ $ cmake -DCMAKE_INSTALL_PREFIX= -DCMAKE_BUILD_TYPE=Release $ make && make install ``` See troubleshooting section if you have problems. ## Windows ### 3rdparty pre-built msvc2015 clang and clazy binaries Building for Windows is a lengthy and tedious task, therefore the maintainer won't be creating them anymore on his free/KDE time. KDAB however has offered to produce these binaries and they will appear on their website. If you really want to build clang and clazy yourself then read on, otherwise skip the building topic. ### Build and install clang These instructions assume your terminal is suitable for development (msvc2015). jom, nmake, git, cmake and cl should be in your PATH. clang and LLVM >= 4.0 are required. Be sure to pass -DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON to CMake when building LLVM, otherwise clazy won't work. ``` > git clone https://github.com/llvm-mirror/llvm.git > cd \tools\ && git clone https://github.com/llvm-mirror/clang.git > git checkout release_40 > cd clang > git checkout release_40 > mkdir \build && cd \build > cmake -DCMAKE_INSTALL_PREFIX=c:\my_install_folder\llvm\ -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles JOM" .. > jom > nmake install > Add c:\my_install_folder\llvm\bin\ to PATH ``` ### Build clazy Be sure to point CLANG_LIBRARY_IMPORT to clang.lib. It's probably inside your LLVM build dir since it doesn't get installed. ``` > cd clazy\ > cmake -DCMAKE_INSTALL_PREFIX=c:\my_install_folder\llvm\ -DCLANG_LIBRARY_IMPORT=C:\path\to\llvm-build\lib\clang.lib -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles JOM" > jom && nmake install ``` ## macOS with MacPorts ### Install clang ``` $ sudo port install clang-3.9 llvm-3.9 $ sudo ln -sf /opt/local/bin/llvm-config-mp-3.9 /opt/local/bin/llvm-config $ sudo port select --set clang mp-clang-3.9 ``` ### Build clazy ``` $ export CXX=clang++ $ cmake $ make $ make install ``` ## macOS with Homebrew The recommended way is to build clazy yourself, but alternatively you can try user recipes, such as: ``` $ brew install kde-mac/kde/clazy ``` for stable branch, or for master: ``` $ brew install kde-mac/kde/clazy --HEAD ``` As these are not verified or tested by the clazy developers please don't report bugs to us. For building yourself, read on. You'll have to install clang and build clazy from source. ### Install clang ``` $ brew install --with-clang llvm ``` ### Build clazy ``` $ export CXX=clang++ $ export LLVM_ROOT=/usr/local/opt/llvm $ cmake $ make $ make install ``` # Setting up your project to build with clazy Note: Wherever `clazy` it mentioned, replace with `clazy-cl.bat` if you're on Windows. Note: If you prefer running clazy over a JSON compilation database instead of using it as a plugin, jump to [clazy-standalone](#clazy-standalone-and-json-database-support). You should now have the clazy command available to you, in `/bin/`. Compile your programs with it instead of clang++/g++. Note that this command is just a convenience wrapper which calls: -`clang++ -Xclang -load -Xclang ClangLazy.so -Xclang -add-plugin -Xclang clang-lazy` +`clang++ -Xclang -load -Xclang ClazyPlugin.so -Xclang -add-plugin -Xclang clazy` If you have multiple versions of clang installed (say clang++-3.8 and clang++-3.9) you can choose which one to use by setting the CLANGXX environment variable, like so: `export CLANGXX=clang++-3.8; clazy` To build a CMake project use: `cmake . -DCMAKE_CXX_COMPILER=clazy` and rebuild. To make it the compiler for qmake projects, just run qmake like: `qmake -spec linux-clang QMAKE_CXX="clazy"` Alternatively, if you want to use clang directly, without the wrapper: -`qmake -spec linux-clang QMAKE_CXXFLAGS="-Xclang -load -Xclang ClangLazy.so -Xclang -add-plugin -Xclang clang-lazy"` +`qmake -spec linux-clang QMAKE_CXXFLAGS="-Xclang -load -Xclang ClazyPlugin.so -Xclang -add-plugin -Xclang clazy"` You can also edit mkspecs/common/clang.conf and change QMAKE_CXX to clazy instead of clang++ and run qmake -spec linux-clang It's recommended that you disable pre-compiled headers and don't use ccache. You're all set, clazy will now run some checks on your project, but not all of them. Read on if you want to enable/disable which checks are run. # List of checks There are many checks and they are divided in levels: - level0: Very stable checks, 99.99% safe, mostly no false-positives, very desirable - level1: The default level. Very similar to level 0, slightly more false-positives but very few. - level2: Also very few false-positives, but contains noisy checks which not everyone agree should be default. - level3: Contains checks with high rate of false-positives. - manual: Checks here need to be enabled explicitly, as they don't belong to any level. Checks here are very stable and have very few false-positives. clazy runs all checks from level1 by default. - Checks from Manual Level: - [container-inside-loop](docs/checks/README-container-inside-loop.md) - [ifndef-define-typo](docs/checks/README-ifndef-define-typo.md) - [inefficient-qlist](docs/checks/README-inefficient-qlist.md) - [isempty-vs-count](docs/checks/README-isempty-vs-count.md) - [qhash-with-char-pointer-key](docs/checks/README-qhash-with-char-pointer-key.md) - [qstring-varargs](docs/checks/README-qstring-varargs.md) - [qt-keywords](docs/checks/README-qt-keywords.md) (fix-qt-keywords) - [qt4-qstring-from-array](docs/checks/README-qt4-qstring-from-array.md) (fix-qt4-qstring-from-array) - [raw-environment-function](docs/checks/README-raw-environment-function.md) - [tr-non-literal](docs/checks/README-tr-non-literal.md) - Checks from Level 0: - [connect-by-name](docs/checks/README-connect-by-name.md) - [connect-non-signal](docs/checks/README-connect-non-signal.md) - [connect-not-normalized](docs/checks/README-connect-not-normalized.md) - [container-anti-pattern](docs/checks/README-container-anti-pattern.md) - [empty-qstringliteral](docs/checks/README-empty-qstringliteral.md) - [fully-qualified-moc-types](docs/checks/README-fully-qualified-moc-types.md) - [lambda-in-connect](docs/checks/README-lambda-in-connect.md) - [lambda-unique-connection](docs/checks/README-lambda-unique-connection.md) - [mutable-container-key](docs/checks/README-mutable-container-key.md) - [qcolor-from-literal](docs/checks/README-qcolor-from-literal.md) - [qdatetime-utc](docs/checks/README-qdatetime-utc.md) (fix-qdatetime-utc) - [qenums](docs/checks/README-qenums.md) - [qfileinfo-exists](docs/checks/README-qfileinfo-exists.md) - [qgetenv](docs/checks/README-qgetenv.md) (fix-qgetenv) - [qmap-with-pointer-key](docs/checks/README-qmap-with-pointer-key.md) - [qstring-arg](docs/checks/README-qstring-arg.md) - [qstring-insensitive-allocation](docs/checks/README-qstring-insensitive-allocation.md) - [qstring-ref](docs/checks/README-qstring-ref.md) (fix-missing-qstringref) - [qt-macros](docs/checks/README-qt-macros.md) - [qvariant-template-instantiation](docs/checks/README-qvariant-template-instantiation.md) - [strict-iterators](docs/checks/README-strict-iterators.md) - [temporary-iterator](docs/checks/README-temporary-iterator.md) - [unused-non-trivial-variable](docs/checks/README-unused-non-trivial-variable.md) - [writing-to-temporary](docs/checks/README-writing-to-temporary.md) - [wrong-qevent-cast](docs/checks/README-wrong-qevent-cast.md) - [wrong-qglobalstatic](docs/checks/README-wrong-qglobalstatic.md) - Checks from Level 1: - [auto-unexpected-qstringbuilder](docs/checks/README-auto-unexpected-qstringbuilder.md) (fix-auto-unexpected-qstringbuilder) - [child-event-qobject-cast](docs/checks/README-child-event-qobject-cast.md) - [connect-3arg-lambda](docs/checks/README-connect-3arg-lambda.md) - [const-signal-or-slot](docs/checks/README-const-signal-or-slot.md) - [detaching-temporary](docs/checks/README-detaching-temporary.md) - [foreach](docs/checks/README-foreach.md) - [incorrect-emit](docs/checks/README-incorrect-emit.md) - [inefficient-qlist-soft](docs/checks/README-inefficient-qlist-soft.md) - [install-event-filter](docs/checks/README-install-event-filter.md) - [non-pod-global-static](docs/checks/README-non-pod-global-static.md) - [overridden-signal](docs/checks/README-overridden-signal.md) - [post-event](docs/checks/README-post-event.md) - [qdeleteall](docs/checks/README-qdeleteall.md) - [qhash-namespace](docs/checks/README-qhash-namespace.md) - [qlatin1string-non-ascii](docs/checks/README-qlatin1string-non-ascii.md) - [qproperty-without-notify](docs/checks/README-qproperty-without-notify.md) - [qstring-left](docs/checks/README-qstring-left.md) - [range-loop](docs/checks/README-range-loop.md) - [returning-data-from-temporary](docs/checks/README-returning-data-from-temporary.md) - [rule-of-two-soft](docs/checks/README-rule-of-two-soft.md) - [skipped-base-method](docs/checks/README-skipped-base-method.md) - [virtual-signal](docs/checks/README-virtual-signal.md) - Checks from Level 2: - [base-class-event](docs/checks/README-base-class-event.md) - [copyable-polymorphic](docs/checks/README-copyable-polymorphic.md) - [ctor-missing-parent-argument](docs/checks/README-ctor-missing-parent-argument.md) - [function-args-by-ref](docs/checks/README-function-args-by-ref.md) - [function-args-by-value](docs/checks/README-function-args-by-value.md) - [global-const-char-pointer](docs/checks/README-global-const-char-pointer.md) - [implicit-casts](docs/checks/README-implicit-casts.md) - [missing-qobject-macro](docs/checks/README-missing-qobject-macro.md) - [missing-typeinfo](docs/checks/README-missing-typeinfo.md) - [old-style-connect](docs/checks/README-old-style-connect.md) (fix-old-style-connect) - [qstring-allocations](docs/checks/README-qstring-allocations.md) (fix-qlatin1string-allocations,fix-fromLatin1_fromUtf8-allocations,fix-fromCharPtrAllocations) - [returning-void-expression](docs/checks/README-returning-void-expression.md) - [rule-of-three](docs/checks/README-rule-of-three.md) - [static-pmf](docs/checks/README-static-pmf.md) - [virtual-call-ctor](docs/checks/README-virtual-call-ctor.md) - Checks from Level 3: - [assert-with-side-effects](docs/checks/README-assert-with-side-effects.md) - [detaching-member](docs/checks/README-detaching-member.md) - [reserve-candidates](docs/checks/README-reserve-candidates.md) - [thread-with-slots](docs/checks/README-thread-with-slots.md) - [unneeded-cast](docs/checks/README-unneeded-cast.md) # Selecting which checks to enable You may want to choose which checks to enable before starting to compile. If you don't specify anything then all checks from level0 and level1 will run. To specify a list of checks to run, or to choose a level, you can use the `CLAZY_CHECKS` env variable or pass arguments to the compiler. You can disable checks by prefixing with `no-`, in case you don't want all checks from a given level. ## Example via env variable ``` export CLAZY_CHECKS="unneeded-cast,qmap-with-key-pointer,virtual-call-ctor" # Enables only these 3 checks export CLAZY_CHECKS="level0,no-qenums" # Enables all checks from level0, except for qenums export CLAZY_CHECKS="level0,detaching-temporary" # Enables all from level0 and also detaching-temporary ``` ## Example via compiler argument -`clazy -Xclang -plugin-arg-clang-lazy -Xclang level0,detaching-temporary` +`clazy -Xclang -plugin-arg-clazy -Xclang level0,detaching-temporary` Don't forget to re-run cmake/qmake/etc if you altered the c++ flags to specify flags. # clazy-standalone and JSON database support The `clazy-standalone` binary allows you to run clazy over a compilation database JSON file, in the same way you would use `clang-tidy` or other clang tooling. This way you don't need to build your application, only the static analysis is performed. `clazy-standalone` supports the same env variables as the clazy plugin. You can also specify a list of checks via the `-checks` argument. Running on one cpp file: `clazy-standalone -checks=install-event-filter,qmap-with-pointer-key,level0 -p compile_commands.json my.file.cpp` Running on all cpp files: `find . -name "*cpp" | xargs clazy-standalone -checks=level2 -p default/compile_commands.json` See https://clang.llvm.org/docs/JSONCompilationDatabase.html for how to generate the compile_commands.json file. Basically it's generated by passing `-DCMAKE_EXPORT_COMPILE_COMMANDS` to CMake, or using [Bear](https://github.com/rizsotto/Bear) to intercept compiler commands, or, if you're using `qbs`: `qbs generate --generator clangdb` **Note:** Be sure the clazy-standalone binary is located in the same folder as the clang binary, otherwise it will have trouble finding builtin headers, like stddef.h. Alternatively, you can symlink to the folder containing the builtin headers: (Assuming clazy was built with `-DCMAKE_INSTALL_PREFIX=/myprefix/`) ``` $ touch foo.c && clang++ '-###' -c foo.c 2>&1 | tr ' ' '\n' | grep -A1 resource "-resource-dir" "/usr/bin/../lib/clang/4.0.1" # this is the interesting path (without the version) $ ln -sf /usr/bin/../lib/clang/ /myprefix/lib/clang $ ln -sf /usr/bin/../include/c++/ /myprefix/include/c++ # Required on macOS ``` If that doesn't work, run `clang -v` and check what's the InstalledDir. Move clazy-standalone to that folder. `clang-tidy` support will be added after is fixed. # Enabling Fixits Some checks support fixits, in which clazy will re-write your source files whenever it can fix something. You can enable a fixit through the env variable, for example: `export CLAZY_FIXIT="fix-qlatin1string-allocations"` Only one fixit can be enabled each time. **WARNING**: Backup your code, don't blame me if a fixit is not applied correctly. For better results don't use parallel builds, otherwise a fixit being applied in an header file might be done twice. # Troubleshooting -- clang: symbol lookup error: `/usr/lib/x86_64-linux-gnu/ClangLazy.so: undefined symbol: _ZNK5clang15DeclarationName11getAsStringEv`. +- clang: symbol lookup error: `/usr/lib/x86_64-linux-gnu/ClazyPlugin.so: undefined symbol: _ZNK5clang15DeclarationName11getAsStringEv`. This is due to mixing ABIs. Your clang/llvm was compiled with the [new gcc c++ ABI][1] but you compiled the clazy plugin with clang (which uses [the old ABI][2]). The solution is to build the clazy plugin with gcc or use a distro which hasn't migrated to gcc5 ABI yet, such as archlinux. [1]: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html [2]: https://llvm.org/bugs/show_bug.cgi?id=23529 - [Fedora] cmake can't find LLVM ? Try building llvm/clang yourself (There are reports that /usr/share/llvm/cmake/LLVM-Config.cmake is buggy). - [Fedora] CommandLine Error: `Option 'opt-bisect-limit' registered more than once!` Remove the llvm-static package and use the dynamically linked libraries instead - Some checks are mysteriously not producing warnings or not applying fixits ? Check if you have ccache interfering and turn it off. - fatal error: 'stddef.h' file not found, while using `clazy-standalone` Be sure the clazy-standalone binary is located in the same folder as the clang binary. - Be sure to disble pch. - macOS: Be sure you're not using Apple Clang - Windows: fatal error LNK1112: module machine type ‘X86’ conflicts with target machine type ‘x64’ If you're building in 32-bit, open clazy-cl.bat and insert a -m32 argument. Should read: %~dp0\clang\clang.exe –driver-mode=cl -m32 (...) # Qt4 compatibility mode When running on codebases that must still compile with Qt4, you can pass `--qt4compat` -(a convenience option equivalent to passing `-Xclang -plugin-arg-clang-lazy -Xclang qt4-compat`) +(a convenience option equivalent to passing `-Xclang -plugin-arg-clazy -Xclang qt4-compat`) to disable checks that only make sense with Qt5. For example, to build a CMake project with Qt4 compatibility use: `CXX="clazy --qt4compat"; cmake .` and rebuild. # Reducing warning noise If you think you found a false-positive, file a bug report. But do make sure to test first without icecc/distcc enabled. If you want to suppress warnings from headers of Qt or 3rd party code, include them with `-isystem` instead of `-I`. Alternatively you can set the CLAZY_HEADER_FILTER env variable to a regexp matching the path where you want warnings. You can also suppress individual warnings by file or by line by inserting comments: - To disable clazy in a specific source file, insert this comment, anywhere in the file: `// clazy:skip` - To disable specific checks in a source file, insert a comment such as `// clazy:excludeall=check1,check2` - To disable specific checks in specific source lines, insert a comment in the same line as the warning: `(...) // clazy:exclude=check1,check2` Don't include the `clazy-` prefix. If, for example, you want to disable qstring-allocations you would write: `// clazy:exclude=qstring-allocations` not `clazy-qstring-allocations`. # Reporting bugs and wishes - bug tracker: - IRC: #kde-clazy (freenode) - E-mail: # Authors - Sérgio Martins with contributions from: - Allen Winter - Kevin Funk - Mathias Hasselmann - Laurent Montel - Albert Astals Cid - Aurélien Gâteau - Hannah von Reth - Volker Krause - Christian Ehrlicher and thanks to: - Klarälvdalens Datakonsult AB (), for letting me work on clazy as a research project # Contributing patches New features go to master and bug fixes go to the 1.4 branch. The prefered way to contributing is by using KDE's phabricator, see: - - If you rather just create a pull request in https://github.com/KDE/clazy for a drive-by change, it's also fine, but beware that the maintainer might forget to check on github and the KDE bot will close the PR. In that case just send a reminder to the maintainer (smartins at kde.org). diff --git a/clazy-cl.bat b/clazy-cl.bat index 64e49f7..4d5497c 100644 --- a/clazy-cl.bat +++ b/clazy-cl.bat @@ -1,2 +1,2 @@ @echo off -clang.exe --driver-mode=cl -Qunused-arguments -Xclang -load -Xclang ClangLazy.dll -Xclang -add-plugin -Xclang clang-lazy -Wno-microsoft-enum-value %* +clang.exe --driver-mode=cl -Qunused-arguments -Xclang -load -Xclang ClazyPlugin.dll -Xclang -add-plugin -Xclang clazy -Wno-microsoft-enum-value %* diff --git a/clazy.bat b/clazy.bat index 6aa80d1..1565b5b 100644 --- a/clazy.bat +++ b/clazy.bat @@ -1,2 +1,2 @@ @echo off -clang.exe -Qunused-arguments -Xclang -load -Xclang ClangLazy.dll -Xclang -add-plugin -Xclang clang-lazy %* +clang.exe -Qunused-arguments -Xclang -load -Xclang ClazyPlugin.dll -Xclang -add-plugin -Xclang clazy %* diff --git a/clazy.cmake b/clazy.cmake index 325d359..c86a889 100644 --- a/clazy.cmake +++ b/clazy.cmake @@ -1,117 +1,117 @@ #!/usr/bin/env sh libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ sharedir=@CMAKE_INSTALL_PREFIX@/@SHARE_INSTALL_DIR@ HELP() { echo "Usage: `basename $0` [options] [clang++-options]" echo echo "Static analyzer for C++/Qt code" echo echo "Options:" echo " --help print program help" echo " --version print the program version" echo " --list print a list of all available checkers, arranged by level" echo " --explain [regexp] print explanations for the checker matching a regexp" echo "or" echo " --explain print explanations for all checkers" echo echo "Any of the options above will print the requested information and then exit." echo echo "Convenience Options:" echo " --qt4compat Qt4 compatibility mode. useful for source code that can build with Qt4" - echo " (this is the same as passing \"-Xclang -plugin-arg-clang-lazy -Xclang qt4-compat\")" + echo " (this is the same as passing \"-Xclang -plugin-arg-clazy -Xclang qt4-compat\")" echo " --qtdeveloper Special option for building Qt5 itself resulting in fewer false positives" - echo " (this is the same as passing \"-Xclang -plugin-arg-clang-lazy -Xclang qt-developer\")" + echo " (this is the same as passing \"-Xclang -plugin-arg-clazy -Xclang qt-developer\")" echo echo "All other options are passed directly to clang++ and handled from there." echo echo "See the clang++ manual for a list of the very large set of options available" echo } VERSION() { echo "clazy version: @CLAZY_PRINT_VERSION@" ${CLANGXX:-clang++} --version | head -1 | awk '{printf("clang++ Version: %s\n",$3)}' } PRLIST() { echo "" echo "Checks from level$1. $2:" ls -1 $sharedir/doc/clazy/level$1/README* | awk -F/ '{printf(" %s\n", $NF)}' | sed s/README-// | sed s/\.md$// | sort } PRINFO() { lst=`ls -1 $sharedir/doc/clazy/level*/README*$1* $sharedir/doc/clazy/manuallevel/README*$1*` for f in $lst do l=`echo $f | awk -F/ '{foo=NF-1; printf(" %s:%s\n", $foo,$NF)}'` level=`echo $l | cut -d: -f1` checker=`echo $l | cut -d: -f2 | sed s/README-// | sed s/\.md$//` echo "== Explanation for checker $checker ($level) ==" cat $f echo done } if ( test $# -gt 0 -a "$1" = "--help" ) then HELP exit fi if ( test $# -gt 0 -a "$1" = "--version" ) then VERSION exit fi if ( test $# -gt 0 -a "$1" = "--list" ) then echo "List of available clazy checkers:" PRLIST 0 "Very stable checks, 100% safe, no false-positives" PRLIST 1 "Mostly stable and safe, rare false-positives" PRLIST 2 "Sometimes has false-positives (20-30%)" PRLIST 3 "Not always correct, high rate of false-positives" exit fi if ( test $# -gt 0 -a "$1" = "--explain" ) then shift PRINFO $@ exit fi ExtraClangOptions="" if ( test $# -gt 0 -a "$1" = "--qt4compat" ) then shift - ExtraClangOptions="-Xclang -plugin-arg-clang-lazy -Xclang qt4-compat" + ExtraClangOptions="-Xclang -plugin-arg-clazy -Xclang qt4-compat" fi if ( test $# -gt 0 -a "$1" = "--qtdeveloper" ) then shift - ExtraClangOptions="-Xclang -plugin-arg-clang-lazy -Xclang qt-developer" + ExtraClangOptions="-Xclang -plugin-arg-clazy -Xclang qt-developer" fi if ( test $# -gt 0 -a "$1" = "--visit-implicit-code" ) then shift - ExtraClangOptions="-Xclang -plugin-arg-clang-lazy -Xclang visit-implicit-code" + ExtraClangOptions="-Xclang -plugin-arg-clazy -Xclang visit-implicit-code" fi -ClangLazyLib=ClangLazy@CMAKE_SHARED_LIBRARY_SUFFIX@ +ClazyPluginLib=ClazyPlugin@CMAKE_SHARED_LIBRARY_SUFFIX@ -if ( test -f "$libdir/$ClangLazyLib" ) +if ( test -f "$libdir/$ClazyPluginLib" ) then # find plugin libraries in install dir export LD_LIBRARY_PATH=$libdir:$LD_LIBRARY_PATH export DYLD_LIBRARY_PATH=$libdir:$DYLD_LIBRARY_PATH -elif ( test -f "$(dirname $0)/lib/$ClangLazyLib" ) +elif ( test -f "$(dirname $0)/lib/$ClazyPluginLib" ) then # find plugin libraries in build dir export LD_LIBRARY_PATH=$(dirname $0)/lib:$LD_LIBRARY_PATH export DYLD_LIBRARY_PATH=$(dirname $0)/lib:$DYLD_LIBRARY_PATH fi -${CLANGXX:-clang++} -Qunused-arguments -Xclang -load -Xclang $ClangLazyLib -Xclang -add-plugin -Xclang clang-lazy $ExtraClangOptions "$@" +${CLANGXX:-clang++} -Qunused-arguments -Xclang -load -Xclang $ClazyPluginLib -Xclang -add-plugin -Xclang clazy $ExtraClangOptions "$@" diff --git a/dev-scripts/make_windows_package.py b/dev-scripts/make_windows_package.py index b060f15..d685db9 100644 --- a/dev-scripts/make_windows_package.py +++ b/dev-scripts/make_windows_package.py @@ -1,110 +1,110 @@ #!/usr/bin/env python import sys, os from shutil import copyfile #-------------------------------------------------- # Change here: CLAZY_VERSION = '1.4' CANDIDATE_SHA1 = 'v1.4_windows_binaries' PACKAGE_DIR = '/c/d/clazy-msvc-package/' PACKAGE_DIR_WIN = 'c:\\d\\clazy-msvc-package\\' #-------------------------------------------------- CLAZY_REPO_URL = "https://github.com/KDE/clazy.git" MSVC_VERSION = os.getenv('MSVC_VERSION', '') LLVM_INSTALL_DIR = os.getenv('LLVM_INSTALL_DIR', '') CLAZY_WORK_DIR = 'work' + MSVC_VERSION CLAZY_ZIP_WITHOUT_EXTENSION = "clazy_v%s-msvc%s" % (CLAZY_VERSION, MSVC_VERSION) CLAZY_ZIP = CLAZY_ZIP_WITHOUT_EXTENSION + '.zip' CLAZY_SRC_ZIP = "clazy_v%s-src.zip" % CLAZY_VERSION IS_FIRST_RUN = (MSVC_VERSION == '2015') def run_command(cmd, abort_on_error = True): print cmd success = (os.system(cmd) == 0) if abort_on_error and not success: sys.exit(1) return success def copy(src, dest): run_command('cp %s %s' % (src, dest)) def check_env(): if MSVC_VERSION not in ['2015', '2017']: print "Error: Set MSVC_VERSION to a proper value. Exiting..." sys.exit(1) if not LLVM_INSTALL_DIR: print "Error: Set LLVM_INSTALL_DIR to a proper value. Exiting..." sys.exit(1) if IS_FIRST_RUN: run_command('rm -rf ' + PACKAGE_DIR) def clone_clazy(sha1, work_dir): if os.path.exists(work_dir): run_command("rm -rf " + work_dir) run_command("git clone %s %s" % (CLAZY_REPO_URL, work_dir)) run_command("git checkout " + sha1) def build_clazy(): cmd = 'cmake -DCMAKE_INSTALL_PREFIX=%s -DCMAKE_BUILD_TYPE=Release -DCLANG_LIBRARY_IMPORT=%s\lib\clang.lib -G "NMake Makefiles JOM" .' % (LLVM_INSTALL_DIR, LLVM_INSTALL_DIR) run_command(cmd) run_command('jom') run_command('jom install') def copy_files(work_dir): if not os.path.exists(PACKAGE_DIR_WIN): os.mkdir(PACKAGE_DIR_WIN) os.mkdir(PACKAGE_DIR_WIN + 'clazy') os.mkdir(PACKAGE_DIR_WIN + 'clazy/share') os.mkdir(PACKAGE_DIR_WIN + 'clazy/bin') os.mkdir(PACKAGE_DIR_WIN + 'clazy/bin/clang') copy("../windows-package/clazy-cl.bat", PACKAGE_DIR + 'clazy/bin') copy("../windows-package/clazy.bat", PACKAGE_DIR + 'clazy/bin') copy("../windows-package/LICENSE-CLAZY.txt", PACKAGE_DIR + 'clazy') copy("../windows-package/LICENSE-LLVM.TXT", PACKAGE_DIR + 'clazy') copy("../windows-package/README.txt", PACKAGE_DIR + 'clazy') copy("../README.md", PACKAGE_DIR + 'clazy/README-CLAZY.md') copy(LLVM_INSTALL_DIR + '/bin/clang.exe', PACKAGE_DIR + 'clazy/bin/clang/') copy(LLVM_INSTALL_DIR + '/bin/clang.exe', PACKAGE_DIR + 'clazy/bin/clang/clang-cl.exe') - copy(LLVM_INSTALL_DIR + '/bin/ClangLazy.dll', PACKAGE_DIR + 'clazy/bin/clang/') + copy(LLVM_INSTALL_DIR + '/bin/ClazyPlugin.dll', PACKAGE_DIR + 'clazy/bin/clang/') copy(LLVM_INSTALL_DIR + '/bin/clazy-standalone.exe', PACKAGE_DIR + 'clazy/bin/clang/') run_command("cp -r %s/lib/clang/ %s" % (LLVM_INSTALL_DIR, PACKAGE_DIR + 'clazy/bin/lib/')) run_command("cp -r %s/share/doc/ %s" % (LLVM_INSTALL_DIR, PACKAGE_DIR + 'clazy/share/')) run_command('unix2dos %s' % PACKAGE_DIR + 'clazy/bin/clazy-cl.bat') run_command('unix2dos %s' % PACKAGE_DIR + 'clazy/bin/clazy.bat') def zip_package(): os.chdir(PACKAGE_DIR_WIN) run_command('zip -r %s clazy/' % (CLAZY_ZIP)) run_command('rm -rf clazy') if IS_FIRST_RUN: run_command('wget --no-check-certificate https://github.com/KDE/clazy/archive/%s.zip -O %s' % (CANDIDATE_SHA1, CLAZY_SRC_ZIP)) run_command('sha1sum %s > sums.txt' % CLAZY_ZIP) run_command('sha256sum %s >> sums.txt' % CLAZY_ZIP) run_command('sha1sum %s >> sums.txt' % CLAZY_SRC_ZIP) run_command('sha256sum %s >> sums.txt' % CLAZY_SRC_ZIP) else: run_command('sha1sum %s >> sums.txt' % CLAZY_ZIP) run_command('sha256sum %s >> sums.txt' % CLAZY_ZIP) run_command("unzip %s -d %s" % (CLAZY_ZIP, CLAZY_ZIP_WITHOUT_EXTENSION)) print "Don't forget to delete %s after testing" % CLAZY_ZIP_WITHOUT_EXTENSION os.chdir('..') check_env() clone_clazy(CANDIDATE_SHA1, CLAZY_WORK_DIR) os.chdir(CLAZY_WORK_DIR) build_clazy() copy_files(CLAZY_WORK_DIR) zip_package() diff --git a/docs/man/clazy.pod b/docs/man/clazy.pod index d693f25..aff3323 100644 --- a/docs/man/clazy.pod +++ b/docs/man/clazy.pod @@ -1,240 +1,240 @@ =encoding utf8 =head1 NAME clazy - a static source code analyzer for Qt5-based C++. =head1 SYNOPSIS clazy [option] [clang++-options] =head1 DESCRIPTION clazy scans C++/Qt source code looking for issues related to good coding practice with of Qt5. In typical use, during code compilation with clazy you will see any such warnings printed to the output normally as you would find any compiler warnings. clazy has the ability to "fix" the offending code in some cases. See the B environment variable description below for more information. =head1 OPTIONS =over 4 =item B<--help> Print help message and exit. =item B<--version> Print version information and exit. =item B<--list> Print a list of all available checkers, arranged by level. =item B<--explain> Print explanations for all checkers. =item B<--explain> Print explanations for the checkers matching the specified regular expression. =back Any of the options above will print the requested information and then exit. =over 4 =item B<--qt4compat> This option runs clazy in Qt4 compatibility mode. Use this when your source code can build with Qt4 and Qt5 in order to easily suppress issues that cannot be fixed due to the requirement of the Qt4 API. This is a convenience option which is identical to directly passing: - "-Xclang -plugin-arg-clang-lazy -Xclang qt4-compat" + "-Xclang -plugin-arg-clazy -Xclang qt4-compat" =back =over 4 =item B<--qtdeveloper> B This option is special for running clazy on Qt itself. Will result in fewer false positives being reported in Qt code. This is a convenience option which is identical to directly passing: - "-Xclang -plugin-arg-clang-lazy -Xclang qt-developer" + "-Xclang -plugin-arg-clazy -Xclang qt-developer" =back All other options are passed directly to clang++ and handled from there. See the clang manual for a list of the very large set of options available, but in normal operation you "compile" your code with clazy just as you would with clang. =head1 EXAMPLES =over 4 =item Print a list of all available checkers, arranged by check level: % clazy --list List of available clazy checkers: Checks from level0. Very stable checks, 100% safe, no false-positives: connect-non-signal container-anti-pattern lambda-in-connect mutable-container-key qdatetime-utc qenums qfileinfo-exists .... =item Compile your CMake project with clazy default checkers: % cmake -DCMAKE_CXX_COMPILER=clazy then make as normal =item Compile your CMake project with level2 checks only (non-Windows): % export CLAZY_CHECKS="level2" % cmake -DCMAKE_CXX_COMPILER=clazy then make as normal =item Compile your qmake project with clazy default checkers: % qmake -spec linux-clang QMAKE_CXX=clazy then make as normal =back =head1 IN-CODE DIRECTIVES clazy supports the following list of in-code directives: =over 4 =item clazy:skip Exempt an entire file from all checks. No clazy tests will run on the file. =item clazy:excludeall= Exempt the entire file from the specified checks. The clazy checks name1, etc will not be run on this file. =item clazy:exclude= Exclude individual lines from specific checks. The clazy checks tests name1, etc. will not be run on the line where this directive is found. =back Don't include the 'clazy-' prefix. For example, to disable the "qstring-allocations" check, you would write: // clazy:exclude=qstring-allocations and not // clazy:exclude=clazy-qstring-allocations Also note that these directives must be C++ style comments; C style comments are ignored. =head1 ENVIRONMENT B - a comma-separated list of checkers or check-sets to run. By default, all checkers from the "level0" and "level1" check-sets will run. B =over 4 =item 1. Enables the 2 checkers "unneeded-cast" and "virtual-call-ctor" only: % export CLAZY_CHECKS="unneeded-cast,virtual-call-ctor" =item 2. Enables all checks from the "level0" check-set, except for "qenums": % export CLAZY_CHECKS="level0,no-qenums" =item 3. Enables all checks from the "level0" check-set along with the "detaching-temporary" checker: % export CLAZY_CHECKS="level0,detaching-temporary" =back B - some checkers are able to automatically re-write your source code whenever it encounters code it can "fix". Enable this "fixit" feature by setting this variable to the name of the checker with a "fixit" capability. B =over 4 =item 1. Fix qlatin1string allocations: % export CLAZY_FIXIT="fix-qlatin1string-allocations" =item 2. Fix old-style (simple cases) connect statements: % export CLAZY_FIXIT=fix-old-style-connect More documentation is provided when running clazy with the B<--explain> command line option. Also note that only 1 fixit checker can be run at a time. =back B - some checkers can adapt their behavior depending on the value of this environment variable. More documentation is provided when running clazy with the B<--explain> command line option. B - if this is variable is set, clazy will not treat warnings as errors, even if the -Werror compiler option is specified. This is useful if you want to use -Werror only for the regular gcc/clang warnings but not for clazy warnings. =head1 COPYRIGHT AND LICENSE Copyright (C) 2015-2017 Klaralvdalens Datakonsult AB, a KDAB Group company, This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . =head1 SEE ALSO clang(1) https://www.kdab.com/use-static-analysis-improve-performance =head1 AUTHORS Sergio Martins Laurent Montel Allen Winter Albert Astals Cid Aurélien Gâteau Kevin Funk Hannah von Reth Volker Krause Christian Ehrlicher Mathias Hasselmann =cut diff --git a/src/Clazy.cpp b/src/Clazy.cpp index 0751a18..642e4f9 100644 --- a/src/Clazy.cpp +++ b/src/Clazy.cpp @@ -1,401 +1,401 @@ /* This file is part of the clazy static checker. Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins Copyright (C) 2015-2017 Sergio Martins This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Utils.h" #include "Clazy.h" #include "clazy_stl.h" #include "checkbase.h" #include "AccessSpecifierManager.h" #include "SourceCompatibilityHelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace clang; using namespace std; using namespace clang::ast_matchers; static void manuallyPopulateParentMap(ParentMap *map, Stmt *s) { if (!s) return; for (Stmt *child : s->children()) { llvm::errs() << "Patching " << child->getStmtClassName() << "\n"; map->setParent(child, s); manuallyPopulateParentMap(map, child); } } ClazyASTConsumer::ClazyASTConsumer(ClazyContext *context) : m_context(context) { #ifndef CLAZY_DISABLE_AST_MATCHERS m_matchFinder = new clang::ast_matchers::MatchFinder(); #endif } void ClazyASTConsumer::addCheck(const std::pair &check) { CheckBase *checkBase = check.first; #ifndef CLAZY_DISABLE_AST_MATCHERS checkBase->registerASTMatchers(*m_matchFinder); #endif //m_createdChecks.push_back(checkBase); const RegisteredCheck &rcheck = check.second; if (rcheck.options & RegisteredCheck::Option_VisitsStmts) m_checksToVisitStmts.push_back(checkBase); if (rcheck.options & RegisteredCheck::Option_VisitsDecls) m_checksToVisitDecls.push_back(checkBase); } ClazyASTConsumer::~ClazyASTConsumer() { #ifndef CLAZY_DISABLE_AST_MATCHERS delete m_matchFinder; #endif delete m_context; } bool ClazyASTConsumer::VisitDecl(Decl *decl) { if (AccessSpecifierManager *a = m_context->accessSpecifierManager) // Needs to visit system headers too (qobject.h for example) a->VisitDeclaration(decl); const SourceLocation locStart = getLocStart(decl); if (locStart.isInvalid() || m_context->sm.isInSystemHeader(locStart)) return true; const bool isFromIgnorableInclude = m_context->ignoresIncludedFiles() && !Utils::isMainFile(m_context->sm, locStart); m_context->lastDecl = decl; if (auto mdecl = dyn_cast(decl)) m_context->lastMethodDecl = mdecl; for (CheckBase *check : m_checksToVisitDecls) { if (!(isFromIgnorableInclude && check->canIgnoreIncludes())) check->VisitDecl(decl); } return true; } bool ClazyASTConsumer::VisitStmt(Stmt *stm) { const SourceLocation locStart = getLocStart(stm); if (locStart.isInvalid() || m_context->sm.isInSystemHeader(locStart)) return true; if (!m_context->parentMap) { if (m_context->ci.getDiagnostics().hasUnrecoverableErrorOccurred()) return false; // ParentMap sometimes crashes when there were errors. Doesn't like a botched AST. m_context->parentMap = new ParentMap(stm); } ParentMap *parentMap = m_context->parentMap; // Workaround llvm bug: Crashes creating a parent map when encountering Catch Statements. if (lastStm && isa(lastStm) && !parentMap->hasParent(stm)) { parentMap->setParent(stm, lastStm); manuallyPopulateParentMap(parentMap, stm); } lastStm = stm; // clang::ParentMap takes a root statement, but there's no root statement in the AST, the root is a declaration // So add to parent map each time we go into a different hierarchy if (!parentMap->hasParent(stm)) parentMap->addStmt(stm); const bool isFromIgnorableInclude = m_context->ignoresIncludedFiles() && !Utils::isMainFile(m_context->sm, locStart); for (CheckBase *check : m_checksToVisitStmts) { if (!(isFromIgnorableInclude && check->canIgnoreIncludes())) check->VisitStmt(stm); } return true; } void ClazyASTConsumer::HandleTranslationUnit(ASTContext &ctx) { if ((m_context->options & ClazyContext::ClazyOption_OnlyQt) && !m_context->isQt()) return; // Run our RecursiveAstVisitor based checks: TraverseDecl(ctx.getTranslationUnitDecl()); #ifndef CLAZY_DISABLE_AST_MATCHERS // Run our AstMatcher base checks: m_matchFinder->matchAST(ctx); #endif } static bool parseArgument(const string &arg, vector &args) { auto it = clazy::find(args, arg); if (it != args.end()) { args.erase(it); return true; } return false; } ClazyASTAction::ClazyASTAction() : PluginASTAction() , m_checkManager(CheckManager::instance()) { } std::unique_ptr ClazyASTAction::CreateASTConsumer(CompilerInstance &, llvm::StringRef) { // NOTE: This method needs to be kept reentrant (but not necessarily thread-safe) // Might be called from multiple threads via libclang, each thread operates on a different instance though std::lock_guard lock(CheckManager::lock()); auto astConsumer = std::unique_ptr(new ClazyASTConsumer(m_context)); auto createdChecks = m_checkManager->createChecks(m_checks, m_context); for (auto check : createdChecks) { astConsumer->addCheck(check); } return std::unique_ptr(astConsumer.release()); } static std::string getEnvVariable(const char *name) { const char *result = getenv(name); if (result) return result; else return std::string(); } bool ClazyASTAction::ParseArgs(const CompilerInstance &ci, const std::vector &args_) { // NOTE: This method needs to be kept reentrant (but not necessarily thread-safe) // Might be called from multiple threads via libclang, each thread operates on a different instance though std::vector args = args_; if (parseArgument("help", args)) { m_context = new ClazyContext(ci, getEnvVariable("CLAZY_HEADER_FILTER"), getEnvVariable("CLAZY_IGNORE_DIRS"), ClazyContext::ClazyOption_None); PrintHelp(llvm::errs()); return true; } if (parseArgument("no-inplace-fixits", args)) { // Unit-tests don't use inplace fixits m_options |= ClazyContext::ClazyOption_NoFixitsInplace; } if (parseArgument("enable-all-fixits", args)) { // This is useful for unit-tests, where we also want to run fixits. Don't use it otherwise. m_options |= ClazyContext::ClazyOption_AllFixitsEnabled; } if (parseArgument("no-autowrite-fixits", args)) m_options |= ClazyContext::ClazyOption_NoFixitsAutoWrite; if (parseArgument("qt4-compat", args)) m_options |= ClazyContext::ClazyOption_Qt4Compat; if (parseArgument("only-qt", args)) m_options |= ClazyContext::ClazyOption_OnlyQt; if (parseArgument("qt-developer", args)) m_options |= ClazyContext::ClazyOption_QtDeveloper; if (parseArgument("visit-implicit-code", args)) m_options |= ClazyContext::ClazyOption_VisitImplicitCode; if (parseArgument("ignore-included-files", args)) m_options |= ClazyContext::ClazyOption_IgnoreIncludedFiles; m_context = new ClazyContext(ci, /*headerFilter=*/ "", /*ignoreDirs=*/ "", m_options); // This argument is for debugging purposes const bool dbgPrintRequestedChecks = parseArgument("print-requested-checks", args); { std::lock_guard lock(CheckManager::lock()); m_checks = m_checkManager->requestedChecks(m_context, args); } if (args.size() > 1) { // Too many arguments. llvm::errs() << "Too many arguments: "; for (const std::string &a : args) llvm::errs() << a << ' '; llvm::errs() << "\n"; PrintHelp(llvm::errs()); return false; } else if (args.size() == 1 && m_checks.empty()) { // Checks were specified but couldn't be found llvm::errs() << "Could not find checks in comma separated string " + args[0] + "\n"; PrintHelp(llvm::errs()); return false; } if (dbgPrintRequestedChecks) printRequestedChecks(); return true; } void ClazyASTAction::printRequestedChecks() const { llvm::errs() << "Requested checks: "; const unsigned int numChecks = m_checks.size(); for (unsigned int i = 0; i < numChecks; ++i) { llvm::errs() << m_checks.at(i).name; const bool isLast = i == numChecks - 1; if (!isLast) { llvm::errs() << ", "; } } llvm::errs() << "\n"; } void ClazyASTAction::PrintHelp(llvm::raw_ostream &ros) const { std::lock_guard lock(CheckManager::lock()); RegisteredCheck::List checks = m_checkManager->availableChecks(MaxCheckLevel); clazy::sort(checks, checkLessThanByLevel); ros << "Available checks and FixIts:\n\n"; int lastPrintedLevel = -1; const auto numChecks = checks.size(); for (unsigned int i = 0; i < numChecks; ++i) { const RegisteredCheck &check = checks[i]; const string levelStr = "level" + to_string(check.level); if (lastPrintedLevel < check.level) { lastPrintedLevel = check.level; if (check.level > 0) ros << "\n"; ros << "- Checks from " << levelStr << ":\n"; } const string relativeReadmePath = "src/checks/" + levelStr + "/README-" + check.name + ".md"; auto padded = check.name; padded.insert(padded.end(), 39 - padded.size(), ' '); ros << " - " << check.name;; auto fixits = m_checkManager->availableFixIts(check.name); if (!fixits.empty()) { ros << " ("; bool isFirst = true; for (const auto& fixit : fixits) { if (isFirst) { isFirst = false; } else { ros << ','; } ros << fixit.name; } ros << ')'; } ros << "\n"; } ros << "\nIf nothing is specified, all checks from level0 and level1 will be run.\n\n"; ros << "To specify which checks to enable set the CLAZY_CHECKS env variable, for example:\n"; ros << " export CLAZY_CHECKS=\"level0\"\n"; ros << " export CLAZY_CHECKS=\"level0,reserve-candidates,qstring-allocations\"\n"; ros << " export CLAZY_CHECKS=\"reserve-candidates\"\n\n"; ros << "or pass as compiler arguments, for example:\n"; - ros << " -Xclang -plugin-arg-clang-lazy -Xclang reserve-candidates,qstring-allocations\n"; + ros << " -Xclang -plugin-arg-clazy -Xclang reserve-candidates,qstring-allocations\n"; ros << "\n"; ros << "To enable FixIts for a check, also set the env variable CLAZY_FIXIT, for example:\n"; ros << " export CLAZY_FIXIT=\"fix-qlatin1string-allocations\"\n\n"; ros << "FixIts are experimental and rewrite your code therefore only one FixIt is allowed per build.\nSpecifying a list of different FixIts is not supported.\nBackup your code before running them.\n"; } ClazyStandaloneASTAction::ClazyStandaloneASTAction(const string &checkList, const string &headerFilter, const string &ignoreDirs, ClazyContext::ClazyOptions options) : clang::ASTFrontendAction() , m_checkList(checkList.empty() ? "level1" : checkList) , m_headerFilter(headerFilter.empty() ? getEnvVariable("CLAZY_HEADER_FILTER") : headerFilter) , m_ignoreDirs(ignoreDirs.empty() ? getEnvVariable("CLAZY_IGNORE_DIRS") : ignoreDirs) , m_options(options) { } unique_ptr ClazyStandaloneASTAction::CreateASTConsumer(CompilerInstance &ci, llvm::StringRef) { auto context = new ClazyContext(ci, m_headerFilter, m_ignoreDirs, m_options); auto astConsumer = new ClazyASTConsumer(context); auto cm = CheckManager::instance(); vector checks; checks.push_back(m_checkList); const RegisteredCheck::List requestedChecks = cm->requestedChecks(context, checks); if (requestedChecks.size() == 0) { llvm::errs() << "No checks were requested!\n" << "\n"; return nullptr; } auto createdChecks = cm->createChecks(requestedChecks, context); for (const auto &check : createdChecks) { astConsumer->addCheck(check); } return unique_ptr(astConsumer); } volatile int ClazyPluginAnchorSource = 0; static FrontendPluginRegistry::Add -X("clang-lazy", "clang lazy plugin"); +X("clazy", "clang lazy plugin"); diff --git a/tests/clazy/test_requested_checks.sh b/tests/clazy/test_requested_checks.sh index ddb8cad..365da2e 100755 --- a/tests/clazy/test_requested_checks.sh +++ b/tests/clazy/test_requested_checks.sh @@ -1,113 +1,113 @@ unset CLAZY_CHECKS unset CLAZY_FIXIT -CLAZY_COMMAND="clazy -c -o /dev/null -xc++ -Xclang -plugin-arg-clang-lazy -Xclang print-requested-checks " +CLAZY_COMMAND="clazy -c -o /dev/null -xc++ -Xclang -plugin-arg-clazy -Xclang print-requested-checks " CLAZY_COMMAND_STDIN=$CLAZY_COMMAND"-" # Test without checks: echo | $CLAZY_COMMAND_STDIN # Test with invalid check: export CLAZY_CHECKS="foo" echo | $CLAZY_COMMAND_STDIN # Test with 1 check specified through env variable export CLAZY_CHECKS="foreach" echo | $CLAZY_COMMAND_STDIN # Test with 2 checks specified through env variable export CLAZY_CHECKS="foreach,writing-to-temporary" echo | $CLAZY_COMMAND_STDIN # Test with 2 checks specified through env variable plus one error export CLAZY_CHECKS="foreach,writing-to-temporary,foo" echo | $CLAZY_COMMAND_STDIN # Test that fixit enables the check unset CLAZY_CHECKS export CLAZY_FIXIT="fix-old-style-connect" echo | $CLAZY_COMMAND_STDIN # Test both check and fixit export CLAZY_CHECKS="old-style-connect" export CLAZY_FIXIT="fix-old-style-connect" echo | $CLAZY_COMMAND_STDIN # Test fixit+check + unrelated check export CLAZY_CHECKS="old-style-connect,foreach" export CLAZY_FIXIT="fix-old-style-connect" echo | $CLAZY_COMMAND_STDIN # test all_checks unset CLAZY_FIXIT export CLAZY_CHECKS="all_checks" echo | $CLAZY_COMMAND_STDIN unset CLAZY_FIXIT unset CLAZY_CHECKS # Test specifying check in command line -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts -) # Pass two checks in command line -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts,foreach -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts,foreach -) # Pass fixits through the command-line -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang fix-old-style-connect -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang fix-old-style-connect -) # Pass level0 -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang level0 -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang level0 -) # Pass level1 -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang level1 -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang level1 -) # Pass level0 + another check -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang reserve-candidates -Xclang -plugin-arg-clang-lazy -Xclang level0 -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang reserve-candidates -Xclang -plugin-arg-clazy -Xclang level0 -) # Pass level0 + another check that's already in level0 -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang qdatetime-utc -Xclang -plugin-arg-clang-lazy -Xclang level0 -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang qdatetime-utc -Xclang -plugin-arg-clazy -Xclang level0 -) # Use a level argument in the checks list -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts,foreach,level0 -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts,foreach,level0 -) # Use a level in env-variable export CLAZY_CHECKS="level1" echo | $CLAZY_COMMAND_STDIN # Should also work with quotes. Users sometimes add quotes in QtCreator. echo Test9 export CLAZY_CHECKS=\"level1\" echo | $CLAZY_COMMAND_STDIN # Use a level in env-variable + another check export CLAZY_CHECKS="level0,reserve-candidates" echo | $CLAZY_COMMAND_STDIN # Use both env variable and compiler argument export CLAZY_CHECKS="level0,reserve-candidates" -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts,level0 -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts,level0 -) unset CLAZY_FIXIT unset CLAZY_CHECKS # Test disabling checks works -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts,foreach,no-foreach -) -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts,no-foreach -) -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang implicit-casts,no-implicit-casts -) -echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clang-lazy -Xclang level0,no-qenums,no-qgetenv -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts,foreach,no-foreach -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts,no-foreach -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang implicit-casts,no-implicit-casts -) +echo | $($CLAZY_COMMAND -Xclang -plugin-arg-clazy -Xclang level0,no-qenums,no-qgetenv -) # Test disabling checks works, now with env variables export CLAZY_CHECKS="implicit-casts,foreach,no-foreach" echo | $CLAZY_COMMAND_STDIN export CLAZY_CHECKS="implicit-casts,no-foreach" echo | $CLAZY_COMMAND_STDIN export CLAZY_CHECKS="implicit-casts,no-implicit-casts" echo | $CLAZY_COMMAND_STDIN export CLAZY_CHECKS="level0,no-qenums,no-qgetenv" echo | $CLAZY_COMMAND_STDIN export CLAZY_CHECKS="no-qenums" echo | $CLAZY_COMMAND_STDIN diff --git a/tests/clazy/test_requested_checks.sh.expected b/tests/clazy/test_requested_checks.sh.expected index f2a071c..7075a5b 100644 --- a/tests/clazy/test_requested_checks.sh.expected +++ b/tests/clazy/test_requested_checks.sh.expected @@ -1,127 +1,127 @@ Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Invalid check: foo Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: foreach Requested checks: foreach, writing-to-temporary Invalid check: foo Requested checks: foreach, writing-to-temporary Requested checks: old-style-connect Requested checks: old-style-connect Requested checks: foreach, old-style-connect Requested checks: auto-unexpected-qstringbuilder, base-class-event, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, copyable-polymorphic, ctor-missing-parent-argument, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, function-args-by-ref, function-args-by-value, global-const-char-pointer, implicit-casts, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, missing-qobject-macro, missing-typeinfo, mutable-container-key, non-pod-global-static, old-style-connect, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-allocations, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, returning-void-expression, rule-of-three, rule-of-two-soft, skipped-base-method, static-pmf, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-call-ctor, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: implicit-casts Requested checks: foreach, implicit-casts Requested checks: old-style-connect Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qenums, qfileinfo-exists, qgetenv, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qenums, qfileinfo-exists, qgetenv, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, reserve-candidates, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qenums, qfileinfo-exists, qgetenv, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, foreach, fully-qualified-moc-types, implicit-casts, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qenums, qfileinfo-exists, qgetenv, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Test9 Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qenums, qfileinfo-exists, qgetenv, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, reserve-candidates, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, implicit-casts, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qenums, qfileinfo-exists, qgetenv, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, reserve-candidates, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: implicit-casts Requested checks: implicit-casts Could not find checks in comma separated string implicit-casts,no-implicit-casts Available checks and FixIts: - Checks from level0: - connect-by-name - connect-non-signal - connect-not-normalized - container-anti-pattern - empty-qstringliteral - fully-qualified-moc-types - lambda-in-connect - lambda-unique-connection - mutable-container-key - qcolor-from-literal - qdatetime-utc (fix-qdatetime-utc) - qenums - qfileinfo-exists - qgetenv (fix-qgetenv) - qmap-with-pointer-key - qstring-arg - qstring-insensitive-allocation - qstring-ref (fix-missing-qstringref) - qt-macros - qvariant-template-instantiation - strict-iterators - temporary-iterator - unused-non-trivial-variable - writing-to-temporary - wrong-qevent-cast - wrong-qglobalstatic - Checks from level1: - auto-unexpected-qstringbuilder (fix-auto-unexpected-qstringbuilder) - child-event-qobject-cast - connect-3arg-lambda - const-signal-or-slot - detaching-temporary - foreach - incorrect-emit - inefficient-qlist-soft - install-event-filter - non-pod-global-static - overridden-signal - post-event - qdeleteall - qhash-namespace - qlatin1string-non-ascii - qproperty-without-notify - qstring-left - range-loop - returning-data-from-temporary - rule-of-two-soft - skipped-base-method - virtual-signal - Checks from level2: - base-class-event - copyable-polymorphic - ctor-missing-parent-argument - function-args-by-ref - function-args-by-value - global-const-char-pointer - implicit-casts - missing-qobject-macro - missing-typeinfo - old-style-connect (fix-old-style-connect) - qstring-allocations (fix-qlatin1string-allocations,fix-fromLatin1_fromUtf8-allocations,fix-fromCharPtrAllocations) - returning-void-expression - rule-of-three - static-pmf - virtual-call-ctor - Checks from level3: - assert-with-side-effects - detaching-member - reserve-candidates - thread-with-slots - unneeded-cast If nothing is specified, all checks from level0 and level1 will be run. To specify which checks to enable set the CLAZY_CHECKS env variable, for example: export CLAZY_CHECKS="level0" export CLAZY_CHECKS="level0,reserve-candidates,qstring-allocations" export CLAZY_CHECKS="reserve-candidates" or pass as compiler arguments, for example: - -Xclang -plugin-arg-clang-lazy -Xclang reserve-candidates,qstring-allocations + -Xclang -plugin-arg-clazy -Xclang reserve-candidates,qstring-allocations To enable FixIts for a check, also set the env variable CLAZY_FIXIT, for example: export CLAZY_FIXIT="fix-qlatin1string-allocations" FixIts are experimental and rewrite your code therefore only one FixIt is allowed per build. Specifying a list of different FixIts is not supported. Backup your code before running them. Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qfileinfo-exists, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: implicit-casts Requested checks: implicit-casts Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qenums, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: connect-by-name, connect-non-signal, connect-not-normalized, container-anti-pattern, empty-qstringliteral, fully-qualified-moc-types, lambda-in-connect, lambda-unique-connection, mutable-container-key, qcolor-from-literal, qdatetime-utc, qfileinfo-exists, qmap-with-pointer-key, qstring-arg, qstring-insensitive-allocation, qstring-ref, qt-macros, qvariant-template-instantiation, strict-iterators, temporary-iterator, unused-non-trivial-variable, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-by-name, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, empty-qstringliteral, foreach, fully-qualified-moc-types, incorrect-emit, inefficient-qlist-soft, install-event-filter, lambda-in-connect, lambda-unique-connection, mutable-container-key, non-pod-global-static, overridden-signal, post-event, qcolor-from-literal, qdatetime-utc, qdeleteall, qfileinfo-exists, qgetenv, qhash-namespace, qlatin1string-non-ascii, qmap-with-pointer-key, qproperty-without-notify, qstring-arg, qstring-insensitive-allocation, qstring-left, qstring-ref, qt-macros, qvariant-template-instantiation, range-loop, returning-data-from-temporary, rule-of-two-soft, skipped-base-method, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qevent-cast, wrong-qglobalstatic diff --git a/tests/run_tests.py b/tests/run_tests.py index 19dbc2a..9873eae 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -1,587 +1,587 @@ #!/usr/bin/env python2 import sys, os, subprocess, string, re, json, threading, multiprocessing, argparse from threading import Thread from sys import platform as _platform def isWindows(): return _platform == 'win32' class QtInstallation: def __init__(self): self.int_version = 000 self.qmake_header_path = "/usr/include/qt/" self.qmake_lib_path = "/usr/lib" def compiler_flags(self): return "-isystem " + self.qmake_header_path + ("" if isWindows() else " -fPIC") + " -L " + self.qmake_lib_path class Test: def __init__(self, check): self.filename = "" self.minimum_qt_version = 500 self.maximum_qt_version = 59999 self.minimum_clang_version = 380 self.compare_everything = False self.isFixedFile = False self.link = False # If true we also call the linker self.check = check self.expects_failure = False self.qt_major_version = 5 # Tests use Qt 5 by default self.env = os.environ self.checks = [] self.flags = "" self.must_fail = False self.blacklist_platforms = [] self.qt4compat = False self.only_qt = False self.qt_developer = False self.header_filter = "" self.ignore_dirs = "" def isScript(self): return self.filename.endswith(".sh") def setQtMajorVersion(self, major_version): if major_version == 4: self.qt_major_version = 4 if self.minimum_qt_version >= 500: self.minimum_qt_version = 400 def envString(self): result = "" for key in self.env: result += key + '="' + self.env[key] + '" ' return result def setEnv(self, e): self.env = os.environ.copy() for key in e: key_str = key.encode('ascii', 'ignore') self.env[key_str] = e[key].encode('ascii', 'ignore') class Check: def __init__(self, name): self.name = name self.minimum_clang_version = 380 # clang 3.8.0 self.minimum_qt_version = 500 self.maximum_qt_version = 59999 self.enabled = True self.clazy_standalone_only = False self.tests = [] #------------------------------------------------------------------------------- # utility functions #1 def get_command_output(cmd, test_env = os.environ): try: output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True, env=test_env) except subprocess.CalledProcessError, e: return e.output,False return output,True def load_json(check_name): check = Check(check_name) filename = check_name + "/config.json" if not os.path.exists(filename): # Ignore this directory return check f = open(filename, 'r') contents = f.read() f.close() decoded = json.loads(contents) check_blacklist_platforms = [] if 'minimum_clang_version' in decoded: check.minimum_clang_version = decoded['minimum_clang_version'] if 'minimum_qt_version' in decoded: check.minimum_qt_version = decoded['minimum_qt_version'] if 'maximum_qt_version' in decoded: check.maximum_qt_version = decoded['maximum_qt_version'] if 'enabled' in decoded: check.enabled = decoded['enabled'] if 'clazy_standalone_only' in decoded: check.clazy_standalone_only = decoded['clazy_standalone_only'] if 'blacklist_platforms' in decoded: check_blacklist_platforms = decoded['blacklist_platforms'] if 'tests' in decoded: for t in decoded['tests']: test = Test(check) test.blacklist_platforms = check_blacklist_platforms test.filename = t['filename'] if 'minimum_qt_version' in t: test.minimum_qt_version = t['minimum_qt_version'] else: test.minimum_qt_version = check.minimum_qt_version if 'maximum_qt_version' in t: test.maximum_qt_version = t['maximum_qt_version'] else: test.maximum_qt_version = check.maximum_qt_version if 'minimum_clang_version' in t: test.minimum_clang_version = t['minimum_clang_version'] else: test.minimum_clang_version = check.minimum_clang_version if 'blacklist_platforms' in t: test.blacklist_platforms = t['blacklist_platforms'] if 'compare_everything' in t: test.compare_everything = t['compare_everything'] if 'isFixedFile' in t: test.isFixedFile = t['isFixedFile'] if 'link' in t: test.link = t['link'] if 'qt_major_version' in t: test.setQtMajorVersion(t['qt_major_version']) if 'env' in t: test.setEnv(t['env']) if 'checks' in t: test.checks = t['checks'] if 'flags' in t: test.flags = t['flags'] if 'must_fail' in t: test.must_fail = t['must_fail'] if 'expects_failure' in t: test.expects_failure = t['expects_failure'] if 'qt4compat' in t: test.qt4compat = t['qt4compat'] if 'only_qt' in t: test.only_qt = t['only_qt'] if 'qt_developer' in t: test.qt_developer = t['qt_developer'] if 'header_filter' in t: test.header_filter = t['header_filter'] if 'ignore_dirs' in t: test.ignore_dirs = t['ignore_dirs'] if not test.checks: test.checks.append(test.check.name) check.tests.append(test) if test.isFixedFile: fileToDelete = check_name + "/" + test.filename if os.path.exists(fileToDelete): os.remove(fileToDelete) return check def find_qt_installation(major_version, qmakes): installation = QtInstallation() for qmake in qmakes: qmake_version_str,success = get_command_output(qmake + " -query QT_VERSION") if success and qmake_version_str.startswith(str(major_version) + "."): qmake_header_path = get_command_output(qmake + " -query QT_INSTALL_HEADERS")[0].strip() qmake_lib_path = get_command_output(qmake + " -query QT_INSTALL_LIBS")[0].strip() if qmake_header_path: installation.qmake_header_path = qmake_header_path if qmake_lib_path: installation.qmake_lib_path = qmake_lib_path ver = qmake_version_str.split('.') installation.int_version = int(ver[0]) * 10000 + int(ver[1]) * 100 + int(ver[2]) if _verbose: print "Found Qt " + str(installation.int_version) + " using qmake " + qmake break if installation.int_version == 0 and major_version >= 5: # Don't warn for missing Qt4 headers print "Error: Couldn't find a Qt" + str(major_version) + " installation" return installation def libraryName(): if _platform == 'win32': - return 'ClangLazy.dll' + return 'ClazyPlugin.dll' elif _platform == 'darwin': - return 'ClangLazy.dylib' + return 'ClazyPlugin.dylib' else: - return 'ClangLazy.so' + return 'ClazyPlugin.so' def link_flags(): flags = "-lQt5Core -lQt5Gui -lQt5Widgets" if _platform.startswith('linux'): flags += " -lstdc++" return flags def clazy_cpp_args(): return "-Wno-unused-value -Qunused-arguments -std=c++14 " def more_clazy_args(): - return " -Xclang -plugin-arg-clang-lazy -Xclang no-inplace-fixits " + clazy_cpp_args() + return " -Xclang -plugin-arg-clazy -Xclang no-inplace-fixits " + clazy_cpp_args() def clazy_standalone_command(test, qt): result = " -- " + clazy_cpp_args() + qt.compiler_flags() + " " + test.flags result = " -no-inplace-fixits -checks=" + string.join(test.checks, ',') + " " + result if not test.isFixedFile: result = " -enable-all-fixits " + result if test.qt4compat: result = " -qt4-compat " + result if test.only_qt: result = " -only-qt " + result if test.qt_developer: result = " -qt-developer " + result if test.header_filter: result = " -header-filter " + test.header_filter + " " + result if test.ignore_dirs: result = " -ignore-dirs " + test.ignore_dirs + " " + result return result def clazy_command(qt, test, filename): if test.isScript(): return "./" + filename if 'CLAZY_CXX' in os.environ: # In case we want to use clazy.bat result = os.environ['CLAZY_CXX'] + more_clazy_args() + qt.compiler_flags() else: clang = os.getenv('CLANGXX', 'clang') - result = clang + " -Xclang -load -Xclang " + libraryName() + " -Xclang -add-plugin -Xclang clang-lazy " + more_clazy_args() + qt.compiler_flags() + result = clang + " -Xclang -load -Xclang " + libraryName() + " -Xclang -add-plugin -Xclang clazy " + more_clazy_args() + qt.compiler_flags() if test.qt4compat: - result = result + " -Xclang -plugin-arg-clang-lazy -Xclang qt4-compat " + result = result + " -Xclang -plugin-arg-clazy -Xclang qt4-compat " if test.only_qt: - result = result + " -Xclang -plugin-arg-clang-lazy -Xclang only-qt " + result = result + " -Xclang -plugin-arg-clazy -Xclang only-qt " if test.qt_developer: - result = result + " -Xclang -plugin-arg-clang-lazy -Xclang qt-developer " + result = result + " -Xclang -plugin-arg-clazy -Xclang qt-developer " if test.link and _platform.startswith('linux'): # Linking on one platform is enough. Won't waste time on macOS and Windows. result = result + " " + link_flags() else: result = result + " -c " - result = result + test.flags + " -Xclang -plugin-arg-clang-lazy -Xclang " + string.join(test.checks, ',') + " " + result = result + test.flags + " -Xclang -plugin-arg-clazy -Xclang " + string.join(test.checks, ',') + " " if not test.isFixedFile: # When compiling the already fixed file disable fixit, we don't want to fix twice result += _enable_fixits_argument + " " result += filename return result def dump_ast_command(test): return "clang -std=c++14 -fsyntax-only -Xclang -ast-dump -fno-color-diagnostics -c " + qt_installation(test.qt_major_version).compiler_flags() + " " + test.flags + " " + test.filename def compiler_name(): if 'CLAZY_CXX' in os.environ: return os.environ['CLAZY_CXX'] # so we can set clazy.bat instead return os.getenv('CLANGXX', 'clang') #------------------------------------------------------------------------------- # Get clang version version,success = get_command_output(compiler_name() + ' --version') match = re.search('clang version (.*?)[ -]', version) try: version = match.group(1) except: print "Could not determine clang version, is it in PATH?" sys.exit(-1) CLANG_VERSION = int(version.replace('.', '')) #------------------------------------------------------------------------------- # Setup argparse parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", action='store_true') parser.add_argument("--no-standalone", action='store_true', help="Don\'t run clazy-standalone") parser.add_argument("--only-standalone", action='store_true', help='Only run clazy-standalone') parser.add_argument("--dump-ast", action='store_true', help='Dump a unit-test AST to file') parser.add_argument("--exclude", help='Comma separated list of checks to ignore') parser.add_argument("check_names", nargs='*', help="The name of the check who's unit-tests will be run. Defaults to running all checks.") args = parser.parse_args() if args.only_standalone and args.no_standalone: print "Error: --only-standalone is incompatible with --no-standalone" sys.exit(1) #------------------------------------------------------------------------------- # Global variables -_enable_fixits_argument = "-Xclang -plugin-arg-clang-lazy -Xclang enable-all-fixits" +_enable_fixits_argument = "-Xclang -plugin-arg-clazy -Xclang enable-all-fixits" _dump_ast = args.dump_ast _verbose = args.verbose _no_standalone = args.no_standalone _only_standalone = args.only_standalone _num_threads = multiprocessing.cpu_count() _lock = threading.Lock() _was_successful = True _qt5_installation = find_qt_installation(5, ["QT_SELECT=5 qmake", "qmake-qt5", "qmake"]) _qt4_installation = find_qt_installation(4, ["QT_SELECT=4 qmake", "qmake-qt4", "qmake"]) _excluded_checks = args.exclude.split(',') if args.exclude is not None else [] #------------------------------------------------------------------------------- # utility functions #2 def qt_installation(major_version): if major_version == 5: return _qt5_installation elif major_version == 4: return _qt4_installation return None def run_command(cmd, output_file = "", test_env = os.environ): lines,success = get_command_output(cmd, test_env) lines = lines.replace("std::_Container_base0", "std::_Vector_base") # Hack for Windows, we have std::_Vector_base in the expected data lines = lines.replace("std::__1::__vector_base_common", "std::_Vector_base") # Hack for macOS lines = lines.replace("std::_Vector_alloc", "std::_Vector_base") if not success and not output_file: print lines return False if _verbose: print "Running: " + cmd print "output_file=" + output_file lines = lines.replace('\r\n', '\n') if output_file: f = open(output_file, 'w') f.writelines(lines) f.close() else: print lines return success def files_are_equal(file1, file2): try: f = open(file1, 'r') lines1 = f.readlines() f.close() f = open(file2, 'r') lines2 = f.readlines() f.close() return lines1 == lines2 except: return False def get_check_names(): return filter(lambda entry: os.path.isdir(entry), os.listdir(".")) # Returns all files with .cpp_fixed extension. These were rewritten by clang. def get_fixed_files(): return filter(lambda entry: entry.endswith('.cpp_fixed.cpp'), os.listdir(".")) def print_differences(file1, file2): # Returns true if the the files are equal return run_command("diff -Naur {} {}".format(file1, file2)) def normalizedCwd(): return os.getcwd().replace('\\', '/') def extract_word(word, in_file, out_file): in_f = open(in_file, 'r') out_f = open(out_file, 'w') for line in in_f: if word in line: line = line.replace('\\', '/') line = line.replace(normalizedCwd() + '/', "") # clazy-standalone prints the complete cpp file path for some reason. Normalize it so it compares OK with the expected output. out_f.write(line) in_f.close() out_f.close() def print_file(filename): f = open(filename, 'r') print f.read() f.close() def file_contains(filename, text): f = open(filename, 'r') contents = f.read() f.close() return text in contents def run_unit_test(test, is_standalone): if test.check.clazy_standalone_only and not is_standalone: return True qt = qt_installation(test.qt_major_version) if _verbose: print print "Qt version: " + str(qt.int_version) print "Qt headers: " + qt.qmake_header_path if qt.int_version < test.minimum_qt_version or qt.int_version > test.maximum_qt_version or CLANG_VERSION < test.minimum_clang_version: if (_verbose): print "Skipping " + test.check_name + " because required version is not available" return True if _platform in test.blacklist_platforms: if (_verbose): print "Skipping " + test.check_name + " because it is blacklisted for this platform" return True checkname = test.check.name filename = checkname + "/" + test.filename output_file = filename + ".out" result_file = filename + ".result" expected_file = filename + ".expected" if is_standalone and test.isScript(): return True if is_standalone: cmd_to_run = "clazy-standalone " + filename + " " + clazy_standalone_command(test, qt) else: cmd_to_run = clazy_command(qt, test, filename) if test.compare_everything: result_file = output_file if test.isFixedFile: result_file = filename must_fail = test.must_fail cmd_success = run_command(cmd_to_run, output_file, test.env) if file_contains(output_file, 'Invalid check: '): return True if (not cmd_success and not must_fail) or (cmd_success and must_fail): print "[FAIL] " + checkname + " (Failed to build test. Check " + output_file + " for details)" print "-------------------" print "Contents of %s:" % output_file print_file(output_file) print "-------------------" print return False if not test.compare_everything and not test.isFixedFile: word_to_grep = "warning:" if not must_fail else "error:" extract_word(word_to_grep, output_file, result_file) printableName = checkname if len(test.check.tests) > 1: printableName += "/" + test.filename if is_standalone: printableName += " (standalone)" success = files_are_equal(expected_file, result_file) if test.expects_failure: if success: print "[XOK] " + printableName return False else: print "[XFAIL] " + printableName print_differences(expected_file, result_file) else: if success: print "[OK] " + printableName else: print "[FAIL] " + printableName print_differences(expected_file, result_file) return False return True def run_unit_tests(tests): result = True for test in tests: if not _only_standalone: result = result and run_unit_test(test, False) if not _no_standalone: result = result and run_unit_test(test, True) global _was_successful, _lock with _lock: _was_successful = _was_successful and result def dump_ast(check): for test in check.tests: ast_filename = test.filename + ".ast" run_command(dump_ast_command(test) + " > " + ast_filename) print "Dumped AST to " + os.getcwd() + "/" + ast_filename #------------------------------------------------------------------------------- def load_checks(all_check_names): checks = [] for name in all_check_names: try: check = load_json(name) if check.enabled: checks.append(check) except: print "Error while loading " + name raise sys.exit(-1) return checks #------------------------------------------------------------------------------- # main if 'CLAZY_NO_WERROR' in os.environ: del os.environ['CLAZY_NO_WERROR'] os.environ['CLAZY_CHECKS'] = '' all_check_names = get_check_names() all_checks = load_checks(all_check_names) requested_check_names = args.check_names requested_check_names = map(lambda x: x.strip("/\\"), requested_check_names) for check_name in requested_check_names: if check_name not in all_check_names: print "Unknown check: " + check_name print sys.exit(-1) if not requested_check_names: requested_check_names = all_check_names requested_checks = filter(lambda check: check.name in requested_check_names and check.name not in _excluded_checks, all_checks) requested_checks = filter(lambda check: check.minimum_clang_version <= CLANG_VERSION, requested_checks) threads = [] if _dump_ast: for check in requested_checks: os.chdir(check.name) dump_ast(check) os.chdir("..") else: list_of_chunks = [[] for x in range(_num_threads)] # Each list is a list of Test to be worked on by a thread i = _num_threads for check in requested_checks: for test in check.tests: if not test.isFixedFile: i = (i + 1) % _num_threads list_of_chunks[i].append(test) for tests in list_of_chunks: if not tests: continue; t = Thread(target=run_unit_tests, args=(tests,)) t.start() threads.append(t) for thread in threads: thread.join() if _was_successful: print "SUCCESS" sys.exit(0) else: print "FAIL" sys.exit(-1) diff --git a/windows-package/clazy-cl.bat b/windows-package/clazy-cl.bat index 84e02f9..5e5f61e 100644 --- a/windows-package/clazy-cl.bat +++ b/windows-package/clazy-cl.bat @@ -1,2 +1,2 @@ @echo off -"%~dp0\clang\clang.exe" --driver-mode=cl -Qunused-arguments -Xclang -load -Xclang ClangLazy.dll -Xclang -add-plugin -Xclang clang-lazy -Wno-microsoft-enum-value %* +"%~dp0\clang\clang.exe" --driver-mode=cl -Qunused-arguments -Xclang -load -Xclang ClazyPlugin.dll -Xclang -add-plugin -Xclang clazy -Wno-microsoft-enum-value %* diff --git a/windows-package/clazy.bat b/windows-package/clazy.bat index 272c9d9..2031f4b 100644 --- a/windows-package/clazy.bat +++ b/windows-package/clazy.bat @@ -1,2 +1,2 @@ @echo off -"%~dp0\clang\clang.exe" -Qunused-arguments -Xclang -load -Xclang ClangLazy.dll -Xclang -add-plugin -Xclang clang-lazy %* +"%~dp0\clang\clang.exe" -Qunused-arguments -Xclang -load -Xclang ClazyPlugin.dll -Xclang -add-plugin -Xclang clazy %*