diff options
368 files changed, 6748 insertions, 2365 deletions
diff --git a/cmake_has_regex_test.cpp b/.cmake_has_regex_test.cpp index b0e20e73..b0e20e73 100644 --- a/cmake_has_regex_test.cpp +++ b/.cmake_has_regex_test.cpp @@ -28,3 +28,5 @@ output.log clazy.1 /dev-scripts/*.log clanglazy_export.h +CPackConfig.cmake +CPackSourceConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 0549f1fa..9139e5bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,13 +14,15 @@ include("GNUInstallDirs") # Version setup set(CLAZY_VERSION_MAJOR "1") -set(CLAZY_VERSION_MINOR "3") +set(CLAZY_VERSION_MINOR "4") 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_SOURCE_DIR}/cmake) -find_package(Clang 3.8 MODULE REQUIRED) +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) @@ -41,7 +43,7 @@ if(CLAZY_BUILD_UTILS_LIB) add_definitions(-DCLAZY_BUILD_UTILS_LIB) endif() -if(MSVC AND NOT CLANG_LIBRARY_IMPORT) +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() @@ -61,7 +63,7 @@ endif() # Look for std::regex support message("Looking for std::regex support...") -try_run(RUN_RESULT COMPILE_RESULT ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/cmake_has_regex_test.cpp) +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) @@ -74,22 +76,11 @@ endif() include(ClazySources.cmake) -if(MSVC) - string(REPLACE "\\" "/" LLVM_LIBS ${LLVM_LIBS}) -endif() - -set(CLAZY_STANDALONE_LLVM_LIBS ${LLVM_LIBS}) - - -if(MSVC) - list(REMOVE_ITEM CLANG_LIBS "-lFrontend") -endif() - include_directories(${CMAKE_BINARY_DIR}) -include_directories(${CLANG_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src) +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 llvm_libs is_standalone) +macro(link_to_llvm name is_standalone) foreach(clang_lib ${CLANG_LIBS}) if(MSVC) get_filename_component(LIB_FILENAME ${clang_lib} NAME) @@ -104,8 +95,8 @@ macro(link_to_llvm name llvm_libs is_standalone) target_link_libraries(${name} ${clang_lib}) endforeach() - foreach(llvm_lib ${llvm_libs}) - if(NOT ${is_standalone} AND NOT APPLE AND NOT MINGW) + 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}) @@ -128,23 +119,16 @@ macro(link_to_llvm name llvm_libs is_standalone) endif() endmacro() -option(CLAZY_STATIC_PLUGIN_LIB "Build plugin into static library" OFF) -set(PLUGIN_LINK_TYPE SHARED) - -if (CLAZY_STATIC_PLUGIN_LIB) - set(PLUGIN_LINK_TYPE STATIC) -endif() - macro(add_clang_plugin name) set(srcs ${ARGN}) - add_library(${name} ${PLUGIN_LINK_TYPE} ${srcs}) + 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} "${LLVM_LIBS}" FALSE) + link_to_llvm(${name} FALSE) if(MSVC) target_link_libraries(${name} ${CLANG_LIBRARY_IMPORT}) # Link against clang.exe to share the plugin registry @@ -164,7 +148,7 @@ if(CLAZY_BUILD_UTILS_LIB) set(clazylib_VERSION ${clazylib_VERSION_MAJOR}.${clazylib_VERSION_MINOR}) add_library(clazylib SHARED ${CLAZY_LIB_SRC}) - link_to_llvm(clazylib "${LLVM_LIBS}" FALSE) + link_to_llvm(clazylib FALSE) generate_export_header(clazylib) set_target_properties(clazylib PROPERTIES VERSION ${clazylib_VERSION} SOVERSION ${clazylib_VERSION_MAJOR}) install(TARGETS clazylib EXPORT LibClazyExport @@ -176,96 +160,135 @@ endif() set(SYMBOL_FILE Lazy.exports) -add_clang_plugin(ClangLazy ${CLAZY_PLUGIN_SRCS}) - -set_target_properties(ClangLazy PROPERTIES - LINKER_LANGUAGE CXX - PREFIX "" -) +if (NOT CLAZY_BUILD_WITH_CLANG) + add_clang_plugin(ClangLazy ${CLAZY_PLUGIN_SRCS}) + set_target_properties(ClangLazy PROPERTIES + LINKER_LANGUAGE CXX + PREFIX "" + ) -install(TARGETS ClangLazy - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) + install(TARGETS ClangLazy + 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") + set(SHARE_INSTALL_DIR ${CMAKE_INSTALL_DATAROOTDIR} CACHE STRING "Share directory name") -if(NOT WIN32) - configure_file(${CMAKE_SOURCE_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_SOURCE_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_SOURCE_DIR}/clazy-cl.bat DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) + 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() -endif() -# Install the explanation README's -set(DOC_INSTALL_DIR ${SHARE_INSTALL_DIR}/doc/clazy) -file(GLOB README_LEVEL0_FILES src/checks/level0/README-*) -file(GLOB README_LEVEL1_FILES src/checks/level1/README-*) -file(GLOB README_LEVEL2_FILES src/checks/level2/README-*) -file(GLOB README_LEVEL3_FILES src/checks/level3/README-*) -file(GLOB README_HIDDENLEVEL_FILES src/checks/hiddenlevel/README-*) -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_HIDDENLEVEL_FILES} DESTINATION ${DOC_INSTALL_DIR}/hiddenlevel) - -# Install more doc files -install(FILES README.md COPYING-LGPL2.txt checks.json DESTINATION ${DOC_INSTALL_DIR}) - -# Build docs -set(MAN_INSTALL_DIR "man/man1") -add_subdirectory(docs) + # Install the explanation README's + set(DOC_INSTALL_DIR ${SHARE_INSTALL_DIR}/doc/clazy) -if(CLAZY_BUILD_UTILS_LIB) - # Install public headers - set(CLAZY_LIB_INCLUDES - src/AccessSpecifierManager.h - src/checkbase.h - src/checkmanager.h - src/clazy_export.h - src/clazy_stl.h - src/ContextUtils.h - src/FixItUtils.h - src/HierarchyUtils.h - src/LoopUtils.h - src/MacroUtils.h - src/QtUtils.h - src/SuppressionManager.h - src/StmtBodyRange.h - src/StringUtils.h - src/TemplateUtils.h - src/TypeUtils.h - src/Utils.h - src/ClazyContext.h - ) - install(FILES ${CLAZY_LIB_INCLUDES} DESTINATION include/clazy) -endif() + 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) + + if(CLAZY_BUILD_UTILS_LIB) + # Install public headers + set(CLAZY_LIB_INCLUDES + src/AccessSpecifierManager.h + src/checkbase.h + src/checkmanager.h + src/clazy_export.h + src/clazy_stl.h + src/ContextUtils.h + src/FixItUtils.h + src/HierarchyUtils.h + src/LoopUtils.h + src/MacroUtils.h + src/QtUtils.h + src/SuppressionManager.h + src/StmtBodyRange.h + src/StringUtils.h + src/TemplateUtils.h + src/TypeUtils.h + src/Utils.h + src/ClazyContext.h + ) + install(FILES ${CLAZY_LIB_INCLUDES} DESTINATION include/clazy) + endif() -# 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") + # rpath + set(CMAKE_SKIP_BUILD_RPATH FALSE) + set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") -endif("${isSystemDir}" STREQUAL "-1") + 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}) + # 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 - target_link_libraries(clazy-standalone clangFrontend) -else() - target_link_libraries(clazy-standalone ClangLazy) -endif() + if(MSVC) + # On MSVC clang-standalone crashes with a meaningless backtrace if linked to ClangLazy.dll + target_link_libraries(clazy-standalone clangFrontend) + else() + target_link_libraries(clazy-standalone ClangLazy) + endif() + + link_to_llvm(clazy-standalone TRUE) -link_to_llvm(clazy-standalone "${CLAZY_STANDALONE_LLVM_LIBS}" TRUE) + install(TARGETS clazy-standalone DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE) -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() @@ -64,3 +64,28 @@ 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 (, 2018) + - New Checks: + connect-by-name + skipped-base-class + 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=<regex> 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=<regex> 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 diff --git a/CheckSources.cmake b/CheckSources.cmake new file mode 100644 index 00000000..4d16efbe --- /dev/null +++ b/CheckSources.cmake @@ -0,0 +1,83 @@ +set(CLAZY_CHECKS_SRCS ${CLAZY_CHECKS_SRCS} + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/container-inside-loop.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/inefficient-qlist.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/isempty-vs-count.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qhash-with-char-pointer-key.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qstring-varargs.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qt-keywords.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/qt4-qstring-from-array.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/raw-environment-function.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/manuallevel/tr-non-literal.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/connect-by-name.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/connect-non-signal.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/connect-not-normalized.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/container-anti-pattern.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/empty-qstringliteral.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/fully-qualified-moc-types.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/lambda-in-connect.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/lambda-unique-connection.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/mutable-container-key.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qcolor-from-literal.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qdatetime-utc.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qenums.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qfileinfo-exists.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qgetenv.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qmap-with-pointer-key.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qstring-arg.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qstring-insensitive-allocation.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qstring-ref.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qt-macros.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qvariant-template-instantiation.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/strict-iterators.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/temporary-iterator.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/unused-non-trivial-variable.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/writing-to-temporary.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/wrong-qevent-cast.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/wrong-qglobalstatic.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/auto-unexpected-qstringbuilder.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/child-event-qobject-cast.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/connect-3arg-lambda.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/const-signal-or-slot.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/detaching-temporary.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/foreach.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/incorrect-emit.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/inefficient-qlist-soft.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/install-event-filter.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/non-pod-global-static.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/overridden-signal.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/post-event.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qdeleteall.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qhash-namespace.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qlatin1string-non-ascii.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qproperty-without-notify.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qstring-left.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/range-loop.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/returning-data-from-temporary.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/rule-of-two-soft.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/skipped-base-method.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/virtual-signal.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/base-class-event.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/copyable-polymorphic.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/ctor-missing-parent-argument.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/function-args-by-ref.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/function-args-by-value.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/global-const-char-pointer.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/implicit-casts.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/missing-qobject-macro.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/missing-typeinfo.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/old-style-connect.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/qstring-allocations.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/returning-void-expression.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/rule-of-three.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/static-pmf.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/virtual-call-ctor.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/assert-with-side-effects.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/detaching-member.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/reserve-candidates.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/thread-with-slots.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/unneeded-cast.cpp +) + +if(HAS_STD_REGEX OR CLAZY_BUILD_WITH_CLANG) + set(CLAZY_CHECKS_SRCS ${CLAZY_CHECKS_SRCS} ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/old-style-connect.cpp) +endif() diff --git a/ClazySources.cmake b/ClazySources.cmake index ad8fa9d7..d0d7a496 100644 --- a/ClazySources.cmake +++ b/ClazySources.cmake @@ -15,81 +15,11 @@ set(CLAZY_LIB_SRC ) set(CLAZY_CHECKS_SRCS - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qcolor-from-literal.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/connect-non-signal.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/connect-not-normalized.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/container-anti-pattern.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/lambda-in-connect.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/lambda-unique-connection.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qdatetimeutc.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qenums.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qfileinfo-exists.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qgetenv.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qmap-with-pointer-key.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qstringarg.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qstring-insensitive-allocation.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qstringref.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qt-macros.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/qvariant-template-instantiation.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/mutable-container-key.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/strict-iterators.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/temporaryiterator.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/unused-non-trivial-variable.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/writingtotemporary.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level0/wrong-qglobalstatic.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/autounexpectedqstringbuilder.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/child-event-qobject-cast.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/connect-3arg-lambda.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/const-signal-or-slot.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/detachingtemporary.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/foreach.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/inefficient-qlist-soft.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/install-event-filter.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/ctor-missing-parent-argument.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/nonpodstatic.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qdeleteall.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qproperty-without-notify.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qstring-left.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qlatin1string-non-ascii.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/range-loop.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/returning-data-from-temporary.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/ruleoftwosoft.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/post-event.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/incorrect-emit.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/overridden-signal.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/qhash-namespace.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level1/virtual-signal.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/base-class-event.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/container-inside-loop.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/function-args-by-ref.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/function-args-by-value.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/globalconstcharpointer.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/implicitcasts.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/missing-qobject-macro.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/missing-type-info.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/qstring-allocations.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/reservecandidates.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/ruleofthree.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/virtualcallsfromctor.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/returning-void-expression.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/copyable-polymorphic.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/assertwithsideeffects.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/detachingmember.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/dynamic_cast.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/level3/thread-with-slots.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/hiddenlevel/inefficientqlist.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/hiddenlevel/isempty-vs-count.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/hiddenlevel/tr-non-literal.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/hiddenlevel/qt4-qstring-from-array.cpp ${CMAKE_CURRENT_LIST_DIR}/src/checks/detachingbase.cpp ${CMAKE_CURRENT_LIST_DIR}/src/checks/inefficientqlistbase.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/checks/requiredresults.cpp ${CMAKE_CURRENT_LIST_DIR}/src/checks/ruleofbase.cpp ) - -if(HAS_STD_REGEX) - set(CLAZY_CHECKS_SRCS ${CLAZY_CHECKS_SRCS} ${CMAKE_CURRENT_LIST_DIR}/src/checks/level2/oldstyleconnect.cpp) -endif() +include(CheckSources.cmake) set(CLAZY_SHARED_SRCS # sources shared between clazy-standalone and clazy plugin ${CLAZY_CHECKS_SRCS} @@ -1,4 +1,4 @@ -clazy v1.3 +clazy v1.4 =========== 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. @@ -13,7 +13,7 @@ Table of contents * [Build and install clang](#build-and-install-clang) * [Build clazy](#build-clazy) * [Windows](#windows) - * [Pre-built msvc2015 clang and clazy binaries](#pre-built-msvc2015-clang-and-clazy-binaries) + * [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) @@ -27,7 +27,7 @@ Table of contents * [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) - * [clang-standalone and JSON database support](#clang-standalone-and-json-database-support) + * [clazy-standalone and JSON database support](#clazy-standalone-and-json-database-support) * [Enabling Fixits](#enabling-fixits) * [Troubleshooting](#troubleshooting) * [Qt4 compatibility mode](#qt4-compatibility-mode) @@ -55,8 +55,8 @@ You can get clazy from: - Other distros: Check llvm/clang build docs. ### Build and install clang -clang and LLVM >= 3.8 are required. -Use clazy v1.1 if you need 3.7 support. +clang and LLVM >= 3.9 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. @@ -142,13 +142,13 @@ $ sudo port select --set clang mp-clang-3.9 The recommended way is to build clazy yourself, but alternatively you can try user recipes, such as: ``` -$ brew install haraldf/kf5/clazy +$ brew install kde-mac/kde/clazy ``` for stable branch, or for master: ``` -$ brew install haraldf/kf5/clazy --HEAD +$ 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. @@ -173,7 +173,7 @@ $ brew install --with-clang llvm # 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](#clang-standalone-and-json-database-support). +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 `<prefix>/bin/`. Compile your programs with it instead of clang++/g++. @@ -205,83 +205,100 @@ 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, no false-positives -- level1: Similar to level0, but sometimes (rarely) there might be some false-positives -- level2: Sometimes has false-positives (20-30%). -- level3: Not always correct, possibly very noisy, might require a knowledgeable developer to review, might have a very big rate of false-positives, might have bugs. +- 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 level0: - - [connect-non-signal](src/checks/level0/README-connect-non-signal.md) - - [connect-not-normalized](src/checks/level0/README-connect-not-normalized.md) - - [container-anti-pattern](src/checks/level0/README-container-anti-pattern.md) - - [lambda-in-connect](src/checks/level0/README-lambda-in-connect.md) - - [lambda-unique-connection](src/checks/level0/README-lambda-unique-connection.md) - - [mutable-container-key](src/checks/level0/README-mutable-container-key.md) - - [qcolor-from-literal](src/checks/level0/README-qcolor-from-literal.md) - - [qdatetime-utc](src/checks/level0/README-qdatetime-utc.md) (fix-qdatetime-utc) - - [qenums](src/checks/level0/README-qenums.md) - - [qfileinfo-exists](src/checks/level0/README-qfileinfo-exists.md) - - [qgetenv](src/checks/level0/README-qgetenv.md) (fix-qgetenv) - - [qmap-with-pointer-key](src/checks/level0/README-qmap-with-pointer-key.md) - - [qstring-arg](src/checks/level0/README-qstring-arg.md) - - [qstring-insensitive-allocation](src/checks/level0/README-qstring-insensitive-allocation.md) - - [qstring-ref](src/checks/level0/README-qstring-ref.md) (fix-missing-qstringref) - - [qt-macros](src/checks/level0/README-qt-macros.md) - - [qvariant-template-instantiation](src/checks/level0/README-qvariant-template-instantiation.md) - - [strict-iterators](src/checks/level0/README-strict-iterators.md) - - [temporary-iterator](src/checks/level0/README-temporary-iterator.md) - - [unused-non-trivial-variable](src/checks/level0/README-unused-non-trivial-variable.md) - - [writing-to-temporary](src/checks/level0/README-writing-to-temporary.md) - - [wrong-qglobalstatic](src/checks/level0/README-wrong-qglobalstatic.md) - -- Checks from level1: - - [auto-unexpected-qstringbuilder](src/checks/level1/README-auto-unexpected-qstringbuilder.md) (fix-auto-unexpected-qstringbuilder) - - [child-event-qobject-cast](src/checks/level1/README-child-event-qobject-cast.md) - - [connect-3arg-lambda](src/checks/level1/README-connect-3arg-lambda.md) - - [const-signal-or-slot](src/checks/level1/README-const-signal-or-slot.md) - - [detaching-temporary](src/checks/level1/README-detaching-temporary.md) - - [foreach](src/checks/level1/README-foreach.md) - - [incorrect-emit](src/checks/level1/README-incorrect-emit.md) - - [inefficient-qlist-soft](src/checks/level1/README-inefficient-qlist-soft.md) - - [install-event-filter](src/checks/level1/README-install-event-filter.md) - - [non-pod-global-static](src/checks/level1/README-non-pod-global-static.md) - - [overridden-signal](src/checks/level1/README-overridden-signal.md) - - [post-event](src/checks/level1/README-post-event.md) - - [qdeleteall](src/checks/level1/README-qdeleteall.md) - - [qhash-namespace](src/checks/level1/README-qhash-namespace.md) - - [qlatin1string-non-ascii](src/checks/level1/README-qlatin1string-non-ascii.md) - - [qproperty-without-notify](src/checks/level1/README-qproperty-without-notify.md) - - [qstring-left](src/checks/level1/README-qstring-left.md) - - [range-loop](src/checks/level1/README-range-loop.md) - - [returning-data-from-temporary](src/checks/level1/README-returning-data-from-temporary.md) - - [rule-of-two-soft](src/checks/level1/README-rule-of-two-soft.md) - - [virtual-signal](src/checks/level1/README-virtual-signal.md) - -- Checks from level2: - - [base-class-event](src/checks/level2/README-base-class-event.md) - - [container-inside-loop](src/checks/level2/README-container-inside-loop.md) - - [copyable-polymorphic](src/checks/level2/README-copyable-polymorphic.md) - - [ctor-missing-parent-argument](src/checks/level2/README-ctor-missing-parent-argument.md) - - [function-args-by-ref](src/checks/level2/README-function-args-by-ref.md) - - [function-args-by-value](src/checks/level2/README-function-args-by-value.md) - - [global-const-char-pointer](src/checks/level2/README-global-const-char-pointer.md) - - [implicit-casts](src/checks/level2/README-implicit-casts.md) - - [missing-qobject-macro](src/checks/level2/README-missing-qobject-macro.md) - - [missing-typeinfo](src/checks/level2/README-missing-typeinfo.md) - - [old-style-connect](src/checks/level2/README-old-style-connect.md) (fix-old-style-connect) - - [qstring-allocations](src/checks/level2/README-qstring-allocations.md) (fix-qlatin1string-allocations,fix-fromLatin1_fromUtf8-allocations,fix-fromCharPtrAllocations) - - [reserve-candidates](src/checks/level2/README-reserve-candidates.md) - - [returning-void-expression](src/checks/level2/README-returning-void-expression.md) - - [rule-of-three](src/checks/level2/README-rule-of-three.md) - - [virtual-call-ctor](src/checks/level2/README-virtual-call-ctor.md) - -- Checks from level3: - - [assert-with-side-effects](src/checks/level3/README-assert-with-side-effects.md) - - [bogus-dynamic-cast](src/checks/level3/README-bogus-dynamic-cast.md) - - [detaching-member](src/checks/level3/README-detaching-member.md) - - [thread-with-slots](src/checks/level3/README-thread-with-slots.md) +- Checks from Manual Level: + - [container-inside-loop](docs/checks/README-container-inside-loop.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 @@ -292,7 +309,7 @@ You can disable checks by prefixing with `no-`, in case you don't want all check ## Example via env variable ``` -export CLAZY_CHECKS="bogus-dynamic-cast,qmap-with-key-pointer,virtual-call-ctor" # Enables only these 3 checks +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 ``` @@ -300,7 +317,7 @@ export CLAZY_CHECKS="level0,detaching-temporary" # Enables all from level0 and a `clazy -Xclang -plugin-arg-clang-lazy -Xclang level0,detaching-temporary` Don't forget to re-run cmake/qmake/etc if you altered the c++ flags to specify flags. -# clang-standalone and JSON database support +# 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, @@ -391,10 +408,12 @@ and rebuild. # Reducing warning noise -If you think you found a false-positive, file a bug report. +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: @@ -438,7 +457,7 @@ and thanks to: # Contributing patches -New features go to master and bug fixes go to 1.3 branch. +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: - <https://community.kde.org/Infrastructure/Phabricator> - <https://phabricator.kde.org/differential/> diff --git a/artwork/clazy.png b/artwork/clazy.png Binary files differnew file mode 100755 index 00000000..cd473b96 --- /dev/null +++ b/artwork/clazy.png diff --git a/artwork/clazy.svgz b/artwork/clazy.svgz Binary files differnew file mode 100755 index 00000000..32ed0df5 --- /dev/null +++ b/artwork/clazy.svgz diff --git a/artwork/ico1024.png b/artwork/ico1024.png Binary files differnew file mode 100755 index 00000000..e55bdf41 --- /dev/null +++ b/artwork/ico1024.png diff --git a/artwork/ico128.png b/artwork/ico128.png Binary files differnew file mode 100755 index 00000000..ecff717e --- /dev/null +++ b/artwork/ico128.png diff --git a/checks.json b/checks.json index 0a826b59..aec842f7 100644 --- a/checks.json +++ b/checks.json @@ -1,109 +1,192 @@ { - "available_categories" : ["readability", "qt4", "containers", "qstring", "cpp", "bug", "performance"], + "available_categories" : ["readability", "qt4", "containers", "qstring", "cpp", "bug", "performance", "deprecation", "qml"], "checks" : [ { + "name" : "qt-keywords", + "level" : -1, + "fixits" : [ + { + "name" : "qt-keywords" + } + ] + }, + { "name" : "inefficient-qlist", "level" : -1, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_decls" : true }, { "name" : "isempty-vs-count", + "class_name" : "IsEmptyVSCount", + "level" : -1, + "categories" : ["readability"], + "visits_stmts" : true + }, + { + "name" : "qstring-varargs", "level" : -1, - "categories" : ["readability"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "qt4-qstring-from-array", + "class_name" : "Qt4QStringFromArray", "level" : -1, "categories" : ["qt4", "qstring"], "fixits" : [ { "name" : "qt4-qstring-from-array" } - ] + ], + "visits_stmts" : true + }, + { + "name" : "tr-non-literal", + "level" : -1, + "categories" : ["bug"], + "visits_stmts" : true + }, + { + "name" : "raw-environment-function", + "level" : -1, + "categories" : ["bug"], + "visits_stmts" : true + }, + { + "name" : "container-inside-loop", + "level" : -1, + "categories" : ["containers", "performance"], + "visits_stmts" : true + }, + { + "name" : "qhash-with-char-pointer-key", + "level" : -1, + "categories" : ["cpp", "bug"], + "visits_decls" : true + }, + { + "name" : "connect-by-name", + "level" : 0, + "categories" : ["bug", "readability"], + "visits_decls" : true }, { "name" : "connect-non-signal", + "minimum_qt_version" : 50700, "level" : 0, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true + }, + { + "name" : "wrong-qevent-cast", + "level" : 0, + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "lambda-in-connect", "level" : 0, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "lambda-unique-connection", "level" : 0, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "qdatetime-utc", + "class_name" : "QDateTimeUtc", "level" : 0, "categories" : ["performance"], "fixits" : [ { "name" : "qdatetime-utc" } - ] + ], + "visits_stmts" : true }, { "name" : "qgetenv", + "class_name" : "QGetEnv", "level" : 0, + "minimum_qt_version" : 50500, "categories" : ["performance"], "fixits" : [ { "name" : "qgetenv" } - ] + ], + "visits_stmts" : true }, { "name" : "qstring-insensitive-allocation", "level" : 0, - "categories" : ["performance", "qstring"] + "categories" : ["performance", "qstring"], + "visits_stmts" : true + }, + { + "name" : "fully-qualified-moc-types", + "class_name" : "FullyQualifiedMocTypes", + "level" : 0, + "categories" : ["bug", "qml"], + "visits_decls" : true }, { "name" : "qvariant-template-instantiation", - "level" : 0 + "level" : 0, + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "unused-non-trivial-variable", "level" : 0, - "categories" : ["readability"] + "categories" : ["readability"], + "visits_stmts" : true }, { "name" : "connect-not-normalized", "level" : 0, - "categories" : ["performance"] + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "mutable-container-key", "level" : 0, - "categories" : ["containers", "bug"] + "categories" : ["containers", "bug"], + "visits_decls" : true }, { "name" : "qenums", "level" : 0, + "minimum_qt_version" : 50500, "categories" : ["deprecation"] }, { "name" : "qmap-with-pointer-key", "level" : 0, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_decls" : true }, { "name" : "qstring-ref", + "class_name" : "StringRefCandidates", "level" : 0, "categories" : ["performance", "qstring"], "fixits" : [ { "name" : "missing-qstringref" } - ] + ], + "visits_stmts" : true }, { "name" : "strict-iterators", "level" : 0, - "categories" : ["containers", "performance", "bug"] + "categories" : ["containers", "performance", "bug"], + "visits_stmts" : true }, { "name" : "writing-to-temporary", @@ -113,22 +196,27 @@ { "name" : "widen-criteria" } - ] + ], + "visits_stmts" : true }, { "name" : "container-anti-pattern", "level" : 0, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_stmts" : true }, { "name" : "qcolor-from-literal", "level" : 0, - "categories" : ["performance"] + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "qfileinfo-exists", + "class_name" : "QFileInfoExists", "level" : 0, - "categories" : ["performance"] + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "qstring-arg", @@ -138,28 +226,42 @@ { "name" : "fillChar-overloads" } - ] + ], + "visits_stmts" : true + }, + { + "name" : "empty-qstringliteral", + "level" : 0, + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "qt-macros", + "class_name" : "QtMacros", "level" : 0, "categories" : ["bug"] }, { "name" : "temporary-iterator", "level" : 0, - "categories" : ["containers", "bug"] + "categories" : ["containers", "bug"], + "visits_stmts" : true }, { "name" : "wrong-qglobalstatic", + "class_name" : "WrongQGlobalStatic", "level" : 0, - "categories" : ["performance"] + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "auto-unexpected-qstringbuilder", + "class_name" : "AutoUnexpectedQStringBuilder", "level" : 1, "categories" : ["bug", "qstring"], + "visits_decls" : true, + "visits_stmts" : true, "fixits" : [ { "name" : "auto-unexpected-qstringbuilder" @@ -169,136 +271,189 @@ { "name" : "connect-3arg-lambda", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "const-signal-or-slot", "level" : 1, - "categories" : ["readability", "bug"] - }, - { - "name" : "ctor-missing-parent-argument", - "level" : 1 + "categories" : ["readability", "bug"], + "visits_decls" : true, + "visits_stmts" : true }, { "name" : "detaching-temporary", "level" : 1, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_stmts" : true }, { "name" : "foreach", "level" : 1, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_stmts" : true }, { "name" : "incorrect-emit", "level" : 1, - "categories" : ["readability"] + "categories" : ["readability"], + "visits_stmts" : true }, { "name" : "inefficient-qlist-soft", "level" : 1, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_decls" : true }, { "name" : "install-event-filter", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "non-pod-global-static", "level" : 1, - "categories" : ["performance"] + "categories" : ["performance"], + "visits_stmts" : true }, { "name" : "post-event", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "qdeleteall", + "class_name" : "QDeleteAll", "level" : 1, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_stmts" : true }, { "name" : "qlatin1string-non-ascii", "level" : 1, - "categories" : ["bug", "qstring"] + "categories" : ["bug", "qstring"], + "visits_stmts" : true }, { "name" : "qproperty-without-notify", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "qstring-left", "level" : 1, - "categories" : ["bug", "performance", "qstring"] + "categories" : ["bug", "performance", "qstring"], + "visits_stmts" : true }, { "name" : "range-loop", "level" : 1, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_stmts" : true }, { "name" : "returning-data-from-temporary", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "rule-of-two-soft", "level" : 1, - "categories" : ["cpp", "bug"] + "categories" : ["cpp", "bug"], + "visits_stmts" : true }, { "name" : "child-event-qobject-cast", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_decls" : true }, { "name" : "virtual-signal", "level" : 1, - "categories" : ["bug", "readability"] + "categories" : ["bug", "readability"], + "visits_decls" : true }, { "name" : "overridden-signal", "level" : 1, - "categories" : ["bug", "readability"] + "categories" : ["bug", "readability"], + "visits_decls" : true }, { "name" : "qhash-namespace", "level" : 1, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_decls" : true }, { - "name" : "base-class-event", + "name" : "skipped-base-method", + "level" : 1, + "categories" : ["bug", "cpp"], + "visits_stmts" : true + }, + { + "name" : "unneeded-cast", + "level" : 3, + "categories" : ["cpp", "readability"], + "options" : [ + { + "name" : "prefer-dynamic-cast-over-qobject" + } + ], + "visits_stmts" : true + }, + { + "name" : "ctor-missing-parent-argument", "level" : 2, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_decls" : true }, { - "name" : "container-inside-loop", + "name" : "base-class-event", "level" : 2, - "categories" : ["containers", "performance"] + "categories" : ["bug"], + "visits_decls" : true }, { "name" : "copyable-polymorphic", "level" : 2, - "categories" : ["cpp", "bug"] + "categories" : ["cpp", "bug"], + "visits_decls" : true }, { "name" : "function-args-by-ref", "level" : 2, - "categories" : ["cpp", "performance"] + "categories" : ["cpp", "performance"], + "options" : [ + { + "name" : "warn-for-overridden-methods" + } + ], + "visits_decls" : true, + "visits_stmts" : true }, { "name" : "function-args-by-value", "level" : 2, - "categories" : ["cpp", "performance"] + "categories" : ["cpp", "performance"], + "options" : [ + { + "name" : "warn-for-overridden-methods" + } + ], + "visits_decls" : true, + "visits_stmts" : true }, { "name" : "global-const-char-pointer", "level" : 2, - "categories" : ["cpp", "performance"] + "categories" : ["cpp", "performance"], + "visits_decls" : true }, { "name" : "implicit-casts", @@ -308,30 +463,39 @@ { "name" : "bool-to-int" } - ] + ], + "visits_stmts" : true }, { "name" : "missing-qobject-macro", - "level" : 2 + "level" : 2, + "categories" : ["bug"], + "visits_decls" : true }, { "name" : "missing-typeinfo", + "class_name" : "MissingTypeInfo", "level" : 2, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_decls" : true }, { "name" : "old-style-connect", "level" : 2, + "minimum_qt_version" : 50500, + "ifndef" : "NO_STD_REGEX", "categories" : ["performance"], "fixits" : [ { "name" : "old-style-connect" } - ] + ], + "visits_stmts" : true }, { "name" : "qstring-allocations", "level" : 2, + "minimum_qt_version" : 50000, "categories" : ["performance", "qstring"], "fixits" : [ { @@ -348,52 +512,57 @@ { "name" : "no-msvc-compat" } - ] - }, - { - "name" : "reserve-candidates", - "level" : 2, - "categories" : ["containers"] + ], + "visits_stmts" : true }, { "name" : "returning-void-expression", "level" : 2, - "categories" : ["readability", "cpp"] + "categories" : ["readability", "cpp"], + "visits_stmts" : true }, { "name" : "rule-of-three", "level" : 2, - "categories" : ["cpp", "bug"] + "categories" : ["cpp", "bug"], + "visits_decls" : true }, { "name" : "virtual-call-ctor", "level" : 2, - "categories" : ["cpp", "bug"] + "categories" : ["cpp", "bug"], + "visits_decls" : true + }, + { + "name" : "static-pmf", + "level" : 2, + "categories" : ["bug"], + "visits_decls" : true }, { "name" : "assert-with-side-effects", "level" : 3, - "categories" : ["bug"] + "categories" : ["bug"], + "visits_stmts" : true }, { "name" : "detaching-member", "level" : 3, - "categories" : ["containers", "performance"] + "categories" : ["containers", "performance"], + "visits_stmts" : true }, { - "name" : "bogus-dynamic-cast", + "name" : "thread-with-slots", "level" : 3, - "categories" : ["performance"], - "options" : [ - { - "name" : "qobject" - } - ] + "categories" : ["bug"], + "visits_decls" : true, + "visits_stmts" : true }, { - "name" : "thread-with-slots", + "name" : "reserve-candidates", "level" : 3, - "categories" : ["bug"] + "categories" : ["containers"], + "visits_stmts" : true } ] } diff --git a/dev-scripts/generate.py b/dev-scripts/generate.py new file mode 100755 index 00000000..1b7633ed --- /dev/null +++ b/dev-scripts/generate.py @@ -0,0 +1,548 @@ +#!/usr/bin/env python + +_license_text = \ +"""/* + This file is part of the clazy static checker. + + Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + Author: Sérgio Martins <sergio.martins@kdab.com> + + 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. +*/ +""" + +import sys, os, json, argparse, datetime, io +from shutil import copyfile + +CHECKS_FILENAME = 'checks.json' +_checks = [] +_specified_check_names = [] +_available_categories = [] + +def checkSortKey(check): + return str(check.level) + check.name + +def level_num_to_enum(n): + if n == -1: + return 'ManualCheckLevel' + if n >= 0 and n <= 3: + return 'CheckLevel' + str(n) + + return 'CheckLevelUndefined' + +def level_num_to_name(n): + if n == -1: + return 'Manual Level' + if n >= 0 and n <= 3: + return 'Level ' + str(n) + + return 'undefined' + +def level_num_to_cmake_readme_variable(n): + if n == -1: + return 'README_manuallevel_FILES' + if n >= 0 and n <= 3: + return 'README_LEVEL%s_FILES' % str(n) + + return 'undefined' + +def clazy_source_path(): + return os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/..") + "/" + +def templates_path(): + return clazy_source_path() + "dev-scripts/templates/" + +def docs_relative_path(): + return "docs/checks/" + +def docs_path(): + return clazy_source_path() + docs_relative_path() + +def read_file(filename): + f = io.open(filename, 'r', newline='\n', encoding='utf8') + contents = f.read() + f.close() + return contents + +def write_file(filename, contents): + f = io.open(filename, 'w', newline='\n', encoding='utf8') + f.write(contents) + f.close() + +def get_copyright(): + year = datetime.datetime.now().year + author = os.getenv('GIT_AUTHOR_NAME', 'Author') + email = os.getenv('GIT_AUTHOR_EMAIL', 'your@email') + return "Copyright (C) %s %s <%s>" % (year, author, email) + +class Check: + def __init__(self): + self.name = "" + self.class_name = "" + self.level = 0 + self.categories = [] + self.minimum_qt_version = 40000 # Qt 4.0.0 + self.fixits = [] + self.visits_stmts = False + self.visits_decls = False + self.ifndef = "" + + def include(self): # Returns for example: "returning-void-expression.h" + oldstyle_headername = (self.name + ".h").replace('-', '') + if os.path.exists(self.path() + oldstyle_headername): + return oldstyle_headername + + return self.name + '.h' + + def qualified_include(self): # Returns for example: "checks/level2/returning-void-expression.h" + return self.basedir() + self.include() + + def qualified_cpp_filename(self): # Returns for example: "checks/level2/returning-void-expression.cpp" + return self.basedir() + self.cpp_filename() + + def cpp_filename(self): # Returns for example: "returning-void-expression.cpp" + filename = self.include() + filename = filename.replace(".h", ".cpp") + return filename + + def path(self): + return clazy_source_path() + self.basedir(True) + "/" + + def basedir(self, with_src=False): + level = 'level' + str(self.level) + if self.level == -1: + level = 'manuallevel' + + if with_src: + return "src/checks/" + level + '/' + return "checks/" + level + '/' + + def readme_name(self): + return "README-" + self.name + ".md" + + def readme_path(self): + return docs_path() + self.readme_name() + + + def supportsQt4(self): + return self.minimum_qt_version < 50000 + + def get_class_name(self): + if self.class_name: + return self.class_name + + # Deduce the class name + splitted = self.name.split('-') + classname = "" + for word in splitted: + if word == 'qt': + word = 'Qt' + else: + word = word.title() + if word.startswith('Q'): + word = 'Q' + word[1:].title() + + classname += word + + return classname + + def valid_name(self): + if self.name in ['clazy']: + return False + if self.name.startswith('level'): + return False + if self.name.startswith('fix'): + return False + return True + + def fixits_text(self): + if not self.fixits: + return "" + + text = "" + fixitnames = [] + for f in self.fixits: + fixitnames.append("fix-" + f) + + text = ','.join(fixitnames) + + return "(" + text + ")" + + def include_guard(self): + guard = self.name.replace('-', '_') + return guard.upper() + + +def load_json(filename): + jsonContents = read_file(filename) + decodedJson = json.loads(jsonContents) + + if 'checks' not in decodedJson: + print("No checks found in " + filename) + return False + + checks = decodedJson['checks'] + + global _available_categories, _checks, _specified_check_names + + if 'available_categories' in decodedJson: + _available_categories = decodedJson['available_categories'] + + for check in checks: + c = Check() + try: + c.name = check['name'] + c.level = check['level'] + if 'categories' in check: + c.categories = check['categories'] + for cat in c.categories: + if cat not in _available_categories: + print('Unknown category ' + cat) + return False + except KeyError: + print("Missing mandatory field while processing " + str(check)) + return False + + if _specified_check_names and c.name not in _specified_check_names: + continue + + if 'class_name' in check: + c.class_name = check['class_name'] + + if 'ifndef' in check: + c.ifndef = check['ifndef'] + + if 'minimum_qt_version' in check: + c.minimum_qt_version = check['minimum_qt_version'] + + if 'visits_stmts' in check: + c.visits_stmts = check['visits_stmts'] + + if 'visits_decls' in check: + c.visits_decls = check['visits_decls'] + + if 'fixits' in check: + for fixit in check['fixits']: + if 'name' not in fixit: + print('fixit doesnt have a name. check=' + str(check)) + return False + c.fixits.append(fixit['name']) + + if not c.valid_name(): + print("Invalid check name: %s" % (c.name())) + return False + _checks.append(c) + + _checks = sorted(_checks, key=checkSortKey) + return True + +def print_checks(checks): + for c in checks: + print(c.name + " " + str(c.level) + " " + str(c.categories)) + +#------------------------------------------------------------------------------- +def generate_register_checks(checks): + text = '#include "checkmanager.h"\n' + for c in checks: + text += '#include "' + c.qualified_include() + '"\n' + text += \ +""" +template <typename T> +RegisteredCheck check(const char *name, CheckLevel level, RegisteredCheck::Options options = RegisteredCheck::Option_None) +{ + auto factoryFuntion = [name](ClazyContext *context){ return new T(name, context); }; + return RegisteredCheck{name, level, factoryFuntion, options}; +} + +void CheckManager::registerChecks() +{ +""" + + for c in checks: + qt4flag = "RegisteredCheck::Option_None" + if not c.supportsQt4(): + qt4flag = "RegisteredCheck::Option_Qt4Incompatible" + + if c.visits_stmts: + qt4flag += " | RegisteredCheck::Option_VisitsStmts" + if c.visits_decls: + qt4flag += " | RegisteredCheck::Option_VisitsDecls" + + qt4flag = qt4flag.replace("RegisteredCheck::Option_None |", "") + + if c.ifndef: + text += "#ifndef " + c.ifndef + "\n" + + text += ' registerCheck(check<%s>("%s", %s, %s));\n' % (c.get_class_name(), c.name, level_num_to_enum(c.level), qt4flag) + + fixitID = 1 + for fixit in c.fixits: + text += ' registerFixIt(%d, "%s", "%s");\n' % (fixitID, "fix-" + fixit, c.name) + fixitID = fixitID * 2 + + if c.ifndef: + text += "#endif" + "\n" + + text += "}\n" + + comment_text = \ +""" +/** + * To add a new check you can either edit this file, or use the python script: + * dev-scripts/generate.py > src/Checks.h + */ +""" + text = _license_text + '\n' + comment_text + '\n' + text + filename = clazy_source_path() + "src/Checks.h" + + old_text = read_file(filename) + if old_text != text: + write_file(filename, text) + print("Generated " + filename) + return True + return False +#------------------------------------------------------------------------------- +def generate_cmake_file(checks): + text = "set(CLAZY_CHECKS_SRCS ${CLAZY_CHECKS_SRCS}\n" + checks_with_regexp = [] + for level in [-1, 0, 1, 2, 3]: + for check in checks: + if check.level == level: + text += " ${CMAKE_CURRENT_LIST_DIR}/src/" + check.qualified_cpp_filename() + "\n" + if check.ifndef == "NO_STD_REGEX": + checks_with_regexp.append(check) + text += ")\n" + + if checks_with_regexp: + text += "\nif(HAS_STD_REGEX OR CLAZY_BUILD_WITH_CLANG)\n" + for check in checks_with_regexp: + text += " set(CLAZY_CHECKS_SRCS ${CLAZY_CHECKS_SRCS} ${CMAKE_CURRENT_LIST_DIR}/src/" + check.qualified_cpp_filename() + ")\n" + text += "endif()\n" + + filename = clazy_source_path() + "CheckSources.cmake" + old_text = read_file(filename) + if old_text != text: + write_file(filename, text) + print("Generated " + filename) + return True + return False +#------------------------------------------------------------------------------- +def create_readmes(checks): + generated = False + for check in checks: + if not os.path.exists(check.readme_path()): + existing_readme = search_in_all_levels(check.readme_name()) + if existing_readme: + contents = read_file(existing_readme) + write_file(check.readme_path(), contents) + os.remove(existing_readme) + print("Moved " + check.readme_name()) + else: + contents = read_file(templates_path() + "check-readme.md") + contents = contents.replace('[check-name]', check.name) + write_file(check.readme_path(), contents) + print("Created " + check.readme_path()) + generated = True + return generated +#------------------------------------------------------------------------------- +def create_unittests(checks): + generated = False + for check in checks: + unittest_folder = clazy_source_path() + "tests/" + check.name + if not os.path.exists(unittest_folder): + os.mkdir(unittest_folder) + print("Created " + unittest_folder) + generated = True + + configjson_file = unittest_folder + "/config.json" + if not os.path.exists(configjson_file): + copyfile(templates_path() + "test-config.json", configjson_file) + print("Created " + configjson_file) + generated = True + + testmain_file = unittest_folder + "/main.cpp" + if not os.path.exists(testmain_file) and check.name != 'non-pod-global-static': + copyfile(templates_path() + "test-main.cpp", testmain_file) + print("Created " + testmain_file) + generated = True + return generated + +#------------------------------------------------------------------------------- +def search_in_all_levels(filename): + for level in ['manuallevel', 'level0', 'level1', 'level2', 'level3']: + complete_filename = clazy_source_path() + 'src/checks/' + level + '/' + filename + if os.path.exists(complete_filename): + return complete_filename + return "" + +#------------------------------------------------------------------------------- +def create_checks(checks): + generated = False + edit_changelog = False + for check in checks: + include_file = check.path() + check.include() + cpp_file = check.path() + check.cpp_filename() + copyright = get_copyright() + include_missing = not os.path.exists(include_file) + cpp_missing = not os.path.exists(cpp_file) + if include_missing: + + existing_include_path = search_in_all_levels(check.include()) + if existing_include_path: + # File already exists, but is in another level. Just move it: + contents = read_file(existing_include_path) + write_file(include_file, contents) + os.remove(existing_include_path) + print("Moved " + check.include()) + else: + contents = read_file(templates_path() + 'check.h') + contents = contents.replace('%1', check.include_guard()) + contents = contents.replace('%2', check.get_class_name()) + contents = contents.replace('%3', check.name) + contents = contents.replace('%4', copyright) + write_file(include_file, contents) + print("Created " + include_file) + edit_changelog = True + generated = True + if cpp_missing: + existing_cpp_path = search_in_all_levels(check.cpp_filename()) + if existing_cpp_path: + # File already exists, but is in another level. Just move it: + contents = read_file(existing_cpp_path) + write_file(cpp_file, contents) + os.remove(existing_cpp_path) + print("Moved " + check.cpp_filename()) + else: + contents = read_file(templates_path() + 'check.cpp') + contents = contents.replace('%1', check.include()) + contents = contents.replace('%2', check.get_class_name()) + contents = contents.replace('%3', copyright) + write_file(cpp_file, contents) + print("Created " + cpp_file) + generated = True + + if edit_changelog: + # We created a new check, let's also edit the ChangeLog + changelog_file = clazy_source_path() + 'Changelog' + contents = read_file(changelog_file) + contents += '\n - <dont forget changelog entry for ' + check.name + '>\n' + write_file(changelog_file, contents) + print('Edited Changelog') + + return generated +#------------------------------------------------------------------------------- +def generate_readme(checks): + filename = clazy_source_path() + "README.md" + f = io.open(filename, 'r', newline='\n', encoding='utf8') + old_contents = f.readlines(); + f.close(); + + new_text_to_insert = "" + for level in ['-1', '0', '1', '2', '3']: + new_text_to_insert += "- Checks from %s:" % level_num_to_name(int(level)) + "\n" + for c in checks: + if str(c.level) == level: + fixits_text = c.fixits_text() + if fixits_text: + fixits_text = " " + fixits_text + new_text_to_insert += " - [%s](%sREADME-%s.md)%s" % (c.name, docs_relative_path(), c.name, fixits_text) + "\n" + new_text_to_insert += "\n" + + + f = io.open(filename, 'w', newline='\n', encoding='utf8') + + skip = False + for line in old_contents: + if skip and line.startswith("#"): + skip = False + + if skip: + continue + + if line.startswith("- Checks from Manual Level:"): + skip = True + f.write(new_text_to_insert) + continue + + f.write(line) + f.close() + + f = io.open(filename, 'r', newline='\n', encoding='utf8') + new_contents = f.readlines(); + f.close(); + + if old_contents != new_contents: + print("Generated " + filename) + return True + return False +#------------------------------------------------------------------------------- +def generate_readmes_cmake_install(checks): + old_contents = "" + filename = clazy_source_path() + 'readmes.cmake' + if os.path.exists(filename): + f = io.open(filename, 'r', newline='\n', encoding='utf8') + old_contents = f.readlines(); + f.close(); + + new_text_to_insert = "" + for level in ['-1', '0', '1', '2', '3']: + new_text_to_insert += 'SET(' + level_num_to_cmake_readme_variable(int(level)) + "\n" + for c in checks: + if str(c.level) == level: + new_text_to_insert += ' ${CMAKE_CURRENT_LIST_DIR}/docs/checks/' + c.readme_name() + '\n' + new_text_to_insert += ')\n\n' + + if old_contents == new_text_to_insert: + return False + + f = io.open(filename, 'w', newline='\n', encoding='utf8') + f.write(new_text_to_insert) + f.close() + return True + +#------------------------------------------------------------------------------- + +complete_json_filename = clazy_source_path() + CHECKS_FILENAME + +if not os.path.exists(complete_json_filename): + print("File doesn't exist: " + complete_json_filename) + exit(1) + + + +parser = argparse.ArgumentParser() +parser.add_argument("--generate", action='store_true', help="Generate src/Checks.h, CheckSources.cmake and README.md") +parser.add_argument("checks", nargs='*', help="Optional check names to build. Useful to speedup builds during development, by building only the specified checks. Default is to build all checks.") +args = parser.parse_args() + +_specified_check_names = args.checks + +if not load_json(complete_json_filename): + exit(1) + +if args.generate: + generated = False + generated = generate_register_checks(_checks) or generated + generated = generate_cmake_file(_checks) or generated + generated = generate_readme(_checks) or generated + generated = create_readmes(_checks) or generated + generated = create_unittests(_checks) or generated + generated = create_checks(_checks) or generated + generated = generate_readmes_cmake_install(_checks) or generated + if not generated: + print("Nothing to do, everything is OK") +else: + parser.print_help(sys.stderr) diff --git a/dev-scripts/templates/check-readme.md b/dev-scripts/templates/check-readme.md new file mode 100644 index 00000000..07c7e2b3 --- /dev/null +++ b/dev-scripts/templates/check-readme.md @@ -0,0 +1 @@ +# [check-name] diff --git a/dev-scripts/templates/check.cpp b/dev-scripts/templates/check.cpp new file mode 100644 index 00000000..841ef57f --- /dev/null +++ b/dev-scripts/templates/check.cpp @@ -0,0 +1,45 @@ +/* + This file is part of the clazy static checker. + + %3 + + 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 "%1" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +%2::%2(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ +} + +void %2::VisitDecl(clang::Decl *decl) +{ +} + +void %2::VisitStmt(clang::Stmt *stmt) +{ +} diff --git a/dev-scripts/templates/check.h b/dev-scripts/templates/check.h new file mode 100644 index 00000000..7998cae2 --- /dev/null +++ b/dev-scripts/templates/check.h @@ -0,0 +1,40 @@ +/* + This file is part of the clazy static checker. + + %4 + + 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. +*/ + +#ifndef CLAZY_%1_H +#define CLAZY_%1_H + +#include "checkbase.h" + + +/** + * See README-%3.md for more info. + */ +class %2 : public CheckBase +{ +public: + explicit %2(const std::string &name, ClazyContext *context); + void VisitDecl(clang::Decl *) override; + void VisitStmt(clang::Stmt *) override; +private: +}; + +#endif diff --git a/tests/config.json b/dev-scripts/templates/test-config.json index e7e6e0cb..e7e6e0cb 100644 --- a/tests/config.json +++ b/dev-scripts/templates/test-config.json diff --git a/dev-scripts/templates/test-main.cpp b/dev-scripts/templates/test-main.cpp new file mode 100644 index 00000000..5ca75994 --- /dev/null +++ b/dev-scripts/templates/test-main.cpp @@ -0,0 +1,6 @@ +#include <QtCore/QObject> +#include <QtCore/QString> + +void test() +{ +} diff --git a/dev-scripts/test_build_on_distros.sh b/dev-scripts/test_build_on_distros.sh deleted file mode 100755 index 6d7424d8..00000000 --- a/dev-scripts/test_build_on_distros.sh +++ /dev/null @@ -1,21 +0,0 @@ -BRANCH=$1 -J_FLAG=$2 -BUILD_SCRIPT=/usr/bin/build-clazy.sh - -if [ -z "$1" ] -then - BRANCH="master" -fi - -function run_test -{ - echo "Testing $1..." - docker run -i -t iamsergio/clazy-$1 sh $BUILD_SCRIPT $BRANCH $J_FLAG &> $1.log - echo $? -} - -run_test ubuntu-17.04 -run_test ubuntu-16.04 -run_test opensuse-tumbleweed -run_test archlinux -run_test fedora-25 diff --git a/src/checks/level3/README-assert-with-side-effects.md b/docs/checks/README-assert-with-side-effects.md index 5f375749..5f375749 100644 --- a/src/checks/level3/README-assert-with-side-effects.md +++ b/docs/checks/README-assert-with-side-effects.md diff --git a/src/checks/level1/README-auto-unexpected-qstringbuilder.md b/docs/checks/README-auto-unexpected-qstringbuilder.md index 71706619..0c9e8143 100644 --- a/src/checks/level1/README-auto-unexpected-qstringbuilder.md +++ b/docs/checks/README-auto-unexpected-qstringbuilder.md @@ -1,6 +1,7 @@ # auto-unexpected-qstringbuilder Finds places where auto is deduced to be `QStringBuilder` instead of `QString`, which introduces crashes. +Also warns for lambdas returning `QStringBuilder`. #### Example diff --git a/src/checks/level2/README-base-class-event.md b/docs/checks/README-base-class-event.md index fc61fca8..fc61fca8 100644 --- a/src/checks/level2/README-base-class-event.md +++ b/docs/checks/README-base-class-event.md diff --git a/src/checks/level1/README-child-event-qobject-cast.md b/docs/checks/README-child-event-qobject-cast.md index 3299e3f7..3299e3f7 100644 --- a/src/checks/level1/README-child-event-qobject-cast.md +++ b/docs/checks/README-child-event-qobject-cast.md diff --git a/src/checks/level1/README-connect-3arg-lambda.md b/docs/checks/README-connect-3arg-lambda.md index e9a47135..3d6296a0 100644 --- a/src/checks/level1/README-connect-3arg-lambda.md +++ b/docs/checks/README-connect-3arg-lambda.md @@ -8,8 +8,17 @@ It's very common to use lambdas to connect signals to slots with different numbe of arguments. This can result in a crash if the signal is emitted after the receiver is deleted. +Another reason for using a context-object is so you explicitly think about in +which thread you want the slot to run in. Note that with a context-object the +connection will be of type `Qt::AutoConnection` instead of `Qt::DirectConnection`, +which you can control if needed, via the 5th (optional) argument. + + In order to reduce false-positives, it will only warn if the lambda body dereferences at least one QObject (other than the sender). It's very hard to not have any false-positives. If you find any you probably can just pass the sender again, as 3rd parameter. + +This will also warn if you pass a lambda to `QTimer::singleShot()` without +using the overload that takes a context object. diff --git a/docs/checks/README-connect-by-name.md b/docs/checks/README-connect-by-name.md new file mode 100644 index 00000000..e5f7d255 --- /dev/null +++ b/docs/checks/README-connect-by-name.md @@ -0,0 +1,10 @@ +# connect-by-name + +Warns when "auto-connection slots" are used. They're also known as "connect by name", a +very old and unpopular feature which shouldn't be used anymore. See http://doc.qt.io/qt-5/qobject.html#auto-connection for more information about them. + +These types of connections are very brittle, as a simple object rename would break your code. +In Qt 5 the PMF connect syntax is recommended as it catches errors at compile time. + +This check simply warns for any slot named like on_*_*, because even if you're not using .ui files +this naming is misleading and not good for readability, as the reader would think you're using auto-connection. diff --git a/src/checks/level0/README-connect-non-signal.md b/docs/checks/README-connect-non-signal.md index 627523c9..627523c9 100644 --- a/src/checks/level0/README-connect-non-signal.md +++ b/docs/checks/README-connect-non-signal.md diff --git a/src/checks/level0/README-connect-not-normalized.md b/docs/checks/README-connect-not-normalized.md index 3931fd69..3931fd69 100644 --- a/src/checks/level0/README-connect-not-normalized.md +++ b/docs/checks/README-connect-not-normalized.md diff --git a/src/checks/level1/README-const-signal-or-slot.md b/docs/checks/README-const-signal-or-slot.md index 9a3e71b2..21020a39 100644 --- a/src/checks/level1/README-const-signal-or-slot.md +++ b/docs/checks/README-const-signal-or-slot.md @@ -3,7 +3,12 @@ Warns when a signal or non-void slot is const. This aims to prevent unintentionally marking a getter as slot, or connecting to -the wrong method. For signals, it's just pointless to mark them as const. +the wrong method. + +For signals it's more of a minor issue. Prevents you from emitting signals from +const methods, as these methods shouldn't change state, and a signal implies state +was changed. Helps minimizing having global state (which is the only state you can +change from a const method). Warns for the following cases: diff --git a/src/checks/level0/README-container-anti-pattern.md b/docs/checks/README-container-anti-pattern.md index b7a77d18..b7a77d18 100644 --- a/src/checks/level0/README-container-anti-pattern.md +++ b/docs/checks/README-container-anti-pattern.md diff --git a/src/checks/level2/README-container-inside-loop.md b/docs/checks/README-container-inside-loop.md index 875851a6..875851a6 100644 --- a/src/checks/level2/README-container-inside-loop.md +++ b/docs/checks/README-container-inside-loop.md diff --git a/src/checks/level2/README-copyable-polymorphic.md b/docs/checks/README-copyable-polymorphic.md index a41ead02..a41ead02 100644 --- a/src/checks/level2/README-copyable-polymorphic.md +++ b/docs/checks/README-copyable-polymorphic.md diff --git a/src/checks/level1/README-ctor-missing-parent-argument.md b/docs/checks/README-ctor-missing-parent-argument.md index 90b9b3ab..90b9b3ab 100644 --- a/src/checks/level1/README-ctor-missing-parent-argument.md +++ b/docs/checks/README-ctor-missing-parent-argument.md diff --git a/src/checks/level3/README-detaching-member.md b/docs/checks/README-detaching-member.md index d5ba9c88..d5ba9c88 100644 --- a/src/checks/level3/README-detaching-member.md +++ b/docs/checks/README-detaching-member.md diff --git a/src/checks/level1/README-detaching-temporary.md b/docs/checks/README-detaching-temporary.md index 60d0196c..60d0196c 100644 --- a/src/checks/level1/README-detaching-temporary.md +++ b/docs/checks/README-detaching-temporary.md diff --git a/docs/checks/README-empty-qstringliteral.md b/docs/checks/README-empty-qstringliteral.md new file mode 100644 index 00000000..3c78ba67 --- /dev/null +++ b/docs/checks/README-empty-qstringliteral.md @@ -0,0 +1,6 @@ +# empty-qstringliteral + +Suggests to use an empty `QString` instead of an empty `QStringLiteral`. +The later causes unneeded code bloat. + +You should use `QString()` instead of `QStringLiteral()` and use `QString("")` instead of `QStringLiteral("")`. diff --git a/src/checks/level1/README-foreach.md b/docs/checks/README-foreach.md index 9c5d3d99..9c5d3d99 100644 --- a/src/checks/level1/README-foreach.md +++ b/docs/checks/README-foreach.md diff --git a/docs/checks/README-fully-qualified-moc-types.md b/docs/checks/README-fully-qualified-moc-types.md new file mode 100644 index 00000000..43f34055 --- /dev/null +++ b/docs/checks/README-fully-qualified-moc-types.md @@ -0,0 +1,26 @@ +# fully-qualified-moc-types + +Warns when a signal, slot or invokable declaration is not using fully-qualified type names, which will break old-style connects +and interaction with QML. + +Also warns if a Q_PROPERTY of type gadget is not fully-qualified (Enums and QObjects in Q_PROPERTY don't need +to be fully qualified). + +Example: + +namespace MyNameSpace { + + struct MyType { (...) }; + + class MyObject : public QObject + { + Q_OBJECT + Q_PROPERTY(MyGadget myprop READ myprop); // Wrong, needs namespace + Q_SIGNALS: + void mySignal(MyType); // Wrong + void mySignal(MyNameSpace::MyType); // OK + }; +} + +Beware that fixing these type names might break user code if they are connecting to them via old style connects, +since the users might have worked around your bug and not included the namespace in their connect statement diff --git a/src/checks/level2/README-function-args-by-ref.md b/docs/checks/README-function-args-by-ref.md index d755bfbc..d755bfbc 100644 --- a/src/checks/level2/README-function-args-by-ref.md +++ b/docs/checks/README-function-args-by-ref.md diff --git a/src/checks/level2/README-function-args-by-value.md b/docs/checks/README-function-args-by-value.md index 1e96e4ae..1e96e4ae 100644 --- a/src/checks/level2/README-function-args-by-value.md +++ b/docs/checks/README-function-args-by-value.md diff --git a/src/checks/level2/README-global-const-char-pointer.md b/docs/checks/README-global-const-char-pointer.md index 42cfa996..42cfa996 100644 --- a/src/checks/level2/README-global-const-char-pointer.md +++ b/docs/checks/README-global-const-char-pointer.md diff --git a/src/checks/level2/README-implicit-casts.md b/docs/checks/README-implicit-casts.md index 8a5f3b0f..8a5f3b0f 100644 --- a/src/checks/level2/README-implicit-casts.md +++ b/docs/checks/README-implicit-casts.md diff --git a/src/checks/level1/README-incorrect-emit.md b/docs/checks/README-incorrect-emit.md index 09640675..09640675 100644 --- a/src/checks/level1/README-incorrect-emit.md +++ b/docs/checks/README-incorrect-emit.md diff --git a/src/checks/level1/README-inefficient-qlist-soft.md b/docs/checks/README-inefficient-qlist-soft.md index beebb3d1..beebb3d1 100644 --- a/src/checks/level1/README-inefficient-qlist-soft.md +++ b/docs/checks/README-inefficient-qlist-soft.md diff --git a/src/checks/hiddenlevel/README-inefficient-qlist.md b/docs/checks/README-inefficient-qlist.md index 3bfe3f1c..3bfe3f1c 100644 --- a/src/checks/hiddenlevel/README-inefficient-qlist.md +++ b/docs/checks/README-inefficient-qlist.md diff --git a/src/checks/level1/README-install-event-filter.md b/docs/checks/README-install-event-filter.md index 567e44ba..567e44ba 100644 --- a/src/checks/level1/README-install-event-filter.md +++ b/docs/checks/README-install-event-filter.md diff --git a/src/checks/hiddenlevel/README-isempty-vs-count.md b/docs/checks/README-isempty-vs-count.md index 4db9a33f..4db9a33f 100644 --- a/src/checks/hiddenlevel/README-isempty-vs-count.md +++ b/docs/checks/README-isempty-vs-count.md diff --git a/src/checks/level0/README-lambda-in-connect.md b/docs/checks/README-lambda-in-connect.md index bfdefec4..bfdefec4 100644 --- a/src/checks/level0/README-lambda-in-connect.md +++ b/docs/checks/README-lambda-in-connect.md diff --git a/src/checks/level0/README-lambda-unique-connection.md b/docs/checks/README-lambda-unique-connection.md index 453f3a28..453f3a28 100644 --- a/src/checks/level0/README-lambda-unique-connection.md +++ b/docs/checks/README-lambda-unique-connection.md diff --git a/src/checks/level2/README-missing-qobject-macro.md b/docs/checks/README-missing-qobject-macro.md index 4b56013a..4b56013a 100644 --- a/src/checks/level2/README-missing-qobject-macro.md +++ b/docs/checks/README-missing-qobject-macro.md diff --git a/src/checks/level2/README-missing-typeinfo.md b/docs/checks/README-missing-typeinfo.md index 675fc2f0..675fc2f0 100644 --- a/src/checks/level2/README-missing-typeinfo.md +++ b/docs/checks/README-missing-typeinfo.md diff --git a/src/checks/level0/README-mutable-container-key.md b/docs/checks/README-mutable-container-key.md index 1c87c017..1c87c017 100644 --- a/src/checks/level0/README-mutable-container-key.md +++ b/docs/checks/README-mutable-container-key.md diff --git a/src/checks/level1/README-non-pod-global-static.md b/docs/checks/README-non-pod-global-static.md index 3dda3d79..3dda3d79 100644 --- a/src/checks/level1/README-non-pod-global-static.md +++ b/docs/checks/README-non-pod-global-static.md diff --git a/src/checks/level2/README-old-style-connect.md b/docs/checks/README-old-style-connect.md index 2cad4e8d..2cad4e8d 100644 --- a/src/checks/level2/README-old-style-connect.md +++ b/docs/checks/README-old-style-connect.md diff --git a/src/checks/level1/README-overridden-signal.md b/docs/checks/README-overridden-signal.md index cc452f4d..8e02084d 100644 --- a/src/checks/level1/README-overridden-signal.md +++ b/docs/checks/README-overridden-signal.md @@ -1,7 +1,7 @@ # overridden-signal Warns when overriding a signal, which might make existing connects not work, if done unintentionally. -Doesn't warn when the overriden signal has a different signature. +Doesn't warn when the overridden signal has a different signature. Warns for: - Overriding signal with non-signal diff --git a/src/checks/level1/README-post-event.md b/docs/checks/README-post-event.md index 2bc3db64..2bc3db64 100644 --- a/src/checks/level1/README-post-event.md +++ b/docs/checks/README-post-event.md diff --git a/src/checks/level0/README-qcolor-from-literal.md b/docs/checks/README-qcolor-from-literal.md index cd2d28fe..cd2d28fe 100644 --- a/src/checks/level0/README-qcolor-from-literal.md +++ b/docs/checks/README-qcolor-from-literal.md diff --git a/src/checks/level0/README-qdatetime-utc.md b/docs/checks/README-qdatetime-utc.md index 1741e78a..1741e78a 100644 --- a/src/checks/level0/README-qdatetime-utc.md +++ b/docs/checks/README-qdatetime-utc.md diff --git a/src/checks/level1/README-qdeleteall.md b/docs/checks/README-qdeleteall.md index 3baea852..3baea852 100644 --- a/src/checks/level1/README-qdeleteall.md +++ b/docs/checks/README-qdeleteall.md diff --git a/src/checks/level0/README-qenums.md b/docs/checks/README-qenums.md index 1c25f058..1c25f058 100644 --- a/src/checks/level0/README-qenums.md +++ b/docs/checks/README-qenums.md diff --git a/src/checks/level0/README-qfileinfo-exists.md b/docs/checks/README-qfileinfo-exists.md index 279fc01c..279fc01c 100644 --- a/src/checks/level0/README-qfileinfo-exists.md +++ b/docs/checks/README-qfileinfo-exists.md diff --git a/src/checks/level0/README-qgetenv.md b/docs/checks/README-qgetenv.md index 3292d798..3292d798 100644 --- a/src/checks/level0/README-qgetenv.md +++ b/docs/checks/README-qgetenv.md diff --git a/src/checks/level1/README-qhash-namespace.md b/docs/checks/README-qhash-namespace.md index ca4b646c..ca4b646c 100644 --- a/src/checks/level1/README-qhash-namespace.md +++ b/docs/checks/README-qhash-namespace.md diff --git a/docs/checks/README-qhash-with-char-pointer-key.md b/docs/checks/README-qhash-with-char-pointer-key.md new file mode 100644 index 00000000..7d475105 --- /dev/null +++ b/docs/checks/README-qhash-with-char-pointer-key.md @@ -0,0 +1,6 @@ +# qhash-with-char-pointer-key + +Finds cases of `QHash<const char *, T>`. It's error-prone as the key is just compared +by the address of the string literal, and not the string literal itself. + +Check is disabled by default as there are valid uses-cases. diff --git a/src/checks/level1/README-qlatin1string-non-ascii.md b/docs/checks/README-qlatin1string-non-ascii.md index 1ba1da41..1ba1da41 100644 --- a/src/checks/level1/README-qlatin1string-non-ascii.md +++ b/docs/checks/README-qlatin1string-non-ascii.md diff --git a/src/checks/level0/README-qmap-with-pointer-key.md b/docs/checks/README-qmap-with-pointer-key.md index 6fe5ed96..6fe5ed96 100644 --- a/src/checks/level0/README-qmap-with-pointer-key.md +++ b/docs/checks/README-qmap-with-pointer-key.md diff --git a/docs/checks/README-qproperty-without-notify.md b/docs/checks/README-qproperty-without-notify.md new file mode 100644 index 00000000..f523c0ab --- /dev/null +++ b/docs/checks/README-qproperty-without-notify.md @@ -0,0 +1,7 @@ +# qproperty-without-notify + +Warns when a non-CONSTANT Q_PROPERTY is missing a NOTIFY signal. + +Objects used in QML (e.g. Qt Quick or Declarative Widgets) need to notify when a property changes. +This is also useful when viewing QObject properties in Gammaray. + diff --git a/src/checks/level2/README-qstring-allocations.md b/docs/checks/README-qstring-allocations.md index fda4278e..fda4278e 100644 --- a/src/checks/level2/README-qstring-allocations.md +++ b/docs/checks/README-qstring-allocations.md diff --git a/src/checks/level0/README-qstring-arg.md b/docs/checks/README-qstring-arg.md index 819a97cf..819a97cf 100644 --- a/src/checks/level0/README-qstring-arg.md +++ b/docs/checks/README-qstring-arg.md diff --git a/src/checks/level0/README-qstring-insensitive-allocation.md b/docs/checks/README-qstring-insensitive-allocation.md index d3b2a7e3..d3b2a7e3 100644 --- a/src/checks/level0/README-qstring-insensitive-allocation.md +++ b/docs/checks/README-qstring-insensitive-allocation.md diff --git a/src/checks/level1/README-qstring-left.md b/docs/checks/README-qstring-left.md index b4b53a98..b4b53a98 100644 --- a/src/checks/level1/README-qstring-left.md +++ b/docs/checks/README-qstring-left.md diff --git a/src/checks/level0/README-qstring-ref.md b/docs/checks/README-qstring-ref.md index 7dcf05e8..7dcf05e8 100644 --- a/src/checks/level0/README-qstring-ref.md +++ b/docs/checks/README-qstring-ref.md diff --git a/docs/checks/README-qstring-varargs.md b/docs/checks/README-qstring-varargs.md new file mode 100644 index 00000000..aa029e46 --- /dev/null +++ b/docs/checks/README-qstring-varargs.md @@ -0,0 +1,13 @@ +# qstring-varagars + +This implements the equivalent of `-Wnon-pod-varargs` but only for `QString`. + +This check is disabled by default and is only useful in cases where you don't want +to enable `-Wnon-pod-varargs`. For example on projects with thousands of benign warnings +(like with CString), where you might only want to fix the `QString` cases. + +#### Example + QString s = (...) + LogError("error %s", s); + + diff --git a/docs/checks/README-qt-keywords.md b/docs/checks/README-qt-keywords.md new file mode 100644 index 00000000..0abca20a --- /dev/null +++ b/docs/checks/README-qt-keywords.md @@ -0,0 +1,12 @@ +# qt-keywords + +Warns when using Qt keywords such as emit, slots, signals and foreach. + +This check is disabled by default and must be explicitly enabled, as using the +above Qt keywords is fine unless you're using 3rdparty headers that also define them, +in which case you'll want to use Q_EMIT, Q_SLOTS, Q_SIGNALS and Q_FOREACH instead. + +It's also recommended you don't use the Qt keywords in public headers. + +Also consider using `CONFIG += no_keywords` (qmake) or `ADD_DEFINITIONS(-DQT_NO_KEYWORDS)` (CMake) +instead of using this check. diff --git a/src/checks/level0/README-qt-macros.md b/docs/checks/README-qt-macros.md index 9d6353d5..9d6353d5 100644 --- a/src/checks/level0/README-qt-macros.md +++ b/docs/checks/README-qt-macros.md diff --git a/src/checks/hiddenlevel/README-qt4-qstring-from-array.md b/docs/checks/README-qt4-qstring-from-array.md index 78339140..78339140 100644 --- a/src/checks/hiddenlevel/README-qt4-qstring-from-array.md +++ b/docs/checks/README-qt4-qstring-from-array.md diff --git a/src/checks/level0/README-qvariant-template-instantiation.md b/docs/checks/README-qvariant-template-instantiation.md index 9a8f5e9a..9a8f5e9a 100644 --- a/src/checks/level0/README-qvariant-template-instantiation.md +++ b/docs/checks/README-qvariant-template-instantiation.md diff --git a/src/checks/level1/README-range-loop.md b/docs/checks/README-range-loop.md index ef9d4eda..ef9d4eda 100644 --- a/src/checks/level1/README-range-loop.md +++ b/docs/checks/README-range-loop.md diff --git a/docs/checks/README-raw-environment-function.md b/docs/checks/README-raw-environment-function.md new file mode 100644 index 00000000..265b02e9 --- /dev/null +++ b/docs/checks/README-raw-environment-function.md @@ -0,0 +1,5 @@ +# raw-environment-function + +Warns when `putenv()` or `qputenv()` are being used and suggests the Qt thread-safe equivalents instead. + +This check is disabled by default and should be enabled manually if you worry about this issue. diff --git a/src/checks/level2/README-reserve-candidates.md b/docs/checks/README-reserve-candidates.md index 6a2c6876..6a2c6876 100644 --- a/src/checks/level2/README-reserve-candidates.md +++ b/docs/checks/README-reserve-candidates.md diff --git a/src/checks/level1/README-returning-data-from-temporary.md b/docs/checks/README-returning-data-from-temporary.md index efef86ed..efef86ed 100644 --- a/src/checks/level1/README-returning-data-from-temporary.md +++ b/docs/checks/README-returning-data-from-temporary.md diff --git a/src/checks/level2/README-returning-void-expression.md b/docs/checks/README-returning-void-expression.md index bb405092..bb405092 100644 --- a/src/checks/level2/README-returning-void-expression.md +++ b/docs/checks/README-returning-void-expression.md diff --git a/src/checks/level2/README-rule-of-three.md b/docs/checks/README-rule-of-three.md index 9e9f64ea..9e9f64ea 100644 --- a/src/checks/level2/README-rule-of-three.md +++ b/docs/checks/README-rule-of-three.md diff --git a/src/checks/level1/README-rule-of-two-soft.md b/docs/checks/README-rule-of-two-soft.md index b7aca962..b7aca962 100644 --- a/src/checks/level1/README-rule-of-two-soft.md +++ b/docs/checks/README-rule-of-two-soft.md diff --git a/docs/checks/README-skipped-base-method.md b/docs/checks/README-skipped-base-method.md new file mode 100644 index 00000000..4475987b --- /dev/null +++ b/docs/checks/README-skipped-base-method.md @@ -0,0 +1,18 @@ +# skipped-base-method + +Warns when calling a method from the "grand-base class" instead of the base-class method. + +Example: +class MyFrame : public QFrame +{ + Q_OBJECT +public: + bool event(QEvent *ev) override + { + (...) + return QWidget::event(ev); // warning: Maybe you meant to call QFrame::changeEvent() instead [-Wclazy-skipped-base-method] + } +}; + +Try to avoid jumping over the direct base method. If you really need to then at least +add a comment in the code, so people know it was intentional. Or even better, an clazy:exclude=skipped-base-method comment, which also sliences this warning. diff --git a/docs/checks/README-static-pmf.md b/docs/checks/README-static-pmf.md new file mode 100644 index 00000000..e27e7b6c --- /dev/null +++ b/docs/checks/README-static-pmf.md @@ -0,0 +1,9 @@ +# static-pmf + +Warns when storing a pointer to QObject member function into a static variable. +Passing such variable to a connect is known to fail when using MingW. + +Example: + +static auto pmf = &QObject::destroyed; +QCOMPARE(pmf, &QObject::destroyed); // fails diff --git a/src/checks/level0/README-strict-iterators.md b/docs/checks/README-strict-iterators.md index 0730710a..0730710a 100644 --- a/src/checks/level0/README-strict-iterators.md +++ b/docs/checks/README-strict-iterators.md diff --git a/src/checks/level0/README-temporary-iterator.md b/docs/checks/README-temporary-iterator.md index c9dcd776..c9dcd776 100644 --- a/src/checks/level0/README-temporary-iterator.md +++ b/docs/checks/README-temporary-iterator.md diff --git a/src/checks/level3/README-thread-with-slots.md b/docs/checks/README-thread-with-slots.md index e4c0efa4..0643364c 100644 --- a/src/checks/level3/README-thread-with-slots.md +++ b/docs/checks/README-thread-with-slots.md @@ -1,6 +1,6 @@ # thread-with-slots -slots in a `QThread` derived classe are usually a code smell, because +slots in a `QThread` derived class are usually a code smell, because they'll run in the thread where the `QThread` `QObject` lives and not in the thread itself. diff --git a/src/checks/hiddenlevel/README-tr-non-literal.md b/docs/checks/README-tr-non-literal.md index 4cf97844..4cf97844 100644 --- a/src/checks/hiddenlevel/README-tr-non-literal.md +++ b/docs/checks/README-tr-non-literal.md diff --git a/docs/checks/README-unneeded-cast.md b/docs/checks/README-unneeded-cast.md new file mode 100644 index 00000000..cedcad6b --- /dev/null +++ b/docs/checks/README-unneeded-cast.md @@ -0,0 +1,21 @@ +# unneeded-cast + +Finds unneeded qobject_cast, static_cast and dynamic_casts. +Warns when you're casting to base or casting to the same type, which doesn't require +any explicit cast. + +Also warns when you're using dynamic_cast for QObjects. qobject_cast is prefered. + + +#### Example + + Foo *a = ...; + Foo *b = qobject_cast<Foo*>(a); + + +To shut the warnings about using qobject_cast over dynamic cast you can set: +`export CLAZY_EXTRA_OPTIONS="unneeded-cast-prefer-dynamic-cast-over-qobject"` + +NOTE: This check has many false-positives. For example, you might cast to base +class to call a non-virtual method, or qobject_cast to itself to check if the +`QObject` destructor is currently being run. diff --git a/docs/checks/README-unused-non-trivial-variable.md b/docs/checks/README-unused-non-trivial-variable.md new file mode 100644 index 00000000..377344bd --- /dev/null +++ b/docs/checks/README-unused-non-trivial-variable.md @@ -0,0 +1,16 @@ +# unused-non-trivial-variable + + Warns about unused Qt value classes. + Compilers usually only warn when trivial classes are unused and don't emit warnings for non-trivial classes. + + This check has a whitelist of common Qt classes such as containers, `QFont`, `QUrl`, etc and warns for those too. + + See `UnusedNonTrivialType::isInterestingType(QualType t)` for a list of all types. + + It's possible to extend the whitelist with user types, by setting the env variable `CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_WHITELIST`. + It accepts a comma separate name of types. + + It's possible to disable the whitelist via exporting `CLAZY_EXTRA_OPTIONS=unused-non-trivial-variable-no-whitelist`, + when this env variable is set clazy will warn for any unused non-trivial type. This will create many false positives, + such as RAII classes, but still useful to run at least once on your codebase. When disabling the whitelist this way it's also possible + to black list types, by setting a comma separated list of types to `CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_BLACKLIST` diff --git a/src/checks/level2/README-virtual-call-ctor.md b/docs/checks/README-virtual-call-ctor.md index 2bf715c9..2bf715c9 100644 --- a/src/checks/level2/README-virtual-call-ctor.md +++ b/docs/checks/README-virtual-call-ctor.md diff --git a/src/checks/level1/README-virtual-signal.md b/docs/checks/README-virtual-signal.md index 7d922ce7..7d922ce7 100644 --- a/src/checks/level1/README-virtual-signal.md +++ b/docs/checks/README-virtual-signal.md diff --git a/src/checks/level0/README-writing-to-temporary.md b/docs/checks/README-writing-to-temporary.md index f39eb5d4..f39eb5d4 100644 --- a/src/checks/level0/README-writing-to-temporary.md +++ b/docs/checks/README-writing-to-temporary.md diff --git a/docs/checks/README-wrong-qevent-cast.md b/docs/checks/README-wrong-qevent-cast.md new file mode 100644 index 00000000..96244231 --- /dev/null +++ b/docs/checks/README-wrong-qevent-cast.md @@ -0,0 +1,11 @@ +# wrong-qevent-cast + +Warns when a QEvent is possibly cast to the wrong derived class via static_cast. + +Example: +switch (ev->type()) { + case QEvent::MouseMove: + auto e = static_cast<QKeyEvent*>(ev); +} + +Currently only casts inside switches are verified. diff --git a/src/checks/level0/README-wrong-qglobalstatic.md b/docs/checks/README-wrong-qglobalstatic.md index 70734870..70734870 100644 --- a/src/checks/level0/README-wrong-qglobalstatic.md +++ b/docs/checks/README-wrong-qglobalstatic.md diff --git a/docs/man/clazy.pod b/docs/man/clazy.pod index 6ec222d9..160d87c7 100644 --- a/docs/man/clazy.pod +++ b/docs/man/clazy.pod @@ -157,9 +157,9 @@ B<Examples:> =over 4 -=item 1. Enables the 2 checkers "bogus-dynamic-cast" and "virtual-call-ctor" only: +=item 1. Enables the 2 checkers "unneeded-cast" and "virtual-call-ctor" only: -% export CLAZY_CHECKS="bogus-dynamic-cast,virtual-call-ctor" +% export CLAZY_CHECKS="unneeded-cast,virtual-call-ctor" =item 2. Enables all checks from the "level0" check-set, except for "qenums": diff --git a/readmes.cmake b/readmes.cmake new file mode 100644 index 00000000..9912bf3b --- /dev/null +++ b/readmes.cmake @@ -0,0 +1,92 @@ +SET(README_manuallevel_FILES + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-container-inside-loop.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-inefficient-qlist.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-isempty-vs-count.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qhash-with-char-pointer-key.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-varargs.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt-keywords.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt4-qstring-from-array.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-raw-environment-function.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-tr-non-literal.md +) + +SET(README_LEVEL0_FILES + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-connect-by-name.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-connect-non-signal.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-connect-not-normalized.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-container-anti-pattern.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-empty-qstringliteral.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-fully-qualified-moc-types.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-lambda-in-connect.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-lambda-unique-connection.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-mutable-container-key.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qcolor-from-literal.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qdatetime-utc.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qenums.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qfileinfo-exists.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qgetenv.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qmap-with-pointer-key.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-arg.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-insensitive-allocation.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-ref.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qt-macros.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qvariant-template-instantiation.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-strict-iterators.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-temporary-iterator.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-unused-non-trivial-variable.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-writing-to-temporary.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-wrong-qevent-cast.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-wrong-qglobalstatic.md +) + +SET(README_LEVEL1_FILES + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-auto-unexpected-qstringbuilder.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-child-event-qobject-cast.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-connect-3arg-lambda.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-const-signal-or-slot.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-detaching-temporary.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-foreach.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-incorrect-emit.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-inefficient-qlist-soft.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-install-event-filter.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-non-pod-global-static.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-overridden-signal.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-post-event.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qdeleteall.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qhash-namespace.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qlatin1string-non-ascii.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qproperty-without-notify.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-left.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-range-loop.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-returning-data-from-temporary.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-rule-of-two-soft.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-skipped-base-method.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-virtual-signal.md +) + +SET(README_LEVEL2_FILES + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-base-class-event.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-copyable-polymorphic.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-ctor-missing-parent-argument.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-function-args-by-ref.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-function-args-by-value.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-global-const-char-pointer.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-implicit-casts.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-missing-qobject-macro.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-missing-typeinfo.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-old-style-connect.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-qstring-allocations.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-returning-void-expression.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-rule-of-three.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-static-pmf.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-virtual-call-ctor.md +) + +SET(README_LEVEL3_FILES + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-assert-with-side-effects.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-detaching-member.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-reserve-candidates.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-thread-with-slots.md + ${CMAKE_CURRENT_LIST_DIR}/docs/checks/README-unneeded-cast.md +) + diff --git a/scripts/fix_json_database.py b/scripts/fix_json_database.py index f0728ffe..167f2b71 100755 --- a/scripts/fix_json_database.py +++ b/scripts/fix_json_database.py @@ -57,6 +57,10 @@ f.close() decoded = json.loads(contents) new_decoded = [] for cmd in decoded: + if 'file' in cmd: + if cmd['file'].endswith('.moc') or cmd['file'].endswith('.rcc') or cmd['file'].endswith('_moc.cpp'): + continue + if 'command' in cmd or 'arguments' in cmd: if 'command' in cmd: cmd['command'] = fix_command(cmd['command']) diff --git a/src/AccessSpecifierManager.cpp b/src/AccessSpecifierManager.cpp index eea709fa..b5282931 100644 --- a/src/AccessSpecifierManager.cpp +++ b/src/AccessSpecifierManager.cpp @@ -85,7 +85,8 @@ public: const bool isSlot = (isSlots || isSignals) ? false : name == "Q_SLOT"; const bool isSignal = (isSlots || isSignals || isSlot) ? false : name == "Q_SIGNAL"; - if (!isSlots && !isSignals && !isSlot && !isSignal) + const bool isInvokable = (isSlots || isSignals || isSlot || isSignal) ? false : name == "Q_INVOKABLE"; + if (!isSlots && !isSignals && !isSlot && !isSignal & !isInvokable) return; SourceLocation loc = range.getBegin(); @@ -95,7 +96,6 @@ public: if (isSignals || isSlots) { QtAccessSpecifierType qtAccessSpecifier = isSlots ? QtAccessSpecifier_Slot : QtAccessSpecifier_Signal; - m_qtAccessSpecifiers.push_back( { loc, clang::AS_none, qtAccessSpecifier } ); } else { // Get the location of the method declaration, so we can compare directly when we visit methods @@ -104,14 +104,17 @@ public: return; if (isSignal) { m_individualSignals.push_back(loc.getRawEncoding()); - } else { + } else if (isSlot) { m_individualSlots.push_back(loc.getRawEncoding()); + } else if (isInvokable) { + m_individualInvokables.push_back(loc.getRawEncoding()); } } } vector<unsigned> m_individualSignals; // Q_SIGNAL vector<unsigned> m_individualSlots; // Q_SLOT + vector<unsigned> m_individualInvokables; // Q_INVOKABLE const CompilerInstance &m_ci; ClazySpecifierList m_qtAccessSpecifiers; }; @@ -134,7 +137,7 @@ const CXXRecordDecl *AccessSpecifierManager::classDefinitionForLoc(SourceLocatio { for (const auto &it : m_specifiersMap) { const CXXRecordDecl *record = it.first; - if (record->getLocStart() < loc && loc < record->getLocEnd()) + if (getLocStart(record) < loc && loc < getLocEnd(record)) return record; } return nullptr; @@ -143,7 +146,7 @@ const CXXRecordDecl *AccessSpecifierManager::classDefinitionForLoc(SourceLocatio void AccessSpecifierManager::VisitDeclaration(Decl *decl) { auto record = dyn_cast<CXXRecordDecl>(decl); - if (!QtUtils::isQObject(record)) + if (!clazy::isQObject(record)) return; const auto &sm = m_ci.getSourceManager(); @@ -168,20 +171,24 @@ void AccessSpecifierManager::VisitDeclaration(Decl *decl) if (!accessSpec || accessSpec->getDeclContext() != record) continue; ClazySpecifierList &specifiers = entryForClassDefinition(record); - sorted_insert(specifiers, {accessSpec->getLocStart(), accessSpec->getAccess(), QtAccessSpecifier_None }, sm); + sorted_insert(specifiers, {getLocStart(accessSpec), accessSpec->getAccess(), QtAccessSpecifier_None }, sm); } } QtAccessSpecifierType AccessSpecifierManager::qtAccessSpecifierType(const CXXMethodDecl *method) const { - if (!method || method->getLocStart().isMacroID()) + if (!method || getLocStart(method).isMacroID()) return QtAccessSpecifier_Unknown; // We want the declaration that's inside class {}, not the ones that are also a method definition // and possibly outside the class method = method->getCanonicalDecl(); - const SourceLocation methodLoc = method->getLocStart(); + const CXXRecordDecl *record = method->getParent(); + if (!record || isa<clang::ClassTemplateSpecializationDecl>(record)) + return QtAccessSpecifier_None; + + const SourceLocation methodLoc = getLocStart(method); // Process Q_SIGNAL: for (auto signalLoc : m_preprocessorCallbacks->m_individualSignals) { @@ -195,11 +202,13 @@ QtAccessSpecifierType AccessSpecifierManager::qtAccessSpecifierType(const CXXMet return QtAccessSpecifier_Slot; } - // Process Q_SLOTS and Q_SIGNALS: + // Process Q_INVOKABLE: + for (auto loc : m_preprocessorCallbacks->m_individualInvokables) { + if (loc == methodLoc.getRawEncoding()) + return QtAccessSpecifier_Invokable; + } - const CXXRecordDecl *record = method->getParent(); - if (!record || isa<clang::ClassTemplateSpecializationDecl>(record)) - return QtAccessSpecifier_None; + // Process Q_SLOTS and Q_SIGNALS: auto it = m_specifiersMap.find(record); if (it == m_specifiersMap.cend()) @@ -222,3 +231,20 @@ QtAccessSpecifierType AccessSpecifierManager::qtAccessSpecifierType(const CXXMet --i; // One before the upper bound is the last access specifier before our method return (*i).qtAccessSpecifier; } + +llvm::StringRef AccessSpecifierManager::qtAccessSpecifierTypeStr(QtAccessSpecifierType t) const +{ + switch (t) { + case QtAccessSpecifier_None: + case QtAccessSpecifier_Unknown: + return ""; + case QtAccessSpecifier_Slot: + return "slot"; + case QtAccessSpecifier_Signal: + return "signal"; + case QtAccessSpecifier_Invokable: + return "invokable"; + default: + return ""; + } +} diff --git a/src/AccessSpecifierManager.h b/src/AccessSpecifierManager.h index 44f4ce48..2cb5e9e9 100644 --- a/src/AccessSpecifierManager.h +++ b/src/AccessSpecifierManager.h @@ -57,7 +57,8 @@ enum QtAccessSpecifierType QtAccessSpecifier_None, QtAccessSpecifier_Unknown, QtAccessSpecifier_Slot, - QtAccessSpecifier_Signal + QtAccessSpecifier_Signal, + QtAccessSpecifier_Invokable }; struct ClazyAccessSpecifier @@ -80,6 +81,11 @@ public: */ QtAccessSpecifierType qtAccessSpecifierType(const clang::CXXMethodDecl*) const; + /** + * Returns a string representations of a Qt Access Specifier Type + */ + llvm::StringRef qtAccessSpecifierTypeStr(QtAccessSpecifierType) const; + private: ClazySpecifierList &entryForClassDefinition(clang::CXXRecordDecl*); const clang::CompilerInstance &m_ci; diff --git a/src/Checks.h b/src/Checks.h new file mode 100644 index 00000000..92a660d7 --- /dev/null +++ b/src/Checks.h @@ -0,0 +1,206 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + Author: Sérgio Martins <sergio.martins@kdab.com> + + 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. +*/ + + +/** + * To add a new check you can either edit this file, or use the python script: + * dev-scripts/generate.py > src/Checks.h + */ + +#include "checkmanager.h" +#include "checks/manuallevel/container-inside-loop.h" +#include "checks/manuallevel/inefficient-qlist.h" +#include "checks/manuallevel/isempty-vs-count.h" +#include "checks/manuallevel/qhash-with-char-pointer-key.h" +#include "checks/manuallevel/qstring-varargs.h" +#include "checks/manuallevel/qt-keywords.h" +#include "checks/manuallevel/qt4-qstring-from-array.h" +#include "checks/manuallevel/raw-environment-function.h" +#include "checks/manuallevel/tr-non-literal.h" +#include "checks/level0/connect-by-name.h" +#include "checks/level0/connect-non-signal.h" +#include "checks/level0/connect-not-normalized.h" +#include "checks/level0/container-anti-pattern.h" +#include "checks/level0/empty-qstringliteral.h" +#include "checks/level0/fully-qualified-moc-types.h" +#include "checks/level0/lambda-in-connect.h" +#include "checks/level0/lambda-unique-connection.h" +#include "checks/level0/mutable-container-key.h" +#include "checks/level0/qcolor-from-literal.h" +#include "checks/level0/qdatetime-utc.h" +#include "checks/level0/qenums.h" +#include "checks/level0/qfileinfo-exists.h" +#include "checks/level0/qgetenv.h" +#include "checks/level0/qmap-with-pointer-key.h" +#include "checks/level0/qstring-arg.h" +#include "checks/level0/qstring-insensitive-allocation.h" +#include "checks/level0/qstring-ref.h" +#include "checks/level0/qt-macros.h" +#include "checks/level0/qvariant-template-instantiation.h" +#include "checks/level0/strict-iterators.h" +#include "checks/level0/temporary-iterator.h" +#include "checks/level0/unused-non-trivial-variable.h" +#include "checks/level0/writing-to-temporary.h" +#include "checks/level0/wrong-qevent-cast.h" +#include "checks/level0/wrong-qglobalstatic.h" +#include "checks/level1/auto-unexpected-qstringbuilder.h" +#include "checks/level1/child-event-qobject-cast.h" +#include "checks/level1/connect-3arg-lambda.h" +#include "checks/level1/const-signal-or-slot.h" +#include "checks/level1/detaching-temporary.h" +#include "checks/level1/foreach.h" +#include "checks/level1/incorrect-emit.h" +#include "checks/level1/inefficient-qlist-soft.h" +#include "checks/level1/install-event-filter.h" +#include "checks/level1/non-pod-global-static.h" +#include "checks/level1/overridden-signal.h" +#include "checks/level1/post-event.h" +#include "checks/level1/qdeleteall.h" +#include "checks/level1/qhash-namespace.h" +#include "checks/level1/qlatin1string-non-ascii.h" +#include "checks/level1/qproperty-without-notify.h" +#include "checks/level1/qstring-left.h" +#include "checks/level1/range-loop.h" +#include "checks/level1/returning-data-from-temporary.h" +#include "checks/level1/rule-of-two-soft.h" +#include "checks/level1/skipped-base-method.h" +#include "checks/level1/virtual-signal.h" +#include "checks/level2/base-class-event.h" +#include "checks/level2/copyable-polymorphic.h" +#include "checks/level2/ctor-missing-parent-argument.h" +#include "checks/level2/function-args-by-ref.h" +#include "checks/level2/function-args-by-value.h" +#include "checks/level2/global-const-char-pointer.h" +#include "checks/level2/implicit-casts.h" +#include "checks/level2/missing-qobject-macro.h" +#include "checks/level2/missing-typeinfo.h" +#include "checks/level2/old-style-connect.h" +#include "checks/level2/qstring-allocations.h" +#include "checks/level2/returning-void-expression.h" +#include "checks/level2/rule-of-three.h" +#include "checks/level2/static-pmf.h" +#include "checks/level2/virtual-call-ctor.h" +#include "checks/level3/assert-with-side-effects.h" +#include "checks/level3/detaching-member.h" +#include "checks/level3/reserve-candidates.h" +#include "checks/level3/thread-with-slots.h" +#include "checks/level3/unneeded-cast.h" + +template <typename T> +RegisteredCheck check(const char *name, CheckLevel level, RegisteredCheck::Options options = RegisteredCheck::Option_None) +{ + auto factoryFuntion = [name](ClazyContext *context){ return new T(name, context); }; + return RegisteredCheck{name, level, factoryFuntion, options}; +} + +void CheckManager::registerChecks() +{ + registerCheck(check<ContainerInsideLoop>("container-inside-loop", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<InefficientQList>("inefficient-qlist", ManualCheckLevel, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<IsEmptyVSCount>("isempty-vs-count", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QHashWithCharPointerKey>("qhash-with-char-pointer-key", ManualCheckLevel, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<QStringVarargs>("qstring-varargs", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QtKeywords>("qt-keywords", ManualCheckLevel, RegisteredCheck::Option_None)); + registerFixIt(1, "fix-qt-keywords", "qt-keywords"); + registerCheck(check<Qt4QStringFromArray>("qt4-qstring-from-array", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts)); + registerFixIt(1, "fix-qt4-qstring-from-array", "qt4-qstring-from-array"); + registerCheck(check<RawEnvironmentFunction>("raw-environment-function", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<TrNonLiteral>("tr-non-literal", ManualCheckLevel, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ConnectByName>("connect-by-name", CheckLevel0, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<ConnectNonSignal>("connect-non-signal", CheckLevel0, RegisteredCheck::Option_Qt4Incompatible | RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ConnectNotNormalized>("connect-not-normalized", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ContainerAntiPattern>("container-anti-pattern", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<EmptyQStringliteral>("empty-qstringliteral", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<FullyQualifiedMocTypes>("fully-qualified-moc-types", CheckLevel0, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<LambdaInConnect>("lambda-in-connect", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<LambdaUniqueConnection>("lambda-unique-connection", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<MutableContainerKey>("mutable-container-key", CheckLevel0, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<QColorFromLiteral>("qcolor-from-literal", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QDateTimeUtc>("qdatetime-utc", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerFixIt(1, "fix-qdatetime-utc", "qdatetime-utc"); + registerCheck(check<QEnums>("qenums", CheckLevel0, RegisteredCheck::Option_Qt4Incompatible)); + registerCheck(check<QFileInfoExists>("qfileinfo-exists", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QGetEnv>("qgetenv", CheckLevel0, RegisteredCheck::Option_Qt4Incompatible | RegisteredCheck::Option_VisitsStmts)); + registerFixIt(1, "fix-qgetenv", "qgetenv"); + registerCheck(check<QMapWithPointerKey>("qmap-with-pointer-key", CheckLevel0, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<QStringArg>("qstring-arg", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QStringInsensitiveAllocation>("qstring-insensitive-allocation", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<StringRefCandidates>("qstring-ref", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerFixIt(1, "fix-missing-qstringref", "qstring-ref"); + registerCheck(check<QtMacros>("qt-macros", CheckLevel0, RegisteredCheck::Option_None)); + registerCheck(check<QVariantTemplateInstantiation>("qvariant-template-instantiation", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<StrictIterators>("strict-iterators", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<TemporaryIterator>("temporary-iterator", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<UnusedNonTrivialVariable>("unused-non-trivial-variable", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<WritingToTemporary>("writing-to-temporary", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<WrongQEventCast>("wrong-qevent-cast", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<WrongQGlobalStatic>("wrong-qglobalstatic", CheckLevel0, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<AutoUnexpectedQStringBuilder>("auto-unexpected-qstringbuilder", CheckLevel1, RegisteredCheck::Option_VisitsStmts | RegisteredCheck::Option_VisitsDecls)); + registerFixIt(1, "fix-auto-unexpected-qstringbuilder", "auto-unexpected-qstringbuilder"); + registerCheck(check<ChildEventQObjectCast>("child-event-qobject-cast", CheckLevel1, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<Connect3ArgLambda>("connect-3arg-lambda", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ConstSignalOrSlot>("const-signal-or-slot", CheckLevel1, RegisteredCheck::Option_VisitsStmts | RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<DetachingTemporary>("detaching-temporary", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<Foreach>("foreach", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<IncorrectEmit>("incorrect-emit", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<InefficientQListSoft>("inefficient-qlist-soft", CheckLevel1, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<InstallEventFilter>("install-event-filter", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<NonPodGlobalStatic>("non-pod-global-static", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<OverriddenSignal>("overridden-signal", CheckLevel1, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<PostEvent>("post-event", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QDeleteAll>("qdeleteall", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QHashNamespace>("qhash-namespace", CheckLevel1, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<QLatin1StringNonAscii>("qlatin1string-non-ascii", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QPropertyWithoutNotify>("qproperty-without-notify", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<QStringLeft>("qstring-left", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<RangeLoop>("range-loop", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ReturningDataFromTemporary>("returning-data-from-temporary", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<RuleOfTwoSoft>("rule-of-two-soft", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<SkippedBaseMethod>("skipped-base-method", CheckLevel1, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<VirtualSignal>("virtual-signal", CheckLevel1, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<BaseClassEvent>("base-class-event", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<CopyablePolymorphic>("copyable-polymorphic", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<CtorMissingParentArgument>("ctor-missing-parent-argument", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<FunctionArgsByRef>("function-args-by-ref", CheckLevel2, RegisteredCheck::Option_VisitsStmts | RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<FunctionArgsByValue>("function-args-by-value", CheckLevel2, RegisteredCheck::Option_VisitsStmts | RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<GlobalConstCharPointer>("global-const-char-pointer", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<ImplicitCasts>("implicit-casts", CheckLevel2, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<MissingQObjectMacro>("missing-qobject-macro", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<MissingTypeInfo>("missing-typeinfo", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); +#ifndef NO_STD_REGEX + registerCheck(check<OldStyleConnect>("old-style-connect", CheckLevel2, RegisteredCheck::Option_Qt4Incompatible | RegisteredCheck::Option_VisitsStmts)); + registerFixIt(1, "fix-old-style-connect", "old-style-connect"); +#endif + registerCheck(check<QStringAllocations>("qstring-allocations", CheckLevel2, RegisteredCheck::Option_Qt4Incompatible | RegisteredCheck::Option_VisitsStmts)); + registerFixIt(1, "fix-qlatin1string-allocations", "qstring-allocations"); + registerFixIt(2, "fix-fromLatin1_fromUtf8-allocations", "qstring-allocations"); + registerFixIt(4, "fix-fromCharPtrAllocations", "qstring-allocations"); + registerCheck(check<ReturningVoidExpression>("returning-void-expression", CheckLevel2, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<RuleOfThree>("rule-of-three", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<StaticPmf>("static-pmf", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<VirtualCallCtor>("virtual-call-ctor", CheckLevel2, RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<AssertWithSideEffects>("assert-with-side-effects", CheckLevel3, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<DetachingMember>("detaching-member", CheckLevel3, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ReserveCandidates>("reserve-candidates", CheckLevel3, RegisteredCheck::Option_VisitsStmts)); + registerCheck(check<ThreadWithSlots>("thread-with-slots", CheckLevel3, RegisteredCheck::Option_VisitsStmts | RegisteredCheck::Option_VisitsDecls)); + registerCheck(check<UnneededCast>("unneeded-cast", CheckLevel3, RegisteredCheck::Option_VisitsStmts)); +} diff --git a/src/Clazy.cpp b/src/Clazy.cpp index c1ff2c67..0fcaed92 100644 --- a/src/Clazy.cpp +++ b/src/Clazy.cpp @@ -61,38 +61,56 @@ static void manuallyPopulateParentMap(ParentMap *map, Stmt *s) ClazyASTConsumer::ClazyASTConsumer(ClazyContext *context) : m_context(context) - , m_matchFinder(nullptr) { - clang::ast_matchers::MatchFinder::MatchFinderOptions options; #ifndef CLAZY_DISABLE_AST_MATCHERS - m_matchFinder = new clang::ast_matchers::MatchFinder(options); + m_matchFinder = new clang::ast_matchers::MatchFinder(); #endif } -void ClazyASTConsumer::addCheck(CheckBase *check) +void ClazyASTConsumer::addCheck(const std::pair<CheckBase *, RegisteredCheck> &check) { + CheckBase *checkBase = check.first; #ifndef CLAZY_DISABLE_AST_MATCHERS - check->registerASTMatchers(*m_matchFinder); + checkBase->registerASTMatchers(*m_matchFinder); #endif - m_createdChecks.push_back(check); + //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() { - delete m_context; +#ifndef CLAZY_DISABLE_AST_MATCHERS delete m_matchFinder; +#endif + delete m_context; } bool ClazyASTConsumer::VisitDecl(Decl *decl) { - const bool isInSystemHeader = m_context->sm.isInSystemHeader(decl->getLocStart()); - - if (AccessSpecifierManager *a = m_context->accessSpecifierManager) + if (AccessSpecifierManager *a = m_context->accessSpecifierManager) // Needs to visit system headers too (qobject.h for example) a->VisitDeclaration(decl); - if (!isInSystemHeader) { - for (CheckBase *check : m_createdChecks) - check->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<CXXMethodDecl>(decl)) + m_context->lastMethodDecl = mdecl; + + for (CheckBase *check : m_checksToVisitDecls) { + if (!(isFromIgnorableInclude && check->canIgnoreIncludes())) + check->VisitDecl(decl); } return true; @@ -100,6 +118,10 @@ bool ClazyASTConsumer::VisitDecl(Decl *decl) 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. @@ -122,10 +144,10 @@ bool ClazyASTConsumer::VisitStmt(Stmt *stm) if (!parentMap->hasParent(stm)) parentMap->addStmt(stm); - const bool isInSystemHeader = m_context->sm.isInSystemHeader(stm->getLocStart()); - if (!isInSystemHeader) { - for (CheckBase *check : m_createdChecks) - check->VisitStatement(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; @@ -147,7 +169,7 @@ void ClazyASTConsumer::HandleTranslationUnit(ASTContext &ctx) static bool parseArgument(const string &arg, vector<string> &args) { - auto it = clazy_std::find(args, arg); + auto it = clazy::find(args, arg); if (it != args.end()) { args.erase(it); return true; @@ -170,14 +192,22 @@ std::unique_ptr<clang::ASTConsumer> ClazyASTAction::CreateASTConsumer(CompilerIn std::lock_guard<std::mutex> lock(CheckManager::lock()); auto astConsumer = std::unique_ptr<ClazyASTConsumer>(new ClazyASTConsumer(m_context)); - CheckBase::List createdChecks = m_checkManager->createChecks(m_checks, m_context); - for (CheckBase *check : createdChecks) { + auto createdChecks = m_checkManager->createChecks(m_checks, m_context); + for (auto check : createdChecks) { astConsumer->addCheck(check); } return std::unique_ptr<clang::ASTConsumer>(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<std::string> &args_) { // NOTE: This method needs to be kept reentrant (but not necessarily thread-safe) @@ -186,14 +216,8 @@ bool ClazyASTAction::ParseArgs(const CompilerInstance &ci, const std::vector<std std::vector<std::string> args = args_; if (parseArgument("help", args)) { - m_context = new ClazyContext(ci, ClazyContext::ClazyOption_None); - PrintHelp(llvm::errs(), HelpMode_Normal); - return true; - } - - if (parseArgument("generateAnchorHeader", args)) { - m_context = new ClazyContext(ci, ClazyContext::ClazyOption_None); - PrintHelp(llvm::errs(), HelpMode_AnchorHeader); + m_context = new ClazyContext(ci, getEnvVariable("CLAZY_HEADER_FILTER"), getEnvVariable("CLAZY_IGNORE_DIRS"), ClazyContext::ClazyOption_None); + PrintHelp(llvm::errs()); return true; } @@ -222,7 +246,10 @@ bool ClazyASTAction::ParseArgs(const CompilerInstance &ci, const std::vector<std if (parseArgument("visit-implicit-code", args)) m_options |= ClazyContext::ClazyOption_VisitImplicitCode; - m_context = new ClazyContext(ci, m_options); + 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); @@ -269,48 +296,14 @@ void ClazyASTAction::printRequestedChecks() const llvm::errs() << "\n"; } -void ClazyASTAction::PrintAnchorHeader(llvm::raw_ostream &ros, RegisteredCheck::List &checks) const -{ - // Generates ClazyAnchorHeader.h. - // Needed so we can support a static build of clazy without the linker discarding our checks. - // You can generate with: - // $ echo | clang -Xclang -load -Xclang ClangLazy.so -Xclang -add-plugin -Xclang clang-lazy -Xclang -plugin-arg-clang-lazy -Xclang generateAnchorHeader -c -xc - - - - ros << "// This file was autogenerated.\n\n"; - ros << "#ifndef CLAZY_ANCHOR_HEADER_H\n#define CLAZY_ANCHOR_HEADER_H\n\n"; - - for (auto &check : checks) { - ros << string("extern volatile int ClazyAnchor_") + check.className + ";\n"; - } - - ros << "\n"; - ros << "int clazy_dummy()\n{\n"; - ros << " return\n"; - - for (auto &check : checks) { - ros << string(" ClazyAnchor_") + check.className + " +\n"; - } - - ros << " 0;\n"; - ros << "}\n\n"; - ros << "#endif\n"; -} - -void ClazyASTAction::PrintHelp(llvm::raw_ostream &ros, HelpMode helpMode) const +void ClazyASTAction::PrintHelp(llvm::raw_ostream &ros) const { std::lock_guard<std::mutex> lock(CheckManager::lock()); RegisteredCheck::List checks = m_checkManager->availableChecks(MaxCheckLevel); - clazy_std::sort(checks, checkLessThanByLevel); - - if (helpMode == HelpMode_AnchorHeader) { - PrintAnchorHeader(ros, checks); - return; - } + clazy::sort(checks, checkLessThanByLevel); ros << "Available checks and FixIts:\n\n"; - const bool useMarkdown = getenv("CLAZY_HELP_USE_MARKDOWN"); int lastPrintedLevel = -1; const auto numChecks = checks.size(); @@ -330,7 +323,7 @@ void ClazyASTAction::PrintHelp(llvm::raw_ostream &ros, HelpMode helpMode) const auto padded = check.name; padded.insert(padded.end(), 39 - padded.size(), ' '); - ros << " - " << (useMarkdown ? "[" : "") << check.name << (useMarkdown ? "](" + relativeReadmePath + ")" : ""); + ros << " - " << check.name;; auto fixits = m_checkManager->availableFixIts(check.name); if (!fixits.empty()) { ros << " ("; @@ -361,19 +354,19 @@ void ClazyASTAction::PrintHelp(llvm::raw_ostream &ros, HelpMode helpMode) const 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, +ClazyStandaloneASTAction::ClazyStandaloneASTAction(const string &checkList, const string &headerFilter, const string &ignoreDirs, ClazyContext::ClazyOptions options) : clang::ASTFrontendAction() - , m_checkList(checkList) + , 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) { - if (m_checkList.empty()) - m_checkList = "level1"; } unique_ptr<ASTConsumer> ClazyStandaloneASTAction::CreateASTConsumer(CompilerInstance &ci, llvm::StringRef) { - auto context = new ClazyContext(ci, m_options); + auto context = new ClazyContext(ci, m_headerFilter, m_ignoreDirs, m_options); auto astConsumer = new ClazyASTConsumer(context); auto cm = CheckManager::instance(); @@ -386,8 +379,8 @@ unique_ptr<ASTConsumer> ClazyStandaloneASTAction::CreateASTConsumer(CompilerInst return nullptr; } - CheckBase::List createdChecks = cm->createChecks(requestedChecks, context); - for (CheckBase *check : createdChecks) { + auto createdChecks = cm->createChecks(requestedChecks, context); + for (const auto &check : createdChecks) { astConsumer->addCheck(check); } diff --git a/src/Clazy.h b/src/Clazy.h index b80c9113..d3d0d71f 100644 --- a/src/Clazy.h +++ b/src/Clazy.h @@ -46,12 +46,6 @@ namespace clang { class ClazyASTAction : public clang::PluginASTAction { public: - - enum HelpMode { - HelpMode_Normal = 0, - HelpMode_AnchorHeader = 1 - }; - ClazyASTAction(); protected: @@ -60,7 +54,7 @@ protected: /// @note This function is reentrant bool ParseArgs(const clang::CompilerInstance &ci, const std::vector<std::string> &args_) override; - void PrintHelp(llvm::raw_ostream &ros, HelpMode = HelpMode_Normal) const; + void PrintHelp(llvm::raw_ostream &ros) const; void PrintAnchorHeader(llvm::raw_ostream &ro, RegisteredCheck::List &checks) const; private: void printRequestedChecks() const; @@ -77,11 +71,16 @@ private: class ClazyStandaloneASTAction : public clang::ASTFrontendAction { public: - explicit ClazyStandaloneASTAction(const std::string &checkList, ClazyContext::ClazyOptions = ClazyContext::ClazyOption_None); + explicit ClazyStandaloneASTAction(const std::string &checkList, + const std::string &headerFilter, + const std::string &ignoreDirs, + ClazyContext::ClazyOptions = ClazyContext::ClazyOption_None); protected: std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef) override; private: - std::string m_checkList; + const std::string m_checkList; + const std::string m_headerFilter; + const std::string m_ignoreDirs; const ClazyContext::ClazyOptions m_options; }; @@ -99,7 +98,7 @@ public: bool VisitDecl(clang::Decl *decl); bool VisitStmt(clang::Stmt *stm); void HandleTranslationUnit(clang::ASTContext &ctx) override; - void addCheck(CheckBase *check); + void addCheck(const std::pair<CheckBase *, RegisteredCheck> &check); ClazyContext *context() const { return m_context; } @@ -107,8 +106,12 @@ private: ClazyASTConsumer(const ClazyASTConsumer &) = delete; clang::Stmt *lastStm = nullptr; ClazyContext *const m_context; - CheckBase::List m_createdChecks; + //CheckBase::List m_createdChecks; + CheckBase::List m_checksToVisitStmts; + CheckBase::List m_checksToVisitDecls; +#ifndef CLAZY_DISABLE_AST_MATCHERS clang::ast_matchers::MatchFinder *m_matchFinder = nullptr; +#endif }; #endif diff --git a/src/ClazyAnchorHeader.h b/src/ClazyAnchorHeader.h index 173ed21a..b8e418c6 100644 --- a/src/ClazyAnchorHeader.h +++ b/src/ClazyAnchorHeader.h @@ -63,7 +63,7 @@ extern volatile int ClazyAnchor_ReturningVoidExpression; extern volatile int ClazyAnchor_RuleOfThree; extern volatile int ClazyAnchor_VirtualCallsFromCTOR; extern volatile int ClazyAnchor_AssertWithSideEffects; -extern volatile int ClazyAnchor_BogusDynamicCast; +extern volatile int ClazyAnchor_UnneededCast; extern volatile int ClazyAnchor_DetachingMember; extern volatile int ClazyAnchor_ThreadWithSlots; @@ -130,7 +130,7 @@ int clazy_dummy() ClazyAnchor_RuleOfThree + ClazyAnchor_VirtualCallsFromCTOR + ClazyAnchor_AssertWithSideEffects + - ClazyAnchor_BogusDynamicCast + + ClazyAnchor_UnneededCast + ClazyAnchor_DetachingMember + ClazyAnchor_ThreadWithSlots + 0; diff --git a/src/ClazyContext.cpp b/src/ClazyContext.cpp index 690609e1..4d4ecd82 100644 --- a/src/ClazyContext.cpp +++ b/src/ClazyContext.cpp @@ -49,18 +49,25 @@ public: } }; -ClazyContext::ClazyContext(const clang::CompilerInstance &compiler, ClazyOptions opts) +ClazyContext::ClazyContext(const clang::CompilerInstance &compiler, + const string &headerFilter, const string &ignoreDirs, ClazyOptions opts) : ci(compiler) , astContext(ci.getASTContext()) , sm(ci.getSourceManager()) , m_noWerror(getenv("CLAZY_NO_WERROR") != nullptr) // Allows user to make clazy ignore -Werror , options(opts) - , extraOptions(clazy_std::splitString(getenv("CLAZY_EXTRA_OPTIONS"), ',')) + , extraOptions(clazy::splitString(getenv("CLAZY_EXTRA_OPTIONS"), ',')) { + if (!headerFilter.empty()) + headerFilterRegex = std::unique_ptr<llvm::Regex>(new llvm::Regex(headerFilter)); + + if (!ignoreDirs.empty()) + ignoreDirsRegex = std::unique_ptr<llvm::Regex>(new llvm::Regex(ignoreDirs)); + const char *fixitsEnv = getenv("CLAZY_FIXIT"); allFixitsEnabled = (options & ClazyOption_AllFixitsEnabled); if (!allFixitsEnabled && fixitsEnv) { - const string fixitsEnvStr = clazy_std::unquoteString(fixitsEnv); + const string fixitsEnvStr = clazy::unquoteString(fixitsEnv); if (fixitsEnvStr == "all_fixits") { allFixitsEnabled = true; } else { diff --git a/src/ClazyContext.h b/src/ClazyContext.h index 758f9c0a..608e86b4 100644 --- a/src/ClazyContext.h +++ b/src/ClazyContext.h @@ -28,9 +28,12 @@ #include <clang/Frontend/CompilerInstance.h> #include <clang/Lex/PreprocessorOptions.h> +#include <clang/Basic/FileManager.h> +#include <llvm/Support/Regex.h> #include <string> #include <vector> +#include <memory> // ClazyContext is just a struct to share data and code between all checks @@ -57,11 +60,15 @@ public: ClazyOption_Qt4Compat = 8, ClazyOption_OnlyQt = 16, // Ignore non-Qt files. This is done by bailing out if QT_CORE_LIB is not set. ClazyOption_QtDeveloper = 32, // For running clazy on Qt itself, optional, but honours specific guidelines - ClazyOption_VisitImplicitCode = 64 // Inspect compiler generated code aswell, useful for custom checks, if then need it + ClazyOption_VisitImplicitCode = 64, // Inspect compiler generated code aswell, useful for custom checks, if they need it + ClazyOption_IgnoreIncludedFiles = 128 // Only warn for the current file being compiled, not on includes (useful for performance reasons) }; typedef int ClazyOptions; - explicit ClazyContext(const clang::CompilerInstance &ci, ClazyOptions = ClazyOption_None); + explicit ClazyContext(const clang::CompilerInstance &ci, + const std::string &headerFilter, + const std::string &ignoreDirs, + ClazyOptions = ClazyOption_None); ~ClazyContext(); bool usingPreCompiledHeaders() const @@ -89,6 +96,11 @@ public: return options & ClazyOption_QtDeveloper; } + bool ignoresIncludedFiles() const + { + return options & ClazyOption_IgnoreIncludedFiles; + } + bool isVisitImplicitCode() const { return options & ClazyContext::ClazyOption_VisitImplicitCode; @@ -96,7 +108,53 @@ public: bool isOptionSet(const std::string &optionName) const { - return clazy_std::contains(extraOptions, optionName); + return clazy::contains(extraOptions, optionName); + } + + bool fileMatchesLoc(const std::unique_ptr<llvm::Regex> ®ex, clang::SourceLocation loc, const clang::FileEntry **file) const + { + if (!regex) + return false; + + if (!(*file)) { + clang::FileID fid = sm.getDecomposedExpansionLoc(loc).first; + *file = sm.getFileEntryForID(fid); + if (!(*file)) { + return false; + } + } + + StringRef fileName((*file)->getName()); + return regex->match(fileName); + } + + bool shouldIgnoreFile(clang::SourceLocation loc) const + { + // 1. Process the regexp that excludes files + const clang::FileEntry *file = nullptr; + if (ignoreDirsRegex) { + const bool matches = fileMatchesLoc(ignoreDirsRegex, loc, &file); + if (matches) + return true; + } + + // 2. Process the regexp that includes files. Has lower priority. + if (!headerFilterRegex || isMainFile(loc)) + return false; + + const bool matches = fileMatchesLoc(headerFilterRegex, loc, &file); + if (!file) + return false; + + return !matches; + } + + bool isMainFile(clang::SourceLocation loc) const + { + if (loc.isMacroID()) + loc = sm.getExpansionLoc(loc); + + return sm.isInFileID(loc, sm.getMainFileID()); } /** @@ -120,6 +178,10 @@ public: clang::FixItRewriter *rewriter = nullptr; bool allFixitsEnabled = false; std::string requestedFixitName; + clang::CXXMethodDecl *lastMethodDecl = nullptr; + clang::Decl *lastDecl = nullptr; + std::unique_ptr<llvm::Regex> headerFilterRegex; + std::unique_ptr<llvm::Regex> ignoreDirsRegex; }; #endif diff --git a/src/ClazyStandaloneMain.cpp b/src/ClazyStandaloneMain.cpp index a84217ae..697091e6 100644 --- a/src/ClazyStandaloneMain.cpp +++ b/src/ClazyStandaloneMain.cpp @@ -56,6 +56,19 @@ static cl::opt<bool> s_qtDeveloper("qt-developer", cl::desc("For running clazy o static cl::opt<bool> s_visitImplicitCode("visit-implicit-code", cl::desc("For visiting implicit code like compiler generated constructors. None of the built-in checks benefit from this, but can be useful for custom checks"), cl::init(false), cl::cat(s_clazyCategory)); +static cl::opt<bool> s_ignoreIncludedFiles("ignore-included-files", cl::desc("Only emit warnings for the current file being compiled and ignore any includes. Useful for performance reasons."), + cl::init(false), cl::cat(s_clazyCategory)); + +static cl::opt<std::string> s_headerFilter("header-filter", cl::desc(R"(Regular expression matching the names of the +headers to output diagnostics from. Diagnostics +from the main file of each translation unit are +always displayed.)"), + cl::init(""), cl::cat(s_clazyCategory)); + +static cl::opt<std::string> s_ignoreDirs("ignore-dirs", cl::desc(R"(Regular expression matching the names of the +directories for which diagnostics should never be emitted. Useful for ignoring 3rdparty code.)"), + cl::init(""), cl::cat(s_clazyCategory)); + static cl::extrahelp s_commonHelp(CommonOptionsParser::HelpMessage); class ClazyToolActionFactory : public clang::tooling::FrontendActionFactory @@ -85,7 +98,10 @@ public: if (s_visitImplicitCode.getValue()) options |= ClazyContext::ClazyOption_VisitImplicitCode; - return new ClazyStandaloneASTAction(s_checks.getValue(), options); + if (s_ignoreIncludedFiles.getValue()) + options |= ClazyContext::ClazyOption_IgnoreIncludedFiles; + + return new ClazyStandaloneASTAction(s_checks.getValue(), s_headerFilter.getValue(), s_ignoreDirs.getValue(), options); } }; diff --git a/src/ContextUtils.cpp b/src/ContextUtils.cpp index 55dc2628..f1c1349b 100644 --- a/src/ContextUtils.cpp +++ b/src/ContextUtils.cpp @@ -32,7 +32,7 @@ using namespace clang; using namespace std; -std::vector<DeclContext *> ContextUtils::contextsForDecl(DeclContext *currentScope) +std::vector<DeclContext *> clazy::contextsForDecl(DeclContext *currentScope) { std::vector<DeclContext *> decls; decls.reserve(20); // jump-start @@ -61,7 +61,7 @@ static string nameForContext(DeclContext *context) return {}; } -string ContextUtils::getMostNeededQualifiedName(const SourceManager &sourceManager, +string clazy::getMostNeededQualifiedName(const SourceManager &sourceManager, CXXMethodDecl *method, DeclContext *currentScope, SourceLocation usageLoc, bool honourUsingDirectives) @@ -70,23 +70,23 @@ string ContextUtils::getMostNeededQualifiedName(const SourceManager &sourceManag return method->getQualifiedNameAsString(); // All namespaces, classes, inner class qualifications - auto methodContexts = ContextUtils::contextsForDecl(method->getDeclContext()); + auto methodContexts = clazy::contextsForDecl(method->getDeclContext()); // Visible scopes in current scope - auto visibleContexts = ContextUtils::contextsForDecl(currentScope); + auto visibleContexts = clazy::contextsForDecl(currentScope); // Collect using directives vector<UsingDirectiveDecl*> usings; if (honourUsingDirectives) { for (DeclContext *context : visibleContexts) {; - clazy_std::append(context->using_directives(), usings); + clazy::append(context->using_directives(), usings); } } for (UsingDirectiveDecl *u : usings) { NamespaceDecl *ns = u->getNominatedNamespace(); if (ns) { - if (sourceManager.isBeforeInSLocAddrSpace(usageLoc, u->getLocStart())) + if (sourceManager.isBeforeInSLocAddrSpace(usageLoc, getLocStart(u))) continue; visibleContexts.push_back(ns->getOriginalNamespace()); @@ -96,7 +96,7 @@ string ContextUtils::getMostNeededQualifiedName(const SourceManager &sourceManag for (DeclContext *context : visibleContexts) { if (context != method->getParent()) { // Don't remove the most immediate - auto it = clazy_std::find_if(methodContexts, [context](DeclContext *c) { + auto it = clazy::find_if(methodContexts, [context](DeclContext *c) { if (c == context) return true; auto ns1 = dyn_cast<NamespaceDecl>(c); @@ -119,7 +119,7 @@ string ContextUtils::getMostNeededQualifiedName(const SourceManager &sourceManag return result; } -bool ContextUtils::canTakeAddressOf(CXXMethodDecl *method, DeclContext *context, bool &isSpecialProtectedCase) +bool clazy::canTakeAddressOf(CXXMethodDecl *method, DeclContext *context, bool &isSpecialProtectedCase) { isSpecialProtectedCase = false; if (!method || !method->getParent()) diff --git a/src/ContextUtils.h b/src/ContextUtils.h index 7783abe8..6bc85e3e 100644 --- a/src/ContextUtils.h +++ b/src/ContextUtils.h @@ -42,7 +42,7 @@ class CXXMethodDecl; class ParentMap; } -namespace ContextUtils +namespace clazy { /** @@ -135,7 +135,7 @@ T* firstContextOfType(clang::DeclContext *context) if (llvm::isa<T>(context)) return llvm::cast<T>(context); - return ContextUtils::firstContextOfType<T>(context->getParent()); + return clazy::firstContextOfType<T>(context->getParent()); } diff --git a/src/FixItUtils.cpp b/src/FixItUtils.cpp index d6f11d2f..f6c05081 100644 --- a/src/FixItUtils.cpp +++ b/src/FixItUtils.cpp @@ -29,11 +29,11 @@ #include <clang/Lex/Lexer.h> #include <StringUtils.h> -using namespace FixItUtils; +using namespace clazy; using namespace clang; using namespace std; -clang::FixItHint FixItUtils::createReplacement(clang::SourceRange range, const std::string &replacement) +clang::FixItHint clazy::createReplacement(clang::SourceRange range, const std::string &replacement) { if (range.getBegin().isInvalid()) { return {}; @@ -42,7 +42,7 @@ clang::FixItHint FixItUtils::createReplacement(clang::SourceRange range, const s } } -clang::FixItHint FixItUtils::createInsertion(clang::SourceLocation start, const std::string &insertion) +clang::FixItHint clazy::createInsertion(clang::SourceLocation start, const std::string &insertion) { if (start.isInvalid()) { return {}; @@ -51,7 +51,7 @@ clang::FixItHint FixItUtils::createInsertion(clang::SourceLocation start, const } } -SourceRange FixItUtils::rangeForLiteral(const ASTContext *context, StringLiteral *lt) +SourceRange clazy::rangeForLiteral(const ASTContext *context, StringLiteral *lt) { if (!lt) return {}; @@ -63,11 +63,11 @@ SourceRange FixItUtils::rangeForLiteral(const ASTContext *context, StringLiteral } SourceRange range; - range.setBegin(lt->getLocStart()); + range.setBegin(getLocStart(lt)); SourceLocation end = Lexer::getLocForEndOfToken(lastTokenLoc, 0, context->getSourceManager(), - context->getLangOpts()); // For some reason lt->getLocStart() is == to lt->getLocEnd() + context->getLangOpts()); // For some reason getLocStart(lt) is == to getLocEnd(lt) if (!end.isValid()) { return {}; @@ -77,13 +77,13 @@ SourceRange FixItUtils::rangeForLiteral(const ASTContext *context, StringLiteral return range; } -void FixItUtils::insertParentMethodCall(const std::string &method, SourceRange range, std::vector<FixItHint> &fixits) +void clazy::insertParentMethodCall(const std::string &method, SourceRange range, std::vector<FixItHint> &fixits) { - fixits.push_back(FixItUtils::createInsertion(range.getEnd(), ")")); - fixits.push_back(FixItUtils::createInsertion(range.getBegin(), method + '(')); + fixits.push_back(clazy::createInsertion(range.getEnd(), ")")); + fixits.push_back(clazy::createInsertion(range.getBegin(), method + '(')); } -bool FixItUtils::insertParentMethodCallAroundStringLiteral(const ASTContext *context, +bool clazy::insertParentMethodCallAroundStringLiteral(const ASTContext *context, const std::string &method, StringLiteral *lt, std::vector<FixItHint> &fixits) @@ -99,7 +99,7 @@ bool FixItUtils::insertParentMethodCallAroundStringLiteral(const ASTContext *con return true; } -SourceLocation FixItUtils::locForNextToken(const ASTContext *context, SourceLocation start, tok::TokenKind kind) +SourceLocation clazy::locForNextToken(const ASTContext *context, SourceLocation start, tok::TokenKind kind) { if (!start.isValid()) return {}; @@ -117,12 +117,12 @@ SourceLocation FixItUtils::locForNextToken(const ASTContext *context, SourceLoca return locForNextToken(context, nextStart, kind); } -SourceLocation FixItUtils::biggestSourceLocationInStmt(const SourceManager &sm, Stmt *stmt) +SourceLocation clazy::biggestSourceLocationInStmt(const SourceManager &sm, Stmt *stmt) { if (!stmt) return {}; - SourceLocation biggestLoc = stmt->getLocEnd(); + SourceLocation biggestLoc = getLocEnd(stmt); for (auto child : stmt->children()) { SourceLocation candidateLoc = biggestSourceLocationInStmt(sm, child); @@ -133,25 +133,25 @@ SourceLocation FixItUtils::biggestSourceLocationInStmt(const SourceManager &sm, return biggestLoc; } -SourceLocation FixItUtils::locForEndOfToken(const ASTContext *context, SourceLocation start, int offset) +SourceLocation clazy::locForEndOfToken(const ASTContext *context, SourceLocation start, int offset) { return Lexer::getLocForEndOfToken(start, offset, context->getSourceManager(), context->getLangOpts()); } -bool FixItUtils::transformTwoCallsIntoOne(const ASTContext *context, CallExpr *call1, CXXMemberCallExpr *call2, +bool clazy::transformTwoCallsIntoOne(const ASTContext *context, CallExpr *call1, CXXMemberCallExpr *call2, const string &replacement, vector<FixItHint> &fixits) { Expr *implicitArgument = call2->getImplicitObjectArgument(); if (!implicitArgument) return false; - const SourceLocation start1 = call1->getLocStart(); - const SourceLocation end1 = FixItUtils::locForEndOfToken(context, start1, -1); // -1 of offset, so we don't need to insert '(' + const SourceLocation start1 = getLocStart(call1); + const SourceLocation end1 = clazy::locForEndOfToken(context, start1, -1); // -1 of offset, so we don't need to insert '(' if (end1.isInvalid()) return false; - const SourceLocation start2 = implicitArgument->getLocEnd(); - const SourceLocation end2 = call2->getLocEnd(); + const SourceLocation start2 = getLocEnd(implicitArgument); + const SourceLocation end2 = getLocEnd(call2); if (start2.isInvalid() || end2.isInvalid()) return false; @@ -160,43 +160,43 @@ bool FixItUtils::transformTwoCallsIntoOne(const ASTContext *context, CallExpr *c // ^ end1 // ^ start2 // ^ end2 - fixits.push_back(FixItUtils::createReplacement({ start1, end1 }, replacement)); - fixits.push_back(FixItUtils::createReplacement({ start2, end2 }, ")")); + fixits.push_back(clazy::createReplacement({ start1, end1 }, replacement)); + fixits.push_back(clazy::createReplacement({ start2, end2 }, ")")); return true; } -bool FixItUtils::transformTwoCallsIntoOneV2(const ASTContext *context, CXXMemberCallExpr *call2, +bool clazy::transformTwoCallsIntoOneV2(const ASTContext *context, CXXMemberCallExpr *call2, const string &replacement, std::vector<FixItHint> &fixits) { Expr *implicitArgument = call2->getImplicitObjectArgument(); if (!implicitArgument) return false; - SourceLocation start = implicitArgument->getLocStart(); - start = FixItUtils::locForEndOfToken(context, start, 0); - const SourceLocation end = call2->getLocEnd(); + SourceLocation start = getLocStart(implicitArgument); + start = clazy::locForEndOfToken(context, start, 0); + const SourceLocation end = getLocEnd(call2); if (start.isInvalid() || end.isInvalid()) return false; - fixits.push_back(FixItUtils::createReplacement({ start, end }, replacement)); + fixits.push_back(clazy::createReplacement({ start, end }, replacement)); return true; } -FixItHint FixItUtils::fixItReplaceWordWithWord(const ASTContext *context, clang::Stmt *begin, +FixItHint clazy::fixItReplaceWordWithWord(const ASTContext *context, clang::Stmt *begin, const string &replacement, const string &replacee) { auto &sm = context->getSourceManager(); - SourceLocation rangeStart = begin->getLocStart(); + SourceLocation rangeStart = getLocStart(begin); SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm, context->getLangOpts()); if (rangeEnd.isInvalid()) { // Fallback. Have seen a case in the wild where the above would fail, it's very rare rangeEnd = rangeStart.getLocWithOffset(replacee.size() - 2); if (rangeEnd.isInvalid()) { - StringUtils::printLocation(sm, rangeStart); - StringUtils::printLocation(sm, rangeEnd); - StringUtils::printLocation(sm, Lexer::getLocForEndOfToken(rangeStart, 0, sm, context->getLangOpts())); + clazy::printLocation(sm, rangeStart); + clazy::printLocation(sm, rangeEnd); + clazy::printLocation(sm, Lexer::getLocForEndOfToken(rangeStart, 0, sm, context->getLangOpts())); return {}; } } @@ -204,9 +204,9 @@ FixItHint FixItUtils::fixItReplaceWordWithWord(const ASTContext *context, clang: return FixItHint::CreateReplacement(SourceRange(rangeStart, rangeEnd), replacement); } -vector<FixItHint> FixItUtils::fixItRemoveToken(const ASTContext *context, Stmt *stmt, bool removeParenthesis) +vector<FixItHint> clazy::fixItRemoveToken(const ASTContext *context, Stmt *stmt, bool removeParenthesis) { - SourceLocation start = stmt->getLocStart(); + SourceLocation start = getLocStart(stmt); SourceLocation end = Lexer::getLocForEndOfToken(start, removeParenthesis ? 0 : -1, context->getSourceManager(), context->getLangOpts()); @@ -217,7 +217,7 @@ vector<FixItHint> FixItUtils::fixItRemoveToken(const ASTContext *context, Stmt * if (removeParenthesis) { // Remove the last parenthesis - fixits.push_back(FixItHint::CreateRemoval(SourceRange(stmt->getLocEnd(), stmt->getLocEnd()))); + fixits.push_back(FixItHint::CreateRemoval(SourceRange(getLocEnd(stmt), getLocEnd(stmt)))); } } diff --git a/src/FixItUtils.h b/src/FixItUtils.h index aed09b4f..95cbed3a 100644 --- a/src/FixItUtils.h +++ b/src/FixItUtils.h @@ -39,7 +39,7 @@ class CallExpr; class CXXMemberCallExpr; } -namespace FixItUtils { +namespace clazy { /** * Replaces whatever is in range, with replacement @@ -82,7 +82,7 @@ CLAZYLIB_EXPORT clang::SourceLocation locForNextToken(const clang::ASTContext *c * * ^ // expr->getLocStart() * ^ // expr->getLocEnd() - * ^ // FixItUtils::locForEndOfToken(expr->getLocStart()) + * ^ // clazy::locForEndOfToken(expr->getLocStart()) */ CLAZYLIB_EXPORT clang::SourceLocation locForEndOfToken(const clang::ASTContext *context, clang::SourceLocation start, int offset = 0); diff --git a/src/FunctionUtils.h b/src/FunctionUtils.h index 327078da..a8f3a499 100644 --- a/src/FunctionUtils.h +++ b/src/FunctionUtils.h @@ -29,11 +29,12 @@ #include "Utils.h" #include "HierarchyUtils.h" +#include "StringUtils.h" #include <clang/AST/Decl.h> #include <string> -namespace FunctionUtils { +namespace clazy { inline bool hasCharPtrArgument(clang::FunctionDecl *func, int expected_arguments = -1) { @@ -65,15 +66,14 @@ inline clang::ValueDecl *valueDeclForCallArgument(clang::CallExpr *call, unsigne clang::Expr *firstArg = call->getArg(argIndex); auto declRef = llvm::isa<clang::DeclRefExpr>(firstArg) ? llvm::cast<clang::DeclRefExpr>(firstArg) - : HierarchyUtils::getFirstChildOfType2<clang::DeclRefExpr>(firstArg); + : clazy::getFirstChildOfType2<clang::DeclRefExpr>(firstArg); if (!declRef) return nullptr; return declRef->getDecl(); } - -inline bool parametersMatch(clang::FunctionDecl *f1, clang::FunctionDecl *f2) +inline bool parametersMatch(const clang::FunctionDecl *f1, const clang::FunctionDecl *f2) { if (!f1 || !f2) return false; @@ -95,6 +95,23 @@ inline bool parametersMatch(clang::FunctionDecl *f1, clang::FunctionDecl *f2) return true; } +/** + * Returns true if a class contains a method with a specific signature. + * (method->getParent() doesn't need to equal record) + */ +inline bool classImplementsMethod(const clang::CXXRecordDecl *record, const clang::CXXMethodDecl *method) +{ + if (!method->getDeclName().isIdentifier()) + return false; + + StringRef methodName = method->getName(); + for (auto m : record->methods()) { + if (!m->isPure() && clazy::name(m) == methodName && parametersMatch(m, method)) + return true; + } + + return false; +} } #endif diff --git a/src/HierarchyUtils.h b/src/HierarchyUtils.h index baaab1f3..a616a9a8 100644 --- a/src/HierarchyUtils.h +++ b/src/HierarchyUtils.h @@ -29,6 +29,7 @@ #include "clazy_export.h" #include "clazy_stl.h" +#include "StringUtils.h" #include <clang/Frontend/CompilerInstance.h> #include <clang/AST/Stmt.h> @@ -36,7 +37,7 @@ #include <clang/AST/ParentMap.h> -namespace HierarchyUtils { +namespace clazy { enum IgnoreStmt { IgnoreNone = 0, @@ -54,7 +55,7 @@ inline bool isChildOf(clang::Stmt *child, clang::Stmt *parent) if (!child || !parent) return false; - return clazy_std::any_of(parent->children(), [child](clang::Stmt *c) { + return clazy::any_of(parent->children(), [child](clang::Stmt *c) { return c == child || isChildOf(child, c); }); } @@ -70,11 +71,11 @@ inline bool isParentOfMemberFunctionCall(clang::Stmt *stm, const std::string &na if (auto expr = llvm::dyn_cast<clang::MemberExpr>(stm)) { auto namedDecl = llvm::dyn_cast<clang::NamedDecl>(expr->getMemberDecl()); - if (namedDecl && namedDecl->getNameAsString() == name) + if (namedDecl && clazy::name(namedDecl) == name) return true; } - return clazy_std::any_of(stm->children(), [name] (clang::Stmt *child) { + return clazy::any_of(stm->children(), [name] (clang::Stmt *child) { return isParentOfMemberFunctionCall(child, name); }); @@ -115,8 +116,12 @@ T* getFirstChildOfType2(clang::Stmt *stm) if (!stm) return nullptr; - if (clazy_std::hasChildren(stm)) { + if (clazy::hasChildren(stm)) { auto child = *(stm->child_begin()); + + if (!child) // can happen + return nullptr; + if (auto s = clang::dyn_cast<T>(child)) return s; @@ -137,7 +142,7 @@ inline clang::Stmt *parent(clang::ParentMap *map, clang::Stmt *s, unsigned int d return nullptr; return depth == 0 ? s - : HierarchyUtils::parent(map, map->getParent(s), depth - 1); + : clazy::parent(map, map->getParent(s), depth - 1); } // Returns the first parent of type T, with max depth depth @@ -171,7 +176,7 @@ inline clang::Stmt * getFirstChildAtDepth(clang::Stmt *s, unsigned int depth) if (depth == 0 || !s) return s; - return clazy_std::hasChildren(s) ? getFirstChildAtDepth(*s->child_begin(), --depth) + return clazy::hasChildren(s) ? getFirstChildAtDepth(*s->child_begin(), --depth) : nullptr; } @@ -224,7 +229,7 @@ std::vector<T*> getStatements(clang::Stmt *body, for (auto child : body->children()) { if (!child) continue; // can happen if (T *childT = clang::dyn_cast<T>(child)) { - if (!startLocation.isValid() || (sm && sm->isBeforeInSLocAddrSpace(sm->getSpellingLoc(startLocation), child->getLocStart()))) + if (!startLocation.isValid() || (sm && sm->isBeforeInSLocAddrSpace(sm->getSpellingLoc(startLocation), getLocStart(child)))) statements.push_back(childT); } @@ -232,7 +237,7 @@ std::vector<T*> getStatements(clang::Stmt *body, --depth; auto childStatements = getStatements<T>(child, sm, startLocation, depth, false, ignoreOptions); - clazy_std::append(childStatements, statements); + clazy::append(childStatements, statements); } return statements; @@ -256,14 +261,19 @@ T* unpeal(clang::Stmt *stmt, IgnoreStmts options = IgnoreNone) return tt; if ((options & IgnoreImplicitCasts) && llvm::isa<clang::ImplicitCastExpr>(stmt)) - return unpeal<T>(HierarchyUtils::getFirstChild(stmt), options); + return unpeal<T>(clazy::getFirstChild(stmt), options); if ((options & IgnoreExprWithCleanups) && llvm::isa<clang::ExprWithCleanups>(stmt)) - return unpeal<T>(HierarchyUtils::getFirstChild(stmt), options); + return unpeal<T>(clazy::getFirstChild(stmt), options); return nullptr; } +inline clang::SwitchStmt* getSwitchFromCase(clang::ParentMap *pmap, clang::CaseStmt *caseStm) +{ + return getFirstParentOfType<clang::SwitchStmt>(pmap, caseStm); +} + } #endif diff --git a/src/LoopUtils.cpp b/src/LoopUtils.cpp index 57202565..ff15b106 100644 --- a/src/LoopUtils.cpp +++ b/src/LoopUtils.cpp @@ -23,6 +23,7 @@ */ #include "LoopUtils.h" +#include "StringUtils.h" #include "clazy_stl.h" #include <clang/AST/ParentMap.h> @@ -34,39 +35,37 @@ using namespace std; using namespace clang; -Stmt *LoopUtils::bodyFromLoop(Stmt *loop) +Stmt *clazy::bodyFromLoop(Stmt *loop) { if (!loop) return nullptr; - if (auto forstm = dyn_cast<ForStmt>(loop)) { + if (auto forstm = dyn_cast<ForStmt>(loop)) return forstm->getBody(); - } - if (auto rangeLoop = dyn_cast<CXXForRangeStmt>(loop)) { + if (auto rangeLoop = dyn_cast<CXXForRangeStmt>(loop)) return rangeLoop->getBody(); - } - if (auto whilestm = dyn_cast<WhileStmt>(loop)) { + + if (auto whilestm = dyn_cast<WhileStmt>(loop)) return whilestm->getBody(); - } - if (auto dostm = dyn_cast<DoStmt>(loop)) { + + if (auto dostm = dyn_cast<DoStmt>(loop)) return dostm->getBody(); - } return nullptr; } -bool LoopUtils::loopCanBeInterrupted(clang::Stmt *stmt, const clang::SourceManager &sm, - clang::SourceLocation onlyBeforeThisLoc) +bool clazy::loopCanBeInterrupted(clang::Stmt *stmt, const clang::SourceManager &sm, + clang::SourceLocation onlyBeforeThisLoc) { if (!stmt) return false; if (isa<ReturnStmt>(stmt) || isa<BreakStmt>(stmt) || isa<ContinueStmt>(stmt)) { if (onlyBeforeThisLoc.isValid()) { - FullSourceLoc sourceLoc(stmt->getLocStart(), sm); + FullSourceLoc sourceLoc(getLocStart(stmt), sm); FullSourceLoc otherSourceLoc(onlyBeforeThisLoc, sm); if (sourceLoc.isBeforeInTranslationUnitThan(otherSourceLoc)) return true; @@ -75,26 +74,25 @@ bool LoopUtils::loopCanBeInterrupted(clang::Stmt *stmt, const clang::SourceManag } } - return clazy_std::any_of(stmt->children(), [&sm, onlyBeforeThisLoc](Stmt *s) { - return LoopUtils::loopCanBeInterrupted(s, sm, onlyBeforeThisLoc); + return clazy::any_of(stmt->children(), [&sm, onlyBeforeThisLoc](Stmt *s) { + return clazy::loopCanBeInterrupted(s, sm, onlyBeforeThisLoc); }); } -clang::Expr *LoopUtils::containerExprForLoop(Stmt *loop) +clang::Expr *clazy::containerExprForLoop(Stmt *loop) { if (!loop) return nullptr; - if (auto rangeLoop = dyn_cast<CXXForRangeStmt>(loop)) { + if (auto rangeLoop = dyn_cast<CXXForRangeStmt>(loop)) return rangeLoop->getRangeInit(); - } if (auto constructExpr = dyn_cast<CXXConstructExpr>(loop)) { if (constructExpr->getNumArgs() < 1) return nullptr; CXXConstructorDecl *constructorDecl = constructExpr->getConstructor(); - if (!constructorDecl || constructorDecl->getNameAsString() != "QForeachContainer") + if (!constructorDecl || clazy::name(constructorDecl) != "QForeachContainer") return nullptr; @@ -104,7 +102,7 @@ clang::Expr *LoopUtils::containerExprForLoop(Stmt *loop) return nullptr; } -VarDecl* LoopUtils::containerDeclForLoop(clang::Stmt *loop) +VarDecl* clazy::containerDeclForLoop(clang::Stmt *loop) { Expr *expr = containerExprForLoop(loop); if (!expr) @@ -118,14 +116,14 @@ VarDecl* LoopUtils::containerDeclForLoop(clang::Stmt *loop) return valueDecl ? dyn_cast<VarDecl>(valueDecl) : nullptr; } -Stmt* LoopUtils::isInLoop(clang::ParentMap *pmap, clang::Stmt *stmt) +Stmt* clazy::isInLoop(clang::ParentMap *pmap, clang::Stmt *stmt) { if (!stmt) return nullptr; Stmt *p = pmap->getParent(stmt); while (p) { - if (LoopUtils::isLoop(p)) + if (clazy::isLoop(p)) return p; p = pmap->getParent(p); } diff --git a/src/LoopUtils.h b/src/LoopUtils.h index 5d76cddf..cfb4cd2f 100644 --- a/src/LoopUtils.h +++ b/src/LoopUtils.h @@ -38,7 +38,7 @@ class Expr; class ParentMap; } -namespace LoopUtils { +namespace clazy { /** * Returns the body of a for, range-foor, while or do-while loop */ diff --git a/src/MacroUtils.h b/src/MacroUtils.h index d531e674..985a6cea 100644 --- a/src/MacroUtils.h +++ b/src/MacroUtils.h @@ -31,7 +31,6 @@ #include <clang/Lex/PreprocessorOptions.h> #include <clang/Basic/SourceLocation.h> -#include <string> #include <vector> namespace clang { @@ -39,14 +38,14 @@ class CompilerInstance; class SourceLocation; } -namespace MacroUtils +namespace clazy { /** * Returns true is macroName was defined via compiler invocation argument. * Like $ gcc -Dfoo main.cpp */ -inline bool isPredefined(const clang::PreprocessorOptions &ppOpts, const std::string ¯oName) +inline bool isPredefined(const clang::PreprocessorOptions &ppOpts, const llvm::StringRef ¯oName) { const auto ¯os = ppOpts.Macros; @@ -61,10 +60,10 @@ inline bool isPredefined(const clang::PreprocessorOptions &ppOpts, const std::st /** * Returns true if the source location loc is inside a macro named macroName. */ -inline bool isInMacro(const clang::ASTContext *context, clang::SourceLocation loc, const std::string ¯oName) +inline bool isInMacro(const clang::ASTContext *context, clang::SourceLocation loc, const llvm::StringRef ¯oName) { if (loc.isValid() && loc.isMacroID()) { - auto macro = clang::Lexer::getImmediateMacroName(loc, context->getSourceManager(), context->getLangOpts()); + StringRef macro = clang::Lexer::getImmediateMacroName(loc, context->getSourceManager(), context->getLangOpts()); return macro == macroName; } @@ -74,9 +73,9 @@ inline bool isInMacro(const clang::ASTContext *context, clang::SourceLocation lo /** * Returns true if the source location loc is inside any of the specified macros. */ -inline bool isInAnyMacro(const clang::ASTContext *context, clang::SourceLocation loc, const std::vector<std::string> ¯oNames) +inline bool isInAnyMacro(const clang::ASTContext *context, clang::SourceLocation loc, const std::vector<llvm::StringRef> ¯oNames) { - return clazy_std::any_of(macroNames, [context, loc](const std::string ¯oName) { + return clazy::any_of(macroNames, [context, loc](const llvm::StringRef ¯oName) { return isInMacro(context, loc, macroName); }); } diff --git a/src/NormalizedSignatureUtils.h b/src/NormalizedSignatureUtils.h index a801cc4b..069bb789 100644 --- a/src/NormalizedSignatureUtils.h +++ b/src/NormalizedSignatureUtils.h @@ -38,7 +38,7 @@ #include <vector> #include <string> -namespace NormalizedSignatureUtils +namespace clazy { inline bool is_space(char s) diff --git a/src/PreProcessorVisitor.cpp b/src/PreProcessorVisitor.cpp index 2ddf9ffd..b2a5a7af 100644 --- a/src/PreProcessorVisitor.cpp +++ b/src/PreProcessorVisitor.cpp @@ -20,6 +20,7 @@ */ #include "PreProcessorVisitor.h" +#include "MacroUtils.h" #include <clang/Frontend/CompilerInstance.h> #include <clang/Lex/Preprocessor.h> @@ -35,6 +36,9 @@ PreProcessorVisitor::PreProcessorVisitor(const clang::CompilerInstance &ci) { Preprocessor &pi = m_ci.getPreprocessor(); pi.addPPCallbacks(std::unique_ptr<PPCallbacks>(this)); + + // This catches -DQT_NO_KEYWORDS passed to compiler. In MacroExpands() we catch when defined via in code + m_isQtNoKeywords = clazy::isPredefined(ci.getPreprocessorOpts(), "QT_NO_KEYWORDS"); } bool PreProcessorVisitor::isBetweenQtNamespaceMacros(SourceLocation loc) @@ -130,6 +134,11 @@ void PreProcessorVisitor::MacroExpands(const Token &MacroNameTok, const MacroDef return; } + if (!m_isQtNoKeywords && ii->getName() == "QT_NO_KEYWORDS") { + m_isQtNoKeywords = true; + return; + } + if (m_qtVersion != -1) return; diff --git a/src/PreProcessorVisitor.h b/src/PreProcessorVisitor.h index 77c014ce..319f5408 100644 --- a/src/PreProcessorVisitor.h +++ b/src/PreProcessorVisitor.h @@ -55,6 +55,9 @@ public: bool isBetweenQtNamespaceMacros(clang::SourceLocation loc); + // Returns true if QT_NO_KEYWORDS is defined + bool isQT_NO_KEYWORDS() const { return m_isQtNoKeywords; } + protected: void MacroExpands(const clang::Token &MacroNameTok, const clang::MacroDefinition &, clang::SourceRange range, const clang::MacroArgs *) override; @@ -68,6 +71,7 @@ private: int m_qtMinorVersion = -1; int m_qtPatchVersion = -1; int m_qtVersion = -1; + bool m_isQtNoKeywords = false; // Indexed by FileId, has a list of QT_BEGIN_NAMESPACE/QT_END_NAMESPACE location std::unordered_map<uint, std::vector<clang::SourceRange>> m_q_namespace_macro_locations; diff --git a/src/QtUtils.cpp b/src/QtUtils.cpp index 8a72392b..05f463d2 100644 --- a/src/QtUtils.cpp +++ b/src/QtUtils.cpp @@ -34,7 +34,7 @@ using namespace std; using namespace clang; -bool QtUtils::isQtIterableClass(clang::CXXRecordDecl *record) +bool clazy::isQtIterableClass(clang::CXXRecordDecl *record) { if (!record) return false; @@ -42,30 +42,41 @@ bool QtUtils::isQtIterableClass(clang::CXXRecordDecl *record) return isQtIterableClass(record->getQualifiedNameAsString()); } -const vector<string> & QtUtils::qtContainers() +const vector<StringRef> & clazy::qtContainers() { - static const vector<string> classes = { "QListSpecialMethods", "QList", "QVector", "QVarLengthArray", "QMap", - "QHash", "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef", - "QByteArray", "QSequentialIterable", "QAssociativeIterable", "QJsonArray", "QLinkedList" }; + static const vector<StringRef> classes = { "QListSpecialMethods", "QList", "QVector", "QVarLengthArray", "QMap", + "QHash", "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef", + "QByteArray", "QSequentialIterable", "QAssociativeIterable", "QJsonArray", "QLinkedList" }; return classes; } -const vector<string> & QtUtils::qtCOWContainers() +const vector<StringRef> & clazy::qtCOWContainers() { - static const vector<string> classes = { "QListSpecialMethods", "QList", "QVector", "QMap", "QHash", - "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef", - "QByteArray", "QJsonArray", "QLinkedList" }; + static const vector<StringRef> classes = { "QListSpecialMethods", "QList", "QVector", "QMap", "QHash", + "QMultiMap", "QMultiHash", "QSet", "QStack", "QQueue", "QString", "QStringRef", + "QByteArray", "QJsonArray", "QLinkedList" }; return classes; } -std::unordered_map<string, std::vector<string> > QtUtils::detachingMethods() +std::unordered_map<string, std::vector<StringRef> > clazy::detachingMethods() { - static std::unordered_map<string, std::vector<string> > map; + static std::unordered_map<string, std::vector<StringRef> > map; + if (map.empty()) { + map = detachingMethodsWithConstCounterParts(); + map["QVector"].push_back("fill"); + } + + return map; +} + +std::unordered_map<string, std::vector<StringRef> > clazy::detachingMethodsWithConstCounterParts() +{ + static std::unordered_map<string, std::vector<StringRef> > map; if (map.empty()) { map["QList"] = {"first", "last", "begin", "end", "front", "back", "operator[]"}; - map["QVector"] = {"first", "last", "begin", "end", "front", "back", "data", "operator[]", "fill" }; - map["QMap"] = {"begin", "end", "first", "find", "last", "lowerBound", "upperBound", "operator[]" }; + map["QVector"] = {"first", "last", "begin", "end", "front", "back", "data", "operator[]" }; + map["QMap"] = {"begin", "end", "first", "find", "last", "operator[]", "lowerBound", "upperBound" }; map["QHash"] = {"begin", "end", "find", "operator[]" }; map["QLinkedList"] = {"first", "last", "begin", "end", "front", "back", "operator[]" }; map["QSet"] = {"begin", "end", "find", "operator[]" }; @@ -83,8 +94,7 @@ std::unordered_map<string, std::vector<string> > QtUtils::detachingMethods() return map; } - -bool QtUtils::isQtCOWIterableClass(clang::CXXRecordDecl *record) +bool clazy::isQtCOWIterableClass(clang::CXXRecordDecl *record) { if (!record) return false; @@ -92,19 +102,19 @@ bool QtUtils::isQtCOWIterableClass(clang::CXXRecordDecl *record) return isQtCOWIterableClass(record->getQualifiedNameAsString()); } -bool QtUtils::isQtCOWIterableClass(const string &className) +bool clazy::isQtCOWIterableClass(const string &className) { const auto &classes = qtCOWContainers(); - return clazy_std::contains(classes, className); + return clazy::contains(classes, className); } -bool QtUtils::isQtIterableClass(const string &className) +bool clazy::isQtIterableClass(StringRef className) { const auto &classes = qtContainers(); - return clazy_std::contains(classes, className); + return clazy::contains(classes, className); } -bool QtUtils::isQtAssociativeContainer(clang::CXXRecordDecl *record) +bool clazy::isQtAssociativeContainer(clang::CXXRecordDecl *record) { if (!record) return false; @@ -112,25 +122,25 @@ bool QtUtils::isQtAssociativeContainer(clang::CXXRecordDecl *record) return isQtAssociativeContainer(record->getNameAsString()); } -bool QtUtils::isQtAssociativeContainer(const string &className) +bool clazy::isQtAssociativeContainer(StringRef className) { - static const vector<string> classes = { "QSet", "QMap", "QHash" }; - return clazy_std::contains(classes, className); + static const vector<StringRef> classes = { "QSet", "QMap", "QHash" }; + return clazy::contains(classes, className); } -bool QtUtils::isQObject(CXXRecordDecl *decl) +bool clazy::isQObject(const CXXRecordDecl *decl) { return TypeUtils::derivesFrom(decl, "QObject"); } -bool QtUtils::isQObject(clang::QualType qt) +bool clazy::isQObject(clang::QualType qt) { qt = TypeUtils::pointeeQualType(qt); const auto t = qt.getTypePtrOrNull(); return t ? isQObject(t->getAsCXXRecordDecl()) : false; } -bool QtUtils::isConvertibleTo(const Type *source, const Type *target) +bool clazy::isConvertibleTo(const Type *source, const Type *target) { if (!source || !target) return false; @@ -159,18 +169,18 @@ bool QtUtils::isConvertibleTo(const Type *source, const Type *target) return false; } -bool QtUtils::isJavaIterator(CXXRecordDecl *record) +bool clazy::isJavaIterator(CXXRecordDecl *record) { if (!record) return false; - static const vector<string> names = { "QHashIterator", "QMapIterator", "QSetIterator", "QListIterator", - "QVectorIterator", "QLinkedListIterator", "QStringListIterator" }; + static const vector<StringRef> names = { "QHashIterator", "QMapIterator", "QSetIterator", "QListIterator", + "QVectorIterator", "QLinkedListIterator", "QStringListIterator" }; - return clazy_std::contains(names, record->getNameAsString()); + return clazy::contains(names, clazy::name(record)); } -bool QtUtils::isJavaIterator(CXXMemberCallExpr *call) +bool clazy::isJavaIterator(CXXMemberCallExpr *call) { if (!call) return false; @@ -178,19 +188,24 @@ bool QtUtils::isJavaIterator(CXXMemberCallExpr *call) return isJavaIterator(call->getRecordDecl()); } -bool QtUtils::isQtContainer(QualType t) +bool clazy::isQtContainer(QualType t) { CXXRecordDecl *record = TypeUtils::typeAsRecord(t); if (!record) return false; - const string typeName = record->getNameAsString(); - return clazy_std::any_of(QtUtils::qtContainers(), [typeName] (const string &container) { + return isQtContainer(record); +} + +bool clazy::isQtContainer(const CXXRecordDecl *record) +{ + const StringRef typeName = clazy::name(record); + return clazy::any_of(clazy::qtContainers(), [typeName] (StringRef container) { return container == typeName; }); } -bool QtUtils::containerNeverDetaches(const clang::VarDecl *valDecl, StmtBodyRange bodyRange) // clazy:exclude=function-args-by-value +bool clazy::containerNeverDetaches(const clang::VarDecl *valDecl, StmtBodyRange bodyRange) // clazy:exclude=function-args-by-value { if (!valDecl) return false; @@ -210,19 +225,19 @@ bool QtUtils::containerNeverDetaches(const clang::VarDecl *valDecl, StmtBodyRang return true; } -bool QtUtils::isAReserveClass(CXXRecordDecl *recordDecl) +bool clazy::isAReserveClass(CXXRecordDecl *recordDecl) { if (!recordDecl) return false; static const std::vector<std::string> classes = {"QVector", "std::vector", "QList", "QSet"}; - return clazy_std::any_of(classes, [recordDecl](const string &className) { + return clazy::any_of(classes, [recordDecl](const string &className) { return TypeUtils::derivesFrom(recordDecl, className); }); } -clang::CXXRecordDecl *QtUtils::getQObjectBaseClass(clang::CXXRecordDecl *recordDecl) +clang::CXXRecordDecl *clazy::getQObjectBaseClass(clang::CXXRecordDecl *recordDecl) { if (!recordDecl) return nullptr; @@ -237,12 +252,12 @@ clang::CXXRecordDecl *QtUtils::getQObjectBaseClass(clang::CXXRecordDecl *recordD } -bool QtUtils::isConnect(FunctionDecl *func) +bool clazy::isConnect(FunctionDecl *func) { return func && func->getQualifiedNameAsString() == "QObject::connect"; } -bool QtUtils::connectHasPMFStyle(FunctionDecl *func) +bool clazy::connectHasPMFStyle(FunctionDecl *func) { // Look for char* arguments for (auto parm : Utils::functionParameters(func)) { @@ -259,7 +274,7 @@ bool QtUtils::connectHasPMFStyle(FunctionDecl *func) return true; } -CXXMethodDecl *QtUtils::pmfFromConnect(CallExpr *funcCall, int argIndex) +CXXMethodDecl *clazy::pmfFromConnect(CallExpr *funcCall, int argIndex) { if (!funcCall) return nullptr; @@ -278,7 +293,7 @@ CXXMethodDecl *QtUtils::pmfFromConnect(CallExpr *funcCall, int argIndex) } -CXXMethodDecl *QtUtils::pmfFromUnary(Expr *expr) +CXXMethodDecl *clazy::pmfFromUnary(Expr *expr) { if (auto uo = dyn_cast<UnaryOperator>(expr)) { return pmfFromUnary(uo); @@ -295,7 +310,7 @@ CXXMethodDecl *QtUtils::pmfFromUnary(Expr *expr) if (!context) return nullptr; - CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(context); + auto record = dyn_cast<CXXRecordDecl>(context); if (!record) return nullptr; @@ -306,12 +321,16 @@ CXXMethodDecl *QtUtils::pmfFromUnary(Expr *expr) return pmfFromUnary(dyn_cast<UnaryOperator>(call->getArg(1))); } else if (auto staticCast = dyn_cast<CXXStaticCastExpr>(expr)) { return pmfFromUnary(staticCast->getSubExpr()); + } else if (auto callexpr = dyn_cast<CallExpr>(expr)) { + // QOverload case, go deeper one level to get to the UnaryOperator + if (callexpr->getNumArgs() == 1) + return pmfFromUnary(callexpr->getArg(0)); } return nullptr; } -CXXMethodDecl *QtUtils::pmfFromUnary(UnaryOperator *uo) +CXXMethodDecl *clazy::pmfFromUnary(UnaryOperator *uo) { if (!uo) return nullptr; @@ -328,7 +347,7 @@ CXXMethodDecl *QtUtils::pmfFromUnary(UnaryOperator *uo) return nullptr; } -bool QtUtils::recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string ¶mType, bool &ok, int &numCtors) +bool clazy::recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string ¶mType, bool &ok, int &numCtors) { ok = true; numCtors = 0; diff --git a/src/QtUtils.h b/src/QtUtils.h index 30c61f92..fcd421f0 100644 --- a/src/QtUtils.h +++ b/src/QtUtils.h @@ -53,7 +53,7 @@ class Expr; struct StmtBodyRange; -namespace QtUtils +namespace clazy { /** @@ -65,7 +65,7 @@ CLAZYLIB_EXPORT bool isQtIterableClass(clang::CXXRecordDecl *record); /** * Overload. */ -CLAZYLIB_EXPORT bool isQtIterableClass(const std::string &className); +CLAZYLIB_EXPORT bool isQtIterableClass(llvm::StringRef className); /** * Returns true if the class is a Qt class which can be iterated with foreach and also implicitly shared. @@ -86,7 +86,7 @@ inline bool isQtCOWIterator(clang::CXXRecordDecl *itRecord) return false; auto parent = llvm::dyn_cast_or_null<clang::CXXRecordDecl>(itRecord->getParent()); - return parent && QtUtils::isQtCOWIterableClass(parent); + return parent && clazy::isQtCOWIterableClass(parent); } /** @@ -97,41 +97,49 @@ CLAZYLIB_EXPORT bool isQtAssociativeContainer(clang::CXXRecordDecl *record); /** * Overload. */ -CLAZYLIB_EXPORT bool isQtAssociativeContainer(const std::string &className); +CLAZYLIB_EXPORT bool isQtAssociativeContainer(llvm::StringRef className); /** * Returns a list of Qt containers. */ -CLAZYLIB_EXPORT const std::vector<std::string> & qtContainers(); +CLAZYLIB_EXPORT const std::vector<llvm::StringRef> & qtContainers(); /** * Returns a list of implicitly shared Qt containers. */ -CLAZYLIB_EXPORT const std::vector<std::string> & qtCOWContainers(); +CLAZYLIB_EXPORT const std::vector<llvm::StringRef> & qtCOWContainers(); /** * Returns a map with the list of method names that detach each container. */ -CLAZYLIB_EXPORT std::unordered_map<std::string, std::vector<std::string>> detachingMethods(); +CLAZYLIB_EXPORT std::unordered_map<std::string, std::vector<llvm::StringRef> > detachingMethods(); + +/** + * Returns a map with the list of method names that detach each container, but only those methods + * with const counterparts. + */ +CLAZYLIB_EXPORT std::unordered_map<std::string, std::vector<llvm::StringRef> > detachingMethodsWithConstCounterParts(); /** * Returns true if a type represents a Qt container class. */ CLAZYLIB_EXPORT bool isQtContainer(clang::QualType); +CLAZYLIB_EXPORT bool isQtContainer(const clang::CXXRecordDecl *); + /** * Returns true if -DQT_BOOTSTRAPPED was passed to the compiler */ inline bool isBootstrapping(const clang::PreprocessorOptions &ppOpts) { - return MacroUtils::isPredefined(ppOpts, "QT_BOOTSTRAPPED"); + return clazy::isPredefined(ppOpts, "QT_BOOTSTRAPPED"); } /** * Returns if decl is or derives from QObject */ -CLAZYLIB_EXPORT bool isQObject(clang::CXXRecordDecl *decl); +CLAZYLIB_EXPORT bool isQObject(const clang::CXXRecordDecl *decl); /** * Overload. @@ -148,7 +156,7 @@ CLAZYLIB_EXPORT bool isConvertibleTo(const clang::Type *source, const clang::Typ */ inline bool isInForeach(const clang::ASTContext *context, clang::SourceLocation loc) { - return MacroUtils::isInAnyMacro(context, loc, { "Q_FOREACH", "foreach" }); + return clazy::isInAnyMacro(context, loc, { "Q_FOREACH", "foreach" }); } /** @@ -226,7 +234,7 @@ CLAZYLIB_EXPORT clang::CXXMethodDecl* pmfFromUnary(clang::UnaryOperator *uo); */ inline clang::ValueDecl *signalSenderForConnect(clang::CallExpr *call) { - return FunctionUtils::valueDeclForCallArgument(call, 0); + return clazy::valueDeclForCallArgument(call, 0); } /** @@ -238,7 +246,7 @@ inline clang::ValueDecl *signalReceiverForConnect(clang::CallExpr *call) if (!call || call->getNumArgs() < 5) return nullptr; - return FunctionUtils::valueDeclForCallArgument(call, 3); + return clazy::valueDeclForCallArgument(call, 3); } /** @@ -248,12 +256,53 @@ inline clang::ValueDecl *signalReceiverForConnect(clang::CallExpr *call) inline clang::CXXMethodDecl* receiverMethodForConnect(clang::CallExpr *call) { - clang::CXXMethodDecl *receiverMethod = QtUtils::pmfFromConnect(call, 2); + clang::CXXMethodDecl *receiverMethod = clazy::pmfFromConnect(call, 2); if (receiverMethod) return receiverMethod; // It's either third or fourth argument - return QtUtils::pmfFromConnect(call, 3); + return clazy::pmfFromConnect(call, 3); +} + + +// Returns if callExpr is a call to qobject_cast() +inline bool is_qobject_cast(clang::Stmt *s, clang::CXXRecordDecl **castTo = nullptr, + clang::CXXRecordDecl **castFrom = nullptr) +{ + if (auto callExpr = llvm::dyn_cast<clang::CallExpr>(s)) { + clang::FunctionDecl *func = callExpr->getDirectCallee(); + if (!func || clazy::name(func) != "qobject_cast") + return false; + + if (castFrom) { + clang::Expr *expr = callExpr->getArg(0); + if (auto implicitCast = llvm::dyn_cast<clang::ImplicitCastExpr>(expr)) { + if (implicitCast->getCastKind() == clang::CK_DerivedToBase) { + expr = implicitCast->getSubExpr(); + } + } + clang::QualType qt = TypeUtils::pointeeQualType(expr->getType()); + if (!qt.isNull()) { + clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl(); + *castFrom = record ? record->getCanonicalDecl() : nullptr; + } + } + + if (castTo) { + auto templateArgs = func->getTemplateSpecializationArgs(); + if (templateArgs->size() == 1) { + const clang::TemplateArgument &arg = templateArgs->get(0); + clang::QualType qt = TypeUtils::pointeeQualType(arg.getAsType()); + if (!qt.isNull()) { + clang::CXXRecordDecl *record = qt->getAsCXXRecordDecl(); + *castTo = record ? record->getCanonicalDecl() : nullptr; + } + } + } + return true; + } + + return false; } } diff --git a/src/SourceCompatibilityHelpers.h b/src/SourceCompatibilityHelpers.h new file mode 100644 index 00000000..41378a77 --- /dev/null +++ b/src/SourceCompatibilityHelpers.h @@ -0,0 +1,59 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + + +#ifndef SOURCE_COMPAT_HELPERS +#define SOURCE_COMPAT_HELPERS + +#include <clang/Basic/SourceLocation.h> +#include <clang/Basic/SourceManager.h> + +template <typename T> +inline clang::SourceLocation getLocStart(const T *t) +{ +#if LLVM_VERSION_MAJOR >= 8 + return t->getBeginLoc(); +#else + return t->getLocStart(); +#endif +} + +template <typename T> +inline clang::SourceLocation getLocEnd(const T *t) +{ +#if LLVM_VERSION_MAJOR >= 8 + return t->getEndLoc(); +#else + return t->getLocEnd(); +#endif +} + +inline clang::CharSourceRange getImmediateExpansionRange(clang::SourceLocation macroLoc, const clang::SourceManager &sm) +{ +#if LLVM_VERSION_MAJOR >= 7 + return sm.getImmediateExpansionRange(macroLoc); +#else + auto pair = sm.getImmediateExpansionRange(macroLoc); + return clang::CharSourceRange(clang::SourceRange(pair.first, pair.second), false); +#endif +} + +#endif diff --git a/src/StmtBodyRange.h b/src/StmtBodyRange.h index 1beb9866..ff67ef22 100644 --- a/src/StmtBodyRange.h +++ b/src/StmtBodyRange.h @@ -48,7 +48,7 @@ struct StmtBodyRange bool isOutsideRange(clang::Stmt *stmt) const { - return isOutsideRange(stmt ? stmt->getLocStart() : clang::SourceLocation()); + return isOutsideRange(stmt ? getLocStart(stmt) : clang::SourceLocation()); } bool isOutsideRange(clang::SourceLocation loc) const diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index bef60e8d..0745f24c 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -29,7 +29,7 @@ using namespace std; using namespace clang; -std::string StringUtils::simpleArgTypeName(clang::FunctionDecl *func, unsigned int index, const clang::LangOptions &lo) +std::string clazy::simpleArgTypeName(clang::FunctionDecl *func, unsigned int index, const clang::LangOptions &lo) { if (!func || index >= func->getNumParams()) return {}; @@ -38,26 +38,26 @@ std::string StringUtils::simpleArgTypeName(clang::FunctionDecl *func, unsigned i return simpleTypeName(parm, lo); } -bool StringUtils::anyArgIsOfSimpleType(clang::FunctionDecl *func, +bool clazy::anyArgIsOfSimpleType(clang::FunctionDecl *func, const std::string &simpleType, const clang::LangOptions &lo) { if (!func) return false; - return clazy_std::any_of(Utils::functionParameters(func), [simpleType,lo](ParmVarDecl *p){ + return clazy::any_of(Utils::functionParameters(func), [simpleType,lo](ParmVarDecl *p){ return simpleTypeName(p, lo) == simpleType; }); } -bool StringUtils::anyArgIsOfAnySimpleType(clang::FunctionDecl *func, +bool clazy::anyArgIsOfAnySimpleType(clang::FunctionDecl *func, const vector<string> &simpleTypes, const clang::LangOptions &lo) { if (!func) return false; - return clazy_std::any_of(simpleTypes, [func,lo](const string &simpleType) { - return StringUtils::anyArgIsOfSimpleType(func, simpleType, lo); + return clazy::any_of(simpleTypes, [func,lo](const string &simpleType) { + return clazy::anyArgIsOfSimpleType(func, simpleType, lo); }); } diff --git a/src/StringUtils.h b/src/StringUtils.h index 8aaee009..b8744f9e 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -27,7 +27,6 @@ #include "clazy_export.h" #include "Utils.h" -#include "HierarchyUtils.h" #include "clazy_stl.h" #include "clang/AST/PrettyPrinter.h" @@ -42,7 +41,7 @@ class LangOpts; } -namespace StringUtils { +namespace clazy { // Returns the class name. // The name will not include any templates, so "QVector::iterator" would be returned for QVector<int>::iterator @@ -108,20 +107,60 @@ inline std::string classNameFor(clang::ParmVarDecl *param) return classNameFor(param->getType()); } +inline llvm::StringRef name(const clang::NamedDecl *decl) +{ + if (decl->getDeclName().isIdentifier()) + return decl->getName(); + + return ""; +} + +inline llvm::StringRef name(const clang::CXXMethodDecl *method) +{ + auto op = method->getOverloadedOperator(); + if (op == clang::OO_Subscript) + return "operator[]"; + if (op == clang::OO_LessLess) + return "operator<<"; + if (op == clang::OO_PlusEqual) + return "operator+="; + + return name(static_cast<const clang::NamedDecl *>(method)); +} + +inline llvm::StringRef name(const clang::CXXConstructorDecl *decl) +{ + return name(decl->getParent()); +} + +inline llvm::StringRef name(const clang::CXXDestructorDecl *decl) +{ + return name(decl->getParent()); +} + +// Returns the type name with or without namespace, depending on how it was written by the user. +// If the user omitted the namespace then the return won't have namespace +inline std::string name(clang::QualType t, clang::LangOptions lo, bool asWritten) +{ + clang::PrintingPolicy p(lo); + p.SuppressScope = asWritten; + return t.getAsString(p); +} + template <typename T> -inline bool isOfClass(T *node, const std::string &className) +inline bool isOfClass(T *node, llvm::StringRef className) { return node && classNameFor(node) == className; } -inline bool functionIsOneOf(clang::FunctionDecl *func, const std::vector<std::string> &functionNames) +inline bool functionIsOneOf(clang::FunctionDecl *func, const std::vector<llvm::StringRef> &functionNames) { - return func && clazy_std::contains(functionNames, func->getNameAsString()); + return func && clazy::contains(functionNames, clazy::name(func)); } -inline bool classIsOneOf(clang::CXXRecordDecl *record, const std::vector<std::string> &classNames) +inline bool classIsOneOf(clang::CXXRecordDecl *record, const std::vector<llvm::StringRef> &classNames) { - return record && clazy_std::contains(classNames, record->getNameAsString()); + return record && clazy::contains(classNames, clazy::name(record)); } inline void printLocation(const clang::SourceManager &sm, clang::SourceLocation loc, bool newLine = true) @@ -141,7 +180,7 @@ inline void printRange(const clang::SourceManager &sm, clang::SourceRange range, inline void printLocation(const clang::SourceManager &sm, const clang::Stmt *s, bool newLine = true) { if (s) - printLocation(sm, s->getLocStart(), newLine); + printLocation(sm, getLocStart(s), newLine); } inline void printLocation(const clang::PresumedLoc &loc, bool newLine = true) @@ -156,7 +195,7 @@ inline std::string qualifiedMethodName(clang::FunctionDecl *func) if (!func) return {}; - clang::CXXMethodDecl *method = clang::dyn_cast<clang::CXXMethodDecl>(func); + auto method = clang::dyn_cast<clang::CXXMethodDecl>(func); if (!method) return func->getQualifiedNameAsString(); @@ -172,27 +211,6 @@ inline std::string qualifiedMethodName(clang::CallExpr *call) return call ? qualifiedMethodName(call->getDirectCallee()) : std::string(); } -inline std::string methodName(clang::CallExpr *call) -{ - if (!call) - return {}; - - clang::FunctionDecl *func = call->getDirectCallee(); - return func ? func->getNameAsString() : std::string(); -} - -inline void printParents(clang::ParentMap *map, clang::Stmt *s) -{ - int level = 0; - llvm::errs() << (s ? s->getStmtClassName() : nullptr) << "\n"; - - while (clang::Stmt *parent = HierarchyUtils::parent(map, s)) { - ++level; - llvm::errs() << std::string(level, ' ') << parent->getStmtClassName() << "\n"; - s = parent; - } -} - inline std::string accessString(clang::AccessSpecifier s) { switch (s) @@ -247,14 +265,14 @@ inline std::string returnTypeName(clang::CallExpr *call, const clang::LangOption return {}; clang::FunctionDecl *func = call->getDirectCallee(); - return func ? StringUtils::typeName(func->getReturnType(), lo, simpleName) : std::string(); + return func ? clazy::typeName(func->getReturnType(), lo, simpleName) : std::string(); } -inline bool hasArgumentOfType(clang::FunctionDecl *func, const std::string &typeName, +inline bool hasArgumentOfType(clang::FunctionDecl *func, llvm::StringRef typeName, const clang::LangOptions &lo, bool simpleName = true) { - return clazy_std::any_of(Utils::functionParameters(func), [simpleName,lo,typeName](clang::ParmVarDecl *param) { - return StringUtils::typeName(param->getType(), lo, simpleName) == typeName; + return clazy::any_of(Utils::functionParameters(func), [simpleName,lo,typeName](clang::ParmVarDecl *param) { + return clazy::typeName(param->getType(), lo, simpleName) == typeName; }); } @@ -282,8 +300,8 @@ inline void dump(const clang::SourceManager &sm, clang::Stmt *s) if (!s) return; - llvm::errs() << "Start=" << s->getLocStart().printToString(sm) - << "; end=" << s->getLocStart().printToString(sm) + llvm::errs() << "Start=" << getLocStart(s).printToString(sm) + << "; end=" << getLocStart(s).printToString(sm) << "\n"; for (auto child : s->children()) diff --git a/src/SuppressionManager.cpp b/src/SuppressionManager.cpp index eb023ddd..1d288836 100644 --- a/src/SuppressionManager.cpp +++ b/src/SuppressionManager.cpp @@ -102,7 +102,7 @@ void SuppressionManager::parseFile(FileID id, const SourceManager &sm, const cla if (token.getKind() == tok::comment) { std::string comment = Lexer::getSpelling(token, sm, lo); - if (clazy_std::contains(comment, "clazy:skip")) { + if (clazy::contains(comment, "clazy:skip")) { suppressions.skipEntireFile = true; return; } @@ -110,7 +110,7 @@ void SuppressionManager::parseFile(FileID id, const SourceManager &sm, const cla static regex rx(R"(clazy:excludeall=(.*?)(\s|$))"); smatch match; if (regex_search(comment, match, rx) && match.size() > 1) { - vector<string> checks = clazy_std::splitString(match[1], ','); + vector<string> checks = clazy::splitString(match[1], ','); suppressions.checksToSkip.insert(checks.cbegin(), checks.cend()); } @@ -123,7 +123,7 @@ void SuppressionManager::parseFile(FileID id, const SourceManager &sm, const cla static regex rx2(R"(clazy:exclude=(.*?)(\s|$))"); if (regex_search(comment, match, rx2) && match.size() > 1) { - vector<string> checks = clazy_std::splitString(match[1], ','); + vector<string> checks = clazy::splitString(match[1], ','); for (const string &checkName : checks) { suppressions.checksToSkipByLine.insert(LineAndCheckName(lineNumber, checkName)); diff --git a/src/TemplateUtils.cpp b/src/TemplateUtils.cpp index 2e5afff7..7267610f 100644 --- a/src/TemplateUtils.cpp +++ b/src/TemplateUtils.cpp @@ -42,7 +42,7 @@ static vector<QualType> typesFromTemplateArguments(const TemplateArgumentList *t return result; } -vector<QualType> TemplateUtils::getTemplateArgumentsTypes(CXXMethodDecl *method) +vector<QualType> clazy::getTemplateArgumentsTypes(CXXMethodDecl *method) { if (!method) return {}; @@ -54,7 +54,7 @@ vector<QualType> TemplateUtils::getTemplateArgumentsTypes(CXXMethodDecl *method) return typesFromTemplateArguments(specializationInfo->TemplateArguments); } -std::vector<clang::QualType> TemplateUtils::getTemplateArgumentsTypes(CXXRecordDecl *record) +std::vector<clang::QualType> clazy::getTemplateArgumentsTypes(CXXRecordDecl *record) { if (!record) return {}; @@ -66,7 +66,7 @@ std::vector<clang::QualType> TemplateUtils::getTemplateArgumentsTypes(CXXRecordD return typesFromTemplateArguments(&(templateDecl->getTemplateInstantiationArgs())); } -ClassTemplateSpecializationDecl *TemplateUtils::templateDecl(Decl *decl) +ClassTemplateSpecializationDecl *clazy::templateDecl(Decl *decl) { if (isa<ClassTemplateSpecializationDecl>(decl)) return dyn_cast<ClassTemplateSpecializationDecl>(decl); @@ -81,7 +81,7 @@ ClassTemplateSpecializationDecl *TemplateUtils::templateDecl(Decl *decl) return dyn_cast<ClassTemplateSpecializationDecl>(classDecl); } -string TemplateUtils::getTemplateArgumentTypeStr(ClassTemplateSpecializationDecl *specialization, +string clazy::getTemplateArgumentTypeStr(ClassTemplateSpecializationDecl *specialization, unsigned int index, const LangOptions &lo, bool recordOnly) { if (!specialization) @@ -98,10 +98,10 @@ string TemplateUtils::getTemplateArgumentTypeStr(ClassTemplateSpecializationDecl return {}; } - return StringUtils::simpleTypeName(args[index].getAsType(), lo); + return clazy::simpleTypeName(args[index].getAsType(), lo); } -clang::QualType TemplateUtils::getTemplateArgumentType(ClassTemplateSpecializationDecl *specialization, unsigned int index) +clang::QualType clazy::getTemplateArgumentType(ClassTemplateSpecializationDecl *specialization, unsigned int index) { if (!specialization) return {}; diff --git a/src/TemplateUtils.h b/src/TemplateUtils.h index 06adebb9..88a083fb 100644 --- a/src/TemplateUtils.h +++ b/src/TemplateUtils.h @@ -32,7 +32,7 @@ namespace clang { class Decl; } -namespace TemplateUtils +namespace clazy { /** * Returns a list of QualTypes for the template arguments. diff --git a/src/TypeUtils.cpp b/src/TypeUtils.cpp index 0dea5120..9098028b 100644 --- a/src/TypeUtils.cpp +++ b/src/TypeUtils.cpp @@ -86,7 +86,7 @@ void TypeUtils::heapOrStackAllocated(Expr *arg, const std::string &type, } std::vector<DeclRefExpr*> declrefs; - HierarchyUtils::getChilds(arg, declrefs, 3); + clazy::getChilds(arg, declrefs, 3); std::vector<DeclRefExpr*> interestingDeclRefs; for (auto declref : declrefs) { @@ -98,7 +98,7 @@ void TypeUtils::heapOrStackAllocated(Expr *arg, const std::string &type, QualType qt = t->isPointerType() ? t->getPointeeType() : declref->getType(); - if (t && type == StringUtils::simpleTypeName(qt, lo)) { + if (t && type == clazy::simpleTypeName(qt, lo)) { interestingDeclRefs.push_back(declref); } } @@ -115,7 +115,8 @@ void TypeUtils::heapOrStackAllocated(Expr *arg, const std::string &type, } } -bool TypeUtils::derivesFrom(CXXRecordDecl *derived, CXXRecordDecl *possibleBase) +bool TypeUtils::derivesFrom(const CXXRecordDecl *derived, const CXXRecordDecl *possibleBase, + std::vector<CXXRecordDecl*> *baseClasses) { if (!derived || !possibleBase || derived == possibleBase) return false; @@ -124,8 +125,11 @@ bool TypeUtils::derivesFrom(CXXRecordDecl *derived, CXXRecordDecl *possibleBase) const Type *type = base.getType().getTypePtrOrNull(); if (!type) continue; CXXRecordDecl *baseDecl = type->getAsCXXRecordDecl(); + baseDecl = baseDecl ? baseDecl->getCanonicalDecl() : nullptr; - if (possibleBase == baseDecl || derivesFrom(baseDecl, possibleBase)) { + if (possibleBase == baseDecl || derivesFrom(baseDecl, possibleBase, baseClasses)) { + if (baseClasses) + baseClasses->push_back(baseDecl); return true; } } @@ -133,7 +137,7 @@ bool TypeUtils::derivesFrom(CXXRecordDecl *derived, CXXRecordDecl *possibleBase) return false; } -bool TypeUtils::derivesFrom(clang::CXXRecordDecl *derived, const std::string &possibleBase) +bool TypeUtils::derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase) { if (!derived || !derived->hasDefinition()) return false; diff --git a/src/TypeUtils.h b/src/TypeUtils.h index b025d333..c2d9c527 100644 --- a/src/TypeUtils.h +++ b/src/TypeUtils.h @@ -124,13 +124,25 @@ namespace TypeUtils return at && at->getDeducedType().isNull(); } + inline const clang::Type * unpealAuto(clang::QualType q) + { + if (q.isNull()) + return nullptr; + + if (auto t = llvm::dyn_cast<clang::AutoType>(q.getTypePtr())) + return t->getDeducedType().getTypePtrOrNull(); + + return q.getTypePtr(); + } + /** * Returns true if childDecl is a descent from parentDecl **/ - CLAZYLIB_EXPORT bool derivesFrom(clang::CXXRecordDecl *derived, clang::CXXRecordDecl *possibleBase); + CLAZYLIB_EXPORT bool derivesFrom(const clang::CXXRecordDecl *derived, const clang::CXXRecordDecl *possibleBase, + std::vector<clang::CXXRecordDecl*> *baseClasses = nullptr); // Overload - CLAZYLIB_EXPORT bool derivesFrom(clang::CXXRecordDecl *derived, const std::string &possibleBase); + CLAZYLIB_EXPORT bool derivesFrom(const clang::CXXRecordDecl *derived, const std::string &possibleBase); // Overload CLAZYLIB_EXPORT bool derivesFrom(clang::QualType derived, const std::string &possibleBase); diff --git a/src/Utils.cpp b/src/Utils.cpp index 14fdac35..dae6a733 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -48,7 +48,7 @@ using namespace std; bool Utils::hasConstexprCtor(CXXRecordDecl *decl) { - return clazy_std::any_of(decl->ctors(), [](CXXConstructorDecl *ctor) { + return clazy::any_of(decl->ctors(), [](CXXConstructorDecl *ctor) { return ctor->isConstexpr(); }); } @@ -57,6 +57,13 @@ CXXRecordDecl * Utils::namedCastInnerDecl(CXXNamedCastExpr *staticOrDynamicCast) { Expr *e = staticOrDynamicCast->getSubExpr(); if (!e) return nullptr; + if (auto implicitCast = dyn_cast<ImplicitCastExpr>(e)) { + // Sometimes it's automatically cast to base + if (implicitCast->getCastKind() == CK_DerivedToBase) { + e = implicitCast->getSubExpr(); + } + } + QualType qt = e->getType(); const Type *t = qt.getTypePtrOrNull(); if (!t) return nullptr; @@ -89,7 +96,7 @@ bool Utils::allChildrenMemberCallsConst(Stmt *stm) return false; } - return clazy_std::all_of(stm->children(), [](Stmt *child) { + return clazy::all_of(stm->children(), [](Stmt *child) { return allChildrenMemberCallsConst(child); }); } @@ -107,24 +114,23 @@ bool Utils::childsHaveSideEffects(Stmt *stm) if (binary && (binary->isAssignmentOp() || binary->isShiftAssignOp() || binary->isCompoundAssignmentOp())) return true; - static std::vector<std::string> method_blacklist; - if (method_blacklist.empty()) { - method_blacklist.push_back("isDestroyed"); - method_blacklist.push_back("isRecursive"); // TODO: Use qualified name instead ? - method_blacklist.push_back("q_func"); - method_blacklist.push_back("d_func"); - method_blacklist.push_back("begin"); - method_blacklist.push_back("end"); - method_blacklist.push_back("data"); - method_blacklist.push_back("fragment"); - method_blacklist.push_back("glIsRenderbuffer"); - } + static const std::vector<StringRef> method_blacklist = { + "isDestroyed", + "isRecursive", // TODO: Use qualified name instead ? + "q_func", + "d_func", + "begin", + "end", + "data", + "fragment", + "glIsRenderbuffer" + }; auto memberCall = dyn_cast<MemberExpr>(stm); if (memberCall) { auto methodDecl = dyn_cast<CXXMethodDecl>(memberCall->getMemberDecl()); if (methodDecl && !methodDecl->isConst() && !methodDecl->isStatic() && - !clazy_std::contains(method_blacklist, methodDecl->getNameAsString())) + !clazy::contains(method_blacklist, clazy::name(methodDecl))) return true; } @@ -136,7 +142,7 @@ bool Utils::childsHaveSideEffects(Stmt *stm) return true; }*/ - return clazy_std::any_of(stm->children(), [](Stmt *s) { + return clazy::any_of(stm->children(), [](Stmt *s) { return childsHaveSideEffects(s); }); } @@ -182,8 +188,8 @@ ValueDecl *Utils::valueDeclForMemberCall(CXXMemberCallExpr *memberCall) } // Maybe there's an implicit cast in between.. - auto memberExprs = HierarchyUtils::getStatements<MemberExpr>(implicitObject, nullptr, {}, /**depth=*/ 1, /*includeParent=*/ true); - auto declRefs = HierarchyUtils::getStatements<DeclRefExpr>(implicitObject, nullptr, {}, /**depth=*/ 1, /*includeParent=*/ true); + auto memberExprs = clazy::getStatements<MemberExpr>(implicitObject, nullptr, {}, /**depth=*/ 1, /*includeParent=*/ true); + auto declRefs = clazy::getStatements<DeclRefExpr>(implicitObject, nullptr, {}, /**depth=*/ 1, /*includeParent=*/ true); if (!memberExprs.empty()) { return memberExprs.at(0)->getMemberDecl(); @@ -204,7 +210,7 @@ ValueDecl *Utils::valueDeclForOperatorCall(CXXOperatorCallExpr *operatorCall) // CXXOperatorCallExpr doesn't have API to access the value decl. // By inspecting several ASTs I noticed it's always in the 2nd child - Stmt *child2 = clazy_std::childAt(operatorCall, 1); + Stmt *child2 = clazy::childAt(operatorCall, 1); if (!child2) return nullptr; @@ -212,7 +218,7 @@ ValueDecl *Utils::valueDeclForOperatorCall(CXXOperatorCallExpr *operatorCall) return memberExpr->getMemberDecl(); } else { vector<DeclRefExpr*> refs; - HierarchyUtils::getChilds<DeclRefExpr>(child2, refs); + clazy::getChilds<DeclRefExpr>(child2, refs); if (refs.size() == 1) { return refs[0]->getDecl(); } @@ -255,7 +261,7 @@ bool Utils::containsNonConstMemberCall(clang::ParentMap *map, Stmt *body, const return false; std::vector<CXXMemberCallExpr*> memberCallExprs; - HierarchyUtils::getChilds<CXXMemberCallExpr>(body, memberCallExprs); + clazy::getChilds<CXXMemberCallExpr>(body, memberCallExprs); for (auto memberCall : memberCallExprs) { CXXMethodDecl *methodDecl = memberCall->getMethodDecl(); if (methodDecl && !methodDecl->isConst()) { @@ -266,7 +272,7 @@ bool Utils::containsNonConstMemberCall(clang::ParentMap *map, Stmt *body, const } std::vector<CXXOperatorCallExpr*> operatorCalls; - HierarchyUtils::getChilds<CXXOperatorCallExpr>(body, operatorCalls); + clazy::getChilds<CXXOperatorCallExpr>(body, operatorCalls); for (auto operatorCall : operatorCalls) { FunctionDecl *fDecl = operatorCall->getDirectCallee(); if (fDecl) { @@ -280,7 +286,7 @@ bool Utils::containsNonConstMemberCall(clang::ParentMap *map, Stmt *body, const } std::vector<BinaryOperator*> assignmentOperators; - HierarchyUtils::getChilds<BinaryOperator>(body, assignmentOperators); + clazy::getChilds<BinaryOperator>(body, assignmentOperators); for (auto op : assignmentOperators) { if (!op->isAssignmentOp()) continue; @@ -298,9 +304,9 @@ static bool isArgOfFunc(T expr, FunctionDecl *fDecl, const VarDecl *varDecl, boo unsigned int param = -1; for (auto arg : expr->arguments()) { ++param; - DeclRefExpr *refExpr = dyn_cast<DeclRefExpr>(arg); + auto refExpr = dyn_cast<DeclRefExpr>(arg); if (!refExpr) { - if (clazy_std::hasChildren(arg)) { + if (clazy::hasChildren(arg)) { Stmt* firstChild = *(arg->child_begin()); // Can be null (bug #362236) refExpr = firstChild ? dyn_cast<DeclRefExpr>(firstChild) : nullptr; if (!refExpr) @@ -345,7 +351,7 @@ bool Utils::isPassedToFunction(const StmtBodyRange &bodyRange, const VarDecl *va Stmt *body = bodyRange.body; std::vector<CallExpr*> callExprs; - HierarchyUtils::getChilds<CallExpr>(body, callExprs); + clazy::getChilds<CallExpr>(body, callExprs); for (CallExpr *callexpr : callExprs) { if (bodyRange.isOutsideRange(callexpr)) continue; @@ -359,7 +365,7 @@ bool Utils::isPassedToFunction(const StmtBodyRange &bodyRange, const VarDecl *va } std::vector<CXXConstructExpr*> constructExprs; - HierarchyUtils::getChilds<CXXConstructExpr>(body, constructExprs); + clazy::getChilds<CXXConstructExpr>(body, constructExprs); for (CXXConstructExpr *constructExpr : constructExprs) { if (bodyRange.isOutsideRange(constructExpr)) continue; @@ -376,12 +382,12 @@ bool Utils::addressIsTaken(const clang::CompilerInstance &ci, Stmt *body, const if (!body || !valDecl) return false; - auto unaries = HierarchyUtils::getStatements<UnaryOperator>(body); - return clazy_std::any_of(unaries, [valDecl](UnaryOperator *op) { + auto unaries = clazy::getStatements<UnaryOperator>(body); + return clazy::any_of(unaries, [valDecl](UnaryOperator *op) { if (op->getOpcode() != clang::UO_AddrOf) return false; - auto declRef = HierarchyUtils::getFirstChildOfType<DeclRefExpr>(op); + auto declRef = clazy::getFirstChildOfType<DeclRefExpr>(op); return declRef && declRef->getDecl() == valDecl; }); } @@ -392,13 +398,13 @@ bool Utils::isAssignedTo(Stmt *body, const VarDecl *varDecl) return false; std::vector<CXXOperatorCallExpr*> operatorCalls; - HierarchyUtils::getChilds<CXXOperatorCallExpr>(body, operatorCalls); + clazy::getChilds<CXXOperatorCallExpr>(body, operatorCalls); for (CXXOperatorCallExpr *operatorExpr : operatorCalls) { FunctionDecl *fDecl = operatorExpr->getDirectCallee(); if (!fDecl) continue; - CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(fDecl); + auto methodDecl = dyn_cast<CXXMethodDecl>(fDecl); if (methodDecl && methodDecl->isCopyAssignmentOperator()) { ValueDecl *valueDecl = Utils::valueDeclForOperatorCall(operatorExpr); if (valueDecl == varDecl) @@ -412,7 +418,7 @@ bool Utils::isAssignedTo(Stmt *body, const VarDecl *varDecl) bool Utils::callHasDefaultArguments(clang::CallExpr *expr) { std::vector<clang::CXXDefaultArgExpr*> exprs; - HierarchyUtils::getChilds<clang::CXXDefaultArgExpr>(expr, exprs, 1); + clazy::getChilds<clang::CXXDefaultArgExpr>(expr, exprs, 1); return !exprs.empty(); } @@ -422,7 +428,7 @@ bool Utils::containsStringLiteral(Stmt *stm, bool allowEmpty, int depth) return false; std::vector<StringLiteral*> stringLiterals; - HierarchyUtils::getChilds<StringLiteral>(stm, stringLiterals, depth); + clazy::getChilds<StringLiteral>(stm, stringLiterals, depth); if (allowEmpty) return !stringLiterals.empty(); @@ -455,26 +461,28 @@ bool Utils::ternaryOperatorIsOfStringLiteral(ConditionalOperator *ternary) return true; } -bool Utils::isAssignOperator(CXXOperatorCallExpr *op, const std::string &className, - const std::string &argumentType, const clang::LangOptions &lo) +bool Utils::isAssignOperator(CXXOperatorCallExpr *op, StringRef className, + StringRef argumentType, const clang::LangOptions &lo) { if (!op) return false; FunctionDecl *functionDecl = op->getDirectCallee(); - if (!functionDecl) + if (!functionDecl || functionDecl->param_size() != 1 ) return false; - CXXMethodDecl *methodDecl = dyn_cast<clang::CXXMethodDecl>(functionDecl); - if (!className.empty() && !StringUtils::isOfClass(methodDecl, className)) - return false; + if (!className.empty()) { + auto methodDecl = dyn_cast<clang::CXXMethodDecl>(functionDecl); + if (!clazy::isOfClass(methodDecl, className)) + return false; + } - if (functionDecl->getNameAsString() != "operator=" || functionDecl->param_size() != 1) + if (functionDecl->getNameAsString() != "operator=") return false; - if (!argumentType.empty() && !StringUtils::hasArgumentOfType(functionDecl, argumentType, lo)) { + if (!argumentType.empty() && !clazy::hasArgumentOfType(functionDecl, argumentType, lo)) return false; - } + return true; } @@ -482,21 +490,21 @@ bool Utils::isAssignOperator(CXXOperatorCallExpr *op, const std::string &classNa bool Utils::isImplicitCastTo(Stmt *s, const string &className) { - ImplicitCastExpr *expr = dyn_cast<ImplicitCastExpr>(s); + auto expr = dyn_cast<ImplicitCastExpr>(s); if (!expr) return false; auto record = expr->getBestDynamicClassType(); - return record && record->getNameAsString() == className; + return record && clazy::name(record) == className; } -bool Utils::isInsideOperatorCall(ParentMap *map, Stmt *s, const std::vector<string> &anyOf) +bool Utils::isInsideOperatorCall(ParentMap *map, Stmt *s, const std::vector<StringRef> &anyOf) { if (!s) return false; - CXXOperatorCallExpr *oper = dyn_cast<CXXOperatorCallExpr>(s); + auto oper = dyn_cast<CXXOperatorCallExpr>(s); if (oper) { auto func = oper->getDirectCallee(); if (func) { @@ -506,34 +514,34 @@ bool Utils::isInsideOperatorCall(ParentMap *map, Stmt *s, const std::vector<stri auto method = dyn_cast<CXXMethodDecl>(func); if (method) { auto record = method->getParent(); - if (record && clazy_std::contains(anyOf, record->getNameAsString())) + if (record && clazy::contains(anyOf, clazy::name(record))) return true; } } } - return isInsideOperatorCall(map, HierarchyUtils::parent(map, s), anyOf); + return isInsideOperatorCall(map, clazy::parent(map, s), anyOf); } -bool Utils::insideCTORCall(ParentMap *map, Stmt *s, const std::vector<string> &anyOf) +bool Utils::insideCTORCall(ParentMap *map, Stmt *s, const std::vector<llvm::StringRef> &anyOf) { if (!s) return false; - CXXConstructExpr *expr = dyn_cast<CXXConstructExpr>(s); - if (expr && expr->getConstructor() && clazy_std::contains(anyOf, expr->getConstructor()->getNameAsString())) { + auto expr = dyn_cast<CXXConstructExpr>(s); + if (expr && expr->getConstructor() && clazy::contains(anyOf, clazy::name(expr->getConstructor()))) { return true; } - return insideCTORCall(map, HierarchyUtils::parent(map, s), anyOf); + return insideCTORCall(map, clazy::parent(map, s), anyOf); } bool Utils::presumedLocationsEqual(const clang::PresumedLoc &l1, const clang::PresumedLoc &l2) { return l1.isValid() && l2.isValid() && l1.getColumn() == l2.getColumn() && l1.getLine() == l2.getLine() && - string(l1.getFilename()) == string(l2.getFilename()); + StringRef(l1.getFilename()) == StringRef(l2.getFilename()); } CXXRecordDecl *Utils::isMemberVariable(ValueDecl *decl) @@ -547,8 +555,8 @@ std::vector<CXXMethodDecl *> Utils::methodsFromString(const CXXRecordDecl *recor return {}; vector<CXXMethodDecl *> methods; - clazy_std::append_if(record->methods(), methods, [methodName](CXXMethodDecl *m) { - return m->getNameAsString() == methodName; + clazy::append_if(record->methods(), methods, [methodName](CXXMethodDecl *m) { + return clazy::name(m) == methodName; }); // Also include the base classes @@ -557,7 +565,7 @@ std::vector<CXXMethodDecl *> Utils::methodsFromString(const CXXRecordDecl *recor if (t) { auto baseMethods = methodsFromString(t->getAsCXXRecordDecl(), methodName); if (!baseMethods.empty()) - clazy_std::append(baseMethods, methods); + clazy::append(baseMethods, methods); } } @@ -615,9 +623,9 @@ bool Utils::isInDerefExpression(Stmt *s, ParentMap *map) Stmt *p = s; do { - p = HierarchyUtils::parent(map, p); - CXXOperatorCallExpr *op = p ? dyn_cast<CXXOperatorCallExpr>(p) : nullptr; - if (op && op->getDirectCallee() && op->getDirectCallee()->getNameAsString() == "operator*") { + p = clazy::parent(map, p); + auto op = p ? dyn_cast<CXXOperatorCallExpr>(p) : nullptr; + if (op && op->getOperator() == OO_Star) { return op; } } while (p); @@ -643,10 +651,10 @@ std::vector<CallExpr *> Utils::callListForChain(CallExpr *lastCallExpr) } if (s) { - CallExpr *callExpr = dyn_cast<CallExpr>(s); + auto callExpr = dyn_cast<CallExpr>(s); if (callExpr && callExpr->getCalleeDecl()) { callexprs.push_back(callExpr); - } else if (MemberExpr *memberExpr = dyn_cast<MemberExpr>(s)) { + } else if (auto memberExpr = dyn_cast<MemberExpr>(s)) { if (isa<FieldDecl>(memberExpr->getMemberDecl())) break; // accessing a public member via . or -> breaks the chain } else if (isa<ConditionalOperator>(s)) { @@ -703,7 +711,7 @@ bool Utils::hasMember(CXXRecordDecl *record, const string &memberTypeName) const Type *t = qt.getTypePtrOrNull(); if (t && t->getAsCXXRecordDecl()) { CXXRecordDecl *rec = t->getAsCXXRecordDecl(); - if (rec->getNameAsString() == memberTypeName) + if (clazy::name(rec) == memberTypeName) return true; } } @@ -714,7 +722,7 @@ bool Utils::hasMember(CXXRecordDecl *record, const string &memberTypeName) bool Utils::isSharedPointer(CXXRecordDecl *record) { static const vector<string> names = { "std::shared_ptr", "QSharedPointer", "boost::shared_ptr" }; - return record ? clazy_std::contains(names, record->getQualifiedNameAsString()) : false; + return record ? clazy::contains(names, record->getQualifiedNameAsString()) : false; } bool Utils::isInitializedExternally(clang::VarDecl *varDecl) @@ -723,27 +731,25 @@ bool Utils::isInitializedExternally(clang::VarDecl *varDecl) return false; DeclContext *context = varDecl->getDeclContext(); - FunctionDecl *fDecl = context ? dyn_cast<FunctionDecl>(context) : nullptr; + auto fDecl = context ? dyn_cast<FunctionDecl>(context) : nullptr; Stmt *body = fDecl ? fDecl->getBody() : nullptr; if (!body) return false; vector<DeclStmt*> declStmts; - HierarchyUtils::getChilds<DeclStmt>(body, declStmts); + clazy::getChilds<DeclStmt>(body, declStmts); for (DeclStmt *declStmt : declStmts) { if (referencesVarDecl(declStmt, varDecl)) { vector<DeclRefExpr*> declRefs; - HierarchyUtils::getChilds<DeclRefExpr>(declStmt, declRefs); - if (!declRefs.empty()) { + clazy::getChilds<DeclRefExpr>(declStmt, declRefs); + if (!declRefs.empty()) return true; - } vector<CallExpr*> callExprs; - HierarchyUtils::getChilds<CallExpr>(declStmt, callExprs); - if (!callExprs.empty()) { + clazy::getChilds<CallExpr>(declStmt, callExprs); + if (!callExprs.empty()) return true; - } } } @@ -753,7 +759,7 @@ bool Utils::isInitializedExternally(clang::VarDecl *varDecl) bool Utils::functionHasEmptyBody(clang::FunctionDecl *func) { Stmt *body = func ? func->getBody() : nullptr; - return !clazy_std::hasChildren(body); + return !clazy::hasChildren(body); } clang::Expr *Utils::isWriteOperator(Stmt *stm) @@ -761,8 +767,7 @@ clang::Expr *Utils::isWriteOperator(Stmt *stm) if (!stm) return nullptr; - if (UnaryOperator *up = dyn_cast<UnaryOperator>(stm)) { - + if (auto up = dyn_cast<UnaryOperator>(stm)) { auto opcode = up->getOpcode(); if (opcode == clang::UO_AddrOf || opcode == clang::UO_Deref) return nullptr; @@ -770,7 +775,7 @@ clang::Expr *Utils::isWriteOperator(Stmt *stm) return up->getSubExpr(); } - if (BinaryOperator *bp = dyn_cast<BinaryOperator>(stm)) + if (auto bp = dyn_cast<BinaryOperator>(stm)) return bp->getLHS(); return nullptr; @@ -784,7 +789,7 @@ bool Utils::referencesVarDecl(clang::DeclStmt *declStmt, clang::VarDecl *varDecl if (declStmt->isSingleDecl() && declStmt->getSingleDecl() == varDecl) return true; - return clazy_std::any_of(declStmt->getDeclGroup(), [varDecl](Decl *decl) { + return clazy::any_of(declStmt->getDeclGroup(), [varDecl](Decl *decl) { return varDecl == decl; }); } @@ -793,26 +798,19 @@ UserDefinedLiteral *Utils::userDefinedLiteral(Stmt *stm, const std::string &type { auto udl = dyn_cast<UserDefinedLiteral>(stm); if (!udl) - udl = HierarchyUtils::getFirstChildOfType<UserDefinedLiteral>(stm); + udl = clazy::getFirstChildOfType<UserDefinedLiteral>(stm); - if (udl && StringUtils::returnTypeName(udl, lo) == type) { + if (udl && clazy::returnTypeName(udl, lo) == type) { return udl; } return nullptr; } -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 8 -clang::FunctionDecl::param_range Utils::functionParameters(clang::FunctionDecl *func) -{ - return func->params(); -} -#else clang::ArrayRef<clang::ParmVarDecl *> Utils::functionParameters(clang::FunctionDecl *func) { return func->parameters(); } -#endif vector<CXXCtorInitializer *> Utils::ctorInitializer(CXXConstructorDecl *ctor, clang::ParmVarDecl *param) { @@ -824,7 +822,7 @@ vector<CXXCtorInitializer *> Utils::ctorInitializer(CXXConstructorDecl *ctor, cl for (auto it = ctor->init_begin(), end = ctor->init_end(); it != end; ++it) { auto ctorInit = *it; vector<DeclRefExpr*> declRefs; - HierarchyUtils::getChilds(ctorInit->getInit(), declRefs); + clazy::getChilds(ctorInit->getInit(), declRefs); for (auto declRef : declRefs) { if (declRef->getDecl() == param) { result.push_back(ctorInit); @@ -842,7 +840,7 @@ bool Utils::ctorInitializerContainsMove(CXXCtorInitializer *init) return false; vector<CallExpr*> calls; - HierarchyUtils::getChilds(init->getInit(), calls); + clazy::getChilds(init->getInit(), calls); for (auto call : calls) { if (FunctionDecl *funcDecl = call->getDirectCallee()) { @@ -857,7 +855,7 @@ bool Utils::ctorInitializerContainsMove(CXXCtorInitializer *init) bool Utils::ctorInitializerContainsMove(const vector<CXXCtorInitializer*> &ctorInits) { - return clazy_std::any_of(ctorInits, [](CXXCtorInitializer *ctorInit) { + return clazy::any_of(ctorInits, [](CXXCtorInitializer *ctorInit) { return Utils::ctorInitializerContainsMove(ctorInit); }); } @@ -865,7 +863,7 @@ bool Utils::ctorInitializerContainsMove(const vector<CXXCtorInitializer*> &ctorI string Utils::filenameForLoc(SourceLocation loc, const clang::SourceManager &sm) { string filename = sm.getFilename(loc); - auto splitted = clazy_std::splitString(filename, '/'); + auto splitted = clazy::splitString(filename, '/'); if (splitted.empty()) return {}; diff --git a/src/Utils.h b/src/Utils.h index 49dbc8a1..dd92a961 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -26,7 +26,9 @@ #define MOREWARNINGS_UTILS_H #include "clazy_export.h" +#include "SourceCompatibilityHelpers.h" +#include <clang/Basic/SourceManager.h> #include <clang/AST/DeclCXX.h> #include <clang/AST/Expr.h> #include <clang/AST/ExprCXX.h> @@ -127,18 +129,18 @@ namespace Utils { CLAZYLIB_EXPORT bool containsStringLiteral(clang::Stmt *, bool allowEmpty = true, int depth = -1); CLAZYLIB_EXPORT bool isInsideOperatorCall(clang::ParentMap *map, clang::Stmt *s, - const std::vector<std::string> &anyOf); + const std::vector<llvm::StringRef> &anyOf); CLAZYLIB_EXPORT bool insideCTORCall(clang::ParentMap *map, clang::Stmt *s, - const std::vector<std::string> &anyOf); + const std::vector<llvm::StringRef> &anyOf); // returns true if the ternary operator has two string literal arguments, such as: // foo ? "bar" : "baz" CLAZYLIB_EXPORT bool ternaryOperatorIsOfStringLiteral(clang::ConditionalOperator*); CLAZYLIB_EXPORT bool isAssignOperator(clang::CXXOperatorCallExpr *op, - const std::string &className, - const std::string &argumentType, const clang::LangOptions &lo); + llvm::StringRef className, + llvm::StringRef argumentType, const clang::LangOptions &lo); CLAZYLIB_EXPORT bool isImplicitCastTo(clang::Stmt *, const std::string &); @@ -274,10 +276,26 @@ namespace Utils { const clang::SourceManager &sm, const clang::LangOptions &lo); + inline bool isMainFile(const clang::SourceManager &sm, clang::SourceLocation loc) + { + if (loc.isMacroID()) + loc = sm.getExpansionLoc(loc); + + return sm.isInFileID(loc, sm.getMainFileID()); + } + /** * Returns true if the string literal contains escaped bytes, such as \x12, \123, \u00F6. */ bool literalContainsEscapedBytes(clang::StringLiteral *lt, const clang::SourceManager &sm, const clang::LangOptions &lo); + + /** + * Returns true if this method overrides one from the base class + */ + inline bool methodOverrides(clang::CXXMethodDecl *method) + { + return method && method->isVirtual() && method->size_overridden_methods() > 0; + } } #endif diff --git a/src/checkbase.cpp b/src/checkbase.cpp index bafebb8c..f423ec83 100644 --- a/src/checkbase.cpp +++ b/src/checkbase.cpp @@ -44,10 +44,10 @@ ClazyPreprocessorCallbacks::ClazyPreprocessorCallbacks(CheckBase *check) { } -void ClazyPreprocessorCallbacks::MacroExpands(const Token ¯oNameTok, const MacroDefinition &, +void ClazyPreprocessorCallbacks::MacroExpands(const Token ¯oNameTok, const MacroDefinition &md, SourceRange range, const MacroArgs *) { - check->VisitMacroExpands(macroNameTok, range); + check->VisitMacroExpands(macroNameTok, range, md.getMacroInfo()); } void ClazyPreprocessorCallbacks::Defined(const Token ¯oNameTok, const MacroDefinition &, SourceRange range) @@ -65,14 +65,14 @@ void ClazyPreprocessorCallbacks::MacroDefined(const Token ¯oNameTok, const M check->VisitMacroDefined(macroNameTok); } -CheckBase::CheckBase(const string &name, const ClazyContext *context, Options) +CheckBase::CheckBase(const string &name, const ClazyContext *context, Options options) : m_sm(context->ci.getSourceManager()) , m_name(name) , m_context(context) , m_astContext(context->astContext) - , m_preprocessorOpts(context->ci.getPreprocessorOpts()) - , m_tu(m_astContext.getTranslationUnitDecl()) , m_preprocessorCallbacks(new ClazyPreprocessorCallbacks(this)) + , m_options(options) + , m_tag(" [-Wclazy-" + m_name + ']') { } @@ -80,21 +80,6 @@ CheckBase::~CheckBase() { } -void CheckBase::VisitStatement(Stmt *stm) -{ - m_lastStmt = stm; - VisitStmt(stm); -} - -void CheckBase::VisitDeclaration(Decl *decl) -{ - m_lastDecl = decl; - if (auto mdecl = dyn_cast<CXXMethodDecl>(decl)) - m_lastMethodDecl = mdecl; - - VisitDecl(decl); -} - void CheckBase::VisitStmt(Stmt *) { // Overriden in derived classes @@ -105,7 +90,7 @@ void CheckBase::VisitDecl(Decl *) // Overriden in derived classes } -void CheckBase::VisitMacroExpands(const Token &, const SourceRange &) +void CheckBase::VisitMacroExpands(const Token &, const SourceRange &, const clang::MacroInfo *) { // Overriden in derived classes } @@ -141,19 +126,19 @@ bool CheckBase::shouldIgnoreFile(SourceLocation loc) const string filename = sm().getFilename(loc); - return clazy_std::any_of(m_filesToIgnore, [filename](const std::string &ignored) { - return clazy_std::contains(filename, ignored); + return clazy::any_of(m_filesToIgnore, [filename](const std::string &ignored) { + return clazy::contains(filename, ignored); }); } void CheckBase::emitWarning(const clang::Decl *d, const std::string &error, bool printWarningTag) { - emitWarning(d->getLocStart(), error, printWarningTag); + emitWarning(getLocStart(d), error, printWarningTag); } void CheckBase::emitWarning(const clang::Stmt *s, const std::string &error, bool printWarningTag) { - emitWarning(s->getLocStart(), error, printWarningTag); + emitWarning(getLocStart(s), error, printWarningTag); } void CheckBase::emitWarning(clang::SourceLocation loc, const std::string &error, bool printWarningTag) @@ -167,15 +152,17 @@ void CheckBase::emitWarning(clang::SourceLocation loc, std::string error, if (m_context->suppressionManager.isSuppressed(m_name, loc, sm(), lo())) return; + if (m_context->shouldIgnoreFile(loc)) + return; + if (loc.isMacroID()) { if (warningAlreadyEmitted(loc)) return; // For warnings in macro arguments we get a warning in each place the argument is used within the expanded macro, so filter all the dups m_emittedWarningsInMacro.push_back(loc.getRawEncoding()); } - const string tag = " [-Wclazy-" + name() + ']'; if (printWarningTag) - error += tag; + error += m_tag; reallyEmitWarning(loc, error, fixits); @@ -184,7 +171,7 @@ void CheckBase::emitWarning(clang::SourceLocation loc, std::string error, if (!l.second.empty()) msg += ' ' + l.second; - reallyEmitWarning(l.first, msg + tag, {}); + reallyEmitWarning(l.first, msg + m_tag, {}); } m_queuedManualInterventionWarnings.clear(); @@ -192,8 +179,7 @@ void CheckBase::emitWarning(clang::SourceLocation loc, std::string error, void CheckBase::emitInternalError(SourceLocation loc, string error) { - const string tag = " [-Wclazy-" + name() + ']'; - llvm::errs() << tag << ": internal error: " << error + llvm::errs() << m_tag << ": internal error: " << error << " at " << loc.printToString(sm()) << "\n"; } @@ -211,7 +197,7 @@ void CheckBase::reallyEmitWarning(clang::SourceLocation loc, const std::string & } } -void CheckBase::queueManualFixitWarning(clang::SourceLocation loc, int fixitType, const string &message) +void CheckBase::queueManualFixitWarning(clang::SourceLocation loc, const string &message, int fixitType) { if (isFixitEnabled(fixitType) && !manualFixitAlreadyQueued(loc)) { m_queuedManualInterventionWarnings.push_back({loc, message}); @@ -245,11 +231,6 @@ bool CheckBase::manualFixitAlreadyQueued(SourceLocation loc) const return false; } -std::vector<string> CheckBase::supportedOptions() const -{ - return {}; -} - bool CheckBase::isOptionSet(const std::string &optionName) const { const string qualifiedName = name() + '-' + optionName; @@ -266,6 +247,12 @@ bool CheckBase::isFixitEnabled(int fixit) const return (m_enabledFixits & fixit) || (m_context->options & ClazyContext::ClazyOption_AllFixitsEnabled); } +bool CheckBase::isFixitEnabled() const +{ + // Checks with only 1 fixit (which is most of them) don't need to pass fixit id + return isFixitEnabled(1); +} + ClazyAstMatcherCallback::ClazyAstMatcherCallback(CheckBase *check) : MatchFinder::MatchCallback() , m_check(check) diff --git a/src/checkbase.h b/src/checkbase.h index d6d84698..463667a3 100644 --- a/src/checkbase.h +++ b/src/checkbase.h @@ -28,6 +28,7 @@ #include "clazy_export.h" #include "clazy_stl.h" +#include "SourceCompatibilityHelpers.h" #include <clang/Basic/SourceManager.h> #include <clang/Frontend/CompilerInstance.h> @@ -53,13 +54,13 @@ class PreprocessorOptions; class CheckBase; class ClazyContext; -enum CheckLevel { +enum CheckLevel { // See README.md for what each level does CheckLevelUndefined = -1, - CheckLevel0 = 0, // 100% safe, no false-positives, very useful - CheckLevel1, // Similar to CheckLevel0, but sometimes (rarely) there might be some false positive - CheckLevel2, // Sometimes has false-positives (20-30%) - CheckLevel3 = 3, // Not always correct, possibly very noisy, requires a knowledgeable developer to review, might have a very big rate of false-positives - HiddenCheckLevel, // The check is hidden and must be explicitly enabled + CheckLevel0 = 0, + CheckLevel1, + CheckLevel2, + CheckLevel3 = 3, + ManualCheckLevel, MaxCheckLevel = CheckLevel3, DefaultCheckLevel = CheckLevel1 }; @@ -93,23 +94,23 @@ class CLAZYLIB_EXPORT CheckBase public: enum Option { - Option_None = 0 + Option_None = 0, + Option_CanIgnoreIncludes = 1 }; typedef int Options; typedef std::vector<CheckBase*> List; - explicit CheckBase(const std::string &name, const ClazyContext *context, Options = Option_None); + explicit CheckBase(const std::string &name, const ClazyContext *context, + Options = Option_None); CheckBase(const CheckBase &other) = delete; virtual ~CheckBase(); - void VisitStatement(clang::Stmt *stm); - void VisitDeclaration(clang::Decl *stm); - std::string name() const { return m_name; } void setEnabledFixits(int); bool isFixitEnabled(int fixit) const; + bool isFixitEnabled() const; void emitWarning(const clang::Decl *, const std::string &error, bool printWarningTag = true); void emitWarning(const clang::Stmt *, const std::string &error, bool printWarningTag = true); @@ -119,10 +120,15 @@ public: virtual void registerASTMatchers(clang::ast_matchers::MatchFinder &) {}; -protected: + bool canIgnoreIncludes() const + { + return m_options & Option_CanIgnoreIncludes; + } + virtual void VisitStmt(clang::Stmt *stm); virtual void VisitDecl(clang::Decl *decl); - virtual void VisitMacroExpands(const clang::Token ¯oNameTok, const clang::SourceRange &); +protected: + virtual void VisitMacroExpands(const clang::Token ¯oNameTok, const clang::SourceRange &, const clang::MacroInfo *minfo = nullptr); virtual void VisitMacroDefined(const clang::Token ¯oNameTok); virtual void VisitDefined(const clang::Token ¯oNameTok, const clang::SourceRange &); virtual void VisitIfdef(clang::SourceLocation, const clang::Token ¯oNameTok); @@ -133,10 +139,9 @@ protected: bool shouldIgnoreFile(clang::SourceLocation) const; void reallyEmitWarning(clang::SourceLocation loc, const std::string &error, const std::vector<clang::FixItHint> &fixits); - void queueManualFixitWarning(clang::SourceLocation loc, int fixitType, const std::string &message = {}); + void queueManualFixitWarning(clang::SourceLocation loc, const std::string &message = {}, int fixitType = 1); bool warningAlreadyEmitted(clang::SourceLocation loc) const; bool manualFixitAlreadyQueued(clang::SourceLocation loc) const; - virtual std::vector<std::string> supportedOptions() const; bool isOptionSet(const std::string &optionName) const; // 3 shortcuts for stuff that litter the codebase all over. @@ -147,12 +152,6 @@ protected: const std::string m_name; const ClazyContext *const m_context; clang::ASTContext &m_astContext; - const clang::PreprocessorOptions &m_preprocessorOpts; - clang::TranslationUnitDecl *const m_tu; - - clang::CXXMethodDecl *m_lastMethodDecl = nullptr; - clang::Decl *m_lastDecl = nullptr; - clang::Stmt *m_lastStmt = nullptr; std::vector<std::string> m_filesToIgnore; private: friend class ClazyPreprocessorCallbacks; @@ -162,6 +161,8 @@ private: std::vector<unsigned int> m_emittedManualFixItsWarningsInMacro; std::vector<std::pair<clang::SourceLocation, std::string>> m_queuedManualInterventionWarnings; int m_enabledFixits = 0; + const Options m_options; + const std::string m_tag; }; #endif diff --git a/src/checkmanager.cpp b/src/checkmanager.cpp index 534e6371..40e9a15c 100644 --- a/src/checkmanager.cpp +++ b/src/checkmanager.cpp @@ -27,6 +27,7 @@ #include "ClazyContext.h" #include "Utils.h" #include "StringUtils.h" +#include "Checks.h" #include <stdlib.h> @@ -41,6 +42,7 @@ std::mutex CheckManager::m_lock; CheckManager::CheckManager() { m_registeredChecks.reserve(100); + registerChecks(); } bool CheckManager::checkExists(const string &name) const @@ -48,56 +50,22 @@ bool CheckManager::checkExists(const string &name) const return checkForName(m_registeredChecks, name) != m_registeredChecks.cend(); } -bool CheckManager::isReservedCheckName(const string &name) const -{ - static const vector<string> names = { "clazy" }; - if (clazy_std::contains(names, name)) - return true; - - // level0, level1, etc are not allowed - if (clazy_std::startsWith(name, s_levelPrefix)) - return true; - - // These are fixit names - if (clazy_std::startsWith(name, s_fixitNamePrefix)) - return true; - - return false; -} - CheckManager *CheckManager::instance() { static CheckManager s_instance; return &s_instance; } -int CheckManager::registerCheck(const std::string &name, const string &className, - CheckLevel level, const FactoryFunction &factory, - RegisteredCheck::Options options) +void CheckManager::registerCheck(const RegisteredCheck &check) { - assert(factory != nullptr); - assert(!name.empty()); - - if (isReservedCheckName(name)) { - llvm::errs() << "Check name not allowed" << name; - assert(false); - } else { - m_registeredChecks.push_back({name, className, level, factory, options}); - } - - return 0; + m_registeredChecks.push_back(check); } -int CheckManager::registerFixIt(int id, const string &fixitName, const string &checkName) +void CheckManager::registerFixIt(int id, const string &fixitName, const string &checkName) { - if (fixitName.empty() || checkName.empty()) { + if (!clazy::startsWith(fixitName, s_fixitNamePrefix)) { assert(false); - return 0; - } - - if (!clazy_std::startsWith(fixitName, s_fixitNamePrefix)) { - assert(false); - return 0; + return; } auto &fixits = m_fixitsByCheckName[checkName]; @@ -105,14 +73,12 @@ int CheckManager::registerFixIt(int id, const string &fixitName, const string &c if (fixit.name == fixitName) { // It can't exist assert(false); - return 0; + return; } } RegisteredFixIt fixit = {id, fixitName}; fixits.push_back(fixit); m_fixitByName.insert({fixitName, fixit}); - - return 0; } CheckBase* CheckManager::createCheck(const string &name, ClazyContext *context) @@ -169,7 +135,7 @@ RegisteredCheck::List CheckManager::requestedChecksThroughEnv(const ClazyContext if (requestedChecksThroughEnv.empty()) { const char *checksEnv = getenv("CLAZY_CHECKS"); if (checksEnv) { - const string checksEnvStr = clazy_std::unquoteString(checksEnv); + const string checksEnvStr = clazy::unquoteString(checksEnv); requestedChecksThroughEnv = checksEnvStr == "all_checks" ? availableChecks(CheckLevel2) : checksForCommaSeparatedString(checksEnvStr, /*by-ref=*/ userDisabledChecks); } @@ -186,7 +152,7 @@ RegisteredCheck::List CheckManager::requestedChecksThroughEnv(const ClazyContext RegisteredCheck::List::const_iterator CheckManager::checkForName(const RegisteredCheck::List &checks, const string &name) const { - return clazy_std::find_if(checks, [name](const RegisteredCheck &r) { + return clazy::find_if(checks, [name](const RegisteredCheck &r) { return r.name == name; } ); } @@ -199,7 +165,7 @@ RegisteredFixIt::List CheckManager::availableFixIts(const string &checkName) con static bool takeArgument(const string &arg, vector<string> &args) { - auto it = clazy_std::find(args, arg); + auto it = clazy::find(args, arg); if (it != args.end()) { args.erase(it, it + 1); return true; @@ -246,8 +212,8 @@ RegisteredCheck::List CheckManager::requestedChecks(const ClazyContext *context, // #4 Add checks from requested level RegisteredCheck::List checksFromRequestedLevel = checksForLevel(requestedLevel); - clazy_std::append(checksFromRequestedLevel, result); - clazy_std::sort_and_remove_dups(result, checkLessThan); + clazy::append(checksFromRequestedLevel, result); + clazy::sort_and_remove_dups(result, checkLessThan); CheckManager::removeChecksFromList(result, userDisabledChecks); if (context->options & ClazyContext::ClazyOption_Qt4Compat) { @@ -264,7 +230,7 @@ RegisteredCheck::List CheckManager::checksForLevel(int level) const { RegisteredCheck::List result; if (level > CheckLevelUndefined && level <= MaxCheckLevel) { - clazy_std::append_if(m_registeredChecks, result, [level](const RegisteredCheck &r) { + clazy::append_if(m_registeredChecks, result, [level](const RegisteredCheck &r) { return r.level <= level; }); } @@ -272,19 +238,19 @@ RegisteredCheck::List CheckManager::checksForLevel(int level) const return result; } -CheckBase::List CheckManager::createChecks(const RegisteredCheck::List &requestedChecks, - ClazyContext *context) +std::vector<std::pair<CheckBase*, RegisteredCheck>> CheckManager::createChecks(const RegisteredCheck::List &requestedChecks, + ClazyContext *context) { assert(context); const string fixitCheckName = checkNameForFixIt(context->requestedFixitName); RegisteredFixIt fixit = m_fixitByName[context->requestedFixitName]; - CheckBase::List checks; + std::vector<std::pair<CheckBase*, RegisteredCheck>> checks; checks.reserve(requestedChecks.size() + 1); for (const auto& check : requestedChecks) { - checks.push_back(createCheck(check.name, context)); + checks.push_back({createCheck(check.name, context), check }); if (check.name == fixitCheckName) { - checks.back()->setEnabledFixits(fixit.id); + checks.back().first->setEnabledFixits(fixit.id); } } @@ -292,8 +258,8 @@ CheckBase::List CheckManager::createChecks(const RegisteredCheck::List &requeste // We have one fixit enabled, we better have the check instance too. if (!fixitCheckName.empty()) { if (checkForName(requestedChecks, fixitCheckName) == requestedChecks.cend()) { - checks.push_back(createCheck(fixitCheckName, context)); - checks.back()->setEnabledFixits(fixit.id); + checks.push_back({createCheck(fixitCheckName, context), {} }); + checks.back().first->setEnabledFixits(fixit.id); } } } @@ -320,7 +286,7 @@ RegisteredCheck::List CheckManager::checksForCommaSeparatedString(const string & RegisteredCheck::List CheckManager::checksForCommaSeparatedString(const string &str, vector<string> &userDisabledChecks) const { - vector<string> checkNames = clazy_std::splitString(str, ','); + vector<string> checkNames = clazy::splitString(str, ','); RegisteredCheck::List result; for (const string &name : checkNames) { @@ -334,17 +300,17 @@ RegisteredCheck::List CheckManager::checksForCommaSeparatedString(const string & auto it = checkForName(m_registeredChecks, checkName); const bool checkDoesntExist = it == m_registeredChecks.cend(); if (checkDoesntExist) { - if (clazy_std::startsWith(name, s_levelPrefix) && name.size() == strlen(s_levelPrefix) + 1) { + if (clazy::startsWith(name, s_levelPrefix) && name.size() == strlen(s_levelPrefix) + 1) { auto lastChar = name.back(); const int digit = lastChar - '0'; if (digit > CheckLevelUndefined && digit <= MaxCheckLevel) { RegisteredCheck::List levelChecks = checksForLevel(digit); - clazy_std::append(levelChecks, result); + clazy::append(levelChecks, result); } else { llvm::errs() << "Invalid level: " << name << "\n"; } } else { - if (clazy_std::startsWith(name, "no-")) { + if (clazy::startsWith(name, "no-")) { string checkName = name; checkName.erase(0, 3); if (checkExists(checkName)) { diff --git a/src/checkmanager.h b/src/checkmanager.h index 4640cfce..199bf5fb 100644 --- a/src/checkmanager.h +++ b/src/checkmanager.h @@ -33,6 +33,8 @@ #include <functional> #include <mutex> #include <unordered_map> +#include <vector> +#include <utility> struct CLAZYLIB_EXPORT RegisteredFixIt { typedef std::vector<RegisteredFixIt> List; @@ -48,14 +50,15 @@ using FactoryFunction = std::function<CheckBase*(ClazyContext *context)>; struct CLAZYLIB_EXPORT RegisteredCheck { enum Option { Option_None = 0, - Option_Qt4Incompatible + Option_Qt4Incompatible = 1, + Option_VisitsStmts = 2, + Option_VisitsDecls = 4 }; typedef std::vector<RegisteredCheck> List; typedef int Options; std::string name; - std::string className; CheckLevel level; FactoryFunction factory; Options options; @@ -86,11 +89,6 @@ public: static CheckManager *instance(); static std::mutex &lock() { return m_lock; } - - int registerCheck(const std::string &name, const std::string &className, - CheckLevel level, const FactoryFunction &, RegisteredCheck::Options = RegisteredCheck::Option_None); - int registerFixIt(int id, const std::string &fititName, const std::string &checkName); - RegisteredCheck::List availableChecks(CheckLevel maxLevel) const; RegisteredCheck::List requestedChecksThroughEnv(const ClazyContext *context) const; RegisteredCheck::List requestedChecksThroughEnv(const ClazyContext *context, std::vector<std::string> &userDisabledChecks) const; @@ -107,7 +105,7 @@ public: * This is a union of the requested checks via env variable and via arguments passed to compiler */ RegisteredCheck::List requestedChecks(const ClazyContext *context, std::vector<std::string> &args); - CheckBase::List createChecks(const RegisteredCheck::List &requestedChecks, ClazyContext *context); + std::vector<std::pair<CheckBase*, RegisteredCheck>> createChecks(const RegisteredCheck::List &requestedChecks, ClazyContext *context); static void removeChecksFromList(RegisteredCheck::List &list, std::vector<std::string> &checkNames); @@ -115,9 +113,11 @@ private: CheckManager(); static std::mutex m_lock; + void registerChecks(); + void registerFixIt(int id, const std::string &fititName, const std::string &checkName); + void registerCheck(const RegisteredCheck &check); bool checkExists(const std::string &name) const; RegisteredCheck::List checksForLevel(int level) const; - bool isReservedCheckName(const std::string &name) const; CheckBase* createCheck(const std::string &name, ClazyContext *context); std::string checkNameForFixIt(const std::string &) const; RegisteredCheck::List m_registeredChecks; @@ -125,17 +125,4 @@ private: std::unordered_map<std::string, RegisteredFixIt > m_fixitByName; }; -#define CLAZY_STRINGIFY2(X) #X -#define CLAZY_STRINGIFY(X) CLAZY_STRINGIFY2(X) - -#define REGISTER_CHECK_WITH_FLAGS(CHECK_NAME, CLASS_NAME, LEVEL, OPTIONS) \ - volatile int ClazyAnchor_##CLASS_NAME = CheckManager::instance()->registerCheck(CHECK_NAME, CLAZY_STRINGIFY(CLASS_NAME), LEVEL, [](ClazyContext *context){ return new CLASS_NAME(CHECK_NAME, context); }, OPTIONS); - -#define REGISTER_CHECK(CHECK_NAME, CLASS_NAME, LEVEL) \ - volatile int ClazyAnchor_##CLASS_NAME = CheckManager::instance()->registerCheck(CHECK_NAME, CLAZY_STRINGIFY(CLASS_NAME), LEVEL, [](ClazyContext *context){ return new CLASS_NAME(CHECK_NAME, context); }); - -#define REGISTER_FIXIT(FIXIT_ID, FIXIT_NAME, CHECK_NAME) \ - static int dummy_##FIXIT_ID = CheckManager::instance()->registerFixIt(FIXIT_ID, FIXIT_NAME, CHECK_NAME); \ - inline void silence_warning_dummy_##FIXIT_ID() { (void)dummy_##FIXIT_ID; } - #endif diff --git a/src/checks/detachingbase.cpp b/src/checks/detachingbase.cpp index eff470d9..aaa172f4 100644 --- a/src/checks/detachingbase.cpp +++ b/src/checks/detachingbase.cpp @@ -22,7 +22,6 @@ Boston, MA 02110-1301, USA. */ -#include "checkmanager.h" #include "detachingbase.h" #include "Utils.h" #include "StringUtils.h" @@ -35,12 +34,12 @@ using namespace clang; using namespace std; -DetachingBase::DetachingBase(const std::string &name, ClazyContext *context) - : CheckBase(name, context) +DetachingBase::DetachingBase(const std::string &name, ClazyContext *context, Options options) + : CheckBase(name, context, options) { } -bool DetachingBase::isDetachingMethod(CXXMethodDecl *method) const +bool DetachingBase::isDetachingMethod(CXXMethodDecl *method, DetachingMethodType detachingMethodType) const { if (!method) return false; @@ -49,13 +48,14 @@ bool DetachingBase::isDetachingMethod(CXXMethodDecl *method) const if (!record) return false; - const string className = record->getNameAsString(); + StringRef className = clazy::name(record); - const std::unordered_map<string, std::vector<string> > &methodsByType = QtUtils::detachingMethods(); + const std::unordered_map<string, std::vector<StringRef> > &methodsByType = detachingMethodType == DetachingMethod ? clazy::detachingMethods() + : clazy::detachingMethodsWithConstCounterParts(); auto it = methodsByType.find(className); if (it != methodsByType.cend()) { const auto &methods = it->second; - if (clazy_std::contains(methods, method->getNameAsString())) + if (clazy::contains(methods, clazy::name(method))) return true; } diff --git a/src/checks/detachingbase.h b/src/checks/detachingbase.h index 2963ea85..10da3586 100644 --- a/src/checks/detachingbase.h +++ b/src/checks/detachingbase.h @@ -41,9 +41,15 @@ class CXXMethodDecl; class DetachingBase : public CheckBase { public: - explicit DetachingBase(const std::string &name, ClazyContext *context); + explicit DetachingBase(const std::string &name, ClazyContext *context, Options = Option_None); protected: - bool isDetachingMethod(clang::CXXMethodDecl *) const; + + enum DetachingMethodType { + DetachingMethod, + DetachingMethodWithConstCounterPart + }; + + bool isDetachingMethod(clang::CXXMethodDecl *, DetachingMethodType = DetachingMethod) const; }; #endif diff --git a/src/checks/inefficientqlistbase.cpp b/src/checks/inefficientqlistbase.cpp index c4d10d17..1e446e1f 100644 --- a/src/checks/inefficientqlistbase.cpp +++ b/src/checks/inefficientqlistbase.cpp @@ -28,7 +28,6 @@ #include "ContextUtils.h" #include "HierarchyUtils.h" #include "TemplateUtils.h" -#include "checkmanager.h" #include "StmtBodyRange.h" #include <clang/AST/Decl.h> @@ -50,7 +49,7 @@ bool InefficientQListBase::shouldIgnoreVariable(VarDecl *varDecl) const DeclContext *context = varDecl->getDeclContext(); FunctionDecl *fDecl = context ? dyn_cast<FunctionDecl>(context) : nullptr; - if ((m_ignoreMode & IgnoreNonLocalVariable) && !ContextUtils::isValueDeclInFunctionContext(varDecl)) { + if ((m_ignoreMode & IgnoreNonLocalVariable) && !clazy::isValueDeclInFunctionContext(varDecl)) { return true; } @@ -85,14 +84,11 @@ void InefficientQListBase::VisitDecl(clang::Decl *decl) if (!t) return; - if (shouldIgnoreVariable(varDecl)) - return; - CXXRecordDecl *recordDecl = t->getAsCXXRecordDecl(); - if (!recordDecl || recordDecl->getNameAsString() != "QList") + if (!recordDecl || recordDecl->getName() != "QList") return; - const std::vector<clang::QualType> types = TemplateUtils::getTemplateArgumentsTypes(recordDecl); + const std::vector<clang::QualType> types = clazy::getTemplateArgumentsTypes(recordDecl); if (types.empty()) return; QualType qt2 = types[0]; @@ -102,8 +98,8 @@ void InefficientQListBase::VisitDecl(clang::Decl *decl) const int size_of_ptr = TypeUtils::sizeOfPointer(&m_astContext, qt2); // in bits const int size_of_T = m_astContext.getTypeSize(qt2); - if (size_of_T > size_of_ptr) { + if (size_of_T > size_of_ptr && !shouldIgnoreVariable(varDecl)) { string s = string("Use QVector instead of QList for type with size " + to_string(size_of_T / 8) + " bytes"); - emitWarning(decl->getLocStart(), s.c_str()); + emitWarning(getLocStart(decl), s.c_str()); } } diff --git a/src/checks/level0/README-unused-non-trivial-variable.md b/src/checks/level0/README-unused-non-trivial-variable.md deleted file mode 100644 index 66d74553..00000000 --- a/src/checks/level0/README-unused-non-trivial-variable.md +++ /dev/null @@ -1,8 +0,0 @@ -# unused-non-trivial-variable - - Warns about unused Qt value classes. - Compilers usually only warn when trivial classes are unused and don't emit warnings for non-trivial classes. - - This check has a whitelist of common Qt classes such as containers, `QFont`, `QUrl`, etc and warns for those too. - - See `UnusedNonTrivialType::isInterestingType(QualType t)` for a list of all types. diff --git a/src/checks/level0/connect-by-name.cpp b/src/checks/level0/connect-by-name.cpp new file mode 100644 index 00000000..e830d823 --- /dev/null +++ b/src/checks/level0/connect-by-name.cpp @@ -0,0 +1,64 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2017 Sergio Martins <smartins@kde.org> + + 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 "connect-by-name.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" +#include "ClazyContext.h" +#include "AccessSpecifierManager.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +ConnectByName::ConnectByName(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ + context->enableAccessSpecifierManager(); +} + +void ConnectByName::VisitDecl(clang::Decl *decl) +{ + auto record = dyn_cast<CXXRecordDecl>(decl); + if (!record) + return; + + AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager; + if (!accessSpecifierManager) + return; + + for (auto method : record->methods()) { + std::string name = method->getNameAsString(); + if (clazy::startsWith(name, "on_")) { + QtAccessSpecifierType qst = accessSpecifierManager->qtAccessSpecifierType(method); + if (qst == QtAccessSpecifier_Slot) { + auto tokens = clazy::splitString(name, '_'); + if (tokens.size() == 3) { + emitWarning(method, "Slots named on_foo_bar are error prone"); + } + } + } + } +} diff --git a/src/checks/level0/connect-by-name.h b/src/checks/level0/connect-by-name.h new file mode 100644 index 00000000..3a46095f --- /dev/null +++ b/src/checks/level0/connect-by-name.h @@ -0,0 +1,38 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2017 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_CONNECT_BY_NAME_H +#define CLAZY_CONNECT_BY_NAME_H + +#include "checkbase.h" + + +/** + * See README-connect-by-name.md for more info. + */ +class ConnectByName : public CheckBase +{ +public: + explicit ConnectByName(const std::string &name, ClazyContext *context); + void VisitDecl(clang::Decl *decl) override; +}; + +#endif diff --git a/src/checks/level0/connect-non-signal.cpp b/src/checks/level0/connect-non-signal.cpp index 19eea4c3..885e618d 100644 --- a/src/checks/level0/connect-non-signal.cpp +++ b/src/checks/level0/connect-non-signal.cpp @@ -21,14 +21,12 @@ */ #include "connect-non-signal.h" - #include "ClazyContext.h" #include "AccessSpecifierManager.h" #include "Utils.h" #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "NormalizedSignatureUtils.h" #include <clang/AST/AST.h> @@ -38,7 +36,7 @@ using namespace std; ConnectNonSignal::ConnectNonSignal(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { context->enableAccessSpecifierManager(); } @@ -46,23 +44,24 @@ ConnectNonSignal::ConnectNonSignal(const std::string &name, ClazyContext *contex void ConnectNonSignal::VisitStmt(clang::Stmt *stmt) { auto call = dyn_cast<CallExpr>(stmt); - AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager; - if (!call || !accessSpecifierManager) + if (!call) return; FunctionDecl *func = call->getDirectCallee(); - if (!QtUtils::isConnect(func) || !QtUtils::connectHasPMFStyle(func)) + if (!clazy::isConnect(func) || !clazy::connectHasPMFStyle(func)) return; - CXXMethodDecl *method = QtUtils::pmfFromConnect(call, /*argIndex=*/ 1); + CXXMethodDecl *method = clazy::pmfFromConnect(call, /*argIndex=*/ 1); if (!method) { - emitInternalError(func->getLocStart(), "couldn't find method from pmf connect"); + emitInternalError(getLocStart(func), "couldn't find method from pmf connect"); return; } + AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager; + if (!accessSpecifierManager) + return; + QtAccessSpecifierType qst = accessSpecifierManager->qtAccessSpecifierType(method); if (qst != QtAccessSpecifier_Unknown && qst != QtAccessSpecifier_Signal) emitWarning(call, method->getQualifiedNameAsString() + string(" is not a signal")); } - -REGISTER_CHECK("connect-non-signal", ConnectNonSignal, CheckLevel0) diff --git a/src/checks/level0/connect-not-normalized.cpp b/src/checks/level0/connect-not-normalized.cpp index d2a05721..7d10ac7b 100644 --- a/src/checks/level0/connect-not-normalized.cpp +++ b/src/checks/level0/connect-not-normalized.cpp @@ -27,7 +27,6 @@ #include "NormalizedSignatureUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -35,7 +34,7 @@ using namespace clang; using namespace std; ConnectNotNormalized::ConnectNotNormalized(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -60,17 +59,17 @@ bool ConnectNotNormalized::handleQ_ARG(CXXConstructExpr *expr) if (name != "QArgument" && name != "QReturnArgument") return false; - StringLiteral *sl = HierarchyUtils::getFirstChildOfType2<StringLiteral>(expr->getArg(0)); + StringLiteral *sl = clazy::getFirstChildOfType2<StringLiteral>(expr->getArg(0)); if (!sl) return false; const std::string original = sl->getString().str(); - const std::string normalized = NormalizedSignatureUtils::normalizedType(original.c_str()); + const std::string normalized = clazy::normalizedType(original.c_str()); if (original == normalized) return false; - emitWarning(expr->getLocStart(), "Signature is not normalized. Use " + normalized + " instead of " + original); + emitWarning(expr, "Signature is not normalized. Use " + normalized + " instead of " + original); return true; } @@ -80,27 +79,27 @@ bool ConnectNotNormalized::handleConnect(CallExpr *callExpr) return false; FunctionDecl *func = callExpr->getDirectCallee(); - if (!func || func->getNumParams() != 1 || func->getNameAsString() != "qFlagLocation") + if (!func || func->getNumParams() != 1 || clazy::name(func) != "qFlagLocation") return false; { // Only warn in connect statements, not disconnect, since there there's no optimization in Qt's side - auto parentCallExpr = HierarchyUtils::getFirstParentOfType<CallExpr>(m_context->parentMap, + auto parentCallExpr = clazy::getFirstParentOfType<CallExpr>(m_context->parentMap, m_context->parentMap->getParent(callExpr), -1); if (!parentCallExpr) return false; FunctionDecl *parentFunc = parentCallExpr->getDirectCallee(); - if (!parentFunc || parentFunc->getNameAsString() != "connect") + if (!parentFunc || clazy::name(parentFunc) != "connect") return false; } Expr *arg1 = callExpr->getArg(0); - StringLiteral *sl = HierarchyUtils::getFirstChildOfType2<StringLiteral>(arg1); + StringLiteral *sl = clazy::getFirstChildOfType2<StringLiteral>(arg1); if (!sl) return false; std::string original = sl->getString().str(); - std::string normalized = NormalizedSignatureUtils::normalizedSignature(original.c_str()); + std::string normalized = clazy::normalizedSignature(original.c_str()); // discard the junk after '\0' normalized = string(normalized.c_str()); @@ -113,8 +112,6 @@ bool ConnectNotNormalized::handleConnect(CallExpr *callExpr) normalized.erase(0, 1); original.erase(0, 1); - emitWarning(callExpr->getLocStart(), "Signature is not normalized. Use " + normalized + " instead of " + original); + emitWarning(getLocStart(callExpr), "Signature is not normalized. Use " + normalized + " instead of " + original); return true; } - -REGISTER_CHECK("connect-not-normalized", ConnectNotNormalized, CheckLevel0) diff --git a/src/checks/level0/container-anti-pattern.cpp b/src/checks/level0/container-anti-pattern.cpp index 54927f54..855c4015 100644 --- a/src/checks/level0/container-anti-pattern.cpp +++ b/src/checks/level0/container-anti-pattern.cpp @@ -21,10 +21,10 @@ #include "container-anti-pattern.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "MacroUtils.h" #include "LoopUtils.h" +#include "HierarchyUtils.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -34,7 +34,7 @@ using namespace std; ContainerAntiPattern::ContainerAntiPattern(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -48,7 +48,7 @@ static bool isInterestingCall(CallExpr *call) "QMap::keys", "QSet::toList", "QSet::values", "QHash::values", "QHash::keys" }; - return clazy_std::contains(methods, StringUtils::qualifiedMethodName(func)); + return clazy::contains(methods, clazy::qualifiedMethodName(func)); } void ContainerAntiPattern::VisitStmt(clang::Stmt *stmt) @@ -70,7 +70,7 @@ void ContainerAntiPattern::VisitStmt(clang::Stmt *stmt) if (!isInterestingCall(callexpr1)) return; - emitWarning(stmt->getLocStart(), "allocating an unneeded temporary container"); + emitWarning(getLocStart(stmt), "allocating an unneeded temporary container"); } bool ContainerAntiPattern::VisitQSet(Stmt *stmt) @@ -80,7 +80,7 @@ bool ContainerAntiPattern::VisitQSet(Stmt *stmt) return false; CXXMethodDecl *secondMethod = secondCall->getMethodDecl(); - const string secondMethodName = StringUtils::qualifiedMethodName(secondMethod); + const string secondMethodName = clazy::qualifiedMethodName(secondMethod); if (secondMethodName != "QSet::isEmpty") return false; @@ -94,28 +94,24 @@ bool ContainerAntiPattern::VisitQSet(Stmt *stmt) return false; CXXMethodDecl *firstMethod = dyn_cast<CXXMethodDecl>(firstFunc); - if (!firstMethod || StringUtils::qualifiedMethodName(firstMethod) != "QSet::intersect") + if (!firstMethod || clazy::qualifiedMethodName(firstMethod) != "QSet::intersect") return false; - emitWarning(stmt->getLocStart(), "Use QSet::intersects() instead"); + emitWarning(getLocStart(stmt), "Use QSet::intersects() instead"); return true; } bool ContainerAntiPattern::handleLoop(Stmt *stm) { - Expr *containerExpr = LoopUtils::containerExprForLoop(stm); + Expr *containerExpr = clazy::containerExprForLoop(stm); if (!containerExpr) return false; - auto memberExpr = HierarchyUtils::getFirstChildOfType2<CXXMemberCallExpr>(containerExpr); + auto memberExpr = clazy::getFirstChildOfType2<CXXMemberCallExpr>(containerExpr); if (isInterestingCall(memberExpr)) { - emitWarning(stm->getLocStart(), "allocating an unneeded temporary container"); + emitWarning(getLocStart(stm), "allocating an unneeded temporary container"); return true; } return false; } - - - -REGISTER_CHECK("container-anti-pattern", ContainerAntiPattern, CheckLevel0) diff --git a/src/checks/level0/empty-qstringliteral.cpp b/src/checks/level0/empty-qstringliteral.cpp new file mode 100644 index 00000000..359ccd03 --- /dev/null +++ b/src/checks/level0/empty-qstringliteral.cpp @@ -0,0 +1,63 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "empty-qstringliteral.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +EmptyQStringliteral::EmptyQStringliteral(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ +} + +void EmptyQStringliteral::VisitStmt(clang::Stmt *stmt) +{ + auto declstm = dyn_cast<DeclStmt>(stmt); + if (!declstm || !declstm->isSingleDecl()) + return; + + auto vd = dyn_cast<VarDecl>(declstm->getSingleDecl()); + if (!vd || vd->getName() != "qstring_literal") + return; + + Expr *expr = vd->getInit(); + auto initListExpr = expr ? dyn_cast<InitListExpr>(expr) : nullptr; + if (!initListExpr || initListExpr->getNumInits() != 2) + return; + + Expr *init = initListExpr->getInit(1); + auto literal = init ? dyn_cast<StringLiteral>(init) : nullptr; + if (!literal || literal->getByteLength() != 0) + return; + + if (!getLocStart(stmt).isMacroID()) + return; + + emitWarning(stmt, "Use QString instead of an empty QStringLiteral"); +} diff --git a/src/checks/level0/empty-qstringliteral.h b/src/checks/level0/empty-qstringliteral.h new file mode 100644 index 00000000..d1acf6a2 --- /dev/null +++ b/src/checks/level0/empty-qstringliteral.h @@ -0,0 +1,38 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_EMPTY_QSTRINGLITERAL_H +#define CLAZY_EMPTY_QSTRINGLITERAL_H + +#include "checkbase.h" + +/** + * See README-empty-qstringliteral.md for more info. + */ +class EmptyQStringliteral : public CheckBase +{ +public: + explicit EmptyQStringliteral(const std::string &name, ClazyContext *context); + void VisitStmt(clang::Stmt *) override; +private: +}; + +#endif diff --git a/src/checks/level0/fully-qualified-moc-types.cpp b/src/checks/level0/fully-qualified-moc-types.cpp new file mode 100644 index 00000000..265991a1 --- /dev/null +++ b/src/checks/level0/fully-qualified-moc-types.cpp @@ -0,0 +1,150 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "fully-qualified-moc-types.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" +#include "ClazyContext.h" +#include "AccessSpecifierManager.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +FullyQualifiedMocTypes::FullyQualifiedMocTypes(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ + context->enableAccessSpecifierManager(); + enablePreProcessorCallbacks(); +} + +void FullyQualifiedMocTypes::VisitDecl(clang::Decl *decl) +{ + auto method = dyn_cast<CXXMethodDecl>(decl); + if (!method) + return; + + AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager; + if (!accessSpecifierManager) + return; + + if (handleQ_PROPERTY(method)) + return; + + if (method->isThisDeclarationADefinition() && !method->hasInlineBody()) + return; + + QtAccessSpecifierType qst = accessSpecifierManager->qtAccessSpecifierType(method); + if (qst != QtAccessSpecifier_Signal && qst != QtAccessSpecifier_Slot && qst != QtAccessSpecifier_Invokable) + return; + + for (auto param : method->parameters()) { + QualType t = TypeUtils::pointeeQualType(param->getType()); + if (!t.isNull()) { + std::string typeName = clazy::name(t, lo(), /*asWritten=*/ true); + if (typeName == "QPrivateSignal") + continue; + + std::string qualifiedTypeName = clazy::name(t, lo(), /*asWritten=*/ false); + + if (typeName != qualifiedTypeName) { + emitWarning(method, string(accessSpecifierManager->qtAccessSpecifierTypeStr(qst)) + " arguments need to be fully-qualified (" + qualifiedTypeName + " instead of " + typeName + ")"); + } + } + } +} + +bool FullyQualifiedMocTypes::isGadget(CXXRecordDecl *record) const +{ + SourceLocation startLoc = getLocStart(record); + for (const SourceLocation &loc : m_qgadgetMacroLocations) { + if (sm().getFileID(loc) != sm().getFileID(startLoc)) + continue; // Different file + + if (sm().isBeforeInSLocAddrSpace(startLoc, loc) && sm().isBeforeInSLocAddrSpace(loc, getLocEnd(record))) + return true; // We found a Q_GADGET after start and before end, it's ours. + } + return false; +} + +bool FullyQualifiedMocTypes::handleQ_PROPERTY(CXXMethodDecl *method) +{ + if (clazy::name(method) != "qt_static_metacall" || !method->hasBody() || method->getDefinition() != method) + return false; + /** + * Basically diffed a .moc file with and without a namespaced property, + * the difference is one reinterpret_cast, under an if (_c == QMetaObject::ReadProperty), so + * that's what we cheak. + * + * The AST doesn't have the Q_PROPERTIES as they expand to nothing, so we have + * to do it this way. + */ + + auto ifs = clazy::getStatements<IfStmt>(method->getBody()); + + for (auto iff : ifs) { + auto bo = dyn_cast<BinaryOperator>(iff->getCond()); + if (!bo) + continue; + + auto enumRefs = clazy::getStatements<DeclRefExpr>(bo->getRHS()); + if (enumRefs.size() == 1) { + auto enumerator = dyn_cast<EnumConstantDecl>(enumRefs.at(0)->getDecl()); + if (enumerator && enumerator->getName() == "ReadProperty") { + auto reinterprets = clazy::getStatements<CXXReinterpretCastExpr>(iff); + for (auto reinterpret : reinterprets) { + QualType qt = TypeUtils::pointeeQualType(reinterpret->getTypeAsWritten()); + auto record = qt->getAsCXXRecordDecl(); + if (!record || !isGadget(record)) + continue; + + string nameAsWritten = clazy::name(qt, lo(), /*asWritten=*/ true); + string fullyQualifiedName = clazy::name(qt, lo(), /*asWritten=*/ false); + if (nameAsWritten != fullyQualifiedName) { + // warn in the cxxrecorddecl, since we don't want to warn in the .moc files. + // Ideally we would do some cross checking with the Q_PROPERTIES, but that's not in the AST + emitWarning(getLocStart(method->getParent()), "Q_PROPERTY of type " + nameAsWritten + " should use full qualification (" + fullyQualifiedName + ")"); + } + } + return true; + } + } + } + + return true; // true, so processing doesn't continue, it's a qt_static_metacall, nothing interesting here unless the properties above +} + +void FullyQualifiedMocTypes::VisitMacroExpands(const clang::Token &MacroNameTok, + const clang::SourceRange &range, const MacroInfo *) +{ + IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); + if (ii && ii->getName() == "Q_GADGET") + registerQ_GADGET(range.getBegin()); +} + +void FullyQualifiedMocTypes::registerQ_GADGET(SourceLocation loc) +{ + m_qgadgetMacroLocations.push_back(loc); +} diff --git a/src/checks/level0/fully-qualified-moc-types.h b/src/checks/level0/fully-qualified-moc-types.h new file mode 100644 index 00000000..f955d92f --- /dev/null +++ b/src/checks/level0/fully-qualified-moc-types.h @@ -0,0 +1,44 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_FULLY_QUALIFIED_MOC_TYPES_H +#define CLAZY_FULLY_QUALIFIED_MOC_TYPES_H + +#include "checkbase.h" +#include <vector> + +/** + * See README-fully-qualified-moc-types.md for more info. + */ +class FullyQualifiedMocTypes : public CheckBase +{ +public: + explicit FullyQualifiedMocTypes(const std::string &name, ClazyContext *context); + void VisitDecl(clang::Decl *) override; +private: + bool isGadget(clang::CXXRecordDecl *record) const; + bool handleQ_PROPERTY(clang::CXXMethodDecl *); + void VisitMacroExpands(const clang::Token &MacroNameTok, + const clang::SourceRange &range, const clang::MacroInfo *minfo = nullptr) override; + void registerQ_GADGET(clang::SourceLocation); + std::vector<clang::SourceLocation> m_qgadgetMacroLocations; +}; +#endif diff --git a/src/checks/level0/lambda-in-connect.cpp b/src/checks/level0/lambda-in-connect.cpp index 2e3f76ae..52c3e7e1 100644 --- a/src/checks/level0/lambda-in-connect.cpp +++ b/src/checks/level0/lambda-in-connect.cpp @@ -22,7 +22,6 @@ #include "lambda-in-connect.h" #include "ClazyContext.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "HierarchyUtils.h" #include "ContextUtils.h" @@ -35,7 +34,7 @@ using namespace std; LambdaInConnect::LambdaInConnect(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -49,27 +48,24 @@ void LambdaInConnect::VisitStmt(clang::Stmt *stmt) if (captures.begin() == captures.end()) return; - auto callExpr = HierarchyUtils::getFirstParentOfType<CallExpr>(m_context->parentMap, lambda); - if (StringUtils::qualifiedMethodName(callExpr) != "QObject::connect") + auto callExpr = clazy::getFirstParentOfType<CallExpr>(m_context->parentMap, lambda); + if (clazy::qualifiedMethodName(callExpr) != "QObject::connect") return; - ValueDecl *senderDecl = QtUtils::signalSenderForConnect(callExpr); + ValueDecl *senderDecl = clazy::signalSenderForConnect(callExpr); if (senderDecl) { const Type *t = senderDecl->getType().getTypePtrOrNull(); if (t && !t->isPointerType()) return; } - ValueDecl *receiverDecl = QtUtils::signalReceiverForConnect(callExpr); + ValueDecl *receiverDecl = clazy::signalReceiverForConnect(callExpr); for (auto capture : captures) { if (capture.getCaptureKind() == clang::LCK_ByRef) { VarDecl *declForCapture = capture.getCapturedVar(); - if (declForCapture && declForCapture != receiverDecl && ContextUtils::isValueDeclInFunctionContext(declForCapture)) + if (declForCapture && declForCapture != receiverDecl && clazy::isValueDeclInFunctionContext(declForCapture)) emitWarning(capture.getLocation(), "captured local variable by reference might go out of scope before lambda is called"); } } } - - -REGISTER_CHECK("lambda-in-connect", LambdaInConnect, CheckLevel0) diff --git a/src/checks/level0/lambda-unique-connection.cpp b/src/checks/level0/lambda-unique-connection.cpp index ad058efe..43a70203 100644 --- a/src/checks/level0/lambda-unique-connection.cpp +++ b/src/checks/level0/lambda-unique-connection.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "AccessSpecifierManager.h" #include "ClazyContext.h" @@ -35,7 +34,7 @@ using namespace std; LambdaUniqueConnection::LambdaUniqueConnection(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -49,13 +48,13 @@ void LambdaUniqueConnection::VisitStmt(clang::Stmt *stmt) // connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type) FunctionDecl *func = call->getDirectCallee(); - if (!func || func->getNumParams() != 5 || !func->isTemplateInstantiation() || !QtUtils::isConnect(func) || !QtUtils::connectHasPMFStyle(func)) + if (!func || func->getNumParams() != 5 || !func->isTemplateInstantiation() || !clazy::isConnect(func) || !clazy::connectHasPMFStyle(func)) return; Expr *typeArg = call->getArg(4); // The type vector<DeclRefExpr*> result; - HierarchyUtils::getChilds(typeArg, result); + clazy::getChilds(typeArg, result); bool found = false; for (auto declRef : result) { @@ -78,7 +77,7 @@ void LambdaUniqueConnection::VisitStmt(clang::Stmt *stmt) if (tempParams->size() != 2) return; - CXXMethodDecl *method = QtUtils::pmfFromConnect(call, 3); + CXXMethodDecl *method = clazy::pmfFromConnect(call, 3); if (method) { // How else to detect if it's the right overload ? It's all templated stuff with the same // names for all the template arguments @@ -87,5 +86,3 @@ void LambdaUniqueConnection::VisitStmt(clang::Stmt *stmt) emitWarning(typeArg, "UniqueConnection is not supported with non-member functions"); } - -REGISTER_CHECK("lambda-unique-connection", LambdaUniqueConnection, CheckLevel0) diff --git a/src/checks/level0/mutable-container-key.cpp b/src/checks/level0/mutable-container-key.cpp index 3571150b..d3912b1d 100644 --- a/src/checks/level0/mutable-container-key.cpp +++ b/src/checks/level0/mutable-container-key.cpp @@ -25,7 +25,6 @@ #include "QtUtils.h" #include "TypeUtils.h" #include "StringUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> #include <vector> @@ -33,14 +32,14 @@ using namespace clang; using namespace std; -static bool isInterestingContainer(const string &name) +static bool isInterestingContainer(StringRef name) { - static const vector<string> containers = { "QMap", "QHash" }; - return clazy_std::contains(containers, name); + static const vector<StringRef> containers = { "QMap", "QHash" }; + return clazy::contains(containers, name); } MutableContainerKey::MutableContainerKey(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -60,13 +59,10 @@ void MutableContainerKey::VisitDecl(clang::Decl *decl) return; auto record = t->isRecordType() ? t->getAsCXXRecordDecl() : nullptr; - if (!StringUtils::classIsOneOf(record, {"QPointer", "QWeakPointer", - "QPersistentModelIndex", "weak_ptr"})) + if (!clazy::classIsOneOf(record, {"QPointer", "QWeakPointer", + "QPersistentModelIndex", "weak_ptr"})) return; - emitWarning(decl->getLocStart(), "Associative container key might be modified externally"); + emitWarning(getLocStart(decl), "Associative container key might be modified externally"); } - - -REGISTER_CHECK("mutable-container-key", MutableContainerKey, CheckLevel0) diff --git a/src/checks/level0/qcolor-from-literal.cpp b/src/checks/level0/qcolor-from-literal.cpp index 4daf712c..79f865bb 100644 --- a/src/checks/level0/qcolor-from-literal.cpp +++ b/src/checks/level0/qcolor-from-literal.cpp @@ -20,8 +20,8 @@ */ #include "StringUtils.h" +#include "HierarchyUtils.h" #include "qcolor-from-literal.h" -#include "checkmanager.h" using namespace clang; using namespace clang::ast_matchers; @@ -65,7 +65,7 @@ public : QColorFromLiteral::QColorFromLiteral(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) , m_astMatcherCallBack(new QColorFromLiteral_Callback(this)) { } @@ -81,11 +81,11 @@ void QColorFromLiteral::VisitStmt(Stmt *stmt) if (!call || call->getNumArgs() != 1) return; - string name = StringUtils::qualifiedMethodName(call); + string name = clazy::qualifiedMethodName(call); if (name != "QColor::setNamedColor") return; - StringLiteral *lt = HierarchyUtils::getFirstChildOfType2<StringLiteral>(call->getArg(0)); + StringLiteral *lt = clazy::getFirstChildOfType2<StringLiteral>(call->getArg(0)); if (handleStringLiteral(lt)) emitWarning(lt, "The ctor taking ints is cheaper than QColor::setNamedColor(QString)"); } @@ -95,5 +95,3 @@ void QColorFromLiteral::registerASTMatchers(MatchFinder &finder) finder.addMatcher(cxxConstructExpr(hasDeclaration(namedDecl(hasName("QColor"))), hasArgument(0, stringLiteral().bind("myLiteral"))), m_astMatcherCallBack); } - -REGISTER_CHECK("qcolor-from-literal", QColorFromLiteral, CheckLevel0) diff --git a/src/checks/level0/qdatetimeutc.cpp b/src/checks/level0/qdatetime-utc.cpp index 5b7c5ef7..62ec49fd 100644 --- a/src/checks/level0/qdatetimeutc.cpp +++ b/src/checks/level0/qdatetime-utc.cpp @@ -22,9 +22,8 @@ Boston, MA 02110-1301, USA. */ -#include "qdatetimeutc.h" +#include "qdatetime-utc.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "FixItUtils.h" @@ -34,11 +33,6 @@ using namespace clang; using namespace std; -enum Fixit { - FixitNone = 0, - FixitAll = 0x1 // More granularity isn't needed I guess -}; - QDateTimeUtc::QDateTimeUtc(const std::string &name, ClazyContext *context) : CheckBase(name, context) { @@ -74,16 +68,12 @@ void QDateTimeUtc::VisitStmt(clang::Stmt *stmt) } std::vector<FixItHint> fixits; - if (isFixitEnabled(FixitAll)) { - const bool success = FixItUtils::transformTwoCallsIntoOneV2(&m_astContext, secondCall, replacement, fixits); + if (isFixitEnabled()) { + const bool success = clazy::transformTwoCallsIntoOneV2(&m_astContext, secondCall, replacement, fixits); if (!success) { - queueManualFixitWarning(secondCall->getLocStart(), FixitAll); + queueManualFixitWarning(getLocStart(secondCall)); } } - emitWarning(stmt->getLocStart(), "Use QDateTime" + replacement + " instead", fixits); + emitWarning(getLocStart(stmt), "Use QDateTime" + replacement + " instead", fixits); } - -const char *const s_checkName = "qdatetime-utc"; -REGISTER_CHECK(s_checkName, QDateTimeUtc, CheckLevel0) -REGISTER_FIXIT(FixitAll, "fix-qdatetime-utc", s_checkName) diff --git a/src/checks/level0/qdatetimeutc.h b/src/checks/level0/qdatetime-utc.h index f499e122..f499e122 100644 --- a/src/checks/level0/qdatetimeutc.h +++ b/src/checks/level0/qdatetime-utc.h diff --git a/src/checks/level0/qenums.cpp b/src/checks/level0/qenums.cpp index 68ae409c..bb99a76d 100644 --- a/src/checks/level0/qenums.cpp +++ b/src/checks/level0/qenums.cpp @@ -25,7 +25,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "PreProcessorVisitor.h" #include <clang/AST/AST.h> @@ -35,14 +34,14 @@ using namespace clang; using namespace std; -Qenums::Qenums(const std::string &name, ClazyContext *context) +QEnums::QEnums(const std::string &name, ClazyContext *context) : CheckBase(name, context) { enablePreProcessorCallbacks(); context->enablePreprocessorVisitor(); } -void Qenums::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &range) +void QEnums::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &range, const clang::MacroInfo *) { PreProcessorVisitor *preProcessorVisitor = m_context->preprocessorVisitor; if (!preProcessorVisitor || preProcessorVisitor->qtVersion() < 50500) @@ -58,7 +57,7 @@ void Qenums::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &ran CharSourceRange crange = Lexer::getAsCharRange(range, sm(), lo()); string text = Lexer::getSourceText(crange, sm(), lo()); - if (clazy_std::contains(text, "::")) + if (clazy::contains(text, "::")) return; } @@ -70,5 +69,3 @@ void Qenums::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &ran emitWarning(range.getBegin(), "Use Q_ENUM instead of Q_ENUMS"); } - -REGISTER_CHECK_WITH_FLAGS("qenums", Qenums, CheckLevel0, RegisteredCheck::Option_Qt4Incompatible) diff --git a/src/checks/level0/qenums.h b/src/checks/level0/qenums.h index 910d6ec5..3f91dcca 100644 --- a/src/checks/level0/qenums.h +++ b/src/checks/level0/qenums.h @@ -27,13 +27,14 @@ /** * See README-qenums for more info. */ -class Qenums : public CheckBase +class QEnums : public CheckBase { public: - explicit Qenums(const std::string &name, ClazyContext *context); + explicit QEnums(const std::string &name, ClazyContext *context); private: void VisitMacroExpands(const clang::Token &MacroNameTok, - const clang::SourceRange &range) override; + const clang::SourceRange &range, + const clang::MacroInfo * = nullptr) override; }; #endif diff --git a/src/checks/level0/qfileinfo-exists.cpp b/src/checks/level0/qfileinfo-exists.cpp index 041423d8..3af7028b 100644 --- a/src/checks/level0/qfileinfo-exists.cpp +++ b/src/checks/level0/qfileinfo-exists.cpp @@ -21,8 +21,8 @@ #include "qfileinfo-exists.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" +#include "HierarchyUtils.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -32,23 +32,20 @@ using namespace std; QFileInfoExists::QFileInfoExists(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } void QFileInfoExists::VisitStmt(clang::Stmt *stmt) { auto existsCall = dyn_cast<CXXMemberCallExpr>(stmt); - std::string methodName = StringUtils::qualifiedMethodName(existsCall); + std::string methodName = clazy::qualifiedMethodName(existsCall); if (methodName != "QFileInfo::exists") return; - CXXConstructExpr* ctorExpr = HierarchyUtils::getFirstChildOfType<CXXConstructExpr>(existsCall); - if (!ctorExpr || StringUtils::simpleArgTypeName(ctorExpr->getConstructor(), 0, lo()) != "QString") + CXXConstructExpr* ctorExpr = clazy::getFirstChildOfType<CXXConstructExpr>(existsCall); + if (!ctorExpr || clazy::simpleArgTypeName(ctorExpr->getConstructor(), 0, lo()) != "QString") return; - emitWarning(stmt->getLocStart(), "Use the static QFileInfo::exists() instead. It's documented to be faster."); + emitWarning(getLocStart(stmt), "Use the static QFileInfo::exists() instead. It's documented to be faster."); } - - -REGISTER_CHECK("qfileinfo-exists", QFileInfoExists, CheckLevel0) diff --git a/src/checks/level0/qgetenv.cpp b/src/checks/level0/qgetenv.cpp index 0eb33f8f..cf536e37 100644 --- a/src/checks/level0/qgetenv.cpp +++ b/src/checks/level0/qgetenv.cpp @@ -24,7 +24,6 @@ #include "qgetenv.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "FixItUtils.h" #include <clang/AST/AST.h> @@ -33,25 +32,17 @@ using namespace clang; using namespace std; -enum Fixit { - FixitNone = 0, - FixitAll = 0x1 // More granularity isn't needed I guess -}; - QGetEnv::QGetEnv(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { - } - - void QGetEnv::VisitStmt(clang::Stmt *stmt) { // Lets check only in function calls. Otherwise there are too many false positives, it's common // to implicit cast to bool when checking pointers for validity, like if (ptr) - CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stmt); + auto *memberCall = dyn_cast<CXXMemberCallExpr>(stmt); if (!memberCall) return; @@ -60,7 +51,7 @@ void QGetEnv::VisitStmt(clang::Stmt *stmt) return; CXXRecordDecl *record = method->getParent(); - if (!record || record->getNameAsString() != "QByteArray") { + if (!record || clazy::name(record) != "QByteArray") { return; } @@ -72,10 +63,10 @@ void QGetEnv::VisitStmt(clang::Stmt *stmt) FunctionDecl *func = qgetEnvCall->getDirectCallee(); - if (!func || func->getNameAsString() != "qgetenv") + if (!func || clazy::name(func) != "qgetenv") return; - string methodname = method->getNameAsString(); + StringRef methodname = clazy::name(method); string errorMsg; std::string replacement; if (methodname == "isEmpty") { @@ -91,19 +82,14 @@ void QGetEnv::VisitStmt(clang::Stmt *stmt) if (!errorMsg.empty()) { std::vector<FixItHint> fixits; - if (isFixitEnabled(FixitAll)) { - const bool success = FixItUtils::transformTwoCallsIntoOne(&m_astContext, qgetEnvCall, memberCall, replacement, fixits); + if (isFixitEnabled()) { + const bool success = clazy::transformTwoCallsIntoOne(&m_astContext, qgetEnvCall, memberCall, replacement, fixits); if (!success) { - queueManualFixitWarning(memberCall->getLocStart(), FixitAll); + queueManualFixitWarning(getLocStart(memberCall)); } } errorMsg += " Use " + replacement + "() instead"; - emitWarning(memberCall->getLocStart(), errorMsg.c_str(), fixits); + emitWarning(getLocStart(memberCall), errorMsg.c_str(), fixits); } } - - -const char *const s_checkName = "qgetenv"; -REGISTER_CHECK_WITH_FLAGS(s_checkName, QGetEnv, CheckLevel0, RegisteredCheck::Option_Qt4Incompatible) -REGISTER_FIXIT(FixitAll, "fix-qgetenv", s_checkName) diff --git a/src/checks/level0/qmap-with-pointer-key.cpp b/src/checks/level0/qmap-with-pointer-key.cpp index 495668df..7217b06c 100644 --- a/src/checks/level0/qmap-with-pointer-key.cpp +++ b/src/checks/level0/qmap-with-pointer-key.cpp @@ -24,7 +24,6 @@ #include "qmap-with-pointer-key.h" #include "Utils.h" -#include "checkmanager.h" #include <clang/AST/DeclCXX.h> #include <clang/AST/Expr.h> @@ -51,8 +50,6 @@ void QMapWithPointerKey::VisitDecl(clang::Decl *decl) QualType qt = templateArguments[0].getAsType(); const Type *t = qt.getTypePtrOrNull(); if (t && t->isPointerType()) { - emitWarning(decl->getLocStart(), "Use QHash<K,T> instead of QMap<K,T> when K is a pointer"); + emitWarning(getLocStart(decl), "Use QHash<K,T> instead of QMap<K,T> when K is a pointer"); } } - -REGISTER_CHECK("qmap-with-pointer-key", QMapWithPointerKey, CheckLevel0) diff --git a/src/checks/level0/qstringarg.cpp b/src/checks/level0/qstring-arg.cpp index edcd5ad2..e66e0eb4 100644 --- a/src/checks/level0/qstringarg.cpp +++ b/src/checks/level0/qstring-arg.cpp @@ -19,10 +19,10 @@ Boston, MA 02110-1301, USA. */ -#include "qstringarg.h" +#include "qstring-arg.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" +#include "HierarchyUtils.h" #include <clang/AST/AST.h> #include <vector> @@ -30,8 +30,8 @@ using namespace clang; using namespace std; -StringArg::StringArg(const std::string &name, ClazyContext *context) - : CheckBase(name, context) +QStringArg::QStringArg(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { m_filesToIgnore = { "qstring.h" }; } @@ -39,7 +39,7 @@ StringArg::StringArg(const std::string &name, ClazyContext *context) static string variableNameFromArg(Expr *arg) { vector<DeclRefExpr*> declRefs; - HierarchyUtils::getChilds<DeclRefExpr>(arg, declRefs); + clazy::getChilds<DeclRefExpr>(arg, declRefs); if (declRefs.size() == 1) { ValueDecl *decl = declRefs.at(0)->getDecl(); return decl ? decl->getNameAsString() : string(); @@ -53,12 +53,12 @@ static CXXMethodDecl* isArgMethod(FunctionDecl *func) if (!func) return nullptr; - CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(func); - if (!method || method->getNameAsString() != "arg") + auto method = dyn_cast<CXXMethodDecl>(func); + if (!method || clazy::name(method) != "arg") return nullptr; CXXRecordDecl *record = method->getParent(); - if (!record || record->getNameAsString() != "QString") + if (!record || clazy::name(record) != "QString") return nullptr; return method; @@ -74,24 +74,24 @@ static bool isArgFuncWithOnlyQString(CallExpr *callExpr) return false; ParmVarDecl *secondParam = method->getParamDecl(1); - if (StringUtils::classNameFor(secondParam) == "QString") + if (clazy::classNameFor(secondParam) == "QString") return true; ParmVarDecl *firstParam = method->getParamDecl(0); - if (StringUtils::classNameFor(firstParam) != "QString") + if (clazy::classNameFor(firstParam) != "QString") return false; // This is a arg(QString, int, QChar) call, it's good if the second parameter is a default param return isa<CXXDefaultArgExpr>(callExpr->getArg(1)); } -bool StringArg::checkMultiArgWarningCase(const vector<clang::CallExpr *> &calls) +bool QStringArg::checkMultiArgWarningCase(const vector<clang::CallExpr *> &calls) { const int size = calls.size(); for (int i = 1; i < size; ++i) { auto call = calls.at(i); if (calls.at(i - 1)->getNumArgs() + call->getNumArgs() <= 9) { - emitWarning(call->getLocEnd(), "Use multi-arg instead"); + emitWarning(getLocEnd(call), "Use multi-arg instead"); return true; } } @@ -99,15 +99,21 @@ bool StringArg::checkMultiArgWarningCase(const vector<clang::CallExpr *> &calls) return false; } -void StringArg::checkForMultiArgOpportunities(CXXMemberCallExpr *memberCall) +void QStringArg::checkForMultiArgOpportunities(CXXMemberCallExpr *memberCall) { if (!isArgFuncWithOnlyQString(memberCall)) return; + if (getLocStart(memberCall).isMacroID()) { + auto macroName = Lexer::getImmediateMacroName(getLocStart(memberCall), sm(), lo()); + if (macroName == "QT_REQUIRE_VERSION") // bug #391851 + return; + } + vector<clang::CallExpr *> callExprs = Utils::callListForChain(memberCall); vector<clang::CallExpr *> argCalls; for (auto call : callExprs) { - if (!clazy_std::contains(m_alreadyProcessedChainedCalls, call) && isArgFuncWithOnlyQString(call)) { + if (!clazy::contains(m_alreadyProcessedChainedCalls, call) && isArgFuncWithOnlyQString(call)) { argCalls.push_back(call); m_alreadyProcessedChainedCalls.push_back(call); } else { @@ -120,13 +126,13 @@ void StringArg::checkForMultiArgOpportunities(CXXMemberCallExpr *memberCall) checkMultiArgWarningCase(argCalls); } -void StringArg::VisitStmt(clang::Stmt *stmt) +void QStringArg::VisitStmt(clang::Stmt *stmt) { - CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stmt); + auto memberCall = dyn_cast<CXXMemberCallExpr>(stmt); if (!memberCall) return; - if (shouldIgnoreFile(stmt->getLocStart())) + if (shouldIgnoreFile(getLocStart(stmt))) return; checkForMultiArgOpportunities(memberCall); @@ -138,47 +144,38 @@ void StringArg::VisitStmt(clang::Stmt *stmt) if (!method) return; - if (StringUtils::simpleArgTypeName(method, method->getNumParams() - 1, lo()) == "QChar") { + if (clazy::simpleArgTypeName(method, method->getNumParams() - 1, lo()) == "QChar") { // The second arg wasn't passed, so this is a safe and unambiguous use, like .arg(1) if (isa<CXXDefaultArgExpr>(memberCall->getArg(1))) return; ParmVarDecl *p = method->getParamDecl(2); - if (p && p->getNameAsString() == "base") { + if (p && clazy::name(p) == "base") { // User went through the trouble specifying a base, lets allow it if it's a literal. vector<IntegerLiteral*> literals; - HierarchyUtils::getChilds<IntegerLiteral>(memberCall->getArg(2), literals); + clazy::getChilds<IntegerLiteral>(memberCall->getArg(2), literals); if (!literals.empty()) return; - string variableName = clazy_std::toLower(variableNameFromArg(memberCall->getArg(2))); - if (clazy_std::contains(variableName, "base")) + string variableName = clazy::toLower(variableNameFromArg(memberCall->getArg(2))); + if (clazy::contains(variableName, "base")) return; } p = method->getParamDecl(1); - if (p && p->getNameAsString() == "fieldWidth") { + if (p && clazy::name(p) == "fieldWidth") { // He specified a literal, so he knows what he's doing, otherwise he would have put it directly in the string vector<IntegerLiteral*> literals; - HierarchyUtils::getChilds<IntegerLiteral>(memberCall->getArg(1), literals); + clazy::getChilds<IntegerLiteral>(memberCall->getArg(1), literals); if (!literals.empty()) return; // the variable is named "width", user knows what he's doing - string variableName = clazy_std::toLower(variableNameFromArg(memberCall->getArg(1))); - if (clazy_std::contains(variableName, "width")) + string variableName = clazy::toLower(variableNameFromArg(memberCall->getArg(1))); + if (clazy::contains(variableName, "width")) return; } - emitWarning(stmt->getLocStart(), "Using QString::arg() with fillChar overload"); + emitWarning(getLocStart(stmt), "Using QString::arg() with fillChar overload"); } } - -std::vector<string> StringArg::supportedOptions() const -{ - static const vector<string> options = { "fillChar-overloads" }; - return options; -} - - -REGISTER_CHECK("qstring-arg", StringArg, CheckLevel0) diff --git a/src/checks/level0/qstringarg.h b/src/checks/level0/qstring-arg.h index e15dbd11..a28263c3 100644 --- a/src/checks/level0/qstringarg.h +++ b/src/checks/level0/qstring-arg.h @@ -33,14 +33,13 @@ class CallExpr; /** * Finds misuse of QString::arg() */ -class StringArg : public CheckBase +class QStringArg : public CheckBase { public: - StringArg(const std::string &name, ClazyContext *context); + explicit QStringArg(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stmt) override; void checkForMultiArgOpportunities(clang::CXXMemberCallExpr *memberCall); private: - std::vector<std::string> supportedOptions() const override; bool checkMultiArgWarningCase(const std::vector<clang::CallExpr *> &calls); std::vector<clang::CallExpr*> m_alreadyProcessedChainedCalls; }; diff --git a/src/checks/level0/qstring-insensitive-allocation.cpp b/src/checks/level0/qstring-insensitive-allocation.cpp index 73c95100..896d254c 100644 --- a/src/checks/level0/qstring-insensitive-allocation.cpp +++ b/src/checks/level0/qstring-insensitive-allocation.cpp @@ -21,7 +21,6 @@ #include "qstring-insensitive-allocation.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -32,7 +31,7 @@ using namespace std; QStringInsensitiveAllocation::QStringInsensitiveAllocation(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -43,7 +42,7 @@ static bool isInterestingCall1(CallExpr *call) return false; static const vector<string> methods = { "QString::toUpper", "QString::toLower" }; - return clazy_std::contains(methods, StringUtils::qualifiedMethodName(func)); + return clazy::contains(methods, clazy::qualifiedMethodName(func)); } static bool isInterestingCall2(CallExpr *call) @@ -54,7 +53,7 @@ static bool isInterestingCall2(CallExpr *call) static const vector<string> methods = { "QString::endsWith", "QString::startsWith", "QString::contains", "QString::compare" }; - return clazy_std::contains(methods, StringUtils::qualifiedMethodName(func)); + return clazy::contains(methods, clazy::qualifiedMethodName(func)); } void QStringInsensitiveAllocation::VisitStmt(clang::Stmt *stmt) @@ -69,8 +68,5 @@ void QStringInsensitiveAllocation::VisitStmt(clang::Stmt *stmt) if (!isInterestingCall1(call1) || !isInterestingCall2(call2)) return; - emitWarning(stmt->getLocStart(), "unneeded allocation"); + emitWarning(getLocStart(stmt), "unneeded allocation"); } - - -REGISTER_CHECK("qstring-insensitive-allocation", QStringInsensitiveAllocation, CheckLevel0) diff --git a/src/checks/level0/qstringref.cpp b/src/checks/level0/qstring-ref.cpp index ba84dd06..0f073e2c 100644 --- a/src/checks/level0/qstringref.cpp +++ b/src/checks/level0/qstring-ref.cpp @@ -19,63 +19,65 @@ Boston, MA 02110-1301, USA. */ -#include "qstringref.h" +#include "qstring-ref.h" #include "ClazyContext.h" #include "Utils.h" #include "HierarchyUtils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "FixItUtils.h" #include <clang/AST/AST.h> -#include <vector> #include <clang/Lex/Lexer.h> +#include <array> +#include <vector> + using namespace clang; using namespace std; -enum Fixit { - FixitNone = 0, - FixitUseQStringRef = 0x1, -}; - StringRefCandidates::StringRefCandidates(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } static bool isInterestingFirstMethod(CXXMethodDecl *method) { - if (!method || method->getParent()->getNameAsString() != "QString") + if (!method || clazy::name(method->getParent()) != "QString") return false; - static const vector<string> list = { "left", "mid", "right" }; - return clazy_std::contains(list, method->getNameAsString()); + static const llvm::SmallVector<StringRef, 3> list = {{ "left", "mid", "right" }}; + return clazy::contains(list, clazy::name(method)); } static bool isInterestingSecondMethod(CXXMethodDecl *method, const clang::LangOptions &lo) { - if (!method || method->getParent()->getNameAsString() != "QString") + if (!method || clazy::name(method->getParent()) != "QString") return false; - static const vector<string> list = { "compare", "contains", "count", "startsWith", "endsWith", "indexOf", - "isEmpty", "isNull", "lastIndexOf", "length", "size", "toDouble", "toFloat", - "toInt", "toUInt", "toULong", "toULongLong", "toUShort", "toUcs4" }; + static const std::array<StringRef, 19> list = {{ "compare", "contains", "count", "startsWith", "endsWith", "indexOf", + "isEmpty", "isNull", "lastIndexOf", "length", "size", "toDouble", "toFloat", + "toInt", "toUInt", "toULong", "toULongLong", "toUShort", "toUcs4" }}; - if (!clazy_std::contains(list, method->getNameAsString())) + if (!clazy::contains(list, clazy::name(method))) return false; - return !StringUtils::anyArgIsOfAnySimpleType(method, {"QRegExp", "QRegularExpression"}, lo); + return !clazy::anyArgIsOfAnySimpleType(method, {"QRegExp", "QRegularExpression"}, lo); } static bool isMethodReceivingQStringRef(CXXMethodDecl *method) { - static const vector<string> list = { "append", "compare", "count", "indexOf", "endsWith", "lastIndexOf", "localAwareCompare", "startsWidth", "operator+=" }; - - if (!method || method->getParent()->getNameAsString() != "QString") + if (!method || clazy::name(method->getParent()) != "QString") return false; - return clazy_std::contains(list, method->getNameAsString()); + static const std::array<StringRef, 8> list = {{ "append", "compare", "count", "indexOf", "endsWith", "lastIndexOf", "localAwareCompare", "startsWidth" }}; + + if (clazy::contains(list, clazy::name(method))) + return true; + + if (method->getOverloadedOperator() == OO_PlusEqual) // operator+= + return true; + + return false; } void StringRefCandidates::VisitStmt(clang::Stmt *stmt) @@ -115,7 +117,7 @@ bool StringRefCandidates::isConvertedToSomethingElse(clang::Stmt* s) const if (!s) return false; - auto constr = HierarchyUtils::getFirstParentOfType<CXXConstructExpr>(m_context->parentMap, s); + auto constr = clazy::getFirstParentOfType<CXXConstructExpr>(m_context->parentMap, s); if (!constr || constr->getNumArgs() == 0) return false; @@ -156,10 +158,10 @@ bool StringRefCandidates::processCase1(CXXMemberCallExpr *memberCall) const string firstMethodName = firstMemberCall->getMethodDecl()->getNameAsString(); std::vector<FixItHint> fixits; - if (isFixitEnabled(FixitUseQStringRef)) + if (isFixitEnabled()) fixits = fixit(firstMemberCall); - emitWarning(firstMemberCall->getLocEnd(), "Use " + firstMethodName + "Ref() instead", fixits); + emitWarning(getLocEnd(firstMemberCall), "Use " + firstMethodName + "Ref() instead", fixits); return true; } @@ -167,7 +169,7 @@ bool StringRefCandidates::processCase1(CXXMemberCallExpr *memberCall) bool StringRefCandidates::processCase2(CallExpr *call) { auto memberCall = dyn_cast<CXXMemberCallExpr>(call); - CXXOperatorCallExpr *operatorCall = dyn_cast<CXXOperatorCallExpr>(call); + auto operatorCall = memberCall ? nullptr : dyn_cast<CXXOperatorCallExpr>(call); CXXMethodDecl *method = nullptr; if (memberCall) { @@ -189,7 +191,7 @@ bool StringRefCandidates::processCase2(CallExpr *call) return false; } - CallExpr *innerCall = HierarchyUtils::getFirstChildOfType2<CallExpr>(temp); + CallExpr *innerCall = clazy::getFirstChildOfType2<CallExpr>(temp); auto innerMemberCall = innerCall ? dyn_cast<CXXMemberCallExpr>(innerCall) : nullptr; if (!innerMemberCall) return false; @@ -199,35 +201,31 @@ bool StringRefCandidates::processCase2(CallExpr *call) return false; std::vector<FixItHint> fixits; - if (isFixitEnabled(FixitUseQStringRef)) { + if (isFixitEnabled()) { fixits = fixit(innerMemberCall); } - emitWarning(call->getLocStart(), "Use " + innerMethod->getNameAsString() + "Ref() instead", fixits); + emitWarning(getLocStart(call), "Use " + innerMethod->getNameAsString() + "Ref() instead", fixits); return true; } std::vector<FixItHint> StringRefCandidates::fixit(CXXMemberCallExpr *call) { - MemberExpr *memberExpr = HierarchyUtils::getFirstChildOfType<MemberExpr>(call); + MemberExpr *memberExpr = clazy::getFirstChildOfType<MemberExpr>(call); if (!memberExpr) { - queueManualFixitWarning(call->getLocStart(), FixitUseQStringRef, "Internal error 1"); + queueManualFixitWarning(getLocStart(call), "Internal error 1"); return {}; } - auto insertionLoc = Lexer::getLocForEndOfToken(memberExpr->getLocEnd(), 0, sm(), lo()); + auto insertionLoc = Lexer::getLocForEndOfToken(getLocEnd(memberExpr), 0, sm(), lo()); // llvm::errs() << insertionLoc.printToString(sm()) << "\n"; if (!insertionLoc.isValid()) { - queueManualFixitWarning(call->getLocStart(), FixitUseQStringRef, "Internal error 2"); + queueManualFixitWarning(getLocStart(call), "Internal error 2"); return {}; } std::vector<FixItHint> fixits; - fixits.push_back(FixItUtils::createInsertion(insertionLoc, "Ref")); + fixits.push_back(clazy::createInsertion(insertionLoc, "Ref")); return fixits; } - -const char *const s_checkName = "qstring-ref"; -REGISTER_CHECK(s_checkName, StringRefCandidates, CheckLevel0) -REGISTER_FIXIT(FixitUseQStringRef, "fix-missing-qstringref", s_checkName) diff --git a/src/checks/level0/qstringref.h b/src/checks/level0/qstring-ref.h index 31c7da1f..31c7da1f 100644 --- a/src/checks/level0/qstringref.h +++ b/src/checks/level0/qstring-ref.h diff --git a/src/checks/level0/qt-macros.cpp b/src/checks/level0/qt-macros.cpp index 28cd722b..22d664f8 100644 --- a/src/checks/level0/qt-macros.cpp +++ b/src/checks/level0/qt-macros.cpp @@ -25,7 +25,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -44,7 +43,7 @@ void QtMacros::VisitMacroDefined(const Token &MacroNameTok) return; IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); - if (ii && clazy_std::startsWith(ii->getName(), "Q_OS_")) + if (ii && clazy::startsWith(ii->getName(), "Q_OS_")) m_OSMacroExists = true; } @@ -56,7 +55,7 @@ void QtMacros::checkIfDef(const Token ¯oNameTok, SourceLocation Loc) if (ii->getName() == "Q_OS_WINDOWS") { emitWarning(Loc, "Q_OS_WINDOWS is wrong, use Q_OS_WIN instead"); - } else if (!m_OSMacroExists && clazy_std::startsWith(ii->getName(), "Q_OS_")) { + } else if (!m_OSMacroExists && clazy::startsWith(ii->getName(), "Q_OS_")) { emitWarning(Loc, "Include qglobal.h before testing Q_OS_ macros"); } } @@ -72,5 +71,3 @@ void QtMacros::VisitIfdef(SourceLocation loc, const Token ¯oNameTok) if (!m_context->usingPreCompiledHeaders()) checkIfDef(macroNameTok, loc); } - -REGISTER_CHECK("qt-macros", QtMacros, CheckLevel0) diff --git a/src/checks/level0/qvariant-template-instantiation.cpp b/src/checks/level0/qvariant-template-instantiation.cpp index 548bdb9e..2e0fd8e7 100644 --- a/src/checks/level0/qvariant-template-instantiation.cpp +++ b/src/checks/level0/qvariant-template-instantiation.cpp @@ -26,26 +26,24 @@ #include "Utils.h" #include "TemplateUtils.h" #include "StringUtils.h" -#include "checkmanager.h" using namespace std; using namespace clang; QVariantTemplateInstantiation::QVariantTemplateInstantiation(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { - } -static bool isMatchingClass(const std::string &name) +static bool isMatchingClass(StringRef name) { - static const vector<string> classes = {"QBitArray", "QByteArray", "QChar", "QDate", "QDateTime", - "QEasingCurve", "QJsonArray", "QJsonDocument", "QJsonObject", - "QJsonValue", "QLocale", "QModelIndex", "QPoint", "QPointF", - "QRect", "QRectF", "QRegExp", "QString", "QRegularExpression", - "QSize", "QSizeF", "QStringList", "QTime", "QUrl", "QUuid" }; + static const vector<StringRef> classes = {"QBitArray", "QByteArray", "QChar", "QDate", "QDateTime", + "QEasingCurve", "QJsonArray", "QJsonDocument", "QJsonObject", + "QJsonValue", "QLocale", "QModelIndex", "QPoint", "QPointF", + "QRect", "QRectF", "QRegExp", "QString", "QRegularExpression", + "QSize", "QSizeF", "QStringList", "QTime", "QUrl", "QUuid" }; - return clazy_std::contains(classes, name); + return clazy::contains(classes, name); } void QVariantTemplateInstantiation::VisitStmt(clang::Stmt *stm) @@ -55,14 +53,14 @@ void QVariantTemplateInstantiation::VisitStmt(clang::Stmt *stm) return; CXXMethodDecl *methodDecl = callExpr->getMethodDecl(); - if (!methodDecl || methodDecl->getNameAsString() != "value") + if (!methodDecl || clazy::name(methodDecl) != "value") return; CXXRecordDecl *decl = methodDecl->getParent(); - if (!decl || decl->getNameAsString() != "QVariant") + if (!decl || clazy::name(decl) != "QVariant") return; - vector<QualType> typeList = TemplateUtils::getTemplateArgumentsTypes(methodDecl); + vector<QualType> typeList = clazy::getTemplateArgumentsTypes(methodDecl); const Type *t = typeList.empty() ? nullptr : typeList[0].getTypePtrOrNull(); if (!t) return; @@ -72,19 +70,17 @@ void QVariantTemplateInstantiation::VisitStmt(clang::Stmt *stm) matches = true; } else { CXXRecordDecl *recordDecl = t->getAsCXXRecordDecl(); - matches = recordDecl && t->isClassType() && isMatchingClass(recordDecl->getNameAsString()); + matches = recordDecl && t->isClassType() && isMatchingClass(clazy::name(recordDecl)); } if (matches) { - string typeName = StringUtils::simpleTypeName(typeList[0], lo()); + string typeName = clazy::simpleTypeName(typeList[0], lo()); typeName[0] = toupper(typeName[0]); string typeName2 = typeName; if (typeName[0] == 'Q') typeName2.erase(0, 1); // Remove first letter std::string error = std::string("Use QVariant::to" + typeName2 + "() instead of QVariant::value<" + typeName + ">()"); - emitWarning(stm->getLocStart(), error.c_str()); + emitWarning(getLocStart(stm), error.c_str()); } } - -REGISTER_CHECK("qvariant-template-instantiation", QVariantTemplateInstantiation, CheckLevel0) diff --git a/src/checks/level0/strict-iterators.cpp b/src/checks/level0/strict-iterators.cpp index dd85a9d8..8bdac226 100644 --- a/src/checks/level0/strict-iterators.cpp +++ b/src/checks/level0/strict-iterators.cpp @@ -26,7 +26,6 @@ #include "QtUtils.h" #include "StringUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -37,7 +36,7 @@ using namespace std; // QVector::iterator isn't even a class, it's a typedef. StrictIterators::StrictIterators(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -56,25 +55,25 @@ bool StrictIterators::handleImplicitCast(ImplicitCastExpr *implicitCast) if (!implicitCast) return false; - const string nameTo = StringUtils::simpleTypeName(implicitCast->getType(), m_context->ci.getLangOpts()); + const string nameTo = clazy::simpleTypeName(implicitCast->getType(), m_context->ci.getLangOpts()); const QualType typeTo = implicitCast->getType(); CXXRecordDecl *recordTo = TypeUtils::parentRecordForTypedef(typeTo); - if (recordTo && !QtUtils::isQtCOWIterableClass(recordTo)) + if (recordTo && !clazy::isQtCOWIterableClass(recordTo)) return false; recordTo = TypeUtils::typeAsRecord(typeTo); - if (recordTo && !QtUtils::isQtCOWIterator(recordTo)) + if (recordTo && !clazy::isQtCOWIterator(recordTo)) return false; assert(implicitCast->getSubExpr()); QualType typeFrom = implicitCast->getSubExpr()->getType(); CXXRecordDecl *recordFrom = TypeUtils::parentRecordForTypedef(typeFrom); - if (recordFrom && !QtUtils::isQtCOWIterableClass(recordFrom)) + if (recordFrom && !clazy::isQtCOWIterableClass(recordFrom)) return false; // const_iterator might be a typedef to pointer, like const T *, instead of a class, so just check for const qualification in that case - if (!(TypeUtils::pointeeQualType(typeTo).isConstQualified() || clazy_std::endsWith(nameTo, "const_iterator"))) + if (!(TypeUtils::pointeeQualType(typeTo).isConstQualified() || clazy::endsWith(nameTo, "const_iterator"))) return false; if (implicitCast->getCastKind() == CK_ConstructorConversion) { @@ -83,17 +82,17 @@ bool StrictIterators::handleImplicitCast(ImplicitCastExpr *implicitCast) } // TODO: some util function to get the name of a nested class - const bool nameToIsIterator = nameTo == "iterator" || clazy_std::endsWith(nameTo, "::iterator"); + const bool nameToIsIterator = nameTo == "iterator" || clazy::endsWith(nameTo, "::iterator"); if (nameToIsIterator) return false; - const string nameFrom = StringUtils::simpleTypeName(typeFrom, m_context->ci.getLangOpts()); - const bool nameFromIsIterator = nameFrom == "iterator" || clazy_std::endsWith(nameFrom, "::iterator"); + const string nameFrom = clazy::simpleTypeName(typeFrom, m_context->ci.getLangOpts()); + const bool nameFromIsIterator = nameFrom == "iterator" || clazy::endsWith(nameFrom, "::iterator"); if (!nameFromIsIterator) return false; - if (recordTo && clazy_std::startsWith(recordTo->getQualifiedNameAsString(), "OrderedSet")) { - string filename = m_sm.getFilename(implicitCast->getLocStart()); + if (recordTo && clazy::startsWith(recordTo->getQualifiedNameAsString(), "OrderedSet")) { + string filename = m_sm.getFilename(getLocStart(implicitCast)); if (filename == "lalr.cpp") // Lots of false positives here, because of const_iterator -> iterator typedefs return false; } @@ -118,19 +117,17 @@ bool StrictIterators::handleOperator(CXXOperatorCallExpr *op) return false; CXXRecordDecl *record = method->getParent(); - if (!QtUtils::isQtCOWIterator(record)) + if (!clazy::isQtCOWIterator(record)) return false; - if (record->getNameAsString() != "iterator") + if (record->getName() != "iterator") return false; ParmVarDecl *p = method->getParamDecl(0); CXXRecordDecl *paramClass = p ? TypeUtils::typeAsRecord(TypeUtils::pointeeQualType(p->getType())) : nullptr; - if (!paramClass || paramClass->getNameAsString() != "const_iterator") + if (!paramClass || paramClass->getName() != "const_iterator") return false; emitWarning(op, "Mixing iterators with const_iterators"); return true; } - -REGISTER_CHECK("strict-iterators", StrictIterators, CheckLevel0) diff --git a/src/checks/level0/temporaryiterator.cpp b/src/checks/level0/temporary-iterator.cpp index 935dd7fc..f36d6f60 100644 --- a/src/checks/level0/temporaryiterator.cpp +++ b/src/checks/level0/temporary-iterator.cpp @@ -23,9 +23,9 @@ Boston, MA 02110-1301, USA. */ -#include "checkmanager.h" + #include "ClazyContext.h" -#include "temporaryiterator.h" +#include "temporary-iterator.h" #include "Utils.h" #include "HierarchyUtils.h" #include "StringUtils.h" @@ -58,12 +58,12 @@ static bool isBlacklistedFunction(const string &name) { // These are fine static const vector<string> list = {"QVariant::toList", "QHash::operator[]", "QMap::operator[]", "QSet::operator[]"}; - return clazy_std::contains(list, name); + return clazy::contains(list, name); } void TemporaryIterator::VisitStmt(clang::Stmt *stm) { - CXXMemberCallExpr *memberExpr = dyn_cast<CXXMemberCallExpr>(stm); + auto memberExpr = dyn_cast<CXXMemberCallExpr>(stm); if (!memberExpr) return; @@ -73,37 +73,36 @@ void TemporaryIterator::VisitStmt(clang::Stmt *stm) return; // Check if it's a container - const std::string className = classDecl->getNameAsString(); - auto it = m_methodsByType.find(className); + auto it = m_methodsByType.find(clazy::name(classDecl)); if (it == m_methodsByType.end()) return; // Check if it's a method returning an iterator - const std::string functionName = methodDecl->getNameAsString(); + const StringRef functionName = clazy::name(methodDecl); const auto &allowedFunctions = it->second; - if (!clazy_std::contains(allowedFunctions, functionName)) + if (!clazy::contains(allowedFunctions, functionName)) return; // Catch getList().cbegin().value(), which is ok - if (HierarchyUtils::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, m_context->parentMap->getParent(memberExpr))) + if (clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, m_context->parentMap->getParent(memberExpr))) return; // Catch variant.toList().cbegin(), which is ok - CXXMemberCallExpr *chainedMemberCall = HierarchyUtils::getFirstChildOfType<CXXMemberCallExpr>(memberExpr); + auto chainedMemberCall = clazy::getFirstChildOfType<CXXMemberCallExpr>(memberExpr); if (chainedMemberCall) { - if (isBlacklistedFunction(StringUtils::qualifiedMethodName(chainedMemberCall->getMethodDecl()))) + if (isBlacklistedFunction(clazy::qualifiedMethodName(chainedMemberCall->getMethodDecl()))) return; } // catch map[foo].cbegin() - CXXOperatorCallExpr *chainedOperatorCall = HierarchyUtils::getFirstChildOfType<CXXOperatorCallExpr>(memberExpr); + CXXOperatorCallExpr *chainedOperatorCall = clazy::getFirstChildOfType<CXXOperatorCallExpr>(memberExpr); if (chainedOperatorCall) { FunctionDecl *func = chainedOperatorCall->getDirectCallee(); if (func) { - CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(func); + auto method = dyn_cast<CXXMethodDecl>(func); if (method) { - if (isBlacklistedFunction(StringUtils::qualifiedMethodName(method))) + if (isBlacklistedFunction(clazy::qualifiedMethodName(method))) return; } } @@ -128,24 +127,22 @@ void TemporaryIterator::VisitStmt(clang::Stmt *stm) if (impl->getCastKind() == CK_LValueToRValue) return; - Stmt *firstChild = HierarchyUtils::getFirstChild(impl); + Stmt *firstChild = clazy::getFirstChild(impl); if (firstChild && isa<ImplicitCastExpr>(firstChild) && dyn_cast<ImplicitCastExpr>(firstChild)->getCastKind() == CK_LValueToRValue) return; } } - CXXConstructExpr *possibleCtorCall = dyn_cast_or_null<CXXConstructExpr>(HierarchyUtils::getFirstChildAtDepth(expr, 2)); + CXXConstructExpr *possibleCtorCall = dyn_cast_or_null<CXXConstructExpr>(clazy::getFirstChildAtDepth(expr, 2)); if (possibleCtorCall) return; - CXXThisExpr *possibleThisCall = dyn_cast_or_null<CXXThisExpr>(HierarchyUtils::getFirstChildAtDepth(expr, 1)); + CXXThisExpr *possibleThisCall = dyn_cast_or_null<CXXThisExpr>(clazy::getFirstChildAtDepth(expr, 1)); if (possibleThisCall) return; // llvm::errs() << "Expression: " << expr->getStmtClassName() << "\n"; - std::string error = std::string("Don't call ") + StringUtils::qualifiedMethodName(methodDecl) + std::string("() on temporary"); - emitWarning(stm->getLocStart(), error.c_str()); + std::string error = std::string("Don't call ") + clazy::qualifiedMethodName(methodDecl) + std::string("() on temporary"); + emitWarning(getLocStart(stm), error.c_str()); } - -REGISTER_CHECK("temporary-iterator", TemporaryIterator, CheckLevel0) diff --git a/src/checks/level0/temporaryiterator.h b/src/checks/level0/temporary-iterator.h index 22316af6..ff64b980 100644 --- a/src/checks/level0/temporaryiterator.h +++ b/src/checks/level0/temporary-iterator.h @@ -43,7 +43,7 @@ public: TemporaryIterator(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stm) override; private: - std::map<std::string, std::vector<std::string>> m_methodsByType; + std::map<llvm::StringRef, std::vector<llvm::StringRef>> m_methodsByType; }; #endif diff --git a/src/checks/level0/unused-non-trivial-variable.cpp b/src/checks/level0/unused-non-trivial-variable.cpp index a05ee2a3..03023ff6 100644 --- a/src/checks/level0/unused-non-trivial-variable.cpp +++ b/src/checks/level0/unused-non-trivial-variable.cpp @@ -21,11 +21,12 @@ #include "unused-non-trivial-variable.h" #include "Utils.h" -#include "checkmanager.h" + #include "StringUtils.h" #include "HierarchyUtils.h" #include "ContextUtils.h" #include "QtUtils.h" +#include "clazy_stl.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -38,8 +39,16 @@ using namespace std; UnusedNonTrivialVariable::UnusedNonTrivialVariable(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { + const char *user_blacklist = getenv("CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_BLACKLIST"); + const char *user_whitelist = getenv("CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_WHITELIST"); + + if (user_blacklist) + m_userBlacklist = clazy::splitString(user_blacklist, ','); + + if (user_whitelist) + m_userWhitelist = clazy::splitString(user_whitelist, ','); } void UnusedNonTrivialVariable::VisitStmt(clang::Stmt *stmt) @@ -52,29 +61,79 @@ void UnusedNonTrivialVariable::VisitStmt(clang::Stmt *stmt) handleVarDecl(dyn_cast<VarDecl>(decl)); } +bool UnusedNonTrivialVariable::isUninterestingType(const CXXRecordDecl *record) const +{ + static const vector<StringRef> blacklist = { "QMutexLocker", "QDebugStateSaver", + "QTextBlockFormat", "QWriteLocker", + "QSignalBlocker", "QReadLocker", "PRNGLocker", "QDBusWriteLocker", "QDBusBlockingCallWatcher", + "QBoolBlocker", "QOrderedMutexLocker", "QTextLine", "QScopedScopeLevelCounter" }; + + // Check some obvious candidates first + StringRef typeName = clazy::name(record); + bool any = clazy::any_of(blacklist, [typeName] (StringRef container) { + return container == typeName; + }); + + if (any) + return true; + + static const vector<StringRef> blacklistedTemplates = { "QScopedPointer", "QSetValueOnDestroy", "QScopedValueRollback" }; + StringRef className = clazy::name(record); + for (StringRef templateName : blacklistedTemplates) { + if (clazy::startsWith(className, templateName)) + return true; + } + + // Now check the user's blacklist, set by env-variable + any = clazy::any_of(m_userBlacklist, [typeName] (const std::string &container) { + return container == typeName; + }); + + if (any) + return true; + + return false; +} + bool UnusedNonTrivialVariable::isInterestingType(QualType t) const { // TODO Remove QColor in Qt6 - static const vector<string> nonTrivialTypes = { "QColor", "QVariant", "QFont", "QUrl", "QIcon", - "QImage", "QPixmap", "QPicture", "QBitmap", "QBrush", - "QPen", "QBuffer", "QCache", "QDateTime", "QDir", "QEvent", - "QFileInfo", "QFontInfo", "QFontMetrics", "QJSValue", "QLocale", - "QRegularExpression", "QRegExp", "QUrlQuery", "QStorageInfo", - "QPersistentModelIndex", "QJsonArray", "QJsonDocument", - "QMimeType", "QBitArray", "QCollator", - "QByteArrayList", "QCollatorSortKey", - "QCursor", "QPalette", "QPainterPath", "QRegion", "QFontInfo", "QTextCursor", - "QStaticText", "QFontMetricsF", "QTextFrameFormat", "QTextImageFormat", - "QNetworkCookie", "QNetworkRequest", "QNetworkConfiguration", - "QHostAddress", "QSqlQuery", "QSqlRecord", "QSqlField", - "QLine", "QLineF", "QRect", "QRectF" - }; - - if (QtUtils::isQtContainer(t)) + static const vector<StringRef> nonTrivialTypes = { "QColor", "QVariant", "QFont", "QUrl", "QIcon", + "QImage", "QPixmap", "QPicture", "QBitmap", "QBrush", + "QPen", "QBuffer", "QCache", "QDateTime", "QDir", "QEvent", + "QFileInfo", "QFontInfo", "QFontMetrics", "QJSValue", "QLocale", + "QRegularExpression", "QRegExp", "QUrlQuery", "QStorageInfo", + "QPersistentModelIndex", "QJsonArray", "QJsonDocument", + "QMimeType", "QBitArray", "QCollator", + "QByteArrayList", "QCollatorSortKey", + "QCursor", "QPalette", "QPainterPath", "QRegion", "QFontInfo", "QTextCursor", + "QStaticText", "QFontMetricsF", "QTextFrameFormat", "QTextImageFormat", + "QNetworkCookie", "QNetworkRequest", "QNetworkConfiguration", + "QHostAddress", "QSqlQuery", "QSqlRecord", "QSqlField", + "QLine", "QLineF", "QRect", "QRectF" + }; + + CXXRecordDecl *record = TypeUtils::typeAsRecord(t); + if (!record) + return false; + + if (isOptionSet("no-whitelist")) { + // Will cause too many false-positives, like RAII classes. Use suppressing comments to silence them. + return !isUninterestingType(record); + } + + if (clazy::isQtContainer(record)) + return true; + + StringRef typeName = clazy::name(record); + bool any = clazy::any_of(nonTrivialTypes, [typeName] (StringRef container) { + return container == typeName; + }); + + if (any) return true; - const string typeName = StringUtils::simpleTypeName(t, lo()); - return clazy_std::any_of(nonTrivialTypes, [typeName] (const string &container) { + return clazy::any_of(m_userWhitelist, [typeName] (const std::string &container) { return container == typeName; }); } @@ -84,22 +143,19 @@ void UnusedNonTrivialVariable::handleVarDecl(VarDecl *varDecl) if (!varDecl || !isInterestingType(varDecl->getType())) return; - auto currentFunc = ContextUtils::firstContextOfType<FunctionDecl>(varDecl->getDeclContext()); + auto currentFunc = clazy::firstContextOfType<FunctionDecl>(varDecl->getDeclContext()); Stmt *body = currentFunc ? currentFunc->getBody() : nullptr; if (!body) return; - SourceLocation locStart = varDecl->getLocStart(); + SourceLocation locStart = getLocStart(varDecl); locStart = sm().getExpansionLoc(locStart); - auto declRefs = HierarchyUtils::getStatements<DeclRefExpr>(body, &sm(), locStart); + auto declRefs = clazy::getStatements<DeclRefExpr>(body, &sm(), locStart); auto pred = [varDecl] (DeclRefExpr *declRef) { return declRef->getDecl() == varDecl; }; - if (!clazy_std::any_of(declRefs, pred)) - emitWarning(locStart, "unused " + StringUtils::simpleTypeName(varDecl->getType(), lo())); + if (!clazy::any_of(declRefs, pred)) + emitWarning(locStart, "unused " + clazy::simpleTypeName(varDecl->getType(), lo())); } - - -REGISTER_CHECK("unused-non-trivial-variable", UnusedNonTrivialVariable, CheckLevel0) diff --git a/src/checks/level0/unused-non-trivial-variable.h b/src/checks/level0/unused-non-trivial-variable.h index 5aaff33b..0a35431f 100644 --- a/src/checks/level0/unused-non-trivial-variable.h +++ b/src/checks/level0/unused-non-trivial-variable.h @@ -44,6 +44,9 @@ public: private: void handleVarDecl(clang::VarDecl *varDecl); bool isInterestingType(clang::QualType t) const; + bool isUninterestingType(const clang::CXXRecordDecl *record) const; + std::vector<std::string> m_userBlacklist; + std::vector<std::string> m_userWhitelist; }; #endif diff --git a/src/checks/level0/writingtotemporary.cpp b/src/checks/level0/writing-to-temporary.cpp index 2b5cfdc1..7467fa12 100644 --- a/src/checks/level0/writingtotemporary.cpp +++ b/src/checks/level0/writing-to-temporary.cpp @@ -22,9 +22,8 @@ Boston, MA 02110-1301, USA. */ -#include "writingtotemporary.h" +#include "writing-to-temporary.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -35,30 +34,24 @@ using namespace std; WritingToTemporary::WritingToTemporary(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) , m_widenCriteria(isOptionSet("widen-criteria")) { m_filesToIgnore = { "qstring.h" }; } -vector<string> WritingToTemporary::supportedOptions() const -{ - static const vector<string> options = { "widen-criteria" }; - return options; -} - static bool isDisallowedClass(const string &className) { static const vector<string> disallowed = { "QTextCursor", "QDomElement", "KConfigGroup", "QWebElement", "QScriptValue", "QTextLine", "QTextBlock", "QDomNode", "QJSValue", "QTextTableCell" }; - return clazy_std::contains(disallowed, className); + return clazy::contains(disallowed, className); } static bool isDisallowedMethod(const string &name) { static const vector<string> disallowed = { "QColor::getCmyk", "QColor::getCmykF" }; - return clazy_std::contains(disallowed, name); + return clazy::contains(disallowed, name); } static bool isKnownType(const string &className) @@ -69,7 +62,7 @@ static bool isKnownType(const string &className) "QVector4D", "QSize", "QSizeF", "QSizePolicy", "QPoint", "QPointF", "QColor" }; - return clazy_std::contains(types, className); + return clazy::contains(types, className); } void WritingToTemporary::VisitStmt(clang::Stmt *stmt) @@ -78,7 +71,7 @@ void WritingToTemporary::VisitStmt(clang::Stmt *stmt) if (!callExpr) return; - if (shouldIgnoreFile(stmt->getLocStart())) + if (shouldIgnoreFile(getLocStart(stmt))) return; // For a chain like getFoo().setBar(), returns {setBar(), getFoo()} @@ -114,14 +107,12 @@ void WritingToTemporary::VisitStmt(clang::Stmt *stmt) if (!secondFuncReturnType || !secondFuncReturnType->isVoidType()) return; - if (!m_widenCriteria && !isKnownType(record->getNameAsString()) && !clazy_std::startsWith(secondFunc->getNameAsString(), "set")) + if (!m_widenCriteria && !isKnownType(record->getNameAsString()) && !clazy::startsWith(secondFunc->getNameAsString(), "set")) return; const string &methodName = secondMethod->getQualifiedNameAsString(); if (isDisallowedMethod(methodName)) return; - emitWarning(stmt->getLocStart(), "Call to temporary is a no-op: " + methodName); + emitWarning(getLocStart(stmt), "Call to temporary is a no-op: " + methodName); } - -REGISTER_CHECK("writing-to-temporary", WritingToTemporary, CheckLevel0) diff --git a/src/checks/level0/writingtotemporary.h b/src/checks/level0/writing-to-temporary.h index 58035e4e..42230e18 100644 --- a/src/checks/level0/writingtotemporary.h +++ b/src/checks/level0/writing-to-temporary.h @@ -43,8 +43,6 @@ class WritingToTemporary : public CheckBase public: explicit WritingToTemporary(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stmt) override; -protected: - std::vector<std::string> supportedOptions() const override; private: const bool m_widenCriteria; }; diff --git a/src/checks/level0/wrong-qevent-cast.cpp b/src/checks/level0/wrong-qevent-cast.cpp new file mode 100644 index 00000000..cc524700 --- /dev/null +++ b/src/checks/level0/wrong-qevent-cast.cpp @@ -0,0 +1,273 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "wrong-qevent-cast.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" +#include "ClazyContext.h" + +#include <clang/AST/AST.h> +#include <unordered_map> + +using namespace clang; +using namespace std; + +typedef vector<StringRef> ClassNameList; + +enum QtUnregularlyNamedEventTypes { + DragEnter = 60, + DragLeave = 62, + OrientationChange = 208, + ActionAdded = 114, + ActionRemoved = 115, + ActionChanged = 99, + ChildAdded = 68, + ChildRemoved = 71, + ChildPolished = 69, + MouseButtonPress = 2, + MouseButtonRelease = 3, + MouseButtonDblClick = 4, + MouseMove = 5, + NonClientAreaMouseMove = 173, + NonClientAreaMouseButtonPress = 174, + NonClientAreaMouseButtonRelease = 175, + NonClientAreaMouseButtonDblClick = 176, + FocusIn = 8, + FocusOut = 9, + FocusAboutToChange = 23, + Gesture = 198, + GestureOverride = 202, + HoverEnter = 127, + HoverLeave = 128, + HoverMove = 129, + TabletEnterProximity = 171, + TabletLeaveProximity = 172, + TabletPress = 92, + TabletMove = 87, + TabletRelease = 93, + ToolTip = 110, + Wheel = 31, + KeyPress = 6, + KeyRelease = 7, + ShortcutOverride = 51, + DragMove = 61, + GraphicsSceneMouseMove = 155, + GraphicsSceneMousePress = 156, + GraphicsSceneMouseRelease = 157, + GraphicsSceneMouseDoubleClick = 158, + GraphicsSceneContextMenu = 159, + GraphicsSceneHoverEnter = 160, + GraphicsSceneHoverMove = 161, + GraphicsSceneHoverLeave = 162, + GraphicsSceneHelp = 163, + GraphicsSceneDragEnter = 164, + GraphicsSceneDragMove = 165, + GraphicsSceneDragLeave = 166, + GraphicsSceneDrop = 167, + GraphicsSceneWheel = 168, + GraphicsSceneResize = 181, + TouchBegin = 194, + TouchEnd = 196, + TouchCancel = 209, + TouchUpdate = 195, + NativeGesture = 197, + MetaCall = 43, + WhatsThis = 111, + ContextMenu = 82, + QueryWhatsThis = 123 + // StatusTip = 112 not irregular, but qtbase casts it to QHelpEvent for some reason, needs investigation +}; + + +WrongQEventCast::WrongQEventCast(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ + + +} + +static bool eventTypeMatchesClass(QtUnregularlyNamedEventTypes eventType, string eventTypeStr, StringRef className) +{ + // In the simplest case, the class is "Q" + eventType + "Event" + string expectedClassName = string("Q") + eventTypeStr + string("Event"); + if (expectedClassName == className) + return true; + + // Otherwise it's unregular and we need a map: + + static unordered_map<QtUnregularlyNamedEventTypes, ClassNameList, std::hash<int>> map = { + { ActionAdded, {"QActionEvent" } }, + { ActionRemoved, {"QActionEvent" } }, + { ActionChanged, {"QActionEvent" } }, + { ChildAdded, {"QChildEvent" } }, + { ChildRemoved, {"QChildEvent" } }, + { ChildPolished, {"QChildEvent" } }, + { MetaCall, {"QDBusSpyCallEvent", "QDBusCallDeliveryEvent"} }, + { DragEnter, {"QDragEnterEvent", "QDragMoveEvent", "QDropEvent" } }, + { DragLeave, {"QDragLeaveEvent", "QDragMoveEvent", "QDropEvent" } }, + { DragMove, {"QDragMoveEvent", "QDropEvent" } }, + { FocusIn, {"QFocusEvent" } }, + { FocusOut, {"QFocusEvent" } }, + { FocusAboutToChange, {"QFocusEvent" } }, + { Gesture, {"QGestureEvent" } }, + { GestureOverride, {"QGestureEvent" } }, + { GraphicsSceneContextMenu, {"QGraphicsSceneEvent" } }, + { GraphicsSceneHoverEnter, { "QGraphicsSceneHoverEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneHoverMove, {"QGraphicsSceneHoverEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneHoverLeave, {"QGraphicsSceneHoverEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneHelp, { "QGraphicsSceneEvent" } }, + { GraphicsSceneDragEnter, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneDragMove, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneDragLeave, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneDrop, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent" } }, + { GraphicsSceneWheel, {"QGraphicsSceneEvent" } }, + { GraphicsSceneResize, {"QGraphicsSceneEvent" } }, + { GraphicsSceneMouseMove, {"QGraphicsSceneMouseEvent" } }, + { GraphicsSceneMousePress, {"QGraphicsSceneMouseEvent" } }, + { GraphicsSceneMouseRelease, {"QGraphicsSceneMouseEvent" } }, + { GraphicsSceneMouseDoubleClick, {"QGraphicsSceneMouseEvent" } }, + //{ StatusTip, {"QStatusTipEvent" } }, + { ToolTip, {"QHelpEvent" } }, + { WhatsThis, {"QHelpEvent" } }, + { QueryWhatsThis, {"QHelpEvent" } }, + { HoverEnter, {"QHoverEvent", "QInputEvent" } }, + { HoverLeave, {"QHoverEvent", "QInputEvent" } }, + { HoverMove, {"QHoverEvent", "QInputEvent" } }, + { KeyPress, {"QKeyEvent", "QInputEvent" } }, + { KeyRelease, {"QKeyEvent", "QInputEvent" } }, + { ShortcutOverride, {"QKeyEvent", "QInputEvent" } }, + { MouseButtonPress, {"QMouseEvent" } }, + { MouseButtonRelease, {"QMouseEvent" } }, + { MouseButtonDblClick, {"QMouseEvent" } }, + { MouseMove, {"QMouseEvent" } }, + { NonClientAreaMouseMove, {"QMouseEvent" } }, + { NonClientAreaMouseButtonPress, {"QMouseEvent" } }, + { NonClientAreaMouseButtonRelease, {"QMouseEvent" } }, + { NonClientAreaMouseButtonRelease, {"QMouseEvent" } }, + { NonClientAreaMouseButtonDblClick, {"QMouseEvent" } }, + { NativeGesture, { "QInputEvent" } }, + { OrientationChange, {"QScreenOrientationChangeEvent" } }, + { TabletEnterProximity, {"QTabletEvent", "QInputEvent" } }, + { TabletLeaveProximity, {"QTabletEvent", "QInputEvent" } }, + { TabletPress, {"QTabletEvent", "QInputEvent" } }, + { TabletMove, {"QTabletEvent", "QInputEvent" } }, + { TabletRelease, {"QTabletEvent", "QInputEvent" } }, + { TouchBegin, {"QTouchEvent", "QInputEvent" } }, + { TouchCancel, {"QTouchEvent", "QInputEvent" } }, + { TouchEnd, {"QTouchEvent", "QInputEvent" } }, + { TouchUpdate, {"QTouchEvent", "QInputEvent" } }, + { Wheel, {"QInputEvent" } }, + { ContextMenu, {"QInputEvent" } } + }; + + auto it = map.find(eventType); + if (it == map.cend()) + return false; + + const ClassNameList &classes = it->second; + const bool found = clazy::find(classes, className) != classes.cend(); + + return found; +} + + +// TODO: Use iterators +CaseStmt* getCaseStatement(clang::ParentMap *pmap, Stmt *stmt, DeclRefExpr *event) +{ + Stmt *s = pmap->getParent(stmt); + + while (s) { + + if (auto ifStmt = dyn_cast<IfStmt>(s)) { + // if there's we're inside an if statement then skip, to avoid false-positives + auto declRef = clazy::getFirstChildOfType2<DeclRefExpr>(ifStmt->getCond()); + if (declRef && declRef->getDecl() == event->getDecl()) + return nullptr; + } + + + if (auto caseStmt = dyn_cast<CaseStmt>(s)) { + auto switchStmt = clazy::getSwitchFromCase(pmap, caseStmt); + if (switchStmt) { + auto declRef = clazy::getFirstChildOfType2<DeclRefExpr>(switchStmt->getCond()); + + switchStmt->getCond()->dump(); + + // Does this switch refer to the same QEvent ? + if (declRef && declRef->getDecl() == event->getDecl()) + return caseStmt; + } + } + + s = pmap->getParent(s); + } + + return nullptr; +} + +void WrongQEventCast::VisitStmt(clang::Stmt *stmt) +{ + auto cast = dyn_cast<CXXStaticCastExpr>(stmt); + if (!cast) + return; + + Expr *e = cast->getSubExpr(); + + QualType t = e ? e->getType() : QualType(); + QualType pointeeType = t.isNull() ? QualType() : TypeUtils::pointeeQualType(t); + CXXRecordDecl *rec = pointeeType.isNull() ? nullptr : pointeeType->getAsCXXRecordDecl(); + + if (!rec || clazy::name(rec) != "QEvent") + return; + + CXXRecordDecl *castTo = Utils::namedCastOuterDecl(cast); + if (!castTo) + return; + + auto declref = clazy::getFirstChildOfType2<DeclRefExpr>(cast->getSubExpr()); + if (!declref) + return; + + auto caseStmt = getCaseStatement(m_context->parentMap, stmt, declref); + if (!caseStmt) + return; + + auto caseValue = clazy::getFirstChildOfType2<DeclRefExpr>(caseStmt->getLHS()); + if (!caseValue) + return; + + + auto enumeratorDecl = dyn_cast<EnumConstantDecl>(caseValue->getDecl()); + if (!enumeratorDecl) + return; + + auto enumeratorVal = static_cast<QtUnregularlyNamedEventTypes>(enumeratorDecl->getInitVal().getExtValue()); + + string eventTypeStr = enumeratorDecl->getNameAsString(); + StringRef castToName = clazy::name(castTo); + + if (eventTypeMatchesClass(enumeratorVal, eventTypeStr, castToName)) + return; + + emitWarning(stmt, string("Cast from a QEvent::") + eventTypeStr + " event to " + string(castToName) + " looks suspicious."); +} diff --git a/src/checks/level0/wrong-qevent-cast.h b/src/checks/level0/wrong-qevent-cast.h new file mode 100644 index 00000000..043cdb82 --- /dev/null +++ b/src/checks/level0/wrong-qevent-cast.h @@ -0,0 +1,39 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_WRONG_QEVENT_CAST_H +#define CLAZY_WRONG_QEVENT_CAST_H + +#include "checkbase.h" + + +/** + * See README-wrong-qevent-cast.md for more info. + */ +class WrongQEventCast : public CheckBase +{ +public: + explicit WrongQEventCast(const std::string &name, ClazyContext *context); + void VisitStmt(clang::Stmt *) override; +private: +}; + +#endif diff --git a/src/checks/level0/wrong-qglobalstatic.cpp b/src/checks/level0/wrong-qglobalstatic.cpp index 02a9f6ee..1c32ee53 100644 --- a/src/checks/level0/wrong-qglobalstatic.cpp +++ b/src/checks/level0/wrong-qglobalstatic.cpp @@ -22,7 +22,6 @@ #include "wrong-qglobalstatic.h" #include "Utils.h" #include "TemplateUtils.h" -#include "checkmanager.h" #include "MacroUtils.h" #include "StringUtils.h" #include "Utils.h" @@ -46,15 +45,15 @@ void WrongQGlobalStatic::VisitStmt(clang::Stmt *stmt) return; CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor(); - if (!ctorDecl || ctorDecl->getNameAsString() != "QGlobalStatic") + if (!ctorDecl || clazy::name(ctorDecl) != "QGlobalStatic") return; - SourceLocation loc = stmt->getLocStart(); - if (MacroUtils::isInMacro(&m_astContext, loc, "Q_GLOBAL_STATIC_WITH_ARGS")) + SourceLocation loc = getLocStart(stmt); + if (clazy::isInMacro(&m_astContext, loc, "Q_GLOBAL_STATIC_WITH_ARGS")) return; CXXRecordDecl *record = ctorDecl->getParent(); - vector<QualType> typeList = TemplateUtils::getTemplateArgumentsTypes(record); + vector<QualType> typeList = clazy::getTemplateArgumentsTypes(record); const Type *t = typeList.empty() ? nullptr : typeList[0].getTypePtrOrNull(); if (!t) return; @@ -71,5 +70,3 @@ void WrongQGlobalStatic::VisitStmt(clang::Stmt *stmt) emitWarning(loc, error.c_str()); } } - -REGISTER_CHECK("wrong-qglobalstatic", WrongQGlobalStatic, CheckLevel0) diff --git a/src/checks/level1/README-qproperty-without-notify.md b/src/checks/level1/README-qproperty-without-notify.md deleted file mode 100644 index c3435f1b..00000000 --- a/src/checks/level1/README-qproperty-without-notify.md +++ /dev/null @@ -1,3 +0,0 @@ -# qproperty - -Warns when a non-CONSTANT Q_PROPERTY is missing a NOTIFY signal. diff --git a/src/checks/level1/autounexpectedqstringbuilder.cpp b/src/checks/level1/auto-unexpected-qstringbuilder.cpp index 857cb275..01ae42bf 100644 --- a/src/checks/level1/autounexpectedqstringbuilder.cpp +++ b/src/checks/level1/auto-unexpected-qstringbuilder.cpp @@ -20,11 +20,11 @@ Boston, MA 02110-1301, USA. */ -#include "autounexpectedqstringbuilder.h" +#include "auto-unexpected-qstringbuilder.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "FixItUtils.h" +#include "TypeUtils.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -32,47 +32,52 @@ using namespace clang; using namespace std; - -enum Fixit { - FixitNone = 0, - FixitUseQString = 0x1, -}; +static bool isQStringBuilder(QualType t) +{ + CXXRecordDecl *record = TypeUtils::typeAsRecord(t); + return record && record->getName() == "QStringBuilder"; +} AutoUnexpectedQStringBuilder::AutoUnexpectedQStringBuilder(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } - void AutoUnexpectedQStringBuilder::VisitDecl(Decl *decl) { VarDecl *varDecl = dyn_cast<VarDecl>(decl); if (!varDecl) return; - const Type *type = varDecl->getType().getTypePtrOrNull(); - if (!type || !type->isRecordType() || !dyn_cast<AutoType>(type)) + QualType qualtype = varDecl->getType(); + const Type *type = qualtype.getTypePtrOrNull(); + if (!type || !type->isRecordType() || !dyn_cast<AutoType>(type) || !isQStringBuilder(qualtype)) return; - CXXRecordDecl *record = type->getAsCXXRecordDecl(); - if (record && record->getNameAsString() == "QStringBuilder") { - std::vector<FixItHint> fixits; + std::vector<FixItHint> fixits; + if (isFixitEnabled()) { + std::string replacement = "QString " + varDecl->getName().str(); - if (isFixitEnabled(FixitUseQString)) { - std::string replacement = "QString " + varDecl->getName().str(); + if (qualtype.isConstQualified()) + replacement = "const " + replacement; - if (varDecl->getType().isConstQualified()) - replacement = "const " + replacement; - - SourceLocation start = varDecl->getLocStart(); - SourceLocation end = varDecl->getLocation(); - fixits.push_back(FixItUtils::createReplacement({ start, end }, replacement)); - } - - emitWarning(decl->getLocStart(), "auto deduced to be QStringBuilder instead of QString. Possible crash.", fixits); + SourceLocation start = getLocStart(varDecl); + SourceLocation end = varDecl->getLocation(); + fixits.push_back(clazy::createReplacement({ start, end }, replacement)); } + + emitWarning(getLocStart(decl), "auto deduced to be QStringBuilder instead of QString. Possible crash.", fixits); } -const char *const s_checkName = "auto-unexpected-qstringbuilder"; -REGISTER_CHECK(s_checkName, AutoUnexpectedQStringBuilder, CheckLevel1) -REGISTER_FIXIT(FixitUseQString, "fix-auto-unexpected-qstringbuilder", s_checkName) +void AutoUnexpectedQStringBuilder::VisitStmt(Stmt *stmt) +{ + auto lambda = dyn_cast<LambdaExpr>(stmt); + if (!lambda) + return; + + CXXMethodDecl *method = lambda->getCallOperator(); + if (!method || !isQStringBuilder(method->getReturnType())) + return; + + emitWarning(getLocStart(stmt), "lambda return type deduced to be QStringBuilder instead of QString. Possible crash."); +} diff --git a/src/checks/level1/autounexpectedqstringbuilder.h b/src/checks/level1/auto-unexpected-qstringbuilder.h index 85cba028..f812eddd 100644 --- a/src/checks/level1/autounexpectedqstringbuilder.h +++ b/src/checks/level1/auto-unexpected-qstringbuilder.h @@ -38,6 +38,7 @@ class AutoUnexpectedQStringBuilder : public CheckBase public: explicit AutoUnexpectedQStringBuilder(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; + void VisitStmt(clang::Stmt *stmt) override; }; #endif diff --git a/src/checks/level1/child-event-qobject-cast.cpp b/src/checks/level1/child-event-qobject-cast.cpp index 9812afe0..1911985a 100644 --- a/src/checks/level1/child-event-qobject-cast.cpp +++ b/src/checks/level1/child-event-qobject-cast.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> #include <clang/AST/DeclCXX.h> @@ -33,12 +32,12 @@ using namespace clang; using namespace std; -ChildEvent_qobject_cast::ChildEvent_qobject_cast(const std::string &name, ClazyContext *context) - : CheckBase(name, context) +ChildEventQObjectCast::ChildEventQObjectCast(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } -void ChildEvent_qobject_cast::VisitDecl(Decl *decl) +void ChildEventQObjectCast::VisitDecl(Decl *decl) { auto childEventMethod = dyn_cast<CXXMethodDecl>(decl); if (!childEventMethod) @@ -49,22 +48,22 @@ void ChildEvent_qobject_cast::VisitDecl(Decl *decl) return; auto methodName = childEventMethod->getNameAsString(); - if (!clazy_std::equalsAny(methodName, {"event", "childEvent", "eventFilter"})) + if (!clazy::equalsAny(methodName, {"event", "childEvent", "eventFilter"})) return; - if (!QtUtils::isQObject(childEventMethod->getParent())) + if (!clazy::isQObject(childEventMethod->getParent())) return; - auto callExprs = HierarchyUtils::getStatements<CallExpr>(body, &(sm())); + auto callExprs = clazy::getStatements<CallExpr>(body, &(sm())); for (auto callExpr : callExprs) { if (callExpr->getNumArgs() != 1) continue; FunctionDecl *fdecl = callExpr->getDirectCallee(); - if (fdecl && fdecl->getNameAsString() == "qobject_cast") { - CXXMemberCallExpr *childCall = dyn_cast<CXXMemberCallExpr>(callExpr->getArg(0)); + if (fdecl && clazy::name(fdecl) == "qobject_cast") { + auto childCall = dyn_cast<CXXMemberCallExpr>(callExpr->getArg(0)); // The call to event->child() if (!childCall) continue; @@ -77,7 +76,3 @@ void ChildEvent_qobject_cast::VisitDecl(Decl *decl) } } } - - - -REGISTER_CHECK("child-event-qobject-cast", ChildEvent_qobject_cast, CheckLevel1) diff --git a/src/checks/level1/child-event-qobject-cast.h b/src/checks/level1/child-event-qobject-cast.h index 138d7cae..d21a08b0 100644 --- a/src/checks/level1/child-event-qobject-cast.h +++ b/src/checks/level1/child-event-qobject-cast.h @@ -28,10 +28,10 @@ /** * See README-child-event-qobject-cast for more info. */ -class ChildEvent_qobject_cast : public CheckBase +class ChildEventQObjectCast : public CheckBase { public: - explicit ChildEvent_qobject_cast(const std::string &name, ClazyContext *context); + explicit ChildEventQObjectCast(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; private: }; diff --git a/src/checks/level1/connect-3arg-lambda.cpp b/src/checks/level1/connect-3arg-lambda.cpp index 12148907..8463b90f 100644 --- a/src/checks/level1/connect-3arg-lambda.cpp +++ b/src/checks/level1/connect-3arg-lambda.cpp @@ -24,31 +24,48 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> using namespace clang; using namespace std; +using uint = unsigned; -Connect3argLambda::Connect3argLambda(const std::string &name, ClazyContext *context) - : CheckBase(name, context) +Connect3ArgLambda::Connect3ArgLambda(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } - -void Connect3argLambda::VisitStmt(clang::Stmt *stmt) +void Connect3ArgLambda::VisitStmt(clang::Stmt *stmt) { auto callExpr = dyn_cast<CallExpr>(stmt); if (!callExpr) return; FunctionDecl *fdecl = callExpr->getDirectCallee(); - if (!QtUtils::isConnect(fdecl) || fdecl->getNumParams() != 3) + if (!fdecl) + return; + + const uint numParams = fdecl->getNumParams(); + if (numParams != 2 && numParams != 3) + return; + + string qualifiedName = fdecl->getQualifiedNameAsString(); + if (qualifiedName == "QTimer::singleShot") { + processQTimer(fdecl, stmt); return; + } - auto lambda = HierarchyUtils::getFirstChildOfType2<LambdaExpr>(callExpr->getArg(2)); + if (qualifiedName == "QMenu::addAction") { + processQMenu(fdecl, stmt); + return; + } + + if (numParams != 3 || !clazy::isConnect(fdecl)) + return; + + auto lambda = clazy::getFirstChildOfType2<LambdaExpr>(callExpr->getArg(2)); if (!lambda) return; @@ -63,15 +80,14 @@ void Connect3argLambda::VisitStmt(clang::Stmt *stmt) if ((senderMemberExpr = dyn_cast<MemberExpr>(s))) break; - s = HierarchyUtils::getFirstChild(s); + s = clazy::getFirstChild(s); } - // The sender can be: this - CXXThisExpr* senderThis = HierarchyUtils::unpeal<CXXThisExpr>(callExpr->getArg(0), HierarchyUtils::IgnoreImplicitCasts); + auto senderThis = clazy::unpeal<CXXThisExpr>(callExpr->getArg(0), clazy::IgnoreImplicitCasts); // The variables used inside the lambda - auto declrefs = HierarchyUtils::getStatements<DeclRefExpr>(lambda->getBody()); + auto declrefs = clazy::getStatements<DeclRefExpr>(lambda->getBody()); ValueDecl *senderDecl = senderDeclRef ? senderDeclRef->getDecl() : nullptr; @@ -82,21 +98,53 @@ void Connect3argLambda::VisitStmt(clang::Stmt *stmt) if (decl == senderDecl) continue; // It's the sender, continue. - if (QtUtils::isQObject(decl->getType())) { + if (clazy::isQObject(decl->getType())) { found = true; break; } } if (!found) { - auto thisexprs = HierarchyUtils::getStatements<CXXThisExpr>(lambda->getBody()); + auto thisexprs = clazy::getStatements<CXXThisExpr>(lambda->getBody()); if (!thisexprs.empty() && !senderThis) found = true; } if (found) - emitWarning(stmt->getLocStart(), "Pass a context object as 3rd connect parameter"); + emitWarning(stmt, "Pass a context object as 3rd connect parameter"); } +void Connect3ArgLambda::processQTimer(FunctionDecl *func, Stmt *stmt) +{ + // Signatures to catch: + // QTimer::singleShot(int msec, Functor functor) + // QTimer::singleShot(int msec, Qt::TimerType timerType, Functor functor) + + const uint numParams = func->getNumParams(); + if (numParams == 2) { + if (func->getParamDecl(0)->getNameAsString() == "interval" && + func->getParamDecl(1)->getNameAsString() == "slot") { + emitWarning(stmt, "Pass a context object as 2nd singleShot parameter"); + } + } else if (numParams == 3) { + if (func->getParamDecl(0)->getNameAsString() == "interval" && + func->getParamDecl(1)->getNameAsString() == "timerType" && + func->getParamDecl(2)->getNameAsString() == "slot") { + emitWarning(stmt, "Pass a context object as 3rd singleShot parameter"); + } + } +} -REGISTER_CHECK("connect-3arg-lambda", Connect3argLambda, CheckLevel1) +void Connect3ArgLambda::processQMenu(FunctionDecl *func, Stmt *stmt) +{ + // Signatures to catch: + // QMenu::addAction(const QString &text, Func1 slot, const QKeySequence &shortcut = 0) + const uint numParams = func->getNumParams(); + if (numParams == 3) { + if (func->getParamDecl(0)->getNameAsString() == "text" && + func->getParamDecl(1)->getNameAsString() == "slot" && + func->getParamDecl(2)->getNameAsString() == "shortcut") { + emitWarning(stmt, "Pass a context object as 2nd singleShot parameter"); + } + } +} diff --git a/src/checks/level1/connect-3arg-lambda.h b/src/checks/level1/connect-3arg-lambda.h index 69b8a1b6..13c819dd 100644 --- a/src/checks/level1/connect-3arg-lambda.h +++ b/src/checks/level1/connect-3arg-lambda.h @@ -28,12 +28,14 @@ /** * See README-connect-3arg-lambda.md for more info. */ -class Connect3argLambda : public CheckBase +class Connect3ArgLambda : public CheckBase { public: - explicit Connect3argLambda(const std::string &name, ClazyContext *context); + explicit Connect3ArgLambda(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stmt) override; private: + void processQTimer(clang::FunctionDecl *, clang::Stmt *); + void processQMenu(clang::FunctionDecl *, clang::Stmt *); }; #endif diff --git a/src/checks/level1/const-signal-or-slot.cpp b/src/checks/level1/const-signal-or-slot.cpp index 77fa0783..c1490658 100644 --- a/src/checks/level1/const-signal-or-slot.cpp +++ b/src/checks/level1/const-signal-or-slot.cpp @@ -24,10 +24,8 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "ClazyContext.h" #include "AccessSpecifierManager.h" - #include <clang/AST/AST.h> using namespace clang; @@ -35,7 +33,7 @@ using namespace std; ConstSignalOrSlot::ConstSignalOrSlot(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { context->enableAccessSpecifierManager(); } @@ -48,10 +46,10 @@ void ConstSignalOrSlot::VisitStmt(clang::Stmt *stmt) return; FunctionDecl *func = call->getDirectCallee(); - if (!QtUtils::isConnect(func) || !QtUtils::connectHasPMFStyle(func)) + if (!clazy::isConnect(func) || !clazy::connectHasPMFStyle(func)) return; - CXXMethodDecl *slot = QtUtils::receiverMethodForConnect(call); + CXXMethodDecl *slot = clazy::receiverMethodForConnect(call); if (!slot || !slot->isConst() || slot->getReturnType()->isVoidType()) // const and returning void must do something, so not a getter return; @@ -91,5 +89,3 @@ void ConstSignalOrSlot::VisitDecl(Decl *decl) emitWarning(decl, "signal " + method->getQualifiedNameAsString() + " shouldn't be const"); } } - -REGISTER_CHECK("const-signal-or-slot", ConstSignalOrSlot, CheckLevel1) diff --git a/src/checks/level1/detachingtemporary.cpp b/src/checks/level1/detaching-temporary.cpp index 0ec22385..ad4004d4 100644 --- a/src/checks/level1/detachingtemporary.cpp +++ b/src/checks/level1/detaching-temporary.cpp @@ -22,8 +22,8 @@ Boston, MA 02110-1301, USA. */ -#include "checkmanager.h" -#include "detachingtemporary.h" + +#include "detaching-temporary.h" #include "Utils.h" #include "StringUtils.h" #include "QtUtils.h" @@ -36,7 +36,7 @@ using namespace clang; using namespace std; DetachingTemporary::DetachingTemporary(const std::string &name, ClazyContext *context) - : DetachingBase(name, context) + : DetachingBase(name, context, Option_CanIgnoreIncludes) { // Extra stuff that isn't really related to detachments but doesn't make sense to call on temporaries m_writeMethodsByType["QString"] = {"push_back", "push_front", "clear", "chop"}; @@ -57,7 +57,7 @@ DetachingTemporary::DetachingTemporary(const std::string &name, ClazyContext *co bool isAllowedChainedClass(const std::string &className) { static const vector<string> allowed = {"QString", "QByteArray", "QVariant"}; - return clazy_std::contains(allowed, className); + return clazy::contains(allowed, className); } bool isAllowedChainedMethod(const std::string &methodName) @@ -69,12 +69,12 @@ bool isAllowedChainedMethod(const std::string &methodName) "QTableWidget::selectedItems", "QNetworkReply::rawHeaderList", "Mailbox::address", "QItemSelection::indexes", "QItemSelectionModel::selectedIndexes", "QMimeData::formats", "i18n", "QAbstractTransition::targetStates"}; - return clazy_std::contains(allowed, methodName); + return clazy::contains(allowed, methodName); } void DetachingTemporary::VisitStmt(clang::Stmt *stm) { - CallExpr *callExpr = dyn_cast<CallExpr>(stm); + auto callExpr = dyn_cast<CallExpr>(stm); if (!callExpr) return; @@ -102,8 +102,8 @@ void DetachingTemporary::VisitStmt(clang::Stmt *stm) return; // const doesn't detach } - CXXMethodDecl *firstMethod = dyn_cast<CXXMethodDecl>(firstFunc); - if (isAllowedChainedMethod(StringUtils::qualifiedMethodName(firstFunc))) { + auto firstMethod = dyn_cast<CXXMethodDecl>(firstFunc); + if (isAllowedChainedMethod(clazy::qualifiedMethodName(firstFunc))) { return; } @@ -112,27 +112,27 @@ void DetachingTemporary::VisitStmt(clang::Stmt *stm) } // Check if this is a QGlobalStatic - if (firstMethod && firstMethod->getParent()->getNameAsString() == "QGlobalStatic") { + if (firstMethod && clazy::name(firstMethod->getParent()) == "QGlobalStatic") { return; } CallExpr *secondCallToBeEvaluated = callExprs.at(callExprs.size() - 2); // This is the call to first() FunctionDecl *detachingFunc = secondCallToBeEvaluated->getDirectCallee(); - CXXMethodDecl *detachingMethod = detachingFunc ? dyn_cast<CXXMethodDecl>(detachingFunc) : nullptr; + auto detachingMethod = detachingFunc ? dyn_cast<CXXMethodDecl>(detachingFunc) : nullptr; const Type *detachingMethodReturnType = detachingMethod ? detachingMethod->getReturnType().getTypePtrOrNull() : nullptr; if (!detachingMethod || !detachingMethodReturnType) return; // Check if it's one of the implicit shared classes CXXRecordDecl *classDecl = detachingMethod->getParent(); - const std::string className = classDecl->getNameAsString(); + StringRef className = clazy::name(classDecl); - const std::unordered_map<string, std::vector<string> > &methodsByType = QtUtils::detachingMethods(); + const std::unordered_map<string, std::vector<StringRef> > &methodsByType = clazy::detachingMethods(); auto it = methodsByType.find(className); auto it2 = m_writeMethodsByType.find(className); - std::vector<std::string> allowedFunctions; - std::vector<std::string> allowedWriteFunctions; + std::vector<StringRef> allowedFunctions; + std::vector<StringRef> allowedWriteFunctions; if (it != methodsByType.end()) { allowedFunctions = it->second; } @@ -142,30 +142,29 @@ void DetachingTemporary::VisitStmt(clang::Stmt *stm) } // Check if it's one of the detaching methods - const std::string functionName = detachingMethod->getNameAsString(); + StringRef functionName = clazy::name(detachingMethod); string error; - const bool isReadFunction = clazy_std::contains(allowedFunctions, functionName); - const bool isWriteFunction = clazy_std::contains(allowedWriteFunctions, functionName); + const bool isReadFunction = clazy::contains(allowedFunctions, functionName); + const bool isWriteFunction = clazy::contains(allowedWriteFunctions, functionName); if (isReadFunction || isWriteFunction) { bool returnTypeIsIterator = false; CXXRecordDecl *returnRecord = detachingMethodReturnType->getAsCXXRecordDecl(); - if (returnRecord) { - returnTypeIsIterator = returnRecord->getNameAsString() == "iterator"; - } + if (returnRecord) + returnTypeIsIterator = clazy::name(returnRecord) == "iterator"; if (isWriteFunction && (detachingMethodReturnType->isVoidType() || returnTypeIsIterator)) { error = std::string("Modifying temporary container is pointless and it also detaches"); } else { - error = std::string("Don't call ") + StringUtils::qualifiedMethodName(detachingMethod) + std::string("() on temporary"); + error = std::string("Don't call ") + clazy::qualifiedMethodName(detachingMethod) + std::string("() on temporary"); } } if (!error.empty()) - emitWarning(stm->getLocStart(), error.c_str()); + emitWarning(getLocStart(stm), error.c_str()); } bool DetachingTemporary::isDetachingMethod(CXXMethodDecl *method) const @@ -180,16 +179,14 @@ bool DetachingTemporary::isDetachingMethod(CXXMethodDecl *method) const if (DetachingBase::isDetachingMethod(method)) return true; - const string className = record->getNameAsString(); + StringRef className = clazy::name(record); auto it = m_writeMethodsByType.find(className); if (it != m_writeMethodsByType.cend()) { const auto &methods = it->second; - if (clazy_std::contains(methods, method->getNameAsString())) + if (clazy::contains(methods, clazy::name(method))) return true; } return false; } - -REGISTER_CHECK("detaching-temporary", DetachingTemporary, CheckLevel1) diff --git a/src/checks/level1/detachingtemporary.h b/src/checks/level1/detaching-temporary.h index 1d693034..4f560e34 100644 --- a/src/checks/level1/detachingtemporary.h +++ b/src/checks/level1/detaching-temporary.h @@ -40,7 +40,7 @@ public: void VisitStmt(clang::Stmt *stm) override; private: bool isDetachingMethod(clang::CXXMethodDecl *method) const; - std::map<std::string, std::vector<std::string>> m_writeMethodsByType; + std::map<llvm::StringRef, std::vector<llvm::StringRef>> m_writeMethodsByType; }; #endif diff --git a/src/checks/level1/foreach.cpp b/src/checks/level1/foreach.cpp index 881eea47..8f00b8ef 100644 --- a/src/checks/level1/foreach.cpp +++ b/src/checks/level1/foreach.cpp @@ -28,8 +28,8 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "PreProcessorVisitor.h" +#include "StringUtils.h" #include <clang/AST/AST.h> @@ -37,7 +37,7 @@ using namespace clang; using namespace std; Foreach::Foreach(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { context->enablePreprocessorVisitor(); } @@ -60,22 +60,22 @@ void Foreach::VisitStmt(clang::Stmt *stmt) if (!m_lastForStmt) return; - CXXConstructExpr *constructExpr = dyn_cast<CXXConstructExpr>(stmt); + auto constructExpr = dyn_cast<CXXConstructExpr>(stmt); if (!constructExpr || constructExpr->getNumArgs() < 1) return; CXXConstructorDecl *constructorDecl = constructExpr->getConstructor(); - if (!constructorDecl || constructorDecl->getNameAsString() != "QForeachContainer") + if (!constructorDecl || clazy::name(constructorDecl) != "QForeachContainer") return; vector<DeclRefExpr*> declRefExprs; - HierarchyUtils::getChilds<DeclRefExpr>(constructExpr, declRefExprs); + clazy::getChilds<DeclRefExpr>(constructExpr, declRefExprs); if (declRefExprs.empty()) return; // Get the container value declaration DeclRefExpr *declRefExpr = declRefExprs.front(); - ValueDecl *valueDecl = dyn_cast<ValueDecl>(declRefExpr->getDecl()); + auto valueDecl = dyn_cast<ValueDecl>(declRefExpr->getDecl()); if (!valueDecl) return; @@ -89,17 +89,17 @@ void Foreach::VisitStmt(clang::Stmt *stmt) return; auto rootBaseClass = Utils::rootBaseClass(containerRecord); - const string containerClassName = rootBaseClass->getNameAsString(); - const bool isQtContainer = QtUtils::isQtIterableClass(containerClassName); + StringRef containerClassName = clazy::name(rootBaseClass); + const bool isQtContainer = clazy::isQtIterableClass(containerClassName); if (containerClassName.empty()) { - emitWarning(stmt->getLocStart(), "internal error, couldn't get class name of foreach container, please report a bug"); + emitWarning(getLocStart(stmt), "internal error, couldn't get class name of foreach container, please report a bug"); return; } else { if (!isQtContainer) { - emitWarning(stmt->getLocStart(), "foreach with STL container causes deep-copy (" + rootBaseClass->getQualifiedNameAsString() + ')'); + emitWarning(getLocStart(stmt), "foreach with STL container causes deep-copy (" + rootBaseClass->getQualifiedNameAsString() + ')'); return; } else if (containerClassName == "QVarLengthArray") { - emitWarning(stmt->getLocStart(), "foreach with QVarLengthArray causes deep-copy"); + emitWarning(getLocStart(stmt), "foreach with QVarLengthArray causes deep-copy"); return; } } @@ -115,7 +115,7 @@ void Foreach::VisitStmt(clang::Stmt *stmt) // Now look inside the for statement for detachments if (containsDetachments(m_lastForStmt, valueDecl)) { - emitWarning(stmt->getLocStart(), "foreach container detached"); + emitWarning(getLocStart(stmt), "foreach container detached"); } } @@ -123,13 +123,13 @@ void Foreach::checkBigTypeMissingRef() { // Get the inner forstm vector<ForStmt*> forStatements; - HierarchyUtils::getChilds<ForStmt>(m_lastForStmt->getBody(), forStatements); + clazy::getChilds<ForStmt>(m_lastForStmt->getBody(), forStatements); if (forStatements.empty()) return; // Get the variable declaration (lhs of foreach) vector<DeclStmt*> varDecls; - HierarchyUtils::getChilds<DeclStmt>(forStatements.at(0), varDecls); + clazy::getChilds<DeclStmt>(forStatements.at(0), varDecls); if (varDecls.empty()) return; @@ -157,7 +157,7 @@ void Foreach::checkBigTypeMissingRef() return; } - emitWarning(varDecl->getLocStart(), error.c_str()); + emitWarning(getLocStart(varDecl), error.c_str()); } } @@ -174,17 +174,17 @@ bool Foreach::containsDetachments(Stmt *stm, clang::ValueDecl *containerValueDec auto recordDecl = dyn_cast<CXXRecordDecl>(declContext); if (recordDecl) { const std::string className = Utils::rootBaseClass(recordDecl)->getQualifiedNameAsString(); - const std::unordered_map<string, std::vector<string> > &detachingMethodsMap = QtUtils::detachingMethods(); + const std::unordered_map<string, std::vector<StringRef> > &detachingMethodsMap = clazy::detachingMethods(); if (detachingMethodsMap.find(className) != detachingMethodsMap.end()) { const std::string functionName = valDecl->getNameAsString(); const auto &allowedFunctions = detachingMethodsMap.at(className); - if (clazy_std::contains(allowedFunctions, functionName)) { + if (clazy::contains(allowedFunctions, functionName)) { Expr *expr = memberExpr->getBase(); if (expr) { DeclRefExpr *refExpr = dyn_cast<DeclRefExpr>(expr); if (!refExpr) { - auto s = HierarchyUtils::getFirstChildAtDepth(expr, 1); + auto s = clazy::getFirstChildAtDepth(expr, 1); refExpr = dyn_cast<DeclRefExpr>(s); if (refExpr) { if (refExpr->getDecl() == containerValueDecl) { // Finally, check if this non-const member call is on the same container we're iterating @@ -199,9 +199,7 @@ bool Foreach::containsDetachments(Stmt *stm, clang::ValueDecl *containerValueDec } } - return clazy_std::any_of(stm->children(), [this, containerValueDecl](Stmt *child) { + return clazy::any_of(stm->children(), [this, containerValueDecl](Stmt *child) { return this->containsDetachments(child, containerValueDecl); }); } - -REGISTER_CHECK("foreach", Foreach, CheckLevel1) diff --git a/src/checks/level1/incorrect-emit.cpp b/src/checks/level1/incorrect-emit.cpp index b63fbedf..405d2691 100644 --- a/src/checks/level1/incorrect-emit.cpp +++ b/src/checks/level1/incorrect-emit.cpp @@ -20,14 +20,12 @@ */ #include "incorrect-emit.h" - #include "AccessSpecifierManager.h" #include "ClazyContext.h" #include "Utils.h" #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> #include <clang/AST/DeclCXX.h> @@ -38,7 +36,7 @@ using namespace clang; using namespace std; IncorrectEmit::IncorrectEmit(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { context->enableAccessSpecifierManager(); enablePreProcessorCallbacks(); @@ -46,7 +44,7 @@ IncorrectEmit::IncorrectEmit(const std::string &name, ClazyContext *context) m_filesToIgnore = { "moc_", ".moc" }; } -void IncorrectEmit::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &range) +void IncorrectEmit::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &range, const MacroInfo *) { IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); if (ii && (ii->getName() == "emit" || ii->getName() == "Q_EMIT")) @@ -64,13 +62,13 @@ void IncorrectEmit::VisitStmt(Stmt *stmt) if (!method || !accessSpecifierManager) return; - if (shouldIgnoreFile(stmt->getLocStart())) + if (shouldIgnoreFile(getLocStart(stmt))) return; - if (Stmt *parent = HierarchyUtils::parent(m_context->parentMap, methodCall)) { + if (Stmt *parent = clazy::parent(m_context->parentMap, methodCall)) { // Check if we're inside a chained call, such as: emit d_func()->mySignal() // We're not interested in the d_func() call, so skip it - if (HierarchyUtils::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, parent)) + if (clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, parent)) return; } @@ -93,10 +91,10 @@ void IncorrectEmit::VisitStmt(Stmt *stmt) void IncorrectEmit::checkCallSignalInsideCTOR(CXXMemberCallExpr *callExpr) { - if (!m_lastMethodDecl) + if (!m_context->lastMethodDecl) return; - auto ctorDecl = dyn_cast<CXXConstructorDecl>(m_lastMethodDecl); + auto ctorDecl = dyn_cast<CXXConstructorDecl>(m_context->lastMethodDecl); if (!ctorDecl) return; @@ -104,15 +102,15 @@ void IncorrectEmit::checkCallSignalInsideCTOR(CXXMemberCallExpr *callExpr) if (!implicitArg || !isa<CXXThisExpr>(implicitArg)) // emit other->sig() is ok return; - if (HierarchyUtils::getFirstParentOfType<LambdaExpr>(m_context->parentMap, callExpr) != nullptr) + if (clazy::getFirstParentOfType<LambdaExpr>(m_context->parentMap, callExpr) != nullptr) return; // Emit is inside a lambda, it's fine - emitWarning(callExpr->getLocStart(), "Emitting inside constructor has no effect"); + emitWarning(getLocStart(callExpr), "Emitting inside constructor has no effect"); } bool IncorrectEmit::hasEmitKeyboard(CXXMemberCallExpr *call) const { - SourceLocation callLoc = call->getLocStart(); + SourceLocation callLoc = getLocStart(call); if (callLoc.isMacroID()) callLoc = sm().getFileLoc(callLoc); @@ -134,5 +132,3 @@ bool IncorrectEmit::hasEmitKeyboard(CXXMemberCallExpr *call) const return false; } - -REGISTER_CHECK("incorrect-emit", IncorrectEmit, CheckLevel1) diff --git a/src/checks/level1/incorrect-emit.h b/src/checks/level1/incorrect-emit.h index f1c896e1..302d05b2 100644 --- a/src/checks/level1/incorrect-emit.h +++ b/src/checks/level1/incorrect-emit.h @@ -41,7 +41,7 @@ public: private: void checkCallSignalInsideCTOR(clang::CXXMemberCallExpr *); void VisitMacroExpands(const clang::Token &MacroNameTok, - const clang::SourceRange &range) override; + const clang::SourceRange &range, const clang::MacroInfo *minfo = nullptr) override; bool hasEmitKeyboard(clang::CXXMemberCallExpr *) const; std::vector<clang::SourceLocation> m_emitLocations; mutable std::unordered_map<unsigned, clang::SourceLocation> m_locationCache; diff --git a/src/checks/level1/inefficient-qlist-soft.cpp b/src/checks/level1/inefficient-qlist-soft.cpp index 0c36dbd5..1adffb74 100644 --- a/src/checks/level1/inefficient-qlist-soft.cpp +++ b/src/checks/level1/inefficient-qlist-soft.cpp @@ -21,7 +21,6 @@ #include "inefficient-qlist-soft.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -30,17 +29,9 @@ using namespace clang; using namespace std; - InefficientQListSoft::InefficientQListSoft(const std::string &name, ClazyContext *context) : InefficientQListBase(name, context, IgnoreNonLocalVariable | IgnoreInFunctionWithSameReturnType | IgnoreIsAssignedToInFunction | IgnoreIsPassedToFunctions| IgnoreIsInitializedByFunctionCall) { } - -void InefficientQListSoft::VisitStmt(clang::Stmt *stmt) -{ -} - - -REGISTER_CHECK("inefficient-qlist-soft", InefficientQListSoft, CheckLevel1) diff --git a/src/checks/level1/inefficient-qlist-soft.h b/src/checks/level1/inefficient-qlist-soft.h index 18cf6c6a..12cf6dd7 100644 --- a/src/checks/level1/inefficient-qlist-soft.h +++ b/src/checks/level1/inefficient-qlist-soft.h @@ -37,7 +37,6 @@ class InefficientQListSoft : public InefficientQListBase { public: explicit InefficientQListSoft(const std::string &name, ClazyContext *context); - void VisitStmt(clang::Stmt *stmt) override; }; #endif diff --git a/src/checks/level1/install-event-filter.cpp b/src/checks/level1/install-event-filter.cpp index 3a2ee03f..92f652be 100644 --- a/src/checks/level1/install-event-filter.cpp +++ b/src/checks/level1/install-event-filter.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -33,11 +32,10 @@ using namespace std; InstallEventFilter::InstallEventFilter(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } - void InstallEventFilter::VisitStmt(clang::Stmt *stmt) { auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(stmt); @@ -52,7 +50,7 @@ void InstallEventFilter::VisitStmt(clang::Stmt *stmt) if (!expr) return; - if (!isa<CXXThisExpr>(HierarchyUtils::getFirstChildAtDepth(expr, 1))) + if (!isa<CXXThisExpr>(clazy::getFirstChildAtDepth(expr, 1))) return; Expr *arg1 = memberCallExpr->getArg(0); @@ -68,5 +66,3 @@ void InstallEventFilter::VisitStmt(clang::Stmt *stmt) emitWarning(stmt, "'this' should usually be the filter object, not the monitored one."); } - -REGISTER_CHECK("install-event-filter", InstallEventFilter, CheckLevel1) diff --git a/src/checks/level1/nonpodstatic.cpp b/src/checks/level1/non-pod-global-static.cpp index 05b510f7..7b0518f2 100644 --- a/src/checks/level1/nonpodstatic.cpp +++ b/src/checks/level1/non-pod-global-static.cpp @@ -22,12 +22,11 @@ Boston, MA 02110-1301, USA. */ -#include "nonpodstatic.h" +#include "non-pod-global-static.h" #include "Utils.h" #include "StringUtils.h" #include "MacroUtils.h" #include "QtUtils.h" -#include "checkmanager.h" #include "ClazyContext.h" #include <clang/AST/DeclCXX.h> @@ -36,36 +35,39 @@ using namespace clang; using namespace std; -static bool shouldIgnoreType(const std::string &name) +static bool shouldIgnoreType(StringRef name) { // Q_GLOBAL_STATIC and such - static vector<string> blacklist = {"Holder", "AFUNC", "QLoggingCategory", "QThreadStorage"}; - return clazy_std::contains(blacklist, name); + static vector<StringRef> blacklist = {"Holder", "AFUNC", "QLoggingCategory", "QThreadStorage"}; + return clazy::contains(blacklist, name); } -NonPodStatic::NonPodStatic(const std::string &name, ClazyContext *context) - : CheckBase(name, context) +NonPodGlobalStatic::NonPodGlobalStatic(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { m_filesToIgnore = { "main.cpp", "qrc_", "qdbusxml2cpp" }; } -void NonPodStatic::VisitStmt(clang::Stmt *stm) +void NonPodGlobalStatic::VisitStmt(clang::Stmt *stm) { - VarDecl *varDecl = m_lastDecl ? dyn_cast<VarDecl>(m_lastDecl) : nullptr; + VarDecl *varDecl = m_context->lastDecl ? dyn_cast<VarDecl>(m_context->lastDecl) : nullptr; if (!varDecl || varDecl->isConstexpr() || varDecl->isExternallyVisible() || !varDecl->isFileVarDecl()) return; - if (shouldIgnoreFile(stm->getLocStart())) + if (shouldIgnoreFile(getLocStart(stm))) return; StorageDuration sd = varDecl->getStorageDuration(); if (sd != StorageDuration::SD_Static) return; - const SourceLocation declStart = varDecl->getLocStart(); - auto macroName = Lexer::getImmediateMacroName(declStart, sm(), lo()); - if (clazy_std::startsWithAny(macroName, { "Q_IMPORT_PLUGIN", "Q_CONSTRUCTOR_FUNCTION", "Q_DESTRUCTOR_FUNCTION"})) // Don't warn on these - return; + const SourceLocation declStart = getLocStart(varDecl); + + if (declStart.isMacroID()) { + auto macroName = Lexer::getImmediateMacroName(declStart, sm(), lo()); + if (clazy::startsWithAny(macroName, { "Q_IMPORT_PLUGIN", "Q_CONSTRUCTOR_FUNCTION", "Q_DESTRUCTOR_FUNCTION"})) // Don't warn on these + return; + } CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stm); if (!ctorExpr) @@ -88,15 +90,13 @@ void NonPodStatic::VisitStmt(clang::Stmt *stm) } } - if (m_context->isQtDeveloper() && QtUtils::isBootstrapping(m_preprocessorOpts)) + if (m_context->isQtDeveloper() && clazy::isBootstrapping(m_context->ci.getPreprocessorOpts())) return; - const string className = recordDecl->getName(); + StringRef className = recordDecl->getName(); if (!shouldIgnoreType(className)) { - std::string error = "non-POD static (" + className + ')'; - emitWarning(declStart, error.c_str()); + std::string error = string("non-POD static (") + className.data() + string(")"); + emitWarning(declStart, error); } } - -REGISTER_CHECK("non-pod-global-static", NonPodStatic, CheckLevel1) diff --git a/src/checks/level1/nonpodstatic.h b/src/checks/level1/non-pod-global-static.h index 64574eaf..a14bae09 100644 --- a/src/checks/level1/nonpodstatic.h +++ b/src/checks/level1/non-pod-global-static.h @@ -32,10 +32,10 @@ * * See README-non-pod-global-static. */ -class NonPodStatic : public CheckBase +class NonPodGlobalStatic : public CheckBase { public: - NonPodStatic(const std::string &name, ClazyContext *context); + explicit NonPodGlobalStatic(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stm) override; }; diff --git a/src/checks/level1/overridden-signal.cpp b/src/checks/level1/overridden-signal.cpp index ea9d2c6c..03756e48 100644 --- a/src/checks/level1/overridden-signal.cpp +++ b/src/checks/level1/overridden-signal.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "AccessSpecifierManager.h" #include "ClazyContext.h" #include "FunctionUtils.h" @@ -36,7 +35,7 @@ using namespace std; OverriddenSignal::OverriddenSignal(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { context->enableAccessSpecifierManager(); } @@ -52,20 +51,19 @@ void OverriddenSignal::VisitDecl(clang::Decl *decl) return; CXXRecordDecl *record = method->getParent(); - if (!QtUtils::isQObject(record)) + CXXRecordDecl *baseClass = clazy::getQObjectBaseClass(record); + if (!baseClass) return; const bool methodIsSignal = accessSpecifierManager->qtAccessSpecifierType(method) == QtAccessSpecifier_Signal; - const std::string methodName = method->getNameAsString(); + const StringRef methodName = clazy::name(method); - - CXXRecordDecl *baseClass = QtUtils::getQObjectBaseClass(record); std::string warningMsg; while (baseClass) { for (auto baseMethod : baseClass->methods()) { - if (baseMethod->getNameAsString() == methodName) { + if (clazy::name(baseMethod) == methodName) { - if (!FunctionUtils::parametersMatch(method, baseMethod)) // overloading is permitted. + if (!clazy::parametersMatch(method, baseMethod)) // overloading is permitted. continue; const bool baseMethodIsSignal = accessSpecifierManager->qtAccessSpecifierType(baseMethod) == QtAccessSpecifier_Signal; @@ -85,9 +83,6 @@ void OverriddenSignal::VisitDecl(clang::Decl *decl) } } - baseClass = QtUtils::getQObjectBaseClass(baseClass); + baseClass = clazy::getQObjectBaseClass(baseClass); } } - - -REGISTER_CHECK("overridden-signal", OverriddenSignal, CheckLevel1) diff --git a/src/checks/level1/post-event.cpp b/src/checks/level1/post-event.cpp index 9c0691b9..84f8bed4 100644 --- a/src/checks/level1/post-event.cpp +++ b/src/checks/level1/post-event.cpp @@ -25,7 +25,6 @@ #include "QtUtils.h" #include "TypeUtils.h" #include "StringUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -34,7 +33,7 @@ using namespace std; PostEvent::PostEvent(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -44,7 +43,7 @@ void PostEvent::VisitStmt(clang::Stmt *stmt) if (!callexpr) return; - auto name = StringUtils::qualifiedMethodName(callexpr); + auto name = clazy::qualifiedMethodName(callexpr); const bool isPostEvent = name == "QCoreApplication::postEvent"; const bool isSendEvent = name == "QCoreApplication::sendEvent"; @@ -55,7 +54,7 @@ void PostEvent::VisitStmt(clang::Stmt *stmt) return; Expr *event = callexpr->getNumArgs() > 1 ? callexpr->getArg(1) : nullptr; - if (!event || StringUtils::simpleTypeName(event->getType(), lo()) != "QEvent *") + if (!event || clazy::simpleTypeName(event->getType(), lo()) != "QEvent *") return; bool isStack = false; @@ -72,5 +71,3 @@ void PostEvent::VisitStmt(clang::Stmt *stmt) // It's something else, like an rvalue, ignore it } } - -REGISTER_CHECK("post-event", PostEvent, CheckLevel1) diff --git a/src/checks/level1/qdeleteall.cpp b/src/checks/level1/qdeleteall.cpp index 753d8667..8ce18e1e 100644 --- a/src/checks/level1/qdeleteall.cpp +++ b/src/checks/level1/qdeleteall.cpp @@ -24,7 +24,6 @@ #include "Utils.h" #include "HierarchyUtils.h" #include "QtUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> #include <vector> @@ -33,14 +32,14 @@ using namespace clang; using namespace std; QDeleteAll::QDeleteAll(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } void QDeleteAll::VisitStmt(clang::Stmt *stmt) { // Find a call to QMap/QSet/QHash::values/keys - CXXMemberCallExpr *offendingCall = dyn_cast<CXXMemberCallExpr>(stmt); + auto offendingCall = dyn_cast<CXXMemberCallExpr>(stmt); FunctionDecl *func = offendingCall ? offendingCall->getDirectCallee() : nullptr; if (!func) return; @@ -51,15 +50,15 @@ void QDeleteAll::VisitStmt(clang::Stmt *stmt) if (isValues || isKeys) { const std::string offendingClassName = offendingCall->getMethodDecl()->getParent()->getNameAsString(); - if (QtUtils::isQtAssociativeContainer(offendingClassName)) { + if (clazy::isQtAssociativeContainer(offendingClassName)) { // Once found see if the first parent call is qDeleteAll int i = 1; - Stmt *p = HierarchyUtils::parent(m_context->parentMap, stmt, i); + Stmt *p = clazy::parent(m_context->parentMap, stmt, i); while (p) { - CallExpr *pc = dyn_cast<CallExpr>(p); + auto pc = dyn_cast<CallExpr>(p); FunctionDecl *f = pc ? pc->getDirectCallee() : nullptr; if (f) { - if (f->getNameAsString() == "qDeleteAll") { + if (clazy::name(f) == "qDeleteAll") { string msg = "qDeleteAll() is being used on an unnecessary temporary container created by " + offendingClassName + "::" + funcName + "()"; if (func->getNumParams() == 0) { if (isValues) { @@ -69,15 +68,13 @@ void QDeleteAll::VisitStmt(clang::Stmt *stmt) } } - emitWarning(p->getLocStart(), msg); + emitWarning(getLocStart(p), msg); } break; } ++i; - p = HierarchyUtils::parent(m_context->parentMap, stmt, i); + p = clazy::parent(m_context->parentMap, stmt, i); } } } } - -REGISTER_CHECK("qdeleteall", QDeleteAll, CheckLevel1) diff --git a/src/checks/level1/qhash-namespace.cpp b/src/checks/level1/qhash-namespace.cpp index c78be0c0..1bc7290e 100644 --- a/src/checks/level1/qhash-namespace.cpp +++ b/src/checks/level1/qhash-namespace.cpp @@ -27,7 +27,6 @@ #include "ContextUtils.h" #include "StringUtils.h" #include "ClazyContext.h" -#include "checkmanager.h" #include "PreProcessorVisitor.h" #include <clang/AST/AST.h> @@ -36,33 +35,33 @@ using namespace clang; using namespace std; -qhash_namespace::qhash_namespace(const std::string &name, ClazyContext *context) +QHashNamespace::QHashNamespace(const std::string &name, ClazyContext *context) : CheckBase(name, context) { if (context->isQtDeveloper()) context->enablePreprocessorVisitor(); } -void qhash_namespace::VisitDecl(clang::Decl *decl) +void QHashNamespace::VisitDecl(clang::Decl *decl) { auto func = dyn_cast<FunctionDecl>(decl); - if (!func || isa<CXXMethodDecl>(func) || func->getNumParams() == 0 || func->getNameAsString() != "qHash") + if (!func || isa<CXXMethodDecl>(func) || func->getNumParams() == 0 || clazy::name(func) != "qHash") return; ParmVarDecl *firstArg = func->getParamDecl(0); - NamespaceDecl *argumentNS = ContextUtils::namespaceForType(firstArg->getType()); - NamespaceDecl *qHashNS = ContextUtils::namespaceForFunction(func); + NamespaceDecl *argumentNS = clazy::namespaceForType(firstArg->getType()); + NamespaceDecl *qHashNS = clazy::namespaceForFunction(func); std::string msg; if (qHashNS && argumentNS) { const string argumentNSstr = argumentNS->getQualifiedNameAsString(); const string qhashNSstr = qHashNS->getQualifiedNameAsString(); if (argumentNSstr != qhashNSstr) - msg = "Move qHash(" + StringUtils::simpleTypeName(firstArg->getType(), lo()) + ") to " + argumentNSstr + " namespace for ADL lookup"; + msg = "Move qHash(" + clazy::simpleTypeName(firstArg->getType(), lo()) + ") to " + argumentNSstr + " namespace for ADL lookup"; } else if (qHashNS && !argumentNS) { - msg = "Move qHash(" + StringUtils::simpleTypeName(firstArg->getType(), lo()) + ") out of namespace " + qHashNS->getQualifiedNameAsString(); + msg = "Move qHash(" + clazy::simpleTypeName(firstArg->getType(), lo()) + ") out of namespace " + qHashNS->getQualifiedNameAsString(); } else if (!qHashNS && argumentNS) { - msg = "Move qHash(" + StringUtils::simpleTypeName(firstArg->getType(), lo()) + ") into " + argumentNS->getQualifiedNameAsString() + " namespace for ADL lookup"; + msg = "Move qHash(" + clazy::simpleTypeName(firstArg->getType(), lo()) + ") into " + argumentNS->getQualifiedNameAsString() + " namespace for ADL lookup"; } if (!msg.empty()) @@ -70,10 +69,8 @@ void qhash_namespace::VisitDecl(clang::Decl *decl) if (m_context->isQtDeveloper()) { PreProcessorVisitor *preProcessorVisitor = m_context->preprocessorVisitor; - if (preProcessorVisitor && !preProcessorVisitor->isBetweenQtNamespaceMacros(func->getLocStart())) { - emitWarning(decl, "qHash(" + StringUtils::simpleTypeName(firstArg->getType(), lo()) + ") must be declared before QT_END_NAMESPACE"); + if (preProcessorVisitor && !preProcessorVisitor->isBetweenQtNamespaceMacros(getLocStart(func))) { + emitWarning(decl, "qHash(" + clazy::simpleTypeName(firstArg->getType(), lo()) + ") must be declared before QT_END_NAMESPACE"); } } } - -REGISTER_CHECK("qhash-namespace", qhash_namespace, CheckLevel1) diff --git a/src/checks/level1/qhash-namespace.h b/src/checks/level1/qhash-namespace.h index 06e51e18..92d72acd 100644 --- a/src/checks/level1/qhash-namespace.h +++ b/src/checks/level1/qhash-namespace.h @@ -28,10 +28,10 @@ /** * See README-qhash-namespace.md for more info. */ -class qhash_namespace : public CheckBase +class QHashNamespace : public CheckBase { public: - explicit qhash_namespace(const std::string &name, ClazyContext *context); + explicit QHashNamespace(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; }; diff --git a/src/checks/level1/qlatin1string-non-ascii.cpp b/src/checks/level1/qlatin1string-non-ascii.cpp index a08f2a76..133ee47f 100644 --- a/src/checks/level1/qlatin1string-non-ascii.cpp +++ b/src/checks/level1/qlatin1string-non-ascii.cpp @@ -25,7 +25,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -34,7 +33,7 @@ using namespace std; QLatin1StringNonAscii::QLatin1StringNonAscii(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -46,9 +45,7 @@ void QLatin1StringNonAscii::VisitStmt(clang::Stmt *stmt) if (!ctor || ctor->getQualifiedNameAsString() != "QLatin1String::QLatin1String") return; - StringLiteral *lt = HierarchyUtils::getFirstChildOfType2<StringLiteral>(stmt); + StringLiteral *lt = clazy::getFirstChildOfType2<StringLiteral>(stmt); if (lt && !Utils::isAscii(lt)) - emitWarning(stmt, "QStringLiteral with non-ascii literal"); + emitWarning(stmt, "QLatin1String with non-ascii literal"); } - -REGISTER_CHECK("qlatin1string-non-ascii", QLatin1StringNonAscii, CheckLevel1) diff --git a/src/checks/level1/qproperty-without-notify.cpp b/src/checks/level1/qproperty-without-notify.cpp index 4922073d..69a08588 100644 --- a/src/checks/level1/qproperty-without-notify.cpp +++ b/src/checks/level1/qproperty-without-notify.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -33,12 +32,12 @@ using namespace std; QPropertyWithoutNotify::QPropertyWithoutNotify(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { enablePreProcessorCallbacks(); } -void QPropertyWithoutNotify::VisitMacroExpands(const clang::Token &MacroNameTok, const clang::SourceRange &range) +void QPropertyWithoutNotify::VisitMacroExpands(const clang::Token &MacroNameTok, const clang::SourceRange &range, const MacroInfo *) { IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); if (!ii) @@ -62,7 +61,7 @@ void QPropertyWithoutNotify::VisitMacroExpands(const clang::Token &MacroNameTok, CharSourceRange crange = Lexer::getAsCharRange(range, sm(), lo()); string text = Lexer::getSourceText(crange, sm(), lo()); - vector<string> split = clazy_std::splitString(text, ' '); + vector<string> split = clazy::splitString(text, ' '); bool found_read = false; bool found_constant = false; @@ -90,5 +89,3 @@ void QPropertyWithoutNotify::VisitMacroExpands(const clang::Token &MacroNameTok, emitWarning(range.getBegin(), "Q_PROPERTY should have either NOTIFY or CONSTANT"); } - -REGISTER_CHECK("qproperty-without-notify", QPropertyWithoutNotify, CheckLevel1) diff --git a/src/checks/level1/qproperty-without-notify.h b/src/checks/level1/qproperty-without-notify.h index 175924e6..53365e75 100644 --- a/src/checks/level1/qproperty-without-notify.h +++ b/src/checks/level1/qproperty-without-notify.h @@ -34,7 +34,7 @@ public: explicit QPropertyWithoutNotify(const std::string &name, ClazyContext *context); private: void VisitMacroExpands(const clang::Token &MacroNameTok, - const clang::SourceRange &range) override; + const clang::SourceRange &range, const clang::MacroInfo *minfo = nullptr) override; bool m_lastIsGadget = false; }; diff --git a/src/checks/level1/qstring-left.cpp b/src/checks/level1/qstring-left.cpp index 934b58bf..82c8e713 100644 --- a/src/checks/level1/qstring-left.cpp +++ b/src/checks/level1/qstring-left.cpp @@ -25,7 +25,6 @@ #include "QtUtils.h" #include "TypeUtils.h" #include "StringUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -34,15 +33,14 @@ using namespace std; QStringLeft::QStringLeft(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } - void QStringLeft::VisitStmt(clang::Stmt *stmt) { auto memberCall = dyn_cast<CXXMemberCallExpr>(stmt); - if (!memberCall || StringUtils::qualifiedMethodName(memberCall) != "QString::left") + if (!memberCall || clazy::qualifiedMethodName(memberCall) != "QString::left") return; if (memberCall->getNumArgs() == 0) // Doesn't happen @@ -59,6 +57,3 @@ void QStringLeft::VisitStmt(clang::Stmt *stmt) } } } - - -REGISTER_CHECK("qstring-left", QStringLeft, CheckLevel1) diff --git a/src/checks/level1/range-loop.cpp b/src/checks/level1/range-loop.cpp index bbe87b3c..f4956613 100644 --- a/src/checks/level1/range-loop.cpp +++ b/src/checks/level1/range-loop.cpp @@ -27,7 +27,6 @@ #include "QtUtils.h" #include "TypeUtils.h" #include "StringUtils.h" -#include "checkmanager.h" #include "LoopUtils.h" #include "StmtBodyRange.h" @@ -37,7 +36,7 @@ using namespace clang; using namespace std; RangeLoop::RangeLoop(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -69,14 +68,14 @@ void RangeLoop::processForRangeLoop(CXXForRangeStmt *rangeLoop) return; CXXRecordDecl *record = t->getAsCXXRecordDecl(); - if (!QtUtils::isQtCOWIterableClass(Utils::rootBaseClass(record))) + if (!clazy::isQtCOWIterableClass(Utils::rootBaseClass(record))) return; - StmtBodyRange bodyRange(nullptr, &sm(), rangeLoop->getLocStart()); - if (QtUtils::containerNeverDetaches(LoopUtils::containerDeclForLoop(rangeLoop), bodyRange)) + StmtBodyRange bodyRange(nullptr, &sm(), getLocStart(rangeLoop)); + if (clazy::containerNeverDetaches(clazy::containerDeclForLoop(rangeLoop), bodyRange)) return; - emitWarning(rangeLoop->getLocStart(), "c++11 range-loop might detach Qt container (" + record->getQualifiedNameAsString() + ')'); + emitWarning(getLocStart(rangeLoop), "c++11 range-loop might detach Qt container (" + record->getQualifiedNameAsString() + ')'); } void RangeLoop::checkPassByConstRefCorrectness(CXXForRangeStmt *rangeLoop) @@ -88,14 +87,12 @@ void RangeLoop::checkPassByConstRefCorrectness(CXXForRangeStmt *rangeLoop) return; if (classif.passNonTriviallyCopyableByConstRef) { - string error; - const string paramStr = StringUtils::simpleTypeName(varDecl->getType(), lo()); - error = "Missing reference in range-for with non trivial type (" + paramStr + ')'; + string msg; + const string paramStr = clazy::simpleTypeName(varDecl->getType(), lo()); + msg = "Missing reference in range-for with non trivial type (" + paramStr + ')'; // We ignore classif.passSmallTrivialByValue because it doesn't matter, the compiler is able // to optimize it, generating the same assembly, regardless of pass by value. - emitWarning(varDecl->getLocStart(), error.c_str()); + emitWarning(getLocStart(varDecl), msg.c_str()); } } - -REGISTER_CHECK("range-loop", RangeLoop, CheckLevel1) diff --git a/src/checks/level1/returning-data-from-temporary.cpp b/src/checks/level1/returning-data-from-temporary.cpp index c50f4868..a6aba109 100644 --- a/src/checks/level1/returning-data-from-temporary.cpp +++ b/src/checks/level1/returning-data-from-temporary.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -33,7 +32,7 @@ using namespace std; ReturningDataFromTemporary::ReturningDataFromTemporary(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -50,8 +49,8 @@ bool ReturningDataFromTemporary::handleReturn(ReturnStmt *ret) if (!ret) return false; - auto memberCall = HierarchyUtils::unpeal<CXXMemberCallExpr>(HierarchyUtils::getFirstChild(ret), HierarchyUtils::IgnoreExprWithCleanups | - HierarchyUtils::IgnoreImplicitCasts); + auto memberCall = clazy::unpeal<CXXMemberCallExpr>(clazy::getFirstChild(ret), clazy::IgnoreExprWithCleanups | + clazy::IgnoreImplicitCasts); handleMemberCall(memberCall, false); return true; } @@ -73,8 +72,8 @@ void ReturningDataFromTemporary::handleDeclStmt(DeclStmt *declStmt) if (!init) continue; - auto memberCall = HierarchyUtils::unpeal<CXXMemberCallExpr>(HierarchyUtils::getFirstChild(init), HierarchyUtils::IgnoreExprWithCleanups | - HierarchyUtils::IgnoreImplicitCasts); + auto memberCall = clazy::unpeal<CXXMemberCallExpr>(clazy::getFirstChild(init), clazy::IgnoreExprWithCleanups | + clazy::IgnoreImplicitCasts); handleMemberCall(memberCall, true); @@ -104,7 +103,7 @@ void ReturningDataFromTemporary::handleMemberCall(CXXMemberCallExpr *memberCall, while (t) { if (dyn_cast<ImplicitCastExpr>(t) || dyn_cast<MaterializeTemporaryExpr>(t)) { - t = HierarchyUtils::getFirstChild(t); + t = clazy::getFirstChild(t); continue; } @@ -139,5 +138,3 @@ void ReturningDataFromTemporary::handleMemberCall(CXXMemberCallExpr *memberCall, emitWarning(memberCall, "Returning data of temporary QByteArray"); } - -REGISTER_CHECK("returning-data-from-temporary", ReturningDataFromTemporary, CheckLevel1) diff --git a/src/checks/level1/ruleoftwosoft.cpp b/src/checks/level1/rule-of-two-soft.cpp index 09fa56e0..ac1d7b58 100644 --- a/src/checks/level1/ruleoftwosoft.cpp +++ b/src/checks/level1/rule-of-two-soft.cpp @@ -19,9 +19,8 @@ Boston, MA 02110-1301, USA. */ -#include "ruleoftwosoft.h" +#include "rule-of-two-soft.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -47,7 +46,7 @@ void RuleOfTwoSoft::VisitStmt(Stmt *s) const bool hasCopyAssignOp = record->hasNonTrivialCopyAssignment(); if (hasCopyCtor && !hasCopyAssignOp && !isBlacklisted(record)) { string msg = "Using assign operator but class " + record->getQualifiedNameAsString() + " has copy-ctor but no assign operator"; - emitWarning(s->getLocStart(), msg); + emitWarning(getLocStart(s), msg); } } } else if (auto ctorExpr = dyn_cast<CXXConstructExpr>(s)) { @@ -58,10 +57,8 @@ void RuleOfTwoSoft::VisitStmt(Stmt *s) const bool hasCopyAssignOp = record->hasNonTrivialCopyAssignment(); if (!hasCopyCtor && hasCopyAssignOp && !isBlacklisted(record)) { string msg = "Using copy-ctor but class " + record->getQualifiedNameAsString() + " has a trivial copy-ctor but non trivial assign operator"; - emitWarning(s->getLocStart(), msg); + emitWarning(getLocStart(s), msg); } } } } - -REGISTER_CHECK("rule-of-two-soft", RuleOfTwoSoft, CheckLevel1) diff --git a/src/checks/level1/ruleoftwosoft.h b/src/checks/level1/rule-of-two-soft.h index 3bf96250..3bf96250 100644 --- a/src/checks/level1/ruleoftwosoft.h +++ b/src/checks/level1/rule-of-two-soft.h diff --git a/src/checks/level1/skipped-base-method.cpp b/src/checks/level1/skipped-base-method.cpp new file mode 100644 index 00000000..7910f208 --- /dev/null +++ b/src/checks/level1/skipped-base-method.cpp @@ -0,0 +1,66 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2017 Sergio Martins <smartins@kde.org> + + 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 "skipped-base-method.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" +#include "FunctionUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +SkippedBaseMethod::SkippedBaseMethod(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ +} + +void SkippedBaseMethod::VisitStmt(clang::Stmt *stmt) +{ + auto memberCall = dyn_cast<CXXMemberCallExpr>(stmt); + if (!memberCall) + return; + + auto expr = memberCall->getImplicitObjectArgument(); + auto thisExpr = clazy::unpeal<CXXThisExpr>(expr, clazy::IgnoreImplicitCasts); + if (!thisExpr) + return; + + const CXXRecordDecl *thisClass = thisExpr->getType()->getPointeeCXXRecordDecl(); + const CXXRecordDecl *baseClass = memberCall->getRecordDecl(); + + std::vector<CXXRecordDecl*> baseClasses; + if (!TypeUtils::derivesFrom(thisClass, baseClass, &baseClasses) || baseClasses.size() < 2) + return; + + // We're calling a grand-base method, so check if a more direct base also implements it + for (int i = baseClasses.size() - 1; i > 0; --i) { // the higher indexes have the most derived classes + CXXRecordDecl *moreDirectBaseClass = baseClasses[i]; + if (clazy::classImplementsMethod(moreDirectBaseClass, memberCall->getMethodDecl())) { + std::string msg = "Maybe you meant to call " + moreDirectBaseClass->getNameAsString() + "::" + memberCall->getMethodDecl()->getNameAsString() + "() instead"; + emitWarning(stmt, msg); + } + } +} diff --git a/src/checks/level1/skipped-base-method.h b/src/checks/level1/skipped-base-method.h new file mode 100644 index 00000000..44c65994 --- /dev/null +++ b/src/checks/level1/skipped-base-method.h @@ -0,0 +1,39 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2017 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_SKIPPED_BASE_METHOD_H +#define CLAZY_SKIPPED_BASE_METHOD_H + +#include "checkbase.h" + + +/** + * See README-skipped-base-method.md for more info. + */ +class SkippedBaseMethod : public CheckBase +{ +public: + explicit SkippedBaseMethod(const std::string &name, ClazyContext *context); + void VisitStmt(clang::Stmt *stmt) override; +private: +}; + +#endif diff --git a/src/checks/level1/virtual-signal.cpp b/src/checks/level1/virtual-signal.cpp index b3d040ad..7a930e9c 100644 --- a/src/checks/level1/virtual-signal.cpp +++ b/src/checks/level1/virtual-signal.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "ClazyContext.h" #include "AccessSpecifierManager.h" @@ -44,7 +43,7 @@ VirtualSignal::VirtualSignal(const std::string &name, ClazyContext *context) void VirtualSignal::VisitDecl(Decl *stmt) { auto method = dyn_cast<CXXMethodDecl>(stmt); - if (!method) + if (!method || !method->isVirtual()) return; AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager; @@ -52,9 +51,19 @@ void VirtualSignal::VisitDecl(Decl *stmt) return; QtAccessSpecifierType qst = accessSpecifierManager->qtAccessSpecifierType(method); - if (qst == QtAccessSpecifier_Signal && method->isVirtual()) - emitWarning(method, "signal is virtual"); -} + if (qst == QtAccessSpecifier_Signal) { + + for (auto m : method->overridden_methods()) { + if (auto baseClass = m->getParent()) { + if (!clazy::isQObject(baseClass)) { + // It's possible that the signal is overriding a method from a non-QObject base class + // if the derived class inherits both QObject and some other interface. + return; + } + } + } -REGISTER_CHECK("virtual-signal", VirtualSignal, CheckLevel1) + emitWarning(method, "signal is virtual"); + } +} diff --git a/src/checks/level2/base-class-event.cpp b/src/checks/level2/base-class-event.cpp index 868597ce..66aaa808 100644 --- a/src/checks/level2/base-class-event.cpp +++ b/src/checks/level2/base-class-event.cpp @@ -24,15 +24,15 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> #include <clang/AST/DeclCXX.h> +#include <array> + using namespace clang; using namespace std; - BaseClassEvent::BaseClassEvent(const std::string &name, ClazyContext *context) : CheckBase(name, context) { @@ -52,35 +52,33 @@ void BaseClassEvent::VisitDecl(Decl *decl) return; CXXRecordDecl *classDecl = method->getParent(); - if (!QtUtils::isQObject(classDecl)) + if (!clazy::isQObject(classDecl)) return; const string className = classDecl->getQualifiedNameAsString(); - if (clazy_std::contains(vector<string>({"QObject", "QWidget"}), className)) + if (clazy::contains(std::array<StringRef, 2>({"QObject", "QWidget"}), className)) return; - CXXRecordDecl *baseClass = QtUtils::getQObjectBaseClass(classDecl); + CXXRecordDecl *baseClass = clazy::getQObjectBaseClass(classDecl); const string baseClassName = baseClass ? baseClass->getQualifiedNameAsString() : string("BaseClass"); - if (isEventFilter && clazy_std::contains(vector<string>({"QObject", "QWidget"}), baseClassName)) { + if (isEventFilter && clazy::contains(std::array<StringRef, 2>({"QObject", "QWidget"}), baseClassName)) { // This is fine, QObject and QWidget eventFilter() don't do anything return; } Stmt *body = method->getBody(); std::vector<ReturnStmt*> returns; - HierarchyUtils::getChilds<ReturnStmt>(body, /*by-ref*/returns); + clazy::getChilds<ReturnStmt>(body, /*by-ref*/returns); for (ReturnStmt *returnStmt : returns) { - Stmt *maybeBoolExpr = clazy_std::childAt(returnStmt, 0); + Stmt *maybeBoolExpr = clazy::childAt(returnStmt, 0); if (!maybeBoolExpr) continue; auto boolExpr = dyn_cast<CXXBoolLiteralExpr>(maybeBoolExpr); if (!boolExpr || boolExpr->getValue()) // if getValue() is true that's a return true, which is fine continue; - emitWarning(returnStmt->getLocStart(), "Return " + baseClassName + "::" + methodName + "() instead of false"); + emitWarning(getLocStart(returnStmt), "Return " + baseClassName + "::" + methodName + "() instead of false"); } } - -REGISTER_CHECK("base-class-event", BaseClassEvent, CheckLevel2) diff --git a/src/checks/level2/copyable-polymorphic.cpp b/src/checks/level2/copyable-polymorphic.cpp index df5246c2..cf835d18 100644 --- a/src/checks/level2/copyable-polymorphic.cpp +++ b/src/checks/level2/copyable-polymorphic.cpp @@ -21,7 +21,6 @@ #include "copyable-polymorphic.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -38,22 +37,18 @@ CopyablePolymorphic::CopyablePolymorphic(const std::string &name, ClazyContext * void CopyablePolymorphic::VisitDecl(clang::Decl *decl) { - CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(decl); + auto record = dyn_cast<CXXRecordDecl>(decl); if (!record || !record->hasDefinition() || record->getDefinition() != record || !record->isPolymorphic()) return; CXXConstructorDecl *copyCtor = Utils::copyCtor(record); - CXXMethodDecl *copyAssign = Utils::copyAssign(record); - const bool hasCallableCopyCtor = copyCtor && !copyCtor->isDeleted() && copyCtor->getAccess() != clang::AS_private; - const bool hasCallableCopyAssign = copyAssign && !copyAssign->isDeleted() && copyAssign->getAccess() != clang::AS_private; - - if (!hasCallableCopyCtor && !hasCallableCopyAssign) - return; - - - emitWarning(record->getLocStart(), "Polymorphic class is copyable. Potential slicing."); + if (!hasCallableCopyCtor) { + CXXMethodDecl *copyAssign = Utils::copyAssign(record); + const bool hasCallableCopyAssign = copyAssign && !copyAssign->isDeleted() && copyAssign->getAccess() != clang::AS_private; + if (!hasCallableCopyAssign) + return; + } + + emitWarning(getLocStart(record), "Polymorphic class " + record->getQualifiedNameAsString() + " is copyable. Potential slicing."); } - - -REGISTER_CHECK("copyable-polymorphic", CopyablePolymorphic, CheckLevel2) diff --git a/src/checks/level1/ctor-missing-parent-argument.cpp b/src/checks/level2/ctor-missing-parent-argument.cpp index 1e80741b..cb4ae7ae 100644 --- a/src/checks/level1/ctor-missing-parent-argument.cpp +++ b/src/checks/level2/ctor-missing-parent-argument.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -55,7 +54,7 @@ void CtorMissingParentArgument::VisitDecl(Decl *decl) auto record = dyn_cast<CXXRecordDecl>(decl); bool ok = false; - if (!QtUtils::isQObject(record)) + if (!clazy::isQObject(record)) return; const bool hasCtors = record->ctor_begin() != record->ctor_end(); @@ -64,19 +63,19 @@ void CtorMissingParentArgument::VisitDecl(Decl *decl) const string parentType = expectedParentTypeFor(record); int numCtors = 0; - const bool hasQObjectParam = QtUtils::recordHasCtorWithParam(record, parentType, /*by-ref*/ok, /*by-ref*/numCtors); + const bool hasQObjectParam = clazy::recordHasCtorWithParam(record, parentType, /*by-ref*/ok, /*by-ref*/numCtors); if (!ok) return; if (numCtors > 0 && !hasQObjectParam) { - clang::CXXRecordDecl *baseClass = QtUtils::getQObjectBaseClass(record); - const bool baseHasQObjectParam = QtUtils::recordHasCtorWithParam(baseClass, parentType, /*by-ref*/ok, /*by-ref*/numCtors); - if (ok && !baseHasQObjectParam && sm().isInSystemHeader(baseClass->getLocStart())) { + clang::CXXRecordDecl *baseClass = clazy::getQObjectBaseClass(record); + const bool baseHasQObjectParam = clazy::recordHasCtorWithParam(baseClass, parentType, /*by-ref*/ok, /*by-ref*/numCtors); + if (ok && !baseHasQObjectParam && sm().isInSystemHeader(getLocStart(baseClass))) { // If the base class ctors don't accept QObject, and it's declared in a system header don't warn return; } - if (baseClass->getNameAsString() == "QCoreApplication") + if (clazy::name(baseClass) == "QCoreApplication") return; emitWarning(decl, record->getQualifiedNameAsString() + @@ -84,6 +83,3 @@ void CtorMissingParentArgument::VisitDecl(Decl *decl) parentType + string(" parent argument in CTOR")); } } - - -REGISTER_CHECK("ctor-missing-parent-argument", CtorMissingParentArgument, CheckLevel2) diff --git a/src/checks/level1/ctor-missing-parent-argument.h b/src/checks/level2/ctor-missing-parent-argument.h index b81d69cf..b81d69cf 100644 --- a/src/checks/level1/ctor-missing-parent-argument.h +++ b/src/checks/level2/ctor-missing-parent-argument.h diff --git a/src/checks/level2/function-args-by-ref.cpp b/src/checks/level2/function-args-by-ref.cpp index a1fa94b7..712ca088 100644 --- a/src/checks/level2/function-args-by-ref.cpp +++ b/src/checks/level2/function-args-by-ref.cpp @@ -4,7 +4,7 @@ Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com Author: Sérgio Martins <sergio.martins@kdab.com> - Copyright (C) 2015 Sergio Martins <smartins@kde.org> + Copyright (C) 2015,2018 Sergio Martins <smartins@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -26,7 +26,8 @@ #include "Utils.h" #include "FixItUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" +#include "ClazyContext.h" +#include "StringUtils.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -34,11 +35,6 @@ using namespace clang; using namespace std; -enum Fixit { - FixitNone = 0, - FixitAll = 0x1 // More granularity isn't needed I guess -}; - static bool shouldIgnoreClass(CXXRecordDecl *record) { if (!record) @@ -59,13 +55,19 @@ static bool shouldIgnoreClass(CXXRecordDecl *record) "QVariantComparisonHelper", "QHashDummyValue", "QCharRef", "QString::Null" }; - return clazy_std::contains(ignoreList, record->getQualifiedNameAsString()); + return clazy::contains(ignoreList, record->getQualifiedNameAsString()); } -static bool shouldIgnoreFunction(clang::FunctionDecl *function) +static bool shouldIgnoreOperator(FunctionDecl *function) { // Too many warnings in operator<< - static const vector<string> ignoreList = {"operator<<"}; + static const vector<StringRef> ignoreList = { "operator<<" }; + + return clazy::contains(ignoreList, clazy::name(function)); +} + +static bool shouldIgnoreFunction(clang::FunctionDecl *function) +{ static const vector<string> qualifiedIgnoreList = {"QDBusMessage::createErrorReply", // Fixed in Qt6 "QMenu::exec", // Fixed in Qt6 "QTextFrame::iterator", // Fixed in Qt6 @@ -77,29 +79,35 @@ static bool shouldIgnoreFunction(clang::FunctionDecl *function) "QSslCertificate::verify", // Fixed in Qt6 "QSslConfiguration::setAllowedNextProtocols" // Fixed in Qt6 }; - if (clazy_std::contains(ignoreList, function->getNameAsString())) - return true; - return clazy_std::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); + return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); } FunctionArgsByRef::FunctionArgsByRef(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } static std::string warningMsgForSmallType(int sizeOf, const std::string &typeName) { std::string sizeStr = std::to_string(sizeOf); - return "Missing reference on large type sizeof " + typeName + " is " + sizeStr + " bytes)"; + return "Missing reference on large type (sizeof " + typeName + " is " + sizeStr + " bytes)"; } void FunctionArgsByRef::processFunction(FunctionDecl *func) { - if (!func || shouldIgnoreFunction(func) || - !func->isThisDeclarationADefinition() || func->isDeleted()) + if (!func || !func->isThisDeclarationADefinition() || func->isDeleted() || shouldIgnoreOperator(func)) + return; + + if (m_context->isQtDeveloper() && shouldIgnoreFunction(func)) return; + const bool warnForOverriddenMethods = isOptionSet("warn-for-overridden-methods"); + if (!warnForOverriddenMethods && Utils::methodOverrides(dyn_cast<CXXMethodDecl>(func))) { + // When overriding you can't change the signature. You should fix the base classes first + return; + } + Stmt *body = func->getBody(); int i = -1; @@ -132,7 +140,7 @@ void FunctionArgsByRef::processFunction(FunctionDecl *func) error = "Missing reference on non-trivial type (" + paramStr + ')'; } - emitWarning(param->getLocStart(), error.c_str(), fixits); + emitWarning(getLocStart(param), error.c_str(), fixits); } } } @@ -144,8 +152,8 @@ void FunctionArgsByRef::VisitDecl(Decl *decl) void FunctionArgsByRef::VisitStmt(Stmt *stmt) { - if (LambdaExpr *lambda = dyn_cast<LambdaExpr>(stmt)) { - if (!shouldIgnoreFile(stmt->getLocStart())) + if (auto lambda = dyn_cast<LambdaExpr>(stmt)) { + if (!shouldIgnoreFile(getLocStart(stmt))) processFunction(lambda->getCallOperator()); } } @@ -155,7 +163,3 @@ clang::FixItHint FunctionArgsByRef::fixit(const ParmVarDecl *, TypeUtils::QualTy FixItHint fixit; return fixit; } - -const char *const s_checkName = "function-args-by-ref"; -REGISTER_CHECK(s_checkName, FunctionArgsByRef, CheckLevel2) -// REGISTER_FIXIT(FixitAll, "fix-func-args", s_checkName) diff --git a/src/checks/level2/function-args-by-value.cpp b/src/checks/level2/function-args-by-value.cpp index 0c9e1fff..5cd37d5c 100644 --- a/src/checks/level2/function-args-by-value.cpp +++ b/src/checks/level2/function-args-by-value.cpp @@ -1,7 +1,7 @@ /* This file is part of the clazy static checker. - Copyright (C) 2016-2017 Sergio Martins <smartins@kde.org> + Copyright (C) 2016-2018 Sergio Martins <smartins@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -21,10 +21,10 @@ #include "function-args-by-value.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "TypeUtils.h" #include "FixItUtils.h" +#include "ClazyContext.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -32,11 +32,6 @@ using namespace clang; using namespace std; -enum Fixit { - FixitNone = 0, - FixitAll = 0x1 // More granularity isn't needed I guess -}; - // TODO, go over all these static bool shouldIgnoreClass(CXXRecordDecl *record) { @@ -58,13 +53,19 @@ static bool shouldIgnoreClass(CXXRecordDecl *record) "QVariantComparisonHelper", "QHashDummyValue", "QCharRef", "QString::Null" }; - return clazy_std::contains(ignoreList, record->getQualifiedNameAsString()); + return clazy::contains(ignoreList, record->getQualifiedNameAsString()); } -static bool shouldIgnoreFunction(clang::FunctionDecl *function) +static bool shouldIgnoreOperator(FunctionDecl *function) { // Too many warnings in operator<< - static const vector<string> ignoreList = {"operator<<"}; + static const vector<StringRef> ignoreList = { "operator<<" }; + + return clazy::contains(ignoreList, clazy::name(function)); +} + +static bool shouldIgnoreFunction(clang::FunctionDecl *function) +{ static const vector<string> qualifiedIgnoreList = {"QDBusMessage::createErrorReply", // Fixed in Qt6 "QMenu::exec", // Fixed in Qt6 "QTextFrame::iterator", // Fixed in Qt6 @@ -76,14 +77,12 @@ static bool shouldIgnoreFunction(clang::FunctionDecl *function) "QSslCertificate::verify", // Fixed in Qt6 "QSslConfiguration::setAllowedNextProtocols" // Fixed in Qt6 }; - if (clazy_std::contains(ignoreList, function->getNameAsString())) - return true; - return clazy_std::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); + return clazy::contains(qualifiedIgnoreList, function->getQualifiedNameAsString()); } FunctionArgsByValue::FunctionArgsByValue(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -94,22 +93,31 @@ void FunctionArgsByValue::VisitDecl(Decl *decl) void FunctionArgsByValue::VisitStmt(Stmt *stmt) { - if (LambdaExpr *lambda = dyn_cast<LambdaExpr>(stmt)) + if (auto lambda = dyn_cast<LambdaExpr>(stmt)) processFunction(lambda->getCallOperator()); } void FunctionArgsByValue::processFunction(FunctionDecl *func) { - if (!func || !func->isThisDeclarationADefinition() || - func->isDeleted() || shouldIgnoreFunction(func)) + if (!func || !func->isThisDeclarationADefinition() || func->isDeleted()) return; auto ctor = dyn_cast<CXXConstructorDecl>(func); - if (ctor) { - if (ctor->isCopyConstructor()) - return; // copy-ctor must take by ref + if (ctor && ctor->isCopyConstructor()) + return; // copy-ctor must take by ref + + const bool warnForOverriddenMethods = isOptionSet("warn-for-overridden-methods"); + if (!warnForOverriddenMethods && Utils::methodOverrides(dyn_cast<CXXMethodDecl>(func))) { + // When overriding you can't change the signature. You should fix the base classes first + return; } + if (shouldIgnoreOperator(func)) + return; + + if (m_context->isQtDeveloper() && shouldIgnoreFunction(func)) + return; + Stmt *body = func->getBody(); int i = -1; @@ -151,9 +159,11 @@ void FunctionArgsByValue::processFunction(FunctionDecl *func) } std::vector<FixItHint> fixits; - if (isFixitEnabled(FixitAll)) { + auto method = dyn_cast<CXXMethodDecl>(func); + const bool isVirtualMethod = method && method->isVirtual(); + if ((!isVirtualMethod || warnForOverriddenMethods) && isFixitEnabled()) { // Don't try to fix virtual methods, as build can fail for (auto redecl : func->redecls()) { // Fix in both header and .cpp - FunctionDecl *fdecl = dyn_cast<FunctionDecl>(redecl); + auto fdecl = dyn_cast<FunctionDecl>(redecl); const ParmVarDecl *param = fdecl->getParamDecl(i); fixits.push_back(fixit(fdecl, param, classif)); } @@ -161,7 +171,7 @@ void FunctionArgsByValue::processFunction(FunctionDecl *func) const string paramStr = param->getType().getAsString(); string error = "Pass small and trivially-copyable type by value (" + paramStr + ')'; - emitWarning(param->getLocStart(), error.c_str(), fixits); + emitWarning(getLocStart(param), error.c_str(), fixits); } } } @@ -173,15 +183,15 @@ FixItHint FunctionArgsByValue::fixit(FunctionDecl *func, const ParmVarDecl *para qt.removeLocalConst(); const string typeName = qt.getAsString(PrintingPolicy(lo())); string replacement = typeName + ' ' + string(param->getName()); - SourceLocation startLoc = param->getLocStart(); - SourceLocation endLoc = param->getLocEnd(); + SourceLocation startLoc = getLocStart(param); + SourceLocation endLoc = getLocEnd(param); const int numRedeclarations = std::distance(func->redecls_begin(), func->redecls_end()); const bool definitionIsAlsoDeclaration = numRedeclarations == 1; const bool isDeclarationButNotDefinition = !func->doesThisDeclarationHaveABody(); if (param->hasDefaultArg() && (isDeclarationButNotDefinition || definitionIsAlsoDeclaration)) { - endLoc = param->getDefaultArg()->getLocStart().getLocWithOffset(-1); + endLoc = getLocStart(param->getDefaultArg()).getLocWithOffset(-1); replacement += " ="; } @@ -191,7 +201,5 @@ FixItHint FunctionArgsByValue::fixit(FunctionDecl *func, const ParmVarDecl *para return {}; } - return FixItUtils::createReplacement({ startLoc, endLoc }, replacement); + return clazy::createReplacement({ startLoc, endLoc }, replacement); } - -REGISTER_CHECK("function-args-by-value", FunctionArgsByValue, CheckLevel2) diff --git a/src/checks/level2/globalconstcharpointer.cpp b/src/checks/level2/global-const-char-pointer.cpp index 54a761d2..6c5b4cdb 100644 --- a/src/checks/level2/globalconstcharpointer.cpp +++ b/src/checks/level2/global-const-char-pointer.cpp @@ -22,8 +22,7 @@ Boston, MA 02110-1301, USA. */ -#include "globalconstcharpointer.h" -#include "checkmanager.h" +#include "global-const-char-pointer.h" #include <clang/AST/Decl.h> #include <clang/AST/DeclCXX.h> @@ -43,7 +42,7 @@ void GlobalConstCharPointer::VisitDecl(clang::Decl *decl) !varDecl->hasExternalFormalLinkage() || decl->isInAnonymousNamespace() || varDecl->hasExternalStorage()) return; - if (shouldIgnoreFile(decl->getLocStart())) + if (shouldIgnoreFile(getLocStart(decl))) return; QualType qt = varDecl->getType(); @@ -56,7 +55,5 @@ void GlobalConstCharPointer::VisitDecl(clang::Decl *decl) if (!pointeeType || !pointeeType->isCharType()) return; - emitWarning(decl->getLocStart(), "non const global char *"); + emitWarning(getLocStart(decl), "non const global char *"); } - -REGISTER_CHECK("global-const-char-pointer", GlobalConstCharPointer, CheckLevel2) diff --git a/src/checks/level2/globalconstcharpointer.h b/src/checks/level2/global-const-char-pointer.h index 3d676b45..3d676b45 100644 --- a/src/checks/level2/globalconstcharpointer.h +++ b/src/checks/level2/global-const-char-pointer.h diff --git a/src/checks/level2/implicitcasts.cpp b/src/checks/level2/implicit-casts.cpp index 36653b5b..2b843a6c 100644 --- a/src/checks/level2/implicitcasts.cpp +++ b/src/checks/level2/implicit-casts.cpp @@ -22,11 +22,10 @@ Boston, MA 02110-1301, USA. */ -#include "implicitcasts.h" +#include "implicit-casts.h" #include "ClazyContext.h" #include "Utils.h" #include "HierarchyUtils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -37,7 +36,7 @@ using namespace std; ImplicitCasts::ImplicitCasts(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { m_filesToIgnore = { "qobject_impl.h", "qdebug.h", "hb-", "qdbusintegrator.cpp", "harfbuzz-", "qunicodetools.cpp" }; @@ -82,7 +81,7 @@ static bool iterateCallExpr(T* callExpr, CheckBase *check) if (!implicitCast || implicitCast->getCastKind() != clang::CK_PointerToBoolean) continue; - check->emitWarning(implicitCast->getLocStart(), "Implicit pointer to bool cast (argument " + std::to_string(i) + ')'); + check->emitWarning(getLocStart(implicitCast), "Implicit pointer to bool cast (argument " + std::to_string(i) + ')'); result = true; } @@ -114,10 +113,10 @@ static bool iterateCallExpr2(T* callExpr, CheckBase *check, ParentMap *parentMap if (!qt.getTypePtrOrNull()->isBooleanType()) // Filter out some bool to const bool continue; - if (HierarchyUtils::getFirstChildOfType<CXXFunctionalCastExpr>(implicitCast)) + if (clazy::getFirstChildOfType<CXXFunctionalCastExpr>(implicitCast)) continue; - if (HierarchyUtils::getFirstChildOfType<CStyleCastExpr>(implicitCast)) + if (clazy::getFirstChildOfType<CStyleCastExpr>(implicitCast)) continue; if (Utils::isInsideOperatorCall(parentMap, implicitCast, {"QTextStream", "QAtomicInt", "QBasicAtomicInt"})) @@ -126,7 +125,7 @@ static bool iterateCallExpr2(T* callExpr, CheckBase *check, ParentMap *parentMap if (Utils::insideCTORCall(parentMap, implicitCast, {"QAtomicInt", "QBasicAtomicInt"})) continue; - check->emitWarning(implicitCast->getLocStart(), "Implicit bool to int cast (argument " + std::to_string(i) + ')'); + check->emitWarning(getLocStart(implicitCast), "Implicit bool to int cast (argument " + std::to_string(i) + ')'); result = true; } @@ -138,18 +137,21 @@ void ImplicitCasts::VisitStmt(clang::Stmt *stmt) // Lets check only in function calls. Otherwise there are too many false positives, it's common // to implicit cast to bool when checking pointers for validity, like if (ptr) - CallExpr *callExpr = dyn_cast<CallExpr>(stmt); - auto ctorExpr = dyn_cast<CXXConstructExpr>(stmt); - if (!callExpr && !ctorExpr) - return; + auto callExpr = dyn_cast<CallExpr>(stmt); + CXXConstructExpr* ctorExpr = nullptr; + if (!callExpr) { + ctorExpr = dyn_cast<CXXConstructExpr>(stmt); + if (!ctorExpr) + return; + } if (isa<CXXOperatorCallExpr>(stmt)) return; - if (isMacroToIgnore(stmt->getLocStart())) + if (isMacroToIgnore(getLocStart(stmt))) return; - if (shouldIgnoreFile(stmt->getLocStart())) + if (shouldIgnoreFile(getLocStart(stmt))) return; FunctionDecl *func = callExpr ? callExpr->getDirectCallee() @@ -176,20 +178,14 @@ bool ImplicitCasts::isBoolToInt(FunctionDecl *func) const return false; // Disabled for now, too many false-positives when interacting with C code static const vector<string> functions = {"QString::arg"}; - return !clazy_std::contains(functions, func->getQualifiedNameAsString()); + return !clazy::contains(functions, func->getQualifiedNameAsString()); } bool ImplicitCasts::isMacroToIgnore(SourceLocation loc) const { - static const vector<string> macros = {"QVERIFY", "Q_UNLIKELY", "Q_LIKELY"}; - auto macro = Lexer::getImmediateMacroName(loc, sm(), lo()); - return clazy_std::contains(macros, macro); -} - -std::vector<string> ImplicitCasts::supportedOptions() const -{ - static const vector<string> options = { "bool-to-int" }; - return options; + static const vector<StringRef> macros = {"QVERIFY", "Q_UNLIKELY", "Q_LIKELY"}; + if (!loc.isMacroID()) + return false; + StringRef macro = Lexer::getImmediateMacroName(loc, sm(), lo()); + return clazy::contains(macros, macro); } - -REGISTER_CHECK("implicit-casts", ImplicitCasts, CheckLevel2) diff --git a/src/checks/level2/implicitcasts.h b/src/checks/level2/implicit-casts.h index b676b457..6cf9fb65 100644 --- a/src/checks/level2/implicitcasts.h +++ b/src/checks/level2/implicit-casts.h @@ -46,7 +46,6 @@ public: ImplicitCasts(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stmt) override; private: - std::vector<std::string> supportedOptions() const override; bool isBoolToInt(clang::FunctionDecl *func) const; bool isMacroToIgnore(clang::SourceLocation loc) const; }; diff --git a/src/checks/level2/missing-qobject-macro.cpp b/src/checks/level2/missing-qobject-macro.cpp index 79c700cb..1828aef1 100644 --- a/src/checks/level2/missing-qobject-macro.cpp +++ b/src/checks/level2/missing-qobject-macro.cpp @@ -22,11 +22,9 @@ #include <llvm/Config/llvm-config.h> #include "missing-qobject-macro.h" - #include "ClazyContext.h" #include "Utils.h" #include "QtUtils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -36,23 +34,23 @@ using namespace clang; using namespace std; -MissingQ_OBJECT::MissingQ_OBJECT(const std::string &name, ClazyContext *context) +MissingQObjectMacro::MissingQObjectMacro(const std::string &name, ClazyContext *context) : CheckBase(name, context) { enablePreProcessorCallbacks(); } -void MissingQ_OBJECT::VisitMacroExpands(const clang::Token &MacroNameTok, const clang::SourceRange &range) +void MissingQObjectMacro::VisitMacroExpands(const clang::Token &MacroNameTok, const clang::SourceRange &range, const MacroInfo *) { IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); if (ii && ii->getName() == "Q_OBJECT") registerQ_OBJECT(range.getBegin()); } -void MissingQ_OBJECT::VisitDecl(clang::Decl *decl) +void MissingQObjectMacro::VisitDecl(clang::Decl *decl) { CXXRecordDecl *record = dyn_cast<CXXRecordDecl>(decl); - if (!record || !record->hasDefinition() || record->getDefinition() != record || !QtUtils::isQObject(record)) + if (!record || !record->hasDefinition() || record->getDefinition() != record || !clazy::isQObject(record)) return; if (record->getDescribedClassTemplate() != nullptr) // moc doesn't accept Q_OBJECT in templates @@ -61,22 +59,20 @@ void MissingQ_OBJECT::VisitDecl(clang::Decl *decl) if (m_context->usingPreCompiledHeaders()) return; - const SourceLocation startLoc = decl->getLocStart(); + const SourceLocation startLoc = getLocStart(decl); for (const SourceLocation &loc : m_qobjectMacroLocations) { if (sm().getFileID(loc) != sm().getFileID(startLoc)) continue; // Different file - if (sm().isBeforeInSLocAddrSpace(startLoc, loc) && sm().isBeforeInSLocAddrSpace(loc, decl->getLocEnd())) + if (sm().isBeforeInSLocAddrSpace(startLoc, loc) && sm().isBeforeInSLocAddrSpace(loc, getLocEnd(decl))) return; // We found a Q_OBJECT after start and before end, it's ours. } emitWarning(startLoc, record->getQualifiedNameAsString() + " is missing a Q_OBJECT macro"); } -void MissingQ_OBJECT::registerQ_OBJECT(SourceLocation loc) +void MissingQObjectMacro::registerQ_OBJECT(SourceLocation loc) { m_qobjectMacroLocations.push_back(loc); } - -REGISTER_CHECK("missing-qobject-macro", MissingQ_OBJECT, CheckLevel2) diff --git a/src/checks/level2/missing-qobject-macro.h b/src/checks/level2/missing-qobject-macro.h index 8b7cfc24..d3ed477d 100644 --- a/src/checks/level2/missing-qobject-macro.h +++ b/src/checks/level2/missing-qobject-macro.h @@ -36,14 +36,14 @@ class SourceLocation; * * See README-missing-qobject for more information */ -class MissingQ_OBJECT : public CheckBase +class MissingQObjectMacro : public CheckBase { public: - explicit MissingQ_OBJECT(const std::string &name, ClazyContext *context); + explicit MissingQObjectMacro(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; private: void VisitMacroExpands(const clang::Token &MacroNameTok, - const clang::SourceRange &range) override; + const clang::SourceRange &range, const clang::MacroInfo *minfo = nullptr) override; void registerQ_OBJECT(clang::SourceLocation); std::vector<clang::SourceLocation> m_qobjectMacroLocations; }; diff --git a/src/checks/level2/missing-type-info.cpp b/src/checks/level2/missing-typeinfo.cpp index d695c02e..f1aa85f6 100644 --- a/src/checks/level2/missing-type-info.cpp +++ b/src/checks/level2/missing-typeinfo.cpp @@ -22,12 +22,11 @@ Boston, MA 02110-1301, USA. */ -#include "missing-type-info.h" +#include "missing-typeinfo.h" #include "Utils.h" #include "TemplateUtils.h" #include "TypeUtils.h" #include "QtUtils.h" -#include "checkmanager.h" #include "StringUtils.h" #include <clang/AST/AST.h> @@ -36,14 +35,14 @@ using namespace std; using namespace clang; -MissingTypeinfo::MissingTypeinfo(const std::string &name, ClazyContext *context) +MissingTypeInfo::MissingTypeInfo(const std::string &name, ClazyContext *context) : CheckBase(name, context) { } -void MissingTypeinfo::VisitDecl(clang::Decl *decl) +void MissingTypeInfo::VisitDecl(clang::Decl *decl) { - ClassTemplateSpecializationDecl *tstdecl = TemplateUtils::templateDecl(decl); + ClassTemplateSpecializationDecl *tstdecl = clazy::templateDecl(decl); if (!tstdecl) return; @@ -55,17 +54,17 @@ void MissingTypeinfo::VisitDecl(clang::Decl *decl) return; } - QualType qt2 = TemplateUtils::getTemplateArgumentType(tstdecl, 0); + QualType qt2 = clazy::getTemplateArgumentType(tstdecl, 0); const Type *t = qt2.getTypePtrOrNull(); CXXRecordDecl *record = t ? t->getAsCXXRecordDecl() : nullptr; if (!record || !record->getDefinition() || typeHasClassification(qt2)) return; // Don't crash if we only have a fwd decl const bool isCopyable = qt2.isTriviallyCopyableType(m_astContext); - const bool isTooBigForQList = isQList && QtUtils::isTooBigForQList(qt2, &m_astContext); + const bool isTooBigForQList = isQList && clazy::isTooBigForQList(qt2, &m_astContext); if ((isQVector || isTooBigForQList) && isCopyable) { - if (sm().isInSystemHeader(record->getLocStart())) + if (sm().isInSystemHeader(getLocStart(record))) return; std::string typeName = record->getName(); @@ -77,18 +76,16 @@ void MissingTypeinfo::VisitDecl(clang::Decl *decl) } } -void MissingTypeinfo::registerQTypeInfo(ClassTemplateSpecializationDecl *decl) +void MissingTypeInfo::registerQTypeInfo(ClassTemplateSpecializationDecl *decl) { if (decl->getName() == "QTypeInfo") { - const string typeName = TemplateUtils::getTemplateArgumentTypeStr(decl, 0, lo(), /**recordOnly=*/true); + const string typeName = clazy::getTemplateArgumentTypeStr(decl, 0, lo(), /**recordOnly=*/true); if (!typeName.empty()) m_typeInfos.insert(typeName); } } -bool MissingTypeinfo::typeHasClassification(QualType qt) const +bool MissingTypeInfo::typeHasClassification(QualType qt) const { - return m_typeInfos.find(StringUtils::simpleTypeName(qt, lo())) != m_typeInfos.end(); + return m_typeInfos.find(clazy::simpleTypeName(qt, lo())) != m_typeInfos.end(); } - -REGISTER_CHECK("missing-typeinfo", MissingTypeinfo, CheckLevel2) diff --git a/src/checks/level2/missing-type-info.h b/src/checks/level2/missing-typeinfo.h index 0705a1dd..eb5f948b 100644 --- a/src/checks/level2/missing-type-info.h +++ b/src/checks/level2/missing-typeinfo.h @@ -38,10 +38,10 @@ class CXXRecordDecl; * * See README-missing-type-info for more info. */ -class MissingTypeinfo : public CheckBase +class MissingTypeInfo : public CheckBase { public: - MissingTypeinfo(const std::string &name, ClazyContext *context); + MissingTypeInfo(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; private: void registerQTypeInfo(clang::ClassTemplateSpecializationDecl *decl); diff --git a/src/checks/level2/oldstyleconnect.cpp b/src/checks/level2/old-style-connect.cpp index c22c6266..ebc4acca 100644 --- a/src/checks/level2/oldstyleconnect.cpp +++ b/src/checks/level2/old-style-connect.cpp @@ -22,10 +22,9 @@ Boston, MA 02110-1301, USA. */ -#include "oldstyleconnect.h" +#include "old-style-connect.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "FixItUtils.h" #include "ContextUtils.h" @@ -44,46 +43,32 @@ using namespace clang; using namespace std; - -enum Fixit { - FixitNone = 0, - FixItConnects = 1 -}; - enum ConnectFlag { ConnectFlag_None = 0, // Not a disconnect or connect ConnectFlag_Connect = 1, // It's a connect ConnectFlag_Disconnect = 2, // It's a disconnect ConnectFlag_QTimerSingleShot = 4, ConnectFlag_OldStyle = 8, // Qt4 style - ConnectFlag_4ArgsDisconnect = 16 , // disconnect(const char *signal = 0, const QObject *receiver = 0, const char *method = 0) const - ConnectFlag_2ArgsDisconnect = 32, //disconnect(const QObject *receiver, const char *method = 0) const - ConnectFlag_5ArgsConnect = 64, // connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) - ConnectFlag_4ArgsConnect = 128, // connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type = Qt::AutoConnection) - ConnectFlag_OldStyleButNonLiteral = 256, // connect(foo, SIGNAL(bar()), foo, variableWithSlotName); // here the slot name isn't a literal - ConnectFlag_QStateAddTransition = 512, - ConnectFlag_Bogus = 1024 + ConnectFlag_4ArgsDisconnect = 16, // disconnect(const char *signal = 0, const QObject *receiver = 0, const char *method = 0) const + ConnectFlag_3ArgsDisconnect = 32, // disconnect(SIGNAL(foo)) + ConnectFlag_2ArgsDisconnect = 64, //disconnect(const QObject *receiver, const char *method = 0) const + ConnectFlag_5ArgsConnect = 128, // connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection) + ConnectFlag_4ArgsConnect = 256, // connect(const QObject *sender, const char *signal, const char *method, Qt::ConnectionType type = Qt::AutoConnection) + ConnectFlag_OldStyleButNonLiteral = 0x200, // connect(foo, SIGNAL(bar()), foo, variableWithSlotName); // here the slot name isn't a literal + ConnectFlag_QStateAddTransition = 0x400, + ConnectFlag_QMenuAddAction = 0x800, + ConnectFlag_QMessageBoxOpen = 0x1000, + ConnectFlag_Bogus = 0x2000 }; -static bool classIsOk(const string &className) +static bool classIsOk(StringRef className) { // List of classes we usually use Qt4 syntax - static const vector<string> okClasses = { "QDBusInterface" }; - return clazy_std::contains(okClasses, className); -} - -static CharSourceRange getImmediateExpansionRange(SourceLocation macroLoc, const SourceManager &sm) -{ -#if LLVM_VERSION_MAJOR >= 7 - return sm.getImmediateExpansionRange(macroLoc); -#else - auto pair = sm.getImmediateExpansionRange(macroLoc); - return CharSourceRange(SourceRange(pair.first, pair.second), false); -#endif + return className != "QDBusInterface"; } OldStyleConnect::OldStyleConnect(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { enablePreProcessorCallbacks(); context->enableAccessSpecifierManager(); @@ -102,12 +87,15 @@ int OldStyleConnect::classifyConnect(FunctionDecl *connectFunc, CallExpr *connec classification |= ConnectFlag_QTimerSingleShot; else if (methodName == "QState::addTransition") classification |= ConnectFlag_QStateAddTransition; - + else if (methodName == "QMenu::addAction") + classification |= ConnectFlag_QMenuAddAction; + else if (methodName == "QMessageBox::open") + classification |= ConnectFlag_QMessageBoxOpen; if (classification == ConnectFlag_None) return classification; - if (QtUtils::connectHasPMFStyle(connectFunc)) + if (clazy::connectHasPMFStyle(connectFunc)) return classification; else classification |= ConnectFlag_OldStyle; @@ -125,6 +113,8 @@ int OldStyleConnect::classifyConnect(FunctionDecl *connectFunc, CallExpr *connec } else if (classification & ConnectFlag_Disconnect) { if (numParams == 4) { classification |= ConnectFlag_4ArgsDisconnect; + } else if (numParams == 3) { + classification |= ConnectFlag_3ArgsDisconnect; } else if (numParams == 2) { classification |= ConnectFlag_2ArgsDisconnect; } else { @@ -136,7 +126,7 @@ int OldStyleConnect::classifyConnect(FunctionDecl *connectFunc, CallExpr *connec // It's old style, but check if all macros are literals int numLiterals = 0; for (auto arg : connectCall->arguments()) { - auto argLocation = arg->getLocStart(); + auto argLocation = getLocStart(arg); string dummy; if (isSignalOrSlot(argLocation, dummy)) ++numLiterals; @@ -152,6 +142,10 @@ int OldStyleConnect::classifyConnect(FunctionDecl *connectFunc, CallExpr *connec classification |= ConnectFlag_OldStyleButNonLiteral; } else if ((classification & ConnectFlag_Disconnect) && numLiterals == 0) { classification |= ConnectFlag_OldStyleButNonLiteral; + } else if ((classification & ConnectFlag_QMenuAddAction) && numLiterals != 1) { + classification |= ConnectFlag_OldStyleButNonLiteral; + } else if ((classification & ConnectFlag_QMessageBoxOpen) && numLiterals != 1) { + classification |= ConnectFlag_OldStyleButNonLiteral; } } @@ -161,18 +155,17 @@ int OldStyleConnect::classifyConnect(FunctionDecl *connectFunc, CallExpr *connec bool OldStyleConnect::isQPointer(Expr *expr) const { vector<CXXMemberCallExpr*> memberCalls; - HierarchyUtils::getChilds<CXXMemberCallExpr>(expr, memberCalls); + clazy::getChilds<CXXMemberCallExpr>(expr, memberCalls); for (auto callExpr : memberCalls) { if (!callExpr->getDirectCallee()) continue; - CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(callExpr->getDirectCallee()); + auto method = dyn_cast<CXXMethodDecl>(callExpr->getDirectCallee()); if (!method) continue; // Any better way to detect it's an operator ? - static regex rx(R"(operator .* \*)"); - if (regex_match(method->getNameAsString(), rx)) + if (clazy::startsWith(method->getNameAsString(), "operator ")) return true; } @@ -181,7 +174,7 @@ bool OldStyleConnect::isQPointer(Expr *expr) const bool OldStyleConnect::isPrivateSlot(const string &name) const { - return clazy_std::any_of(m_privateSlots, [name](const PrivateSlot &slot) { + return clazy::any_of(m_privateSlots, [name](const PrivateSlot &slot) { return slot.name == name; }); } @@ -192,8 +185,8 @@ void OldStyleConnect::VisitStmt(Stmt *s) if (!call) return; - if (m_lastMethodDecl && m_context->isQtDeveloper() && m_lastMethodDecl->getParent() && - m_lastMethodDecl->getParent()->getNameAsString() == "QObject") // Don't warn of stuff inside qobject.h + if (m_context->lastMethodDecl && m_context->isQtDeveloper() && m_context->lastMethodDecl->getParent() && + clazy::name(m_context->lastMethodDecl->getParent()) == "QObject") // Don't warn of stuff inside qobject.h return; FunctionDecl *function = call->getDirectCallee(); @@ -212,11 +205,11 @@ void OldStyleConnect::VisitStmt(Stmt *s) return; if (classification & ConnectFlag_Bogus) { - emitWarning(s->getLocStart(), "Internal error"); + emitWarning(getLocStart(s), "Internal error"); return; } - emitWarning(s->getLocStart(), "Old Style Connect", fixits(classification, call)); + emitWarning(getLocStart(s), "Old Style Connect", fixits(classification, call)); } void OldStyleConnect::addPrivateSlot(const PrivateSlot &slot) @@ -224,7 +217,7 @@ void OldStyleConnect::addPrivateSlot(const PrivateSlot &slot) m_privateSlots.push_back(slot); } -void OldStyleConnect::VisitMacroExpands(const Token ¯oNameTok, const SourceRange &range) +void OldStyleConnect::VisitMacroExpands(const Token ¯oNameTok, const SourceRange &range, const MacroInfo *) { IdentifierInfo *ii = macroNameTok.getIdentifierInfo(); if (!ii || ii->getName() != "Q_PRIVATE_SLOT") @@ -278,7 +271,7 @@ bool OldStyleConnect::isSignalOrSlot(SourceLocation loc, string ¯oName) cons vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) { - if (!isFixitEnabled(FixItConnects)) + if (!isFixitEnabled()) return {}; if (!call) { @@ -289,7 +282,20 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) if (classification & ConnectFlag_2ArgsDisconnect) { // Not implemented yet string msg = "Fix it not implemented for disconnect with 2 args"; - queueManualFixitWarning(call->getLocStart(), FixItConnects, msg); + queueManualFixitWarning(getLocStart(call), msg); + return {}; + } + + if (classification & ConnectFlag_3ArgsDisconnect) { + // Not implemented yet + string msg = "Fix it not implemented for disconnect with 3 args"; + queueManualFixitWarning(getLocStart(call), msg); + return {}; + } + + if (classification & ConnectFlag_QMessageBoxOpen) { + string msg = "Fix it not implemented for QMessageBox::open()"; + queueManualFixitWarning(getLocStart(call), msg); return {}; } @@ -299,7 +305,7 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) string macroName; CXXMethodDecl *senderMethod = nullptr; for (auto arg : call->arguments()) { - SourceLocation s = arg->getLocStart(); + SourceLocation s = getLocStart(arg); static const CXXRecordDecl *lastRecordDecl = nullptr; if (isSignalOrSlot(s, macroName)) { macroNum++; @@ -311,14 +317,14 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) llvm::errs() << "This first macro shouldn't enter this path"; if (!lastRecordDecl) { string msg = "Failed to get class name for implicit receiver"; - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } } if (!lastRecordDecl) { string msg = "Failed to get class name for explicit receiver"; - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } @@ -330,7 +336,7 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) if (isPrivateSlot(methodName)) { msg = "Converting Q_PRIVATE_SLOTS not implemented yet\n"; } else { - if (m_context->isQtDeveloper() && classIsOk(lastRecordDecl->getNameAsString())) { + if (m_context->isQtDeveloper() && classIsOk(clazy::name(lastRecordDecl))) { // This is OK return {}; } else { @@ -338,12 +344,12 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) } } - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } else if (methods.size() != 1) { string msg = string("Too many overloads (") + to_string(methods.size()) + string(") for method ") + methodName + " for record " + lastRecordDecl->getNameAsString(); - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } else { AccessSpecifierManager *a = m_context->accessSpecifierManager; @@ -355,7 +361,7 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) // The method is actually a signal and the user used SLOT() // bail out with the fixing. string msg = string("Can't fix. SLOT macro used but method " + methodName + " is a signal"); - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } } @@ -371,16 +377,16 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) const unsigned int numReceiverParams = methodDecl->getNumParams(); if (numReceiverParams > senderMethod->getNumParams()) { string msg = string("Receiver has more parameters (") + to_string(methodDecl->getNumParams()) + ") than signal (" + to_string(senderMethod->getNumParams()) + ')'; - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } for (unsigned int i = 0; i < numReceiverParams; ++i) { ParmVarDecl *receiverParm = methodDecl->getParamDecl(i); ParmVarDecl *senderParm = senderMethod->getParamDecl(i); - if (!QtUtils::isConvertibleTo(senderParm->getType().getTypePtr(), receiverParm->getType().getTypePtrOrNull())) { + if (!clazy::isConvertibleTo(senderParm->getType().getTypePtr(), receiverParm->getType().getTypePtrOrNull())) { string msg = string("Sender's parameters are incompatible with the receiver's"); - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } } @@ -388,28 +394,34 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) if ((classification & ConnectFlag_QTimerSingleShot) && methodDecl->getNumParams() > 0) { string msg = "(QTimer) Fixit not implemented for slot with arguments, use a lambda"; - queueManualFixitWarning(s, FixItConnects, msg); + queueManualFixitWarning(s, msg); return {}; } - DeclContext *context = m_lastDecl->getDeclContext(); + if ((classification & ConnectFlag_QMenuAddAction) && methodDecl->getNumParams() > 0) { + string msg = "(QMenu) Fixit not implemented for slot with arguments, use a lambda"; + queueManualFixitWarning(s, msg); + return {}; + } + + DeclContext *context = m_context->lastDecl->getDeclContext(); bool isSpecialProtectedCase = false; - if (!ContextUtils::canTakeAddressOf(methodDecl, context, /*by-ref*/isSpecialProtectedCase)) { - string msg = "Can't fix " + StringUtils::accessString(methodDecl->getAccess()) + ' ' + macroName + ' ' + methodDecl->getQualifiedNameAsString(); - queueManualFixitWarning(s, FixItConnects, msg); + if (!clazy::canTakeAddressOf(methodDecl, context, /*by-ref*/isSpecialProtectedCase)) { + string msg = "Can't fix " + clazy::accessString(methodDecl->getAccess()) + ' ' + macroName + ' ' + methodDecl->getQualifiedNameAsString(); + queueManualFixitWarning(s, msg); return {}; } string qualifiedName; - auto contextRecord = ContextUtils::firstContextOfType<CXXRecordDecl>(m_lastDecl->getDeclContext()); - const bool isInInclude = sm().getMainFileID() != sm().getFileID(call->getLocStart()); + auto contextRecord = clazy::firstContextOfType<CXXRecordDecl>(m_context->lastDecl->getDeclContext()); + const bool isInInclude = sm().getMainFileID() != sm().getFileID(getLocStart(call)); if (isSpecialProtectedCase && contextRecord) { // We're inside a derived class trying to take address of a protected base member, must use &Derived::method instead of &Base::method. qualifiedName = contextRecord->getNameAsString() + "::" + methodDecl->getNameAsString() ; } else { - qualifiedName = ContextUtils::getMostNeededQualifiedName(sm(), methodDecl, context, call->getLocStart(), !isInInclude); // (In includes ignore using directives) + qualifiedName = clazy::getMostNeededQualifiedName(sm(), methodDecl, context, getLocStart(call), !isInInclude); // (In includes ignore using directives) } CharSourceRange expansionRange = getImmediateExpansionRange(s, sm()); @@ -429,11 +441,11 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) if (record) { lastRecordDecl = record; if (isQPointer(expr)) { - auto endLoc = FixItUtils::locForNextToken(&m_astContext, arg->getLocStart(), tok::comma); + auto endLoc = clazy::locForNextToken(&m_astContext, getLocStart(arg), tok::comma); if (endLoc.isValid()) { fixits.push_back(FixItHint::CreateInsertion(endLoc, ".data()")); } else { - queueManualFixitWarning(s, FixItConnects, "Can't fix this QPointer case"); + queueManualFixitWarning(s, "Can't fix this QPointer case"); return {}; } } @@ -443,7 +455,3 @@ vector<FixItHint> OldStyleConnect::fixits(int classification, CallExpr *call) return fixits; } - -const char *const s_checkName = "old-style-connect"; -REGISTER_CHECK_WITH_FLAGS(s_checkName, OldStyleConnect, CheckLevel2, RegisteredCheck::Option_Qt4Incompatible) -REGISTER_FIXIT(FixItConnects, "fix-old-style-connect", s_checkName) diff --git a/src/checks/level2/oldstyleconnect.h b/src/checks/level2/old-style-connect.h index 609d2b31..3aadb237 100644 --- a/src/checks/level2/oldstyleconnect.h +++ b/src/checks/level2/old-style-connect.h @@ -52,7 +52,7 @@ public: void VisitStmt(clang::Stmt *) override; void addPrivateSlot(const PrivateSlot &); protected: - void VisitMacroExpands(const clang::Token ¯oNameTok, const clang::SourceRange &) override; + void VisitMacroExpands(const clang::Token ¯oNameTok, const clang::SourceRange &, const clang::MacroInfo *minfo = nullptr) override; private: std::string signalOrSlotNameFromMacro(clang::SourceLocation macroLoc); std::vector<clang::FixItHint> fixits(int classification, clang::CallExpr *); diff --git a/src/checks/level2/qstring-allocations.cpp b/src/checks/level2/qstring-allocations.cpp index 96e21b95..9ec6caae 100644 --- a/src/checks/level2/qstring-allocations.cpp +++ b/src/checks/level2/qstring-allocations.cpp @@ -30,7 +30,6 @@ #include "FixItUtils.h" #include "FunctionUtils.h" #include "QtUtils.h" -#include "checkmanager.h" #include <clang/AST/DeclCXX.h> #include <clang/AST/ExprCXX.h> @@ -64,13 +63,13 @@ struct Latin1Expr { }; QStringAllocations::QStringAllocations(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } void QStringAllocations::VisitStmt(clang::Stmt *stm) { - if (m_context->isQtDeveloper() && QtUtils::isBootstrapping(m_preprocessorOpts)) { + if (m_context->isQtDeveloper() && clazy::isBootstrapping(m_context->ci.getPreprocessorOpts())) { // During bootstrap many QString::fromLatin1() are used instead of tr(), which causes // much noise return; @@ -84,13 +83,13 @@ void QStringAllocations::VisitStmt(clang::Stmt *stm) static bool betterTakeQLatin1String(CXXMethodDecl *method, StringLiteral *lt) { - static const vector<string> methods = {"append", "compare", "endsWith", "startsWith", "insert", - "lastIndexOf", "prepend", "replace", "contains", "indexOf" }; + static const vector<StringRef> methods = {"append", "compare", "endsWith", "startsWith", "insert", + "lastIndexOf", "prepend", "replace", "contains", "indexOf" }; - if (!StringUtils::isOfClass(method, "QString")) + if (!clazy::isOfClass(method, "QString")) return false; - return (!lt || Utils::isAscii(lt)) && clazy_std::contains(methods, method->getNameAsString()); + return (!lt || Utils::isAscii(lt)) && clazy::contains(methods, clazy::name(method)); } // Returns the first occurrence of a QLatin1String(char*) CTOR call @@ -99,11 +98,11 @@ Latin1Expr QStringAllocations::qlatin1CtorExpr(Stmt *stm, ConditionalOperator * if (!stm) return {}; - CXXConstructExpr *constructExpr = dyn_cast<CXXConstructExpr>(stm); + auto constructExpr = dyn_cast<CXXConstructExpr>(stm); if (constructExpr) { CXXConstructorDecl *ctor = constructExpr->getConstructor(); const int numArgs = ctor->getNumParams(); - if (StringUtils::isOfClass(ctor, "QLatin1String")) { + if (clazy::isOfClass(ctor, "QLatin1String")) { if (Utils::containsStringLiteral(constructExpr, /*allowEmpty=*/ false, 2)) return {constructExpr, /*enableFixits=*/ numArgs == 1}; @@ -155,7 +154,7 @@ static StringLiteral* stringLiteralForCall(Stmt *call) return nullptr; vector<StringLiteral*> literals; - HierarchyUtils::getChilds(call, literals, 2); + clazy::getChilds(call, literals, 2); return literals.empty() ? nullptr : literals[0]; } @@ -166,17 +165,16 @@ void QStringAllocations::VisitCtor(Stmt *stm) return; CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor(); - if (!StringUtils::isOfClass(ctorDecl, "QString")) + if (!clazy::isOfClass(ctorDecl, "QString")) return; - static const vector<string> blacklistedParentCtors = { "QRegExp", "QIcon" }; - if (Utils::insideCTORCall(m_context->parentMap, stm, blacklistedParentCtors)) { + if (Utils::insideCTORCall(m_context->parentMap, stm, { "QRegExp", "QIcon" })) { // https://blogs.kde.org/2015/11/05/qregexp-qstringliteral-crash-exit return; } if (!isOptionSet("no-msvc-compat")) { - InitListExpr *initializerList = HierarchyUtils::getFirstParentOfType<InitListExpr>(m_context->parentMap, ctorExpr); + InitListExpr *initializerList = clazy::getFirstParentOfType<InitListExpr>(m_context->parentMap, ctorExpr); if (initializerList != nullptr) return; // Nothing to do here, MSVC doesn't like it @@ -188,9 +186,9 @@ void QStringAllocations::VisitCtor(Stmt *stm) bool isQLatin1String = false; string paramType; - if (FunctionUtils::hasCharPtrArgument(ctorDecl, 1)) { + if (clazy::hasCharPtrArgument(ctorDecl, 1)) { paramType = "const char*"; - } else if (ctorDecl->param_size() == 1 && StringUtils::hasArgumentOfType(ctorDecl, "QLatin1String", lo())) { + } else if (ctorDecl->param_size() == 1 && clazy::hasArgumentOfType(ctorDecl, "QLatin1String", lo())) { paramType = "QLatin1String"; isQLatin1String = true; } else { @@ -209,65 +207,65 @@ void QStringAllocations::VisitCtor(Stmt *stm) auto qlatin1Ctor = qlatin1expr.qlatin1ctorexpr; - if (qlatin1Ctor->getLocStart().isMacroID()) { - auto macroName = Lexer::getImmediateMacroName(qlatin1Ctor->getLocStart(), sm(), lo()); + if (getLocStart(qlatin1Ctor).isMacroID()) { + auto macroName = Lexer::getImmediateMacroName(getLocStart(qlatin1Ctor), sm(), lo()); if (macroName == "Q_GLOBAL_STATIC_WITH_ARGS") // bug #391807 return; } vector<FixItHint> fixits; if (qlatin1expr.enableFixit && isFixitEnabled(QLatin1StringAllocations)) { - if (!qlatin1Ctor->getLocStart().isMacroID()) { + if (!getLocStart(qlatin1Ctor).isMacroID()) { if (!ternary) { fixits = fixItReplaceWordWithWord(qlatin1Ctor, "QStringLiteral", "QLatin1String", QLatin1StringAllocations); - bool shouldRemoveQString = qlatin1Ctor->getLocStart().getRawEncoding() != stm->getLocStart().getRawEncoding() && dyn_cast_or_null<CXXBindTemporaryExpr>(HierarchyUtils::parent(m_context->parentMap, ctorExpr)); + bool shouldRemoveQString = getLocStart(qlatin1Ctor).getRawEncoding() != getLocStart(stm).getRawEncoding() && dyn_cast_or_null<CXXBindTemporaryExpr>(clazy::parent(m_context->parentMap, ctorExpr)); if (shouldRemoveQString) { // This is the case of QString(QLatin1String("foo")), which we just fixed to be QString(QStringLiteral("foo)), so now remove QString - auto removalFixits = FixItUtils::fixItRemoveToken(&m_astContext, ctorExpr, true); + auto removalFixits = clazy::fixItRemoveToken(&m_astContext, ctorExpr, true); if (removalFixits.empty()) { - queueManualFixitWarning(ctorExpr->getLocStart(), QLatin1StringAllocations, "Internal error: invalid start or end location"); + queueManualFixitWarning(getLocStart(ctorExpr), "Internal error: invalid start or end location", QLatin1StringAllocations); } else { - clazy_std::append(removalFixits, fixits); + clazy::append(removalFixits, fixits); } } } else { fixits = fixItReplaceWordWithWordInTernary(ternary); } } else { - queueManualFixitWarning(qlatin1Ctor->getLocStart(), QLatin1StringAllocations, "Can't use QStringLiteral in macro"); + queueManualFixitWarning(getLocStart(qlatin1Ctor), "Can't use QStringLiteral in macro", QLatin1StringAllocations); } } - emitWarning(stm->getLocStart(), msg, fixits); + emitWarning(getLocStart(stm), msg, fixits); } else { vector<FixItHint> fixits; - if (clazy_std::hasChildren(ctorExpr)) { + if (clazy::hasChildren(ctorExpr)) { auto pointerDecay = dyn_cast<ImplicitCastExpr>(*(ctorExpr->child_begin())); - if (clazy_std::hasChildren(pointerDecay)) { + if (clazy::hasChildren(pointerDecay)) { StringLiteral *lt = dyn_cast<StringLiteral>(*pointerDecay->child_begin()); if (lt && isFixitEnabled(CharPtrAllocations)) { - Stmt *grandParent = HierarchyUtils::parent(m_context->parentMap, lt, 2); - Stmt *grandGrandParent = HierarchyUtils::parent(m_context->parentMap, lt, 3); - Stmt *grandGrandGrandParent = HierarchyUtils::parent(m_context->parentMap, lt, 4); + Stmt *grandParent = clazy::parent(m_context->parentMap, lt, 2); + Stmt *grandGrandParent = clazy::parent(m_context->parentMap, lt, 3); + Stmt *grandGrandGrandParent = clazy::parent(m_context->parentMap, lt, 4); if (grandParent == ctorExpr && grandGrandParent && isa<CXXBindTemporaryExpr>(grandGrandParent) && grandGrandGrandParent && isa<CXXFunctionalCastExpr>(grandGrandGrandParent)) { // This is the case of QString("foo"), replace QString const bool literalIsEmpty = lt->getLength() == 0; - if (literalIsEmpty && HierarchyUtils::getFirstParentOfType<MemberExpr>(m_context->parentMap, ctorExpr) == nullptr) + if (literalIsEmpty && clazy::getFirstParentOfType<MemberExpr>(m_context->parentMap, ctorExpr) == nullptr) fixits = fixItReplaceWordWithWord(ctorExpr, "QLatin1String", "QString", CharPtrAllocations); - else if (!ctorExpr->getLocStart().isMacroID()) + else if (!getLocStart(ctorExpr).isMacroID()) fixits = fixItReplaceWordWithWord(ctorExpr, "QStringLiteral", "QString", CharPtrAllocations); else - queueManualFixitWarning(ctorExpr->getLocStart(), CharPtrAllocations, "Can't use QStringLiteral in macro."); + queueManualFixitWarning(getLocStart(ctorExpr), "Can't use QStringLiteral in macro.", CharPtrAllocations); } else { - auto parentMemberCallExpr = HierarchyUtils::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, lt, /*maxDepth=*/6); // 6 seems like a nice max from the ASTs I've seen + auto parentMemberCallExpr = clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, lt, /*maxDepth=*/6); // 6 seems like a nice max from the ASTs I've seen string replacement = "QStringLiteral"; if (parentMemberCallExpr) { FunctionDecl *fDecl = parentMemberCallExpr->getDirectCallee(); if (fDecl) { - CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(fDecl); + auto method = dyn_cast<CXXMethodDecl>(fDecl); if (method && betterTakeQLatin1String(method, lt)) { replacement = "QLatin1String"; } @@ -280,7 +278,7 @@ void QStringAllocations::VisitCtor(Stmt *stm) } } - emitWarning(stm->getLocStart(), msg, fixits); + emitWarning(getLocStart(stm), msg, fixits); } } @@ -289,7 +287,7 @@ vector<FixItHint> QStringAllocations::fixItReplaceWordWithWord(clang::Stmt *begi StringLiteral *lt = stringLiteralForCall(begin); if (replacee == "QLatin1String") { if (lt && !Utils::isAscii(lt)) { - emitWarning(lt->getLocStart(), "Don't use QLatin1String with non-latin1 literals"); + emitWarning(getLocStart(lt), "Don't use QLatin1String with non-latin1 literals"); return {}; } } @@ -298,9 +296,9 @@ vector<FixItHint> QStringAllocations::fixItReplaceWordWithWord(clang::Stmt *begi return {}; vector<FixItHint> fixits; - FixItHint fixit = FixItUtils::fixItReplaceWordWithWord(&m_astContext, begin, replacement, replacee); + FixItHint fixit = clazy::fixItReplaceWordWithWord(&m_astContext, begin, replacement, replacee); if (fixit.isNull()) { - queueManualFixitWarning(begin->getLocStart(), fixitType); + queueManualFixitWarning(getLocStart(begin), "", fixitType); } else { fixits.push_back(fixit); } @@ -311,18 +309,18 @@ vector<FixItHint> QStringAllocations::fixItReplaceWordWithWord(clang::Stmt *begi vector<FixItHint> QStringAllocations::fixItReplaceWordWithWordInTernary(clang::ConditionalOperator *ternary) { vector<CXXConstructExpr*> constructExprs; - HierarchyUtils::getChilds<CXXConstructExpr>(ternary, constructExprs, 1); // depth = 1, only the two immediate expressions + clazy::getChilds<CXXConstructExpr>(ternary, constructExprs, 1); // depth = 1, only the two immediate expressions vector<FixItHint> fixits; fixits.reserve(2); if (constructExprs.size() != 2) { - llvm::errs() << "Weird ternary operator with " << constructExprs.size() << " at " << ternary->getLocStart().printToString(sm()) << "\n"; + llvm::errs() << "Weird ternary operator with " << constructExprs.size() << " at " << getLocStart(ternary).printToString(sm()) << "\n"; assert(false); return fixits; } for (int i = 0; i < 2; ++i) { - SourceLocation rangeStart = constructExprs[i]->getLocStart(); + SourceLocation rangeStart = getLocStart(constructExprs[i]); SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm(), lo()); fixits.push_back(FixItHint::CreateReplacement(SourceRange(rangeStart, rangeEnd), "QStringLiteral")); } @@ -345,7 +343,7 @@ static bool isQStringLiteralCandidate(Stmt *s, ParentMap *map, const LangOptions return true; auto constructExpr = dyn_cast<CXXConstructExpr>(s); - if (StringUtils::isOfClass(constructExpr, "QString")) + if (clazy::isOfClass(constructExpr, "QString")) return true; if (Utils::isAssignOperator(dyn_cast<CXXOperatorCallExpr>(s), "QString", "QLatin1String", lo)) @@ -357,14 +355,14 @@ static bool isQStringLiteralCandidate(Stmt *s, ParentMap *map, const LangOptions CallExpr *callExpr = dyn_cast<CallExpr>(s); StringLiteral *literal = stringLiteralForCall(callExpr); auto operatorCall = dyn_cast<CXXOperatorCallExpr>(s); - if (operatorCall && StringUtils::returnTypeName(operatorCall, lo) != "QTestData") { + if (operatorCall && clazy::returnTypeName(operatorCall, lo) != "QTestData") { // QTest::newRow will static_assert when using QLatin1String // Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system"); - string className = StringUtils::classNameFor(operatorCall); + string className = clazy::classNameFor(operatorCall); if (className == "QString") { return false; - } else if (className.empty() && StringUtils::hasArgumentOfType(operatorCall->getDirectCallee(), "QString", lo)) { + } else if (className.empty() && clazy::hasArgumentOfType(operatorCall->getDirectCallee(), "QString", lo)) { return false; } } @@ -378,7 +376,7 @@ static bool isQStringLiteralCandidate(Stmt *s, ParentMap *map, const LangOptions } if (currentCall == 0 || dyn_cast<ImplicitCastExpr>(s) || dyn_cast<CXXBindTemporaryExpr>(s) || dyn_cast<MaterializeTemporaryExpr>(s)) // skip this cruft - return isQStringLiteralCandidate(HierarchyUtils::parent(map, s), map, lo, sm, currentCall + 1); + return isQStringLiteralCandidate(clazy::parent(map, s), map, lo, sm, currentCall + 1); return false; } @@ -388,10 +386,10 @@ std::vector<FixItHint> QStringAllocations::fixItReplaceFromLatin1OrFromUtf8(Call vector<FixItHint> fixits; std::string replacement = isQStringLiteralCandidate(callExpr, m_context->parentMap, lo(), sm()) ? "QStringLiteral" - : "QLatin1String"; + : "QLatin1String"; - if (replacement == "QStringLiteral" && callExpr->getLocStart().isMacroID()) { - queueManualFixitWarning(callExpr->getLocStart(), FromLatin1_FromUtf8Allocations, "Can't use QStringLiteral in macro!"); + if (replacement == "QStringLiteral" && getLocStart(callExpr).isMacroID()) { + queueManualFixitWarning(getLocStart(callExpr), "Can't use QStringLiteral in macro!", FromLatin1_FromUtf8Allocations); return {}; } @@ -410,13 +408,13 @@ std::vector<FixItHint> QStringAllocations::fixItReplaceFromLatin1OrFromUtf8(Call } } - auto classNameLoc = Lexer::getLocForEndOfToken(callExpr->getLocStart(), 0, sm(), lo()); + auto classNameLoc = Lexer::getLocForEndOfToken(getLocStart(callExpr), 0, sm(), lo()); auto scopeOperatorLoc = Lexer::getLocForEndOfToken(classNameLoc, 0, sm(), lo()); auto methodNameLoc = Lexer::getLocForEndOfToken(scopeOperatorLoc, -1, sm(), lo()); - SourceRange range(callExpr->getLocStart(), methodNameLoc); + SourceRange range(getLocStart(callExpr), methodNameLoc); fixits.push_back(FixItHint::CreateReplacement(range, replacement)); } else { - queueManualFixitWarning(callExpr->getLocStart(), FromLatin1_FromUtf8Allocations, "Internal error: literal is null"); + queueManualFixitWarning(getLocStart(callExpr), "Internal error: literal is null", FromLatin1_FromUtf8Allocations); } return fixits; @@ -426,28 +424,28 @@ std::vector<FixItHint> QStringAllocations::fixItRawLiteral(clang::StringLiteral { vector<FixItHint> fixits; - SourceRange range = FixItUtils::rangeForLiteral(&m_astContext, lt); + SourceRange range = clazy::rangeForLiteral(&m_astContext, lt); if (range.isInvalid()) { if (lt) { - queueManualFixitWarning(lt->getLocStart(), CharPtrAllocations, "Internal error: Can't calculate source location"); + queueManualFixitWarning(getLocStart(lt), "Internal error: Can't calculate source location", CharPtrAllocations); } return {}; } - SourceLocation start = lt->getLocStart(); + SourceLocation start = getLocStart(lt); if (start.isMacroID()) { - queueManualFixitWarning(start, CharPtrAllocations, "Can't use QStringLiteral in macro.."); + queueManualFixitWarning(start, "Can't use QStringLiteral in macro", CharPtrAllocations); } else { if (Utils::literalContainsEscapedBytes(lt, sm(), lo())) return {}; string revisedReplacement = lt->getLength() == 0 ? "QLatin1String" : replacement; // QLatin1String("") is better than QStringLiteral("") - if (revisedReplacement == "QStringLiteral" && lt->getLocStart().isMacroID()) { - queueManualFixitWarning(lt->getLocStart(), CharPtrAllocations, "Can't use QStringLiteral in macro..."); + if (revisedReplacement == "QStringLiteral" && getLocStart(lt).isMacroID()) { + queueManualFixitWarning(getLocStart(lt), "Can't use QStringLiteral in macro...", CharPtrAllocations); return {}; } - FixItUtils::insertParentMethodCall(revisedReplacement, range, /**by-ref*/fixits); + clazy::insertParentMethodCall(revisedReplacement, range, /**by-ref*/fixits); } return fixits; @@ -459,14 +457,14 @@ void QStringAllocations::VisitOperatorCall(Stmt *stm) if (!operatorCall) return; - if (StringUtils::returnTypeName(operatorCall, lo()) == "QTestData") { + if (clazy::returnTypeName(operatorCall, lo()) == "QTestData") { // QTest::newRow will static_assert when using QLatin1String // Q_STATIC_ASSERT_X(QMetaTypeId2<T>::Defined, "Type is not registered, please use the Q_DECLARE_METATYPE macro to make it known to Qt's meta-object system"); return; } std::vector<StringLiteral*> stringLiterals; - HierarchyUtils::getChilds<StringLiteral>(operatorCall, stringLiterals); + clazy::getChilds<StringLiteral>(operatorCall, stringLiterals); // We're only after string literals, str.contains(some_method_returning_const_char_is_fine()) if (stringLiterals.empty()) @@ -477,16 +475,16 @@ void QStringAllocations::VisitOperatorCall(Stmt *stm) return; CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(funcDecl); - if (!StringUtils::isOfClass(methodDecl, "QString")) + if (!clazy::isOfClass(methodDecl, "QString")) return; - if (!FunctionUtils::hasCharPtrArgument(methodDecl)) + if (!clazy::hasCharPtrArgument(methodDecl)) return; vector<FixItHint> fixits; vector<StringLiteral*> literals; - HierarchyUtils::getChilds<StringLiteral>(stm, literals, 2); + clazy::getChilds<StringLiteral>(stm, literals, 2); if (!isOptionSet("no-msvc-compat") && !literals.empty()) { if (literals[0]->getNumConcatenated() > 1) { @@ -496,7 +494,7 @@ void QStringAllocations::VisitOperatorCall(Stmt *stm) if (isFixitEnabled(CharPtrAllocations)) { if (literals.empty()) { - queueManualFixitWarning(stm->getLocStart(), CharPtrAllocations, "Couldn't find literal"); + queueManualFixitWarning(getLocStart(stm), "Couldn't find literal", CharPtrAllocations); } else { const string replacement = Utils::isAscii(literals[0]) ? "QLatin1String" : "QStringLiteral"; fixits = fixItRawLiteral(literals[0], replacement); @@ -504,7 +502,7 @@ void QStringAllocations::VisitOperatorCall(Stmt *stm) } string msg = string("QString(const char*) being called"); - emitWarning(stm->getLocStart(), msg, fixits); + emitWarning(getLocStart(stm), msg, fixits); } void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) @@ -514,14 +512,14 @@ void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) return; FunctionDecl *functionDecl = callExpr->getDirectCallee(); - if (!StringUtils::functionIsOneOf(functionDecl, {"fromLatin1", "fromUtf8"})) + if (!clazy::functionIsOneOf(functionDecl, {"fromLatin1", "fromUtf8"})) return; CXXMethodDecl *methodDecl = dyn_cast<CXXMethodDecl>(functionDecl); - if (!StringUtils::isOfClass(methodDecl, "QString")) + if (!clazy::isOfClass(methodDecl, "QString")) return; - if (!Utils::callHasDefaultArguments(callExpr) || !FunctionUtils::hasCharPtrArgument(functionDecl, 2)) // QString::fromLatin1("foo", 1) is ok + if (!Utils::callHasDefaultArguments(callExpr) || !clazy::hasCharPtrArgument(functionDecl, 2)) // QString::fromLatin1("foo", 1) is ok return; if (!containsStringLiteralNoCallExpr(callExpr)) @@ -535,11 +533,11 @@ void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) } vector<ConditionalOperator*> ternaries; - HierarchyUtils::getChilds(callExpr, ternaries, 2); + clazy::getChilds(callExpr, ternaries, 2); if (!ternaries.empty()) { auto ternary = ternaries[0]; if (Utils::ternaryOperatorIsOfStringLiteral(ternary)) { - emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal")); + emitWarning(getLocStart(stmt), string("QString::fromLatin1() being passed a literal")); } return; @@ -548,14 +546,14 @@ void QStringAllocations::VisitFromLatin1OrUtf8(Stmt *stmt) std::vector<FixItHint> fixits; if (isFixitEnabled(FromLatin1_FromUtf8Allocations)) { - const FromFunction fromFunction = functionDecl->getNameAsString() == "fromLatin1" ? FromLatin1 : FromUtf8; + const FromFunction fromFunction = clazy::name(functionDecl) == "fromLatin1" ? FromLatin1 : FromUtf8; fixits = fixItReplaceFromLatin1OrFromUtf8(callExpr, fromFunction); } - if (functionDecl->getNameAsString() == "fromLatin1") { - emitWarning(stmt->getLocStart(), string("QString::fromLatin1() being passed a literal"), fixits); + if (clazy::name(functionDecl) == "fromLatin1") { + emitWarning(getLocStart(stmt), string("QString::fromLatin1() being passed a literal"), fixits); } else { - emitWarning(stmt->getLocStart(), string("QString::fromUtf8() being passed a literal"), fixits); + emitWarning(getLocStart(stmt), string("QString::fromUtf8() being passed a literal"), fixits); } } @@ -581,19 +579,5 @@ void QStringAllocations::VisitAssignOperatorQLatin1String(Stmt *stmt) : fixItReplaceWordWithWordInTernary(ternary); } - emitWarning(stmt->getLocStart(), string("QString::operator=(QLatin1String(\"literal\")"), fixits); + emitWarning(getLocStart(stmt), string("QString::operator=(QLatin1String(\"literal\")"), fixits); } - -vector<string> QStringAllocations::supportedOptions() const -{ - // no-msvc-compat - use QStringLiteral inside arrays, which is fine if you don't use MSVC - - static const vector<string> options = { "no-msvc-compat" }; - return options; -} - -const char *const s_checkName = "qstring-allocations"; -REGISTER_CHECK_WITH_FLAGS(s_checkName, QStringAllocations, CheckLevel2, RegisteredCheck::Option_Qt4Incompatible) -REGISTER_FIXIT(QLatin1StringAllocations, "fix-qlatin1string-allocations", s_checkName) -REGISTER_FIXIT(FromLatin1_FromUtf8Allocations, "fix-fromLatin1_fromUtf8-allocations", s_checkName) -REGISTER_FIXIT(CharPtrAllocations, "fix-fromCharPtrAllocations", s_checkName) diff --git a/src/checks/level2/qstring-allocations.h b/src/checks/level2/qstring-allocations.h index be1007bd..310e79c2 100644 --- a/src/checks/level2/qstring-allocations.h +++ b/src/checks/level2/qstring-allocations.h @@ -62,8 +62,6 @@ public: QStringAllocations(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stm) override; -protected: - std::vector<std::string> supportedOptions() const override; private: void VisitCtor(clang::Stmt *); void VisitOperatorCall(clang::Stmt *); diff --git a/src/checks/level2/returning-void-expression.cpp b/src/checks/level2/returning-void-expression.cpp index 8b37b17c..22c349cd 100644 --- a/src/checks/level2/returning-void-expression.cpp +++ b/src/checks/level2/returning-void-expression.cpp @@ -23,9 +23,9 @@ #include "Utils.h" #include "HierarchyUtils.h" #include "ContextUtils.h" +#include "ClazyContext.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -34,21 +34,21 @@ using namespace std; ReturningVoidExpression::ReturningVoidExpression(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } void ReturningVoidExpression::VisitStmt(clang::Stmt *stmt) { auto ret = dyn_cast<ReturnStmt>(stmt); - if (!ret || !clazy_std::hasChildren(ret)) + if (!ret || !clazy::hasChildren(ret)) return; QualType qt = ret->getRetValue()->getType(); if (qt.isNull() || !qt->isVoidType()) return; - DeclContext *context = ContextUtils::contextForDecl(m_lastDecl); + DeclContext *context = clazy::contextForDecl(m_context->lastDecl); if (!context) return; @@ -59,5 +59,3 @@ void ReturningVoidExpression::VisitStmt(clang::Stmt *stmt) emitWarning(stmt, "Returning a void expression"); } - -REGISTER_CHECK("returning-void-expression", ReturningVoidExpression, CheckLevel2) diff --git a/src/checks/level2/ruleofthree.cpp b/src/checks/level2/rule-of-three.cpp index 3baf85ff..fa490019 100644 --- a/src/checks/level2/ruleofthree.cpp +++ b/src/checks/level2/rule-of-three.cpp @@ -19,10 +19,9 @@ Boston, MA 02110-1301, USA. */ -#include "ruleofthree.h" +#include "rule-of-three.h" #include "Utils.h" #include "MacroUtils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "TypeUtils.h" @@ -47,12 +46,12 @@ void RuleOfThree::VisitDecl(clang::Decl *decl) if (record != record->getDefinition()) return; - if (shouldIgnoreFile(decl->getLocStart())) + if (shouldIgnoreFile(getLocStart(decl))) return; - const SourceLocation recordStart = record->getLocStart(); + const SourceLocation recordStart = getLocStart(record); if (recordStart.isMacroID()) { - if (MacroUtils::isInMacro(&m_astContext, recordStart, "Q_GLOBAL_STATIC_INTERNAL")) + if (clazy::isInMacro(&m_astContext, recordStart, "Q_GLOBAL_STATIC_INTERNAL")) return; } @@ -82,8 +81,8 @@ void RuleOfThree::VisitDecl(clang::Decl *decl) if (numImplemented == 0 || numImplemented == 3) // Rule of 3 respected return; - vector<string> hasList; - vector<string> missingList; + vector<StringRef> hasList; + vector<StringRef> missingList; if (hasUserDtor) hasList.push_back("dtor"); else @@ -101,12 +100,6 @@ void RuleOfThree::VisitDecl(clang::Decl *decl) const int numNotImplemented = missingList.size(); - const string className = record->getNameAsString(); - if (shouldIgnoreType(className)) - return; - - const string classQualifiedName = record->getQualifiedNameAsString(); - if (hasUserDtor && numImplemented == 1) { // Protected dtor is a way for a non-polymorphic base class avoid being deleted if (destructor->getAccess() == clang::AccessSpecifier::AS_protected) @@ -125,8 +118,10 @@ void RuleOfThree::VisitDecl(clang::Decl *decl) if (Utils::hasMember(record, "QSharedDataPointer")) return; // These need boiler-plate copy ctor and dtor + const string className = record->getNameAsString(); + const string classQualifiedName = record->getQualifiedNameAsString(); const string filename = sm().getFilename(recordStart); - if (clazy_std::endsWith(className, "Private") && clazy_std::endsWithAny(filename, { ".cpp", ".cxx", "_p.h" })) + if (clazy::endsWith(className, "Private") && clazy::endsWithAny(filename, { ".cpp", ".cxx", "_p.h" })) return; // Lots of RAII classes fall into this category. And even Private (d-pointer) classes, warning in that case would just be noise string msg = classQualifiedName + " has "; @@ -147,15 +142,5 @@ void RuleOfThree::VisitDecl(clang::Decl *decl) msg += ", "; } - emitWarning(decl->getLocStart(), msg); -} - -bool RuleOfThree::shouldIgnoreType(const std::string &className) const -{ - static const vector<string> types = { "QTransform" // Fixed for Qt 6 - }; - - return clazy_std::contains(types, className); + emitWarning(getLocStart(decl), msg); } - -REGISTER_CHECK("rule-of-three", RuleOfThree, CheckLevel2) diff --git a/src/checks/level2/ruleofthree.h b/src/checks/level2/rule-of-three.h index 9f1d3f12..9f1d3f12 100644 --- a/src/checks/level2/ruleofthree.h +++ b/src/checks/level2/rule-of-three.h diff --git a/src/checks/level2/static-pmf.cpp b/src/checks/level2/static-pmf.cpp new file mode 100644 index 00000000..c77635dd --- /dev/null +++ b/src/checks/level2/static-pmf.cpp @@ -0,0 +1,58 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "static-pmf.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +StaticPmf::StaticPmf(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ +} + +void StaticPmf::VisitDecl(clang::Decl *decl) +{ + auto vardecl = dyn_cast<VarDecl>(decl); + if (!vardecl || !vardecl->isStaticLocal()) + return; + + const Type *t = TypeUtils::unpealAuto(vardecl->getType()); + if (!t) + return; + + auto memberPointerType = dyn_cast<clang::MemberPointerType>(t); + if (!memberPointerType || !memberPointerType->isMemberFunctionPointer()) + return; + + auto record = memberPointerType->getMostRecentCXXRecordDecl(); + if (!clazy::isQObject(record)) + return; + + emitWarning(vardecl, "Static pointer to member has portability issues"); +} diff --git a/src/checks/level2/static-pmf.h b/src/checks/level2/static-pmf.h new file mode 100644 index 00000000..9bf7feee --- /dev/null +++ b/src/checks/level2/static-pmf.h @@ -0,0 +1,39 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_STATIC_PMF_H +#define CLAZY_STATIC_PMF_H + +#include "checkbase.h" + + +/** + * See README-static-pmf.md for more info. + */ +class StaticPmf : public CheckBase +{ +public: + explicit StaticPmf(const std::string &name, ClazyContext *context); + void VisitDecl(clang::Decl *) override; +private: +}; + +#endif diff --git a/src/checks/level2/virtualcallsfromctor.cpp b/src/checks/level2/virtual-call-ctor.cpp index b452e7b8..cc94d3f3 100644 --- a/src/checks/level2/virtualcallsfromctor.cpp +++ b/src/checks/level2/virtual-call-ctor.cpp @@ -22,10 +22,9 @@ Boston, MA 02110-1301, USA. */ -#include "virtualcallsfromctor.h" +#include "virtual-call-ctor.h" #include "Utils.h" #include "HierarchyUtils.h" -#include "checkmanager.h" #include <clang/AST/Decl.h> #include <clang/AST/DeclCXX.h> @@ -33,26 +32,20 @@ using namespace std; using namespace clang; -VirtualCallsFromCTOR::VirtualCallsFromCTOR(const std::string &name, ClazyContext *context) +VirtualCallCtor::VirtualCallCtor(const std::string &name, ClazyContext *context) : CheckBase(name, context) { - -} - -void VirtualCallsFromCTOR::VisitStmt(clang::Stmt *stm) -{ - } -void VirtualCallsFromCTOR::VisitDecl(Decl *decl) +void VirtualCallCtor::VisitDecl(Decl *decl) { - CXXConstructorDecl *ctorDecl = dyn_cast<CXXConstructorDecl>(decl); - CXXDestructorDecl *dtorDecl = dyn_cast<CXXDestructorDecl>(decl); - if (ctorDecl == nullptr && dtorDecl == nullptr) + auto ctorDecl = dyn_cast<CXXConstructorDecl>(decl); + auto dtorDecl = dyn_cast<CXXDestructorDecl>(decl); + if (!ctorDecl && !dtorDecl) return; Stmt *ctorOrDtorBody = ctorDecl ? ctorDecl->getBody() : dtorDecl->getBody(); - if (ctorOrDtorBody == nullptr) + if (!ctorOrDtorBody) return; CXXRecordDecl *classDecl = ctorDecl ? ctorDecl->getParent() : dtorDecl->getParent(); @@ -60,45 +53,44 @@ void VirtualCallsFromCTOR::VisitDecl(Decl *decl) std::vector<Stmt*> processedStmts; SourceLocation loc = containsVirtualCall(classDecl, ctorOrDtorBody, processedStmts); if (loc.isValid()) { - if (ctorDecl != nullptr) { - emitWarning(decl->getLocStart(), "Calling pure virtual function in CTOR"); + if (ctorDecl) { + emitWarning(getLocStart(decl), "Calling pure virtual function in CTOR"); } else { - emitWarning(decl->getLocStart(), "Calling pure virtual function in DTOR"); + emitWarning(getLocStart(decl), "Calling pure virtual function in DTOR"); } emitWarning(loc, "Called here"); } } -SourceLocation VirtualCallsFromCTOR::containsVirtualCall(clang::CXXRecordDecl *classDecl, clang::Stmt *stmt, std::vector<Stmt*> &processedStmts) +SourceLocation VirtualCallCtor::containsVirtualCall(clang::CXXRecordDecl *classDecl, clang::Stmt *stmt, + std::vector<Stmt*> &processedStmts) { - if (stmt == nullptr) + if (!stmt) return {}; // already processed ? we don't want recurring calls - if (clazy_std::contains(processedStmts, stmt)) + if (clazy::contains(processedStmts, stmt)) return {}; processedStmts.push_back(stmt); std::vector<CXXMemberCallExpr*> memberCalls; - HierarchyUtils::getChilds<CXXMemberCallExpr>(stmt, memberCalls); + clazy::getChilds<CXXMemberCallExpr>(stmt, memberCalls); for (CXXMemberCallExpr *callExpr : memberCalls) { CXXMethodDecl *memberDecl = callExpr->getMethodDecl(); - if (!memberDecl || dyn_cast<CXXThisExpr>(callExpr->getImplicitObjectArgument()) == nullptr) + if (!memberDecl || !isa<CXXThisExpr>(callExpr->getImplicitObjectArgument())) continue; if (memberDecl->getParent() == classDecl) { if (memberDecl->isPure()) { - return callExpr->getLocStart(); + return getLocStart(callExpr); } else { if (containsVirtualCall(classDecl, memberDecl->getBody(), processedStmts).isValid()) - return callExpr->getLocStart(); + return getLocStart(callExpr); } } } return {}; } - -REGISTER_CHECK("virtual-call-ctor", VirtualCallsFromCTOR, CheckLevel2) diff --git a/src/checks/level2/virtualcallsfromctor.h b/src/checks/level2/virtual-call-ctor.h index b0586e95..0c3be313 100644 --- a/src/checks/level2/virtualcallsfromctor.h +++ b/src/checks/level2/virtual-call-ctor.h @@ -43,15 +43,15 @@ class SourceLocation; * This plugin only checks for pure virtuals, ignoring non-pure, which in theory you shouldn't call, * but seems common practice. */ -class VirtualCallsFromCTOR : public CheckBase +class VirtualCallCtor : public CheckBase { public: - VirtualCallsFromCTOR(const std::string &name, ClazyContext *context); - void VisitStmt(clang::Stmt *stm) override; + VirtualCallCtor(const std::string &name, ClazyContext *context); void VisitDecl(clang::Decl *decl) override; private: - clang::SourceLocation containsVirtualCall(clang::CXXRecordDecl *classDecl, clang::Stmt *stmt, std::vector<clang::Stmt*> &processedStmts); + clang::SourceLocation containsVirtualCall(clang::CXXRecordDecl *classDecl, clang::Stmt *stmt, + std::vector<clang::Stmt*> &processedStmts); }; diff --git a/src/checks/level3/README-bogus-dynamic-cast.md b/src/checks/level3/README-bogus-dynamic-cast.md deleted file mode 100644 index 940c77c6..00000000 --- a/src/checks/level3/README-bogus-dynamic-cast.md +++ /dev/null @@ -1,13 +0,0 @@ -# dynamic-cast - -Finds places where a dynamic cast is redundant or when dynamic casting to base class, which is unneeded. -Optionally it can also find places where a qobject_cast should be used instead. - -#### Example - - Foo *a = ...; - Foo *b = dynamic_cast<Foo*>(a); - - -If you prefer to use qobject_cast instead of dynamic_cast you can catch those cases with: -`export CLAZY_EXTRA_OPTIONS="bogus-dynamic-cast-qobject` diff --git a/src/checks/level3/assertwithsideeffects.cpp b/src/checks/level3/assert-with-side-effects.cpp index a71d2974..3a450eb5 100644 --- a/src/checks/level3/assertwithsideeffects.cpp +++ b/src/checks/level3/assert-with-side-effects.cpp @@ -22,11 +22,10 @@ Boston, MA 02110-1301, USA. */ -#include "assertwithsideeffects.h" +#include "assert-with-side-effects.h" #include "Utils.h" #include "MacroUtils.h" #include "StringUtils.h" -#include "checkmanager.h" #include <clang/AST/Expr.h> #include <clang/AST/Stmt.h> @@ -42,18 +41,18 @@ enum Aggressiveness }; AssertWithSideEffects::AssertWithSideEffects(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) , m_aggressiveness(NormalAggressiveness) { } -static bool functionIsOk(const string &name) +static bool functionIsOk(StringRef name) { - static const vector<string> whitelist = {"qFuzzyIsNull", "qt_noop", "qt_assert", "qIsFinite", "qIsInf", - "qIsNaN", "qIsNumericType", "operator==", "operator<", "operator>", "operator<=", "operator>=", "operator!=", "operator+", "operator-" - "q_func", "d_func", "isEmptyHelper" - "qCross", "qMin", "qMax", "qBound", "priv", "qobject_cast", "dbusService"}; - return clazy_std::contains(whitelist, name); + static const vector<StringRef> whitelist = {"qFuzzyIsNull", "qt_noop", "qt_assert", "qIsFinite", "qIsInf", + "qIsNaN", "qIsNumericType", "operator==", "operator<", "operator>", "operator<=", "operator>=", "operator!=", "operator+", "operator-" + "q_func", "d_func", "isEmptyHelper" + "qCross", "qMin", "qMax", "qBound", "priv", "qobject_cast", "dbusService"}; + return clazy::contains(whitelist, name); } static bool methodIsOK(const string &name) @@ -63,28 +62,28 @@ static bool methodIsOK(const string &name) "QByteArray::data", "QBasicMutex::isRecursive", "QLinkedList::begin", "QLinkedList::end", "QDataBuffer::first", "QOpenGLFunctions::glIsRenderbuffer"}; - return clazy_std::contains(whitelist, name); + return clazy::contains(whitelist, name); } void AssertWithSideEffects::VisitStmt(Stmt *stm) { - const SourceLocation stmStart = stm->getLocStart(); - if (!MacroUtils::isInMacro(&m_astContext, stmStart, "Q_ASSERT")) + const SourceLocation stmStart = getLocStart(stm); + if (!clazy::isInMacro(&m_astContext, stmStart, "Q_ASSERT")) return; bool warn = false; const bool checkfunctions = m_aggressiveness & AlsoCheckFunctionCallsAggressiveness; - CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stm); + auto memberCall = dyn_cast<CXXMemberCallExpr>(stm); if (memberCall) { if (checkfunctions) { CXXMethodDecl *method = memberCall->getMethodDecl(); - if (!method->isConst() && !methodIsOK(StringUtils::qualifiedMethodName(method)) && !functionIsOk(method->getNameAsString())) { - // llvm::errs() << "reason1 " << StringUtils::qualifiedMethodName(method) << "\n"; + if (!method->isConst() && !methodIsOK(clazy::qualifiedMethodName(method)) && !functionIsOk(clazy::name(method))) { + // llvm::errs() << "reason1 " << clazy::qualifiedMethodName(method) << "\n"; warn = true; } } - } else if (CallExpr *call = dyn_cast<CallExpr>(stm)) { + } else if (auto call = dyn_cast<CallExpr>(stm)) { // Non member function calls not allowed FunctionDecl *func = call->getDirectCallee(); @@ -93,30 +92,28 @@ void AssertWithSideEffects::VisitStmt(Stmt *stm) if (isa<CXXMethodDecl>(func)) // This will be visited next, so ignore it now return; - const std::string funcName = func->getNameAsString(); - if (functionIsOk(funcName)) { + if (functionIsOk(clazy::name(func))) { return; } - // llvm::errs() << "reason2 " << funcName << "\n"; warn = true; } - } else if (BinaryOperator *op = dyn_cast<BinaryOperator>(stm)) { + } else if (auto op = dyn_cast<BinaryOperator>(stm)) { if (op->isAssignmentOp()) { if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(op->getLHS())) { ValueDecl *valueDecl = declRef->getDecl(); - if (valueDecl && sm().isBeforeInSLocAddrSpace(valueDecl->getLocStart(), stmStart)) { + if (valueDecl && sm().isBeforeInSLocAddrSpace(getLocStart(valueDecl), stmStart)) { // llvm::errs() << "reason3\n"; warn = true; } } } - } else if (UnaryOperator *op = dyn_cast<UnaryOperator>(stm)) { - if (DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(op->getSubExpr())) { + } else if (auto op = dyn_cast<UnaryOperator>(stm)) { + if (auto declRef = dyn_cast<DeclRefExpr>(op->getSubExpr())) { ValueDecl *valueDecl = declRef->getDecl(); auto type = op->getOpcode(); if (type != UnaryOperatorKind::UO_Deref && type != UnaryOperatorKind::UO_AddrOf) { - if (valueDecl && sm().isBeforeInSLocAddrSpace(valueDecl->getLocStart(), stmStart)) { + if (valueDecl && sm().isBeforeInSLocAddrSpace(getLocStart(valueDecl), stmStart)) { // llvm::errs() << "reason5 " << op->getOpcodeStr() << "\n"; warn = true; } @@ -128,5 +125,3 @@ void AssertWithSideEffects::VisitStmt(Stmt *stm) emitWarning(stmStart, "Code inside Q_ASSERT has side-effects but won't be built in release mode"); } } - -REGISTER_CHECK("assert-with-side-effects", AssertWithSideEffects, CheckLevel3) diff --git a/src/checks/level3/assertwithsideeffects.h b/src/checks/level3/assert-with-side-effects.h index 4c63e53c..4c63e53c 100644 --- a/src/checks/level3/assertwithsideeffects.h +++ b/src/checks/level3/assert-with-side-effects.h diff --git a/src/checks/level3/detachingmember.cpp b/src/checks/level3/detaching-member.cpp index e4bff4ab..39101fa9 100644 --- a/src/checks/level3/detachingmember.cpp +++ b/src/checks/level3/detaching-member.cpp @@ -22,9 +22,8 @@ Boston, MA 02110-1301, USA. */ -#include "detachingmember.h" +#include "detaching-member.h" #include "ClazyContext.h" -#include "checkmanager.h" #include "Utils.h" #include "HierarchyUtils.h" #include "StringUtils.h" @@ -37,23 +36,23 @@ using namespace clang; using namespace std; DetachingMember::DetachingMember(const std::string &name, ClazyContext *context) - : DetachingBase(name, context) + : DetachingBase(name, context, Option_CanIgnoreIncludes) { m_filesToIgnore = { "qstring.h" }; } void DetachingMember::VisitStmt(clang::Stmt *stm) { - CallExpr *callExpr = dyn_cast<CallExpr>(stm); + auto callExpr = dyn_cast<CallExpr>(stm); if (!callExpr) return; - CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(callExpr); - CXXOperatorCallExpr *operatorExpr = dyn_cast<CXXOperatorCallExpr>(callExpr); + auto memberCall = dyn_cast<CXXMemberCallExpr>(callExpr); + auto operatorExpr = dyn_cast<CXXOperatorCallExpr>(callExpr); if (!memberCall && !operatorExpr) return; - if (shouldIgnoreFile(stm->getLocStart())) + if (shouldIgnoreFile(getLocStart(stm))) return; CXXMethodDecl *method = nullptr; @@ -61,10 +60,10 @@ void DetachingMember::VisitStmt(clang::Stmt *stm) if (operatorExpr) { FunctionDecl *func = operatorExpr->getDirectCallee(); method = func ? dyn_cast<CXXMethodDecl>(func) : nullptr; - if (!method || method->getNameAsString() != "operator[]") + if (!method || clazy::name(method) != "operator[]") return; - auto memberExpr = HierarchyUtils::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, operatorExpr); + auto memberExpr = clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, operatorExpr); CXXMethodDecl *parentMemberDecl = memberExpr ? memberExpr->getMethodDecl() : nullptr; if (parentMemberDecl && !parentMemberDecl->isConst()) { // Don't warn for s.m_listOfValues[0].nonConstMethod(); @@ -81,45 +80,45 @@ void DetachingMember::VisitStmt(clang::Stmt *stm) memberDecl = Utils::valueDeclForMemberCall(memberCall); } - if (!method || !memberDecl || !Utils::isMemberVariable(memberDecl) || !isDetachingMethod(method) || method->isConst()) + if (!method || !memberDecl || !Utils::isMemberVariable(memberDecl) || !isDetachingMethod(method, DetachingMethodWithConstCounterPart) || method->isConst()) return; // Catch cases like m_foo[0] = .. , which is fine - auto parentUnaryOp = HierarchyUtils::getFirstParentOfType<UnaryOperator>(m_context->parentMap, callExpr); + auto parentUnaryOp = clazy::getFirstParentOfType<UnaryOperator>(m_context->parentMap, callExpr); if (parentUnaryOp) { // m_foo[0]++ is OK return; } - auto parentOp = HierarchyUtils::getFirstParentOfType<CXXOperatorCallExpr>(m_context->parentMap, HierarchyUtils::parent(m_context->parentMap, callExpr)); + auto parentOp = clazy::getFirstParentOfType<CXXOperatorCallExpr>(m_context->parentMap, clazy::parent(m_context->parentMap, callExpr)); if (parentOp) { FunctionDecl *parentFunc = parentOp->getDirectCallee(); const string parentFuncName = parentFunc ? parentFunc->getNameAsString() : ""; - if (clazy_std::startsWith(parentFuncName, "operator")) { + if (clazy::startsWith(parentFuncName, "operator")) { // m_foo[0] = ... is OK return; } } - auto parentBinaryOp = HierarchyUtils::getFirstParentOfType<BinaryOperator>(m_context->parentMap, callExpr); + auto parentBinaryOp = clazy::getFirstParentOfType<BinaryOperator>(m_context->parentMap, callExpr); if (parentBinaryOp && parentBinaryOp->isAssignmentOp()) { // m_foo[0] += .. is OK Expr *lhs = parentBinaryOp->getLHS(); - if (callExpr == lhs || HierarchyUtils::isChildOf(callExpr, lhs)) + if (callExpr == lhs || clazy::isChildOf(callExpr, lhs)) return; } - const bool returnsNonConstIterator = clazy_std::endsWith(memberCall ? memberCall->getType().getAsString() : "", "::iterator"); + const bool returnsNonConstIterator = clazy::endsWith(memberCall ? memberCall->getType().getAsString() : "", "::iterator"); if (returnsNonConstIterator) { // If we're calling begin()/end() as arguments to a function taking non-const iterators it's fine // Such as qSort(list.begin(), list.end()); - auto parentCall = HierarchyUtils::getFirstParentOfType<CallExpr>(m_context->parentMap, HierarchyUtils::parent(m_context->parentMap, memberCall)); + auto parentCall = clazy::getFirstParentOfType<CallExpr>(m_context->parentMap, clazy::parent(m_context->parentMap, memberCall)); FunctionDecl *parentFunc = parentCall ? parentCall->getDirectCallee() : nullptr; if (parentFunc && parentFunc->getNumParams() == parentCall->getNumArgs()) { int i = 0; for (auto argExpr : parentCall->arguments()) { - if (CXXMemberCallExpr *expr2 = HierarchyUtils::getFirstChildOfType<CXXMemberCallExpr>(argExpr)) { + if (CXXMemberCallExpr *expr2 = clazy::getFirstChildOfType<CXXMemberCallExpr>(argExpr)) { if (expr2 == memberCall) { // Success, we found which arg ParmVarDecl *parm = parentFunc->getParamDecl(i); @@ -135,7 +134,5 @@ void DetachingMember::VisitStmt(clang::Stmt *stm) } } - emitWarning(stm->getLocStart(), "Potential detachment due to calling " + method->getQualifiedNameAsString() + "()"); + emitWarning(getLocStart(stm), "Potential detachment due to calling " + method->getQualifiedNameAsString() + "()"); } - -REGISTER_CHECK("detaching-member", DetachingMember, CheckLevel3) diff --git a/src/checks/level3/detachingmember.h b/src/checks/level3/detaching-member.h index 96d92a9a..96d92a9a 100644 --- a/src/checks/level3/detachingmember.h +++ b/src/checks/level3/detaching-member.h diff --git a/src/checks/level3/dynamic_cast.cpp b/src/checks/level3/dynamic_cast.cpp deleted file mode 100644 index bc180adc..00000000 --- a/src/checks/level3/dynamic_cast.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - 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 <sergio.martins@kdab.com> - - Copyright (C) 2015 Sergio Martins <smartins@kde.org> - - 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 "dynamic_cast.h" -#include "Utils.h" -#include "QtUtils.h" -#include "checkmanager.h" -#include "TypeUtils.h" - -#include <clang/AST/DeclCXX.h> -#include <clang/AST/ExprCXX.h> - -using namespace clang; - -BogusDynamicCast::BogusDynamicCast(const std::string &name, ClazyContext *context) - : CheckBase(name, context) -{ -} - -void BogusDynamicCast::VisitStmt(clang::Stmt *stm) -{ - auto dynExp = dyn_cast<CXXDynamicCastExpr>(stm); - if (!dynExp) - return; - - auto namedCast = dyn_cast<CXXNamedCastExpr>(stm); - CXXRecordDecl *castFrom = Utils::namedCastInnerDecl(namedCast); - if (!castFrom) - return; - - if (isOptionSet("qobject") && QtUtils::isQObject(castFrom)) // Very noisy and not very useful, and qobject_cast can fail too - emitWarning(dynExp->getLocStart(), "Use qobject_cast rather than dynamic_cast"); - - CXXRecordDecl *castTo = Utils::namedCastOuterDecl(namedCast); - if (!castTo) - return; - - if (castFrom == castTo) { - emitWarning(stm->getLocStart(), "Casting to itself"); - } else if (TypeUtils::derivesFrom(/*child=*/castFrom, castTo)) { - emitWarning(stm->getLocStart(), "explicitly casting to base is unnecessary"); - } -} - -REGISTER_CHECK("bogus-dynamic-cast", BogusDynamicCast, CheckLevel3) diff --git a/src/checks/level2/reservecandidates.cpp b/src/checks/level3/reserve-candidates.cpp index 1346740a..3a8bb359 100644 --- a/src/checks/level2/reservecandidates.cpp +++ b/src/checks/level3/reserve-candidates.cpp @@ -22,17 +22,17 @@ Boston, MA 02110-1301, USA. */ -#include "reservecandidates.h" +#include "reserve-candidates.h" #include "ClazyContext.h" #include "Utils.h" #include "clazy_stl.h" #include "MacroUtils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "QtUtils.h" #include "ContextUtils.h" #include "HierarchyUtils.h" #include "LoopUtils.h" +#include "ClazyContext.h" #include <clang/AST/Decl.h> #include <clang/AST/DeclCXX.h> @@ -47,7 +47,7 @@ using namespace clang; using namespace std; ReserveCandidates::ReserveCandidates(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -72,10 +72,10 @@ static bool isCandidateMethod(CXXMethodDecl *methodDecl) if (!classDecl) return false; - if (!clazy_std::equalsAny(methodDecl->getNameAsString(), { "append", "push_back", "push", "operator<<", "operator+=" })) + if (!clazy::equalsAny(clazy::name(methodDecl), { "append", "push_back", "push", "operator<<", "operator+=" })) return false; - if (!QtUtils::isAReserveClass(classDecl)) + if (!clazy::isAReserveClass(classDecl)) return false; // Catch cases like: QList<T>::append(const QList<T> &), which don't make sense to reserve. @@ -97,7 +97,7 @@ static bool isCandidate(CallExpr *oper) bool ReserveCandidates::containerWasReserved(clang::ValueDecl *valueDecl) const { - return valueDecl && clazy_std::contains(m_foundReserves, valueDecl); + return valueDecl && clazy::contains(m_foundReserves, valueDecl); } bool ReserveCandidates::acceptsValueDecl(ValueDecl *valueDecl) const @@ -111,7 +111,7 @@ bool ReserveCandidates::acceptsValueDecl(ValueDecl *valueDecl) const if (!valueDecl || isa<ParmVarDecl>(valueDecl) || containerWasReserved(valueDecl)) return false; - if (ContextUtils::isValueDeclInFunctionContext(valueDecl)) + if (clazy::isValueDeclInFunctionContext(valueDecl)) return true; // Actually, lets allow for some member variables containers if they are being used inside CTORs or DTORs @@ -119,11 +119,11 @@ bool ReserveCandidates::acceptsValueDecl(ValueDecl *valueDecl) const // human inspection, if such member function would be called in a loop we would be constantly calling reserve // and in that case the built-in exponential growth is better. - if (!m_lastMethodDecl || !(isa<CXXConstructorDecl>(m_lastMethodDecl) || isa<CXXDestructorDecl>(m_lastMethodDecl))) + if (!m_context->lastMethodDecl || !(isa<CXXConstructorDecl>(m_context->lastMethodDecl) || isa<CXXDestructorDecl>(m_context->lastMethodDecl))) return false; CXXRecordDecl *record = Utils::isMemberVariable(valueDecl); - if (record && m_lastMethodDecl->getParent() == record) + if (record && m_context->lastMethodDecl->getParent() == record) return true; return false; @@ -136,13 +136,13 @@ bool ReserveCandidates::isReserveCandidate(ValueDecl *valueDecl, Stmt *loopBody, const bool isMemberVariable = Utils::isMemberVariable(valueDecl); // We only want containers defined outside of the loop we're examining - if (!isMemberVariable && sm().isBeforeInSLocAddrSpace(loopBody->getLocStart(), valueDecl->getLocStart())) + if (!isMemberVariable && sm().isBeforeInSLocAddrSpace(getLocStart(loopBody), getLocStart(valueDecl))) return false; - if (isInComplexLoop(callExpr, valueDecl->getLocStart(), isMemberVariable)) + if (isInComplexLoop(callExpr, getLocStart(valueDecl), isMemberVariable)) return false; - if (LoopUtils::loopCanBeInterrupted(loopBody, m_sm, callExpr->getLocStart())) + if (clazy::loopCanBeInterrupted(loopBody, m_context->sm, getLocStart(callExpr))) return false; return true; @@ -153,11 +153,11 @@ void ReserveCandidates::VisitStmt(clang::Stmt *stm) if (registerReserveStatement(stm)) return; - auto body = LoopUtils::bodyFromLoop(stm); + auto body = clazy::bodyFromLoop(stm); if (!body) return; - const bool isForeach = MacroUtils::isInMacro(&m_astContext, stm->getLocStart(), "Q_FOREACH"); + const bool isForeach = clazy::isInMacro(&m_astContext, getLocStart(stm), "Q_FOREACH"); // If the body is another loop, we have nesting, ignore it now since the inner loops will be visited soon. if (isa<DoStmt>(body) || isa<WhileStmt>(body) || (!isForeach && isa<ForStmt>(body))) @@ -169,9 +169,9 @@ void ReserveCandidates::VisitStmt(clang::Stmt *stm) // Get the list of member calls and operator<< that are direct childs of the loop statements // If it's inside an if statement we don't care. - auto callExprs = HierarchyUtils::getStatements<CallExpr>(body, nullptr, {}, /*depth=*/ 1, + auto callExprs = clazy::getStatements<CallExpr>(body, nullptr, {}, /*depth=*/ 1, /*includeParent=*/ true, - HierarchyUtils::IgnoreExprWithCleanups); + clazy::IgnoreExprWithCleanups); for (CallExpr *callExpr : callExprs) { @@ -180,7 +180,7 @@ void ReserveCandidates::VisitStmt(clang::Stmt *stm) ValueDecl *valueDecl = Utils::valueDeclForCallExpr(callExpr); if (isReserveCandidate(valueDecl, body, callExpr)) - emitWarning(callExpr->getLocStart(), "Reserve candidate"); + emitWarning(getLocStart(callExpr), "Reserve candidate"); } } @@ -192,18 +192,18 @@ bool ReserveCandidates::registerReserveStatement(Stmt *stm) return false; CXXMethodDecl *methodDecl = memberCall->getMethodDecl(); - if (!methodDecl || methodDecl->getNameAsString() != "reserve") + if (!methodDecl || clazy::name(methodDecl) != "reserve") return false; CXXRecordDecl *decl = methodDecl->getParent(); - if (!QtUtils::isAReserveClass(decl)) + if (!clazy::isAReserveClass(decl)) return false; ValueDecl *valueDecl = Utils::valueDeclForMemberCall(memberCall); if (!valueDecl) return false; - if (!clazy_std::contains(m_foundReserves, valueDecl)) + if (!clazy::contains(m_foundReserves, valueDecl)) m_foundReserves.push_back(valueDecl); return true; @@ -215,10 +215,10 @@ bool ReserveCandidates::expressionIsComplex(clang::Expr *expr) const return false; vector<CallExpr*> callExprs; - HierarchyUtils::getChilds<CallExpr>(expr, callExprs); + clazy::getChilds<CallExpr>(expr, callExprs); for (CallExpr *callExpr : callExprs) { - if (QtUtils::isJavaIterator(dyn_cast<CXXMemberCallExpr>(callExpr))) + if (clazy::isJavaIterator(dyn_cast<CXXMemberCallExpr>(callExpr))) continue; QualType qt = callExpr->getType(); @@ -228,7 +228,7 @@ bool ReserveCandidates::expressionIsComplex(clang::Expr *expr) const } vector<ArraySubscriptExpr*> subscriptExprs; - HierarchyUtils::getChilds<ArraySubscriptExpr>(expr, subscriptExprs); + clazy::getChilds<ArraySubscriptExpr>(expr, subscriptExprs); if (!subscriptExprs.empty()) return true; @@ -236,7 +236,7 @@ bool ReserveCandidates::expressionIsComplex(clang::Expr *expr) const if (binary && binary->isAssignmentOp()) { // Filter things like for ( ...; ...; next = node->next) Expr *rhs = binary->getRHS(); - if (isa<MemberExpr>(rhs) || (isa<ImplicitCastExpr>(rhs) && dyn_cast_or_null<MemberExpr>(HierarchyUtils::getFirstChildAtDepth(rhs, 1)))) + if (isa<MemberExpr>(rhs) || (isa<ImplicitCastExpr>(rhs) && dyn_cast_or_null<MemberExpr>(clazy::getFirstChildAtDepth(rhs, 1)))) return true; } @@ -277,18 +277,18 @@ bool ReserveCandidates::isInComplexLoop(clang::Stmt *s, SourceLocation declLocat static vector<unsigned int> nonComplexOnesCache; static vector<unsigned int> complexOnesCache; - auto rawLoc = s->getLocStart().getRawEncoding(); + auto rawLoc = getLocStart(s).getRawEncoding(); // For some reason we generate two warnings on some foreaches, so cache the ones we processed // and return true so we don't trigger a warning - if (clazy_std::contains(nonComplexOnesCache, rawLoc) || clazy_std::contains(complexOnesCache, rawLoc)) + if (clazy::contains(nonComplexOnesCache, rawLoc) || clazy::contains(complexOnesCache, rawLoc)) return true; Stmt *parent = s; PresumedLoc lastForeachForStm; - while ((parent = HierarchyUtils::parent(m_context->parentMap, parent))) { - const SourceLocation parentStart = parent->getLocStart(); + while ((parent = clazy::parent(m_context->parentMap, parent))) { + const SourceLocation parentStart = getLocStart(parent); if (!isMemberVariable && sm().isBeforeInSLocAddrSpace(parentStart, declLocation)) { nonComplexOnesCache.push_back(rawLoc); return false; @@ -300,7 +300,7 @@ bool ReserveCandidates::isInComplexLoop(clang::Stmt *s, SourceLocation declLocat return true; } - if (QtUtils::isInForeach(&m_astContext, parentStart)) { + if (clazy::isInForeach(&m_astContext, parentStart)) { auto ploc = sm().getPresumedLoc(parentStart); if (Utils::presumedLocationsEqual(ploc, lastForeachForStm)) { // Q_FOREACH comes in pairs, because each has two for statements inside, so ignore one when counting @@ -324,5 +324,3 @@ bool ReserveCandidates::isInComplexLoop(clang::Stmt *s, SourceLocation declLocat nonComplexOnesCache.push_back(rawLoc); return false; } - -REGISTER_CHECK("reserve-candidates", ReserveCandidates, CheckLevel2) diff --git a/src/checks/level2/reservecandidates.h b/src/checks/level3/reserve-candidates.h index 9b9ed5eb..9b9ed5eb 100644 --- a/src/checks/level2/reservecandidates.h +++ b/src/checks/level3/reserve-candidates.h diff --git a/src/checks/level3/thread-with-slots.cpp b/src/checks/level3/thread-with-slots.cpp index 590aec65..36b5f2aa 100644 --- a/src/checks/level3/thread-with-slots.cpp +++ b/src/checks/level3/thread-with-slots.cpp @@ -24,7 +24,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include "ClazyContext.h" #include "AccessSpecifierManager.h" @@ -36,7 +35,7 @@ using namespace std; static bool hasMutexes(Stmt *body) { - auto declrefs = HierarchyUtils::getStatements<DeclRefExpr>(body); + auto declrefs = clazy::getStatements<DeclRefExpr>(body); for (auto declref : declrefs) { ValueDecl *valueDecl = declref->getDecl(); if (CXXRecordDecl *record = TypeUtils::typeAsRecord(valueDecl->getType())) { @@ -64,10 +63,10 @@ void ThreadWithSlots::VisitStmt(clang::Stmt *stmt) return; FunctionDecl *connectFunc = callExpr->getDirectCallee(); - if (!QtUtils::isConnect(connectFunc)) + if (!clazy::isConnect(connectFunc)) return; - CXXMethodDecl *slot = QtUtils::receiverMethodForConnect(callExpr); + CXXMethodDecl *slot = clazy::receiverMethodForConnect(callExpr); if (!slot || !TypeUtils::derivesFrom(slot->getParent(), "QThread")) return; @@ -105,7 +104,7 @@ void ThreadWithSlots::VisitDecl(Decl *decl) // If we use member mutexes, let's not warn either bool accessesNonMutexMember = false; - auto memberexprs = HierarchyUtils::getStatements<MemberExpr>(body); + auto memberexprs = clazy::getStatements<MemberExpr>(body); for (auto memberexpr : memberexprs) { ValueDecl *valueDecl = memberexpr->getMemberDecl(); if (CXXRecordDecl *record = TypeUtils::typeAsRecord(valueDecl->getType())) { @@ -122,4 +121,4 @@ void ThreadWithSlots::VisitDecl(Decl *decl) emitWarning(method, "Slot " + method->getQualifiedNameAsString() + " might not run in the expected thread"); } -REGISTER_CHECK("thread-with-slots", ThreadWithSlots, CheckLevel3) + diff --git a/src/checks/level3/unneeded-cast.cpp b/src/checks/level3/unneeded-cast.cpp new file mode 100644 index 00000000..d0b5e58f --- /dev/null +++ b/src/checks/level3/unneeded-cast.cpp @@ -0,0 +1,117 @@ +/* + 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 <sergio.martins@kdab.com> + + Copyright (C) 2015 Sergio Martins <smartins@kde.org> + + 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 "unneeded-cast.h" +#include "Utils.h" +#include "QtUtils.h" +#include "TypeUtils.h" +#include "HierarchyUtils.h" +#include "ClazyContext.h" + +#include <clang/AST/DeclCXX.h> +#include <clang/AST/ExprCXX.h> + +using namespace llvm; +using namespace clang; + +UnneededCast::UnneededCast(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) +{ +} + +void UnneededCast::VisitStmt(clang::Stmt *stm) +{ + if (handleNamedCast(dyn_cast<CXXNamedCastExpr>(stm))) + return; + + handleQObjectCast(stm); +} + +bool UnneededCast::handleNamedCast(CXXNamedCastExpr *namedCast) +{ + if (!namedCast) + return false; + + const bool isDynamicCast = isa<CXXDynamicCastExpr>(namedCast); + const bool isStaticCast = isDynamicCast ? false : isa<CXXStaticCastExpr>(namedCast); + + if (!isDynamicCast && !isStaticCast) + return false; + + if (getLocStart(namedCast).isMacroID()) + return false; + + CXXRecordDecl *castFrom = namedCast ? Utils::namedCastInnerDecl(namedCast) : nullptr; + if (!castFrom || !castFrom->hasDefinition() || std::distance(castFrom->bases_begin(), castFrom->bases_end()) > 1) + return false; + + if (isStaticCast) { + if (auto implicitCast = dyn_cast<ImplicitCastExpr>(namedCast->getSubExpr())) { + if (implicitCast->getCastKind() == CK_NullToPointer) { + // static_cast<Foo*>(0) is OK, and sometimes needed + return false; + } + } + + // static_cast to base is needed in ternary operators + if (clazy::getFirstParentOfType<ConditionalOperator>(m_context->parentMap, namedCast) != nullptr) + return false; + } + + if (isDynamicCast && !isOptionSet("prefer-dynamic-cast-over-qobject") && clazy::isQObject(castFrom)) + emitWarning(getLocStart(namedCast), "Use qobject_cast rather than dynamic_cast"); + + CXXRecordDecl *castTo = Utils::namedCastOuterDecl(namedCast); + if (!castTo) + return false; + + return maybeWarn(namedCast, castFrom, castTo); +} + +bool UnneededCast::handleQObjectCast(Stmt *stm) +{ + CXXRecordDecl *castTo = nullptr; + CXXRecordDecl *castFrom = nullptr; + + if (!clazy::is_qobject_cast(stm, &castTo, &castFrom)) + return false; + + return maybeWarn(stm, castFrom, castTo); +} + +bool UnneededCast::maybeWarn(Stmt *stmt, CXXRecordDecl *castFrom, CXXRecordDecl *castTo) +{ + castFrom = castFrom->getCanonicalDecl(); + castTo = castTo->getCanonicalDecl(); + + if (castFrom == castTo) { + emitWarning(getLocStart(stmt), "Casting to itself"); + return true; + } else if (TypeUtils::derivesFrom(/*child=*/castFrom, castTo)) { + emitWarning(getLocStart(stmt), "explicitly casting to base is unnecessary"); + return true; + } + + return false; +} diff --git a/src/checks/level3/dynamic_cast.h b/src/checks/level3/unneeded-cast.h index 071211d4..ffb22dd3 100644 --- a/src/checks/level3/dynamic_cast.h +++ b/src/checks/level3/unneeded-cast.h @@ -22,23 +22,28 @@ Boston, MA 02110-1301, USA. */ -#ifndef CLANG_LAZY_DYNAMIC_CAST_H -#define CLANG_LAZY_DYNAMIC_CAST_H +#ifndef CLAZY_UNNEEDED_CAST_H +#define CLAZY_UNNEEDED_CAST_H #include "checkbase.h" -#include <map> -#include <vector> -#include <string> +namespace { +class CXXNAmedCastExpr; +} /** - * Finds redundant dynamic casts. + * Finds redundant casts. + * See README-unneeded-cast.md for more info. */ -class BogusDynamicCast : public CheckBase +class UnneededCast : public CheckBase { public: - BogusDynamicCast(const std::string &name, ClazyContext *context); + UnneededCast(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stm) override; +private: + bool handleNamedCast(clang::CXXNamedCastExpr *); + bool handleQObjectCast(clang::Stmt *); + bool maybeWarn(clang::Stmt *, clang::CXXRecordDecl *from, clang::CXXRecordDecl *to); }; #endif diff --git a/src/checks/level2/container-inside-loop.cpp b/src/checks/manuallevel/container-inside-loop.cpp index 360d0f74..024484da 100644 --- a/src/checks/level2/container-inside-loop.cpp +++ b/src/checks/manuallevel/container-inside-loop.cpp @@ -22,7 +22,6 @@ #include "container-inside-loop.h" #include "ClazyContext.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "LoopUtils.h" #include "StmtBodyRange.h" @@ -36,7 +35,7 @@ using namespace std; ContainerInsideLoop::ContainerInsideLoop(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -47,14 +46,14 @@ void ContainerInsideLoop::VisitStmt(clang::Stmt *stmt) return; CXXConstructorDecl *ctor = ctorExpr->getConstructor(); - if (!ctor || !clazy_std::equalsAny(StringUtils::classNameFor(ctor), { "QVector", "std::vector", "QList" })) + if (!ctor || !clazy::equalsAny(clazy::classNameFor(ctor), { "QVector", "std::vector", "QList" })) return; DeclStmt *declStm = dyn_cast_or_null<DeclStmt>(m_context->parentMap->getParent(stmt)); if (!declStm || !declStm->isSingleDecl()) return; - Stmt *loopStmt = LoopUtils::isInLoop(m_context->parentMap, stmt); + Stmt *loopStmt = clazy::isInLoop(m_context->parentMap, stmt); if (!loopStmt) return; @@ -65,8 +64,5 @@ void ContainerInsideLoop::VisitStmt(clang::Stmt *stmt) if (Utils::isPassedToFunction(StmtBodyRange(loopStmt), varDecl, true)) return; - emitWarning(stmt->getLocStart(), "container inside loop causes unneeded allocations"); + emitWarning(getLocStart(stmt), "container inside loop causes unneeded allocations"); } - - -REGISTER_CHECK("container-inside-loop", ContainerInsideLoop, CheckLevel2) diff --git a/src/checks/level2/container-inside-loop.h b/src/checks/manuallevel/container-inside-loop.h index a3b62190..a3b62190 100644 --- a/src/checks/level2/container-inside-loop.h +++ b/src/checks/manuallevel/container-inside-loop.h diff --git a/src/checks/hiddenlevel/inefficientqlist.cpp b/src/checks/manuallevel/inefficient-qlist.cpp index 1e06d3df..e5831c87 100644 --- a/src/checks/hiddenlevel/inefficientqlist.cpp +++ b/src/checks/manuallevel/inefficient-qlist.cpp @@ -22,11 +22,10 @@ Boston, MA 02110-1301, USA. */ -#include "inefficientqlist.h" +#include "inefficient-qlist.h" #include "Utils.h" #include "TypeUtils.h" #include "TemplateUtils.h" -#include "checkmanager.h" #include <clang/AST/Decl.h> #include <clang/AST/DeclCXX.h> @@ -40,5 +39,3 @@ InefficientQList::InefficientQList(const std::string &name, ClazyContext *contex : InefficientQListBase(name, context, IgnoreNone) { } - -REGISTER_CHECK("inefficient-qlist", InefficientQList, HiddenCheckLevel) diff --git a/src/checks/hiddenlevel/inefficientqlist.h b/src/checks/manuallevel/inefficient-qlist.h index 5b3ce1f4..5b3ce1f4 100644 --- a/src/checks/hiddenlevel/inefficientqlist.h +++ b/src/checks/manuallevel/inefficient-qlist.h diff --git a/src/checks/hiddenlevel/isempty-vs-count.cpp b/src/checks/manuallevel/isempty-vs-count.cpp index 1ba17de6..c1700780 100644 --- a/src/checks/hiddenlevel/isempty-vs-count.cpp +++ b/src/checks/manuallevel/isempty-vs-count.cpp @@ -21,7 +21,6 @@ #include "isempty-vs-count.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "QtUtils.h" @@ -33,27 +32,24 @@ using namespace std; IsEmptyVSCount::IsEmptyVSCount(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } void IsEmptyVSCount::VisitStmt(clang::Stmt *stmt) { - ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(stmt); + auto cast = dyn_cast<ImplicitCastExpr>(stmt); if (!cast || cast->getCastKind() != clang::CK_IntegralToBoolean) return; - CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(*(cast->child_begin())); + auto memberCall = dyn_cast<CXXMemberCallExpr>(*(cast->child_begin())); CXXMethodDecl *method = memberCall ? memberCall->getMethodDecl() : nullptr; - if (!StringUtils::functionIsOneOf(method, {"size", "count", "length"})) + if (!clazy::functionIsOneOf(method, {"size", "count", "length"})) return; - if (!StringUtils::classIsOneOf(method->getParent(), QtUtils::qtContainers())) + if (!clazy::classIsOneOf(method->getParent(), clazy::qtContainers())) return; - emitWarning(stmt->getLocStart(), "use isEmpty() instead"); + emitWarning(getLocStart(stmt), "use isEmpty() instead"); } - - -REGISTER_CHECK("isempty-vs-count", IsEmptyVSCount, HiddenCheckLevel) diff --git a/src/checks/hiddenlevel/isempty-vs-count.h b/src/checks/manuallevel/isempty-vs-count.h index 6cd87856..6cd87856 100644 --- a/src/checks/hiddenlevel/isempty-vs-count.h +++ b/src/checks/manuallevel/isempty-vs-count.h diff --git a/src/checks/manuallevel/qhash-with-char-pointer-key.cpp b/src/checks/manuallevel/qhash-with-char-pointer-key.cpp new file mode 100644 index 00000000..fe985fec --- /dev/null +++ b/src/checks/manuallevel/qhash-with-char-pointer-key.cpp @@ -0,0 +1,56 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "qhash-with-char-pointer-key.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +QHashWithCharPointerKey::QHashWithCharPointerKey(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ +} + +void QHashWithCharPointerKey::VisitDecl(clang::Decl *decl) +{ + auto tsdecl = Utils::templateSpecializationFromVarDecl(decl); + if (!tsdecl || tsdecl->getName() != "QHash") // For QMap you shouldn't use any kind of pointers, that's handled in another check + return; + + const TemplateArgumentList &templateArguments = tsdecl->getTemplateArgs(); + if (templateArguments.size() != 2) + return; + + QualType qt = templateArguments[0].getAsType(); + if (!qt.isNull() && qt->isPointerType()) { + qt = TypeUtils::pointeeQualType(qt); + if (!qt.isNull() && !qt->isPointerType() && qt->isCharType()) { + emitWarning(getLocStart(decl), "Using QHash<const char *, T> is dangerous"); + } + } +} diff --git a/src/checks/manuallevel/qhash-with-char-pointer-key.h b/src/checks/manuallevel/qhash-with-char-pointer-key.h new file mode 100644 index 00000000..99bfed32 --- /dev/null +++ b/src/checks/manuallevel/qhash-with-char-pointer-key.h @@ -0,0 +1,39 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_QHASH_WITH_CHAR_POINTER_KEY_H +#define CLAZY_QHASH_WITH_CHAR_POINTER_KEY_H + +#include "checkbase.h" + + +/** + * See README-qhash-with-char-pointer-key.md for more info. + */ +class QHashWithCharPointerKey : public CheckBase +{ +public: + explicit QHashWithCharPointerKey(const std::string &name, ClazyContext *context); + void VisitDecl(clang::Decl *) override; +private: +}; + +#endif diff --git a/src/checks/manuallevel/qstring-varargs.cpp b/src/checks/manuallevel/qstring-varargs.cpp new file mode 100644 index 00000000..1ce97201 --- /dev/null +++ b/src/checks/manuallevel/qstring-varargs.cpp @@ -0,0 +1,61 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "qstring-varargs.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +QStringVarargs::QStringVarargs(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) +{ +} + +void QStringVarargs::VisitStmt(clang::Stmt *stmt) +{ + auto binop = dyn_cast<BinaryOperator>(stmt); + if (!binop || binop->getOpcode() != BO_Comma) + return; + + auto callexpr = dyn_cast<CallExpr>(binop->getLHS()); + if (!callexpr) + return; + + FunctionDecl *func = callexpr->getDirectCallee(); + if (!func || func->getName() != "__builtin_trap") + return; + + QualType qt = binop->getRHS()->getType(); + CXXRecordDecl *record = qt->getAsCXXRecordDecl(); + if (!record) + return; + + StringRef name = clazy::name(record); + if (name == "QString" || name == "QByteArray") + emitWarning(stmt, string("Passing ") + name.data() + string(" to variadic function")); +} diff --git a/src/checks/manuallevel/qstring-varargs.h b/src/checks/manuallevel/qstring-varargs.h new file mode 100644 index 00000000..3151b84d --- /dev/null +++ b/src/checks/manuallevel/qstring-varargs.h @@ -0,0 +1,39 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_QSTRING_VARARGS_H +#define CLAZY_QSTRING_VARARGS_H + +#include "checkbase.h" + + +/** + * See README-qstring-varargs.md for more info. + */ +class QStringVarargs : public CheckBase +{ +public: + explicit QStringVarargs(const std::string &name, ClazyContext *context); + void VisitStmt(clang::Stmt *stmt) override; +private: +}; + +#endif diff --git a/src/checks/manuallevel/qt-keywords.cpp b/src/checks/manuallevel/qt-keywords.cpp new file mode 100644 index 00000000..1cd9085a --- /dev/null +++ b/src/checks/manuallevel/qt-keywords.cpp @@ -0,0 +1,74 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "qt-keywords.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" +#include "FixItUtils.h" +#include "ClazyContext.h" +#include "PreProcessorVisitor.h" + +#include <clang/Lex/MacroInfo.h> +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + +QtKeywords::QtKeywords(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ + enablePreProcessorCallbacks(); + context->enablePreprocessorVisitor(); +} + +void QtKeywords::VisitMacroExpands(const Token ¯oNameTok, const SourceRange &range, const clang::MacroInfo *minfo) +{ + IdentifierInfo *ii = macroNameTok.getIdentifierInfo(); + if (!ii || !minfo) + return; + + if (auto ppvisitor = m_context->preprocessorVisitor) { + // Save some CPU cycles. No point in running if QT_NO_KEYWORDS + if (ppvisitor->isQT_NO_KEYWORDS()) + return; + } + + static const vector<StringRef> keywords = { "foreach", "signals", "slots", "emit" }; + std::string name = ii->getName(); + if (!clazy::contains(keywords, name)) + return; + + // Make sure the macro is Qt's. It must be defined in Qt's headers, not 3rdparty + std::string qtheader = sm().getFilename(sm().getSpellingLoc(minfo->getDefinitionLoc())); + if (!clazy::endsWith(qtheader, "qglobal.h") && !clazy::endsWith(qtheader, "qobjectdefs.h")) + return; + + std::vector<FixItHint> fixits; + if (isFixitEnabled()) { + std::string replacement = "Q_" + name; + std::transform(replacement.begin(), replacement.end(), replacement.begin(), ::toupper); + fixits.push_back(clazy::createReplacement(range, replacement)); + } + + emitWarning(range.getBegin(), "Using a Qt keyword (" + string(ii->getName()) + ")", fixits); +} diff --git a/src/checks/manuallevel/qt-keywords.h b/src/checks/manuallevel/qt-keywords.h new file mode 100644 index 00000000..aaaefc88 --- /dev/null +++ b/src/checks/manuallevel/qt-keywords.h @@ -0,0 +1,40 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_QT_KEYWORDS_H +#define CLAZY_QT_KEYWORDS_H + +#include "checkbase.h" + + +/** + * See README-qt-keywords.md for more info. + */ +class QtKeywords : public CheckBase +{ +public: + explicit QtKeywords(const std::string &name, ClazyContext *context); +protected: + void VisitMacroExpands(const clang::Token &, const clang::SourceRange &, + const clang::MacroInfo *) override; +}; + +#endif diff --git a/src/checks/hiddenlevel/qt4-qstring-from-array.cpp b/src/checks/manuallevel/qt4-qstring-from-array.cpp index 8fe0dba5..a0f02ac4 100644 --- a/src/checks/hiddenlevel/qt4-qstring-from-array.cpp +++ b/src/checks/manuallevel/qt4-qstring-from-array.cpp @@ -23,9 +23,9 @@ #include "qt4-qstring-from-array.h" #include "ClazyContext.h" #include "Utils.h" -#include "checkmanager.h" #include "StringUtils.h" #include "FixItUtils.h" +#include "HierarchyUtils.h" #include <clang/AST/AST.h> #include <clang/Lex/Lexer.h> @@ -33,13 +33,8 @@ using namespace clang; using namespace std; -enum FixIt { - FixItNone, - FixItToFromLatin1 -}; - -Qt4_QStringFromArray::Qt4_QStringFromArray(const std::string &name, ClazyContext *context) - : CheckBase(name, context) +Qt4QStringFromArray::Qt4QStringFromArray(const std::string &name, ClazyContext *context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } @@ -61,7 +56,7 @@ static bool isInterestingCtorCall(CXXConstructorDecl *ctor, bool &is_char_array, { is_char_array = false; is_byte_array = false; - if (!ctor || !StringUtils::isOfClass(ctor, "QString")) + if (!ctor || !clazy::isOfClass(ctor, "QString")) return false; for (auto param : Utils::functionParameters(ctor)) { @@ -77,7 +72,7 @@ static bool isInterestingCtorCall(CXXConstructorDecl *ctor, bool &is_char_array, static bool isInterestingMethod(const string &methodName) { static const vector<string> methods = { "append", "prepend", "operator=", "operator==", "operator!=", "operator<", "operator<=", "operator>", "operator>=", "operator+=" }; - return clazy_std::contains(methods, methodName); + return clazy::contains(methods, methodName); } static bool isInterestingMethodCall(CXXMethodDecl *method, string &methodName, bool &is_char_array, bool &is_byte_array) @@ -87,7 +82,7 @@ static bool isInterestingMethodCall(CXXMethodDecl *method, string &methodName, b if (!method) return false; - if (method->getParent()->getNameAsString() != "QString" || method->getNumParams() != 1) + if (clazy::name(method->getParent()) != "QString" || method->getNumParams() != 1) return false; methodName = method->getNameAsString(); @@ -111,7 +106,7 @@ static bool isInterestingOperatorCall(CXXOperatorCallExpr *op, string &operatorN return isInterestingMethodCall(dyn_cast<CXXMethodDecl>(func), operatorName, is_char_array, is_byte_array); } -void Qt4_QStringFromArray::VisitStmt(clang::Stmt *stm) +void Qt4QStringFromArray::VisitStmt(clang::Stmt *stm) { CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(stm); CXXOperatorCallExpr *operatorCall = dyn_cast<CXXOperatorCallExpr>(stm); @@ -159,13 +154,13 @@ void Qt4_QStringFromArray::VisitStmt(clang::Stmt *stm) } } - emitWarning(stm->getLocStart(), message, fixits); + emitWarning(getLocStart(stm), message, fixits); } -std::vector<FixItHint> Qt4_QStringFromArray::fixCtorCall(CXXConstructExpr *ctorExpr) +std::vector<FixItHint> Qt4QStringFromArray::fixCtorCall(CXXConstructExpr *ctorExpr) { - Stmt *parent = HierarchyUtils::parent(m_context->parentMap, ctorExpr); // CXXBindTemporaryExpr - Stmt *grandParent = HierarchyUtils::parent(m_context->parentMap, parent); //CXXFunctionalCastExpr + Stmt *parent = clazy::parent(m_context->parentMap, ctorExpr); // CXXBindTemporaryExpr + Stmt *grandParent = clazy::parent(m_context->parentMap, parent); //CXXFunctionalCastExpr if (parent && grandParent && isa<CXXBindTemporaryExpr>(parent) && isa<CXXFunctionalCastExpr>(grandParent)) { return fixitReplaceWithFromLatin1(ctorExpr); @@ -174,71 +169,71 @@ std::vector<FixItHint> Qt4_QStringFromArray::fixCtorCall(CXXConstructExpr *ctorE } } -std::vector<FixItHint> Qt4_QStringFromArray::fixOperatorCall(CXXOperatorCallExpr *op) +std::vector<FixItHint> Qt4QStringFromArray::fixOperatorCall(CXXOperatorCallExpr *op) { vector<FixItHint> fixits; if (op->getNumArgs() == 2) { Expr *e = op->getArg(1); - SourceLocation start = e->getLocStart(); - SourceLocation end = Lexer::getLocForEndOfToken(FixItUtils::biggestSourceLocationInStmt(sm(), e), 0, sm(), lo()); + SourceLocation start = getLocStart(e); + SourceLocation end = Lexer::getLocForEndOfToken(clazy::biggestSourceLocationInStmt(sm(), e), 0, sm(), lo()); SourceRange range = { start, end }; if (range.isInvalid()) { - emitWarning(op->getLocStart(), "internal error"); + emitWarning(getLocStart(op), "internal error"); return {}; } - FixItUtils::insertParentMethodCall("QString::fromLatin1", {start, end}, /*by-ref*/fixits); + clazy::insertParentMethodCall("QString::fromLatin1", {start, end}, /*by-ref*/fixits); } else { - emitWarning(op->getLocStart(), "internal error"); + emitWarning(getLocStart(op), "internal error"); } return fixits; } -std::vector<FixItHint> Qt4_QStringFromArray::fixMethodCallCall(clang::CXXMemberCallExpr *memberExpr) +std::vector<FixItHint> Qt4QStringFromArray::fixMethodCallCall(clang::CXXMemberCallExpr *memberExpr) { vector<FixItHint> fixits; if (memberExpr->getNumArgs() == 1) { Expr *e = *(memberExpr->arg_begin()); - SourceLocation start = e->getLocStart(); - SourceLocation end = Lexer::getLocForEndOfToken(FixItUtils::biggestSourceLocationInStmt(sm(), e), 0, sm(), lo()); + SourceLocation start = getLocStart(e); + SourceLocation end = Lexer::getLocForEndOfToken(clazy::biggestSourceLocationInStmt(sm(), e), 0, sm(), lo()); SourceRange range = { start, end }; if (range.isInvalid()) { - emitWarning(memberExpr->getLocStart(), "internal error"); + emitWarning(getLocStart(memberExpr), "internal error"); return {}; } - FixItUtils::insertParentMethodCall("QString::fromLatin1", {start, end}, /*by-ref*/fixits); + clazy::insertParentMethodCall("QString::fromLatin1", {start, end}, /*by-ref*/fixits); } else { - emitWarning(memberExpr->getLocStart(), "internal error"); + emitWarning(getLocStart(memberExpr), "internal error"); } return fixits; } -std::vector<FixItHint> Qt4_QStringFromArray::fixitReplaceWithFromLatin1(CXXConstructExpr *ctorExpr) +std::vector<FixItHint> Qt4QStringFromArray::fixitReplaceWithFromLatin1(CXXConstructExpr *ctorExpr) { const string replacement = "QString::fromLatin1"; const string replacee = "QString"; vector<FixItHint> fixits; - SourceLocation rangeStart = ctorExpr->getLocStart(); + SourceLocation rangeStart = getLocStart(ctorExpr); SourceLocation rangeEnd = Lexer::getLocForEndOfToken(rangeStart, -1, sm(), lo()); if (rangeEnd.isInvalid()) { // Fallback. Have seen a case in the wild where the above would fail, it's very rare rangeEnd = rangeStart.getLocWithOffset(replacee.size() - 2); if (rangeEnd.isInvalid()) { - StringUtils::printLocation(sm(), rangeStart); - StringUtils::printLocation(sm(), rangeEnd); - StringUtils::printLocation(sm(), Lexer::getLocForEndOfToken(rangeStart, 0, sm(), lo())); - queueManualFixitWarning(ctorExpr->getLocStart(), FixItToFromLatin1); + clazy::printLocation(sm(), rangeStart); + clazy::printLocation(sm(), rangeEnd); + clazy::printLocation(sm(), Lexer::getLocForEndOfToken(rangeStart, 0, sm(), lo())); + queueManualFixitWarning(getLocStart(ctorExpr)); return {}; } } @@ -247,24 +242,20 @@ std::vector<FixItHint> Qt4_QStringFromArray::fixitReplaceWithFromLatin1(CXXConst return fixits; } -std::vector<FixItHint> Qt4_QStringFromArray::fixitInsertFromLatin1(CXXConstructExpr *ctorExpr) +std::vector<FixItHint> Qt4QStringFromArray::fixitInsertFromLatin1(CXXConstructExpr *ctorExpr) { vector<FixItHint> fixits; SourceRange range; Expr *arg = *(ctorExpr->arg_begin()); - range.setBegin(arg->getLocStart()); - range.setEnd(Lexer::getLocForEndOfToken(FixItUtils::biggestSourceLocationInStmt(sm(), ctorExpr), 0, sm(), lo())); + range.setBegin(getLocStart(arg)); + range.setEnd(Lexer::getLocForEndOfToken(clazy::biggestSourceLocationInStmt(sm(), ctorExpr), 0, sm(), lo())); if (range.isInvalid()) { - emitWarning(ctorExpr->getLocStart(), "Internal error"); + emitWarning(getLocStart(ctorExpr), "Internal error"); return {}; } - FixItUtils::insertParentMethodCall("QString::fromLatin1", range, fixits); + clazy::insertParentMethodCall("QString::fromLatin1", range, fixits); return fixits; } - -const char *const s_checkName = "qt4-qstring-from-array"; -REGISTER_CHECK(s_checkName, Qt4_QStringFromArray, HiddenCheckLevel) -REGISTER_FIXIT(FixItToFromLatin1, "fix-qt4-qstring-from-array", s_checkName) diff --git a/src/checks/hiddenlevel/qt4-qstring-from-array.h b/src/checks/manuallevel/qt4-qstring-from-array.h index 1c64859d..01391bee 100644 --- a/src/checks/hiddenlevel/qt4-qstring-from-array.h +++ b/src/checks/manuallevel/qt4-qstring-from-array.h @@ -40,10 +40,10 @@ class CXXMemberCallExpr; * * Run only in Qt 4 code. */ -class Qt4_QStringFromArray : public CheckBase +class Qt4QStringFromArray : public CheckBase { public: - explicit Qt4_QStringFromArray(const std::string &name, ClazyContext *context); + explicit Qt4QStringFromArray(const std::string &name, ClazyContext *context); void VisitStmt(clang::Stmt *stmt) override; private: std::vector<clang::FixItHint> fixCtorCall(clang::CXXConstructExpr *ctorExpr); diff --git a/src/checks/manuallevel/raw-environment-function.cpp b/src/checks/manuallevel/raw-environment-function.cpp new file mode 100644 index 00000000..5e8d6a85 --- /dev/null +++ b/src/checks/manuallevel/raw-environment-function.cpp @@ -0,0 +1,55 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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 "raw-environment-function.h" +#include "Utils.h" +#include "HierarchyUtils.h" +#include "QtUtils.h" +#include "TypeUtils.h" + +#include <clang/AST/AST.h> + +using namespace clang; +using namespace std; + + +RawEnvironmentFunction::RawEnvironmentFunction(const std::string &name, ClazyContext *context) + : CheckBase(name, context) +{ +} + +void RawEnvironmentFunction::VisitStmt(clang::Stmt *stmt) +{ + auto callexpr = dyn_cast<CallExpr>(stmt); + if (!callexpr) + return; + + FunctionDecl *func = callexpr->getDirectCallee(); + if (!func) + return; + + StringRef funcName = func->getName(); + if (func->getName() == "putenv") + emitWarning(stmt, "Prefer using qputenv instead of putenv"); + + if (funcName == "getenv") + emitWarning(stmt, "Prefer using qgetenv instead of getenv"); +} diff --git a/src/checks/manuallevel/raw-environment-function.h b/src/checks/manuallevel/raw-environment-function.h new file mode 100644 index 00000000..55153b4f --- /dev/null +++ b/src/checks/manuallevel/raw-environment-function.h @@ -0,0 +1,38 @@ +/* + This file is part of the clazy static checker. + + Copyright (C) 2018 Sergio Martins <smartins@kde.org> + + 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. +*/ + +#ifndef CLAZY_RAW_ENVIRONMENT_FUNCTION_H +#define CLAZY_RAW_ENVIRONMENT_FUNCTION_H + +#include "checkbase.h" + +/** + * See README-raw-environment-function.md for more info. + */ +class RawEnvironmentFunction : public CheckBase +{ +public: + explicit RawEnvironmentFunction(const std::string &name, ClazyContext *context); + void VisitStmt(clang::Stmt *) override; +private: +}; + +#endif diff --git a/src/checks/hiddenlevel/tr-non-literal.cpp b/src/checks/manuallevel/tr-non-literal.cpp index 09504708..19e87685 100644 --- a/src/checks/hiddenlevel/tr-non-literal.cpp +++ b/src/checks/manuallevel/tr-non-literal.cpp @@ -25,7 +25,6 @@ #include "HierarchyUtils.h" #include "QtUtils.h" #include "TypeUtils.h" -#include "checkmanager.h" #include <clang/AST/AST.h> @@ -34,11 +33,10 @@ using namespace std; TrNonLiteral::TrNonLiteral(const std::string &name, ClazyContext *context) - : CheckBase(name, context) + : CheckBase(name, context, Option_CanIgnoreIncludes) { } - void TrNonLiteral::VisitStmt(clang::Stmt *stmt) { auto callExpr = dyn_cast<CallExpr>(stmt); @@ -50,8 +48,6 @@ void TrNonLiteral::VisitStmt(clang::Stmt *stmt) return; Expr *arg1 = callExpr->getArg(0); - if (HierarchyUtils::getFirstChildOfType2<StringLiteral>(arg1) == nullptr) + if (clazy::getFirstChildOfType2<StringLiteral>(arg1) == nullptr) emitWarning(stmt, "tr() without a literal string"); } - -REGISTER_CHECK("tr-non-literal", TrNonLiteral, HiddenCheckLevel) diff --git a/src/checks/hiddenlevel/tr-non-literal.h b/src/checks/manuallevel/tr-non-literal.h index 240d72df..240d72df 100644 --- a/src/checks/hiddenlevel/tr-non-literal.h +++ b/src/checks/manuallevel/tr-non-literal.h diff --git a/src/checks/requiredresults.cpp b/src/checks/requiredresults.cpp index 220a4e42..e9a0f09b 100644 --- a/src/checks/requiredresults.cpp +++ b/src/checks/requiredresults.cpp @@ -24,7 +24,7 @@ #include "requiredresults.h" #include "Utils.h" -#include "checkmanager.h" + #include <clang/AST/DeclCXX.h> #include <clang/AST/Expr.h> @@ -37,50 +37,48 @@ RequiredResults::RequiredResults(const std::string &name, ClazyContext *context) { } -bool RequiredResults::shouldIgnoreMethod(const std::string &qualifiedName) +bool RequiredResults::shouldIgnoreMethod(const StringRef &qualifiedName) { - static std::vector<std::string> files; - if (files.empty()) { - files.reserve(31); - files.push_back("QDir::mkdir"); - files.push_back("QDir::rmdir"); - files.push_back("QDir::mkpath"); - files.push_back("QDBusConnection::send"); - - files.push_back("QRegExp::indexIn"); - files.push_back("QRegExp::exactMatch"); - files.push_back("QQmlProperty::write"); - files.push_back("QQmlProperty::reset"); - files.push_back("QWidget::winId"); - files.push_back("QtWaylandClient::QWaylandEglWindow::contentFBO"); - files.push_back("ProString::updatedHash"); + static const std::vector<StringRef> files = { + "QDir::mkdir", + "QDir::rmdir", + "QDir::mkpath", + "QDBusConnection::send", + + "QRegExp::indexIn", + "QRegExp::exactMatch", + "QQmlProperty::write", + "QQmlProperty::reset", + "QWidget::winId", + "QtWaylandClient::QWaylandEglWindow::contentFBO", + "ProString::updatedHash", // kdepim - files.push_back("KCalCore::Incidence::recurrence"); - files.push_back("KCalCore::RecurrenceRule::Private::buildCache"); - files.push_back("KAlarmCal::KAEvent::updateKCalEvent"); - files.push_back("Akonadi::Server::Collection::clearMimeTypes"); - files.push_back("Akonadi::Server::Collection::addMimeType"); - files.push_back("Akonadi::Server::PimItem::addFlag"); - files.push_back("Akonadi::Server::PimItem::addTag"); + "KCalCore::Incidence::recurrence", + "KCalCore::RecurrenceRule::Private::buildCache", + "KAlarmCal::KAEvent::updateKCalEvent", + "Akonadi::Server::Collection::clearMimeTypes", + "Akonadi::Server::Collection::addMimeType", + "Akonadi::Server::PimItem::addFlag", + "Akonadi::Server::PimItem::addTag", // kf5 libs - files.push_back("KateVi::Command::execute"); - files.push_back("KArchiveDirectory::copyTo"); - files.push_back("KBookmarkManager::saveAs"); - files.push_back("KBookmarkManager::save"); - files.push_back("KLineEditPrivate::copySqueezedText"); - files.push_back("KJS::UString::Rep::hash"); - files.push_back("KCModuleProxy::realModule"); - files.push_back("KCategorizedView::visualRect"); - files.push_back("KateLineLayout::textLine"); - files.push_back("DOM::HTMLCollectionImpl::firstItem"); - files.push_back("DOM::HTMLCollectionImpl::nextItem"); - files.push_back("DOM::HTMLCollectionImpl::firstItem"); - files.push_back("ImapResourceBase::settings"); - } - - return clazy_std::contains(files, qualifiedName); + "KateVi::Command::execute", + "KArchiveDirectory::copyTo", + "KBookmarkManager::saveAs", + "KBookmarkManager::save", + "KLineEditPrivate::copySqueezedText", + "KJS::UString::Rep::hash", + "KCModuleProxy::realModule", + "KCategorizedView::visualRect", + "KateLineLayout::textLine", + "DOM::HTMLCollectionImpl::firstItem", + "DOM::HTMLCollectionImpl::nextItem", + "DOM::HTMLCollectionImpl::firstItem", + "ImapResourceBase::settings" + }; + + return clazy::contains(files, qualifiedName); } void RequiredResults::VisitStmt(clang::Stmt *stm) @@ -94,12 +92,12 @@ void RequiredResults::VisitStmt(clang::Stmt *stm) if (!callExpr) continue; - CXXMethodDecl *methodDecl = callExpr->getMethodDecl(); + CXXMethodDecl *methodDecl = callExpr->getMethodDecl(); if (!methodDecl || !methodDecl->isConst()) continue; std::string methodName = methodDecl->getQualifiedNameAsString(); - if (shouldIgnoreMethod(methodName)) // Filter out some false positives + if (shouldIgnoreMethod(StringRef(methodName.c_str()))) // Filter out some false positives continue; QualType qt = methodDecl->getReturnType(); @@ -118,7 +116,7 @@ void RequiredResults::VisitStmt(clang::Stmt *stm) } // qt.isConstQualified() not working !? TODO: Replace this string parsing when I figure it out - if (type->isReferenceType() && !clazy_std::contains(qt.getAsString(), "const ")) { + if (type->isReferenceType() && !clazy::contains(qt.getAsString(), "const ")) { bailout = true; break; } @@ -126,9 +124,7 @@ void RequiredResults::VisitStmt(clang::Stmt *stm) if (!bailout) { std::string error = std::string("Unused result of const member (") + methodName + ')'; - emitWarning(callExpr->getLocStart(), error.c_str()); + emitWarning(getLocStart(callExpr), error.c_str()); } } } - -// REGISTER_CHECK("unused-result", RequiredResults) diff --git a/src/checks/ruleofbase.cpp b/src/checks/ruleofbase.cpp index 13972fb3..2344adff 100644 --- a/src/checks/ruleofbase.cpp +++ b/src/checks/ruleofbase.cpp @@ -32,10 +32,10 @@ RuleOfBase::RuleOfBase(const std::string &name, ClazyContext *context) bool RuleOfBase::isBlacklisted(CXXRecordDecl *record) const { - if (!record || clazy_std::startsWith(record->getQualifiedNameAsString(), "std::")) + if (!record || clazy::startsWith(record->getQualifiedNameAsString(), "std::")) return true; - const auto qualifiedName = StringUtils::classNameFor(record); + const auto qualifiedName = clazy::classNameFor(record); static const vector<string> blacklisted = { "QAtomicInt", "QBasicAtomicInteger", "QAtomicInteger", "QBasicAtomicPointer", "QList::iterator", "QList::const_iterator", "QTextBlock::iterator", @@ -61,5 +61,5 @@ bool RuleOfBase::isBlacklisted(CXXRecordDecl *record) const "QBitRef", "QJsonValueRef", "QTypedArrayData::iterator" }; - return clazy_std::contains(blacklisted, qualifiedName); + return clazy::contains(blacklisted, qualifiedName); } diff --git a/src/clazy_stl.h b/src/clazy_stl.h index 43367425..73f638ed 100644 --- a/src/clazy_stl.h +++ b/src/clazy_stl.h @@ -27,7 +27,7 @@ #include <algorithm> #include <sstream> -namespace clazy_std { +namespace clazy { // Don't use .begin() or cend(), clang's ranges don't have them // Don't use .size(), clang's ranges doesn't have it @@ -73,33 +73,12 @@ bool any_of(const Range &r, Pred pred) return std::any_of(r.begin(), r.end(), pred); } -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 7 - - template<typename Pred> - bool any_of(const clang::StmtRange &r, Pred pred) - { - return std::any_of(r.first, r.second, pred); - } - -#endif - - template<typename Range, typename Pred> bool all_of(const Range &r, Pred pred) { return std::all_of(r.begin(), r.end(), pred); } -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 7 - - template<typename Pred> - bool all_of(const clang::StmtRange &r, Pred pred) - { - return std::all_of(r.first, r.second, pred); - } - -#endif - template <typename Range> size_t count(const Range &r) { @@ -109,33 +88,26 @@ size_t count(const Range &r) template<typename SrcContainer, typename DstContainer> void append(const SrcContainer &src, DstContainer &dst) { - dst.reserve(clazy_std::count(dst) + clazy_std::count(src)); + dst.reserve(clazy::count(dst) + clazy::count(src)); std::copy(src.begin(), src.end(), std::back_inserter(dst)); } template<typename SrcContainer, typename DstContainer, typename Pred> void append_if(const SrcContainer &src, DstContainer &dst, Pred pred) { - dst.reserve(clazy_std::count(dst) + clazy_std::count(src)); + dst.reserve(clazy::count(dst) + clazy::count(src)); std::copy_if(src.begin(), src.end(), std::back_inserter(dst), pred); } -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR < 8 - inline bool isEmpty(const clang::StmtRange &r) - { - return r.empty(); - } -#else - template <typename Range> - bool isEmpty(const Range &r) - { - return r.begin() == r.end(); - } -#endif +template <typename Range> +bool isEmpty(const Range &r) +{ + return r.begin() == r.end(); +} inline bool hasChildren(clang::Stmt *s) { - return s && !clazy_std::isEmpty(s->children()); + return s && !clazy::isEmpty(s->children()); } inline clang::Stmt* childAt(clang::Stmt *s, int index) @@ -166,8 +138,8 @@ inline bool startsWith(const std::string &target, const std::string &maybeBeginn */ inline bool startsWithAny(const std::string &target, const std::vector<std::string> &beginningCandidates) { - return clazy_std::any_of(beginningCandidates, [target](const std::string &maybeBeginning) { - return clazy_std::startsWith(target, maybeBeginning); + return clazy::any_of(beginningCandidates, [target](const std::string &maybeBeginning) { + return clazy::startsWith(target, maybeBeginning); }); } @@ -176,7 +148,7 @@ inline bool startsWithAny(const std::string &target, const std::vector<std::stri */ inline bool equalsAny(const std::string &target, const std::vector<std::string> &candidates) { - return clazy_std::any_of(candidates, [target](const std::string &candidate) { + return clazy::any_of(candidates, [target](const std::string &candidate) { return candidate == target; }); } @@ -195,8 +167,8 @@ inline bool endsWith(const std::string &target, const std::string &maybeEnding) */ inline bool endsWithAny(const std::string &target, const std::vector<std::string> &endingCandidates) { - return clazy_std::any_of(endingCandidates, [target](const std::string &maybeEnding) { - return clazy_std::endsWith(target, maybeEnding); + return clazy::any_of(endingCandidates, [target](const std::string &maybeEnding) { + return clazy::endsWith(target, maybeEnding); }); } @@ -226,7 +198,7 @@ inline std::vector<std::string> splitString(const char *str, char separator) if (!str) return {}; - return clazy_std::splitString(std::string(str), separator); + return clazy::splitString(std::string(str), separator); } template<typename Container, typename LessThan> diff --git a/tests/auto-unexpected-qstringbuilder/main.cpp b/tests/auto-unexpected-qstringbuilder/main.cpp index 5c511d15..a160d315 100644 --- a/tests/auto-unexpected-qstringbuilder/main.cpp +++ b/tests/auto-unexpected-qstringbuilder/main.cpp @@ -7,5 +7,13 @@ void test() const auto s2 = "tests/" % QString::fromLatin1("foo"); // Warning const QString s3 = "tests/" + QString::fromLatin1("foo"); // OK const QString s4 = "tests/" % QString::fromLatin1("foo"); // OK + + [] { + return "tests/" % QString::fromLatin1("foo"); // Warn + }; + + [] { + return QString(); // OK + }; } diff --git a/tests/auto-unexpected-qstringbuilder/main.cpp.expected b/tests/auto-unexpected-qstringbuilder/main.cpp.expected index 65ca82ae..7d2032d8 100644 --- a/tests/auto-unexpected-qstringbuilder/main.cpp.expected +++ b/tests/auto-unexpected-qstringbuilder/main.cpp.expected @@ -1,2 +1,3 @@ auto-unexpected-qstringbuilder/main.cpp:6:5: warning: auto deduced to be QStringBuilder instead of QString. Possible crash. [-Wclazy-auto-unexpected-qstringbuilder] auto-unexpected-qstringbuilder/main.cpp:7:5: warning: auto deduced to be QStringBuilder instead of QString. Possible crash. [-Wclazy-auto-unexpected-qstringbuilder] +auto-unexpected-qstringbuilder/main.cpp:11:5: warning: lambda return type deduced to be QStringBuilder instead of QString. Possible crash. [-Wclazy-auto-unexpected-qstringbuilder] diff --git a/tests/auto-unexpected-qstringbuilder/main.cpp_fixed.cpp.expected b/tests/auto-unexpected-qstringbuilder/main.cpp_fixed.cpp.expected index 3f27fb78..aa5640d6 100644 --- a/tests/auto-unexpected-qstringbuilder/main.cpp_fixed.cpp.expected +++ b/tests/auto-unexpected-qstringbuilder/main.cpp_fixed.cpp.expected @@ -7,5 +7,13 @@ void test() const QString s2 = "tests/" % QString::fromLatin1("foo"); // Warning const QString s3 = "tests/" + QString::fromLatin1("foo"); // OK const QString s4 = "tests/" % QString::fromLatin1("foo"); // OK + + [] { + return "tests/" % QString::fromLatin1("foo"); // Warn + }; + + [] { + return QString(); // OK + }; } diff --git a/tests/bogus-dynamic-cast/main.cpp b/tests/bogus-dynamic-cast/main.cpp deleted file mode 100644 index bca5b416..00000000 --- a/tests/bogus-dynamic-cast/main.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include <QtCore/QObject> - - - - - - - - - - - - - - - -struct A -{ - virtual void foo() { } -}; - -struct B : public A -{ - -}; - -void test() -{ - A *a = new A(); - B *b = new B(); - - dynamic_cast<A*>(b); // warning: Casting to base - dynamic_cast<A*>(a); // warning: Casting to itself - dynamic_cast<B*>(a); // OK - dynamic_cast<B*>(b); // warning: Casting to itself -} - -class MyObj : public QObject -{ -public: -}; - -void testQObjectCast(QObject *o) -{ - dynamic_cast<MyObj*>(o); // OK -} diff --git a/tests/bogus-dynamic-cast/main.cpp.expected b/tests/bogus-dynamic-cast/main.cpp.expected deleted file mode 100644 index 80da945e..00000000 --- a/tests/bogus-dynamic-cast/main.cpp.expected +++ /dev/null @@ -1,6 +0,0 @@ -bogus-dynamic-cast/main.cpp:32:5: warning: explicitly casting to base is unnecessary [-Wclazy-bogus-dynamic-cast] - dynamic_cast<A*>(b); // warning: Casting to base -bogus-dynamic-cast/main.cpp:33:5: warning: Casting to itself [-Wclazy-bogus-dynamic-cast] - dynamic_cast<A*>(a); // warning: Casting to itself -bogus-dynamic-cast/main.cpp:35:5: warning: Casting to itself [-Wclazy-bogus-dynamic-cast] - dynamic_cast<B*>(b); // warning: Casting to itself diff --git a/tests/bogus-dynamic-cast/qobjectcast.cpp.expected b/tests/bogus-dynamic-cast/qobjectcast.cpp.expected deleted file mode 100644 index 32bc5d4e..00000000 --- a/tests/bogus-dynamic-cast/qobjectcast.cpp.expected +++ /dev/null @@ -1 +0,0 @@ -bogus-dynamic-cast/qobjectcast.cpp:7:5: warning: Use qobject_cast rather than dynamic_cast [-Wclazy-bogus-dynamic-cast] diff --git a/tests/clazy-standalone/config.json b/tests/clazy-standalone/config.json new file mode 100644 index 00000000..66d92f0c --- /dev/null +++ b/tests/clazy-standalone/config.json @@ -0,0 +1,30 @@ +{ + "clazy_standalone_only" : true, + "tests" : [ + { + "filename" : "header_filter.cpp", + "header_filter" : "", + "checks" : ["qdatetime-utc"] + }, + { + "filename" : "header_filter2.cpp", + "header_filter" : ".*_foo.*.h", + "checks" : ["qdatetime-utc"] + }, + { + "filename" : "header_filter2.cpp", + "header_filter" : ".*_foo.*.h", + "checks" : ["qdatetime-utc"] + }, + { + "filename" : "fileToIgnore.cpp", + "ignore_dirs" : ".*fileToIgnore.*", + "checks" : ["qdatetime-utc"] + }, + { + "filename" : "fileToNotIgnore.cpp", + "ignore_dirs" : ".*fileToIgnore.*", + "checks" : ["qdatetime-utc"] + } + ] +} diff --git a/tests/clazy-standalone/fileToIgnore.cpp b/tests/clazy-standalone/fileToIgnore.cpp new file mode 100644 index 00000000..7fbbeb29 --- /dev/null +++ b/tests/clazy-standalone/fileToIgnore.cpp @@ -0,0 +1,7 @@ +#include "header_filter_foo.h" +#include "header_filter_bar.h" + +void t() +{ + QDateTime::currentDateTime().toTime_t(); +} diff --git a/tests/clazy-standalone/fileToIgnore.cpp.expected b/tests/clazy-standalone/fileToIgnore.cpp.expected new file mode 100644 index 00000000..2dd58f65 --- /dev/null +++ b/tests/clazy-standalone/fileToIgnore.cpp.expected @@ -0,0 +1,2 @@ +clazy-standalone/header_filter_foo.h:5:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] +clazy-standalone/header_filter_bar.h:5:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] diff --git a/tests/clazy-standalone/fileToNotIgnore.cpp b/tests/clazy-standalone/fileToNotIgnore.cpp new file mode 100644 index 00000000..33eebaac --- /dev/null +++ b/tests/clazy-standalone/fileToNotIgnore.cpp @@ -0,0 +1,6 @@ +#include <QtCore/QDateTime> + +void t() +{ + QDateTime::currentDateTime().toTime_t(); +} diff --git a/tests/clazy-standalone/fileToNotIgnore.cpp.expected b/tests/clazy-standalone/fileToNotIgnore.cpp.expected new file mode 100644 index 00000000..6f14967d --- /dev/null +++ b/tests/clazy-standalone/fileToNotIgnore.cpp.expected @@ -0,0 +1 @@ +clazy-standalone/fileToNotIgnore.cpp:5:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] diff --git a/tests/clazy-standalone/header_filter.cpp b/tests/clazy-standalone/header_filter.cpp new file mode 100644 index 00000000..7fbbeb29 --- /dev/null +++ b/tests/clazy-standalone/header_filter.cpp @@ -0,0 +1,7 @@ +#include "header_filter_foo.h" +#include "header_filter_bar.h" + +void t() +{ + QDateTime::currentDateTime().toTime_t(); +} diff --git a/tests/clazy-standalone/header_filter.cpp.expected b/tests/clazy-standalone/header_filter.cpp.expected new file mode 100644 index 00000000..d4c0d27b --- /dev/null +++ b/tests/clazy-standalone/header_filter.cpp.expected @@ -0,0 +1,3 @@ +clazy-standalone/header_filter_foo.h:5:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] +clazy-standalone/header_filter_bar.h:5:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] +clazy-standalone/header_filter.cpp:6:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] diff --git a/tests/clazy-standalone/header_filter2.cpp b/tests/clazy-standalone/header_filter2.cpp new file mode 100644 index 00000000..7fbbeb29 --- /dev/null +++ b/tests/clazy-standalone/header_filter2.cpp @@ -0,0 +1,7 @@ +#include "header_filter_foo.h" +#include "header_filter_bar.h" + +void t() +{ + QDateTime::currentDateTime().toTime_t(); +} diff --git a/tests/clazy-standalone/header_filter2.cpp.expected b/tests/clazy-standalone/header_filter2.cpp.expected new file mode 100644 index 00000000..403dc9b1 --- /dev/null +++ b/tests/clazy-standalone/header_filter2.cpp.expected @@ -0,0 +1,2 @@ +clazy-standalone/header_filter_foo.h:5:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] +clazy-standalone/header_filter2.cpp:6:5: warning: Use QDateTime::currentDateTimeUtc().toTime_t() instead [-Wclazy-qdatetime-utc] diff --git a/tests/clazy-standalone/header_filter_bar.h b/tests/clazy-standalone/header_filter_bar.h new file mode 100644 index 00000000..c7609ff1 --- /dev/null +++ b/tests/clazy-standalone/header_filter_bar.h @@ -0,0 +1,6 @@ +#include <QtCore/QDateTime> + +void test2() +{ + QDateTime::currentDateTime().toTime_t(); +} diff --git a/tests/clazy-standalone/header_filter_foo.h b/tests/clazy-standalone/header_filter_foo.h new file mode 100644 index 00000000..1404090e --- /dev/null +++ b/tests/clazy-standalone/header_filter_foo.h @@ -0,0 +1,6 @@ +#include <QtCore/QDateTime> + +void test() +{ + QDateTime::currentDateTime().toTime_t(); +} diff --git a/tests/clazy/test_requested_checks.sh.expected b/tests/clazy/test_requested_checks.sh.expected index 798d640f..f2a071cc 100644 --- a/tests/clazy/test_requested_checks.sh.expected +++ b/tests/clazy/test_requested_checks.sh.expected @@ -1,6 +1,6 @@ -Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, 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 Invalid check: foo -Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, 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: foreach Requested checks: foreach, writing-to-temporary Invalid check: foo @@ -8,29 +8,32 @@ 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-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, container-inside-loop, copyable-polymorphic, ctor-missing-parent-argument, detaching-temporary, foreach, 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, reserve-candidates, returning-data-from-temporary, returning-void-expression, rule-of-three, rule-of-two-soft, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-call-ctor, virtual-signal, writing-to-temporary, wrong-qglobalstatic +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-non-signal, connect-not-normalized, container-anti-pattern, 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-qglobalstatic -Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qglobalstatic -Requested checks: connect-non-signal, connect-not-normalized, container-anti-pattern, 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-qglobalstatic -Requested checks: connect-non-signal, connect-not-normalized, container-anti-pattern, 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-qglobalstatic -Requested checks: connect-non-signal, connect-not-normalized, container-anti-pattern, foreach, 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-qglobalstatic -Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, 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: 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-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qglobalstatic -Requested checks: connect-non-signal, connect-not-normalized, container-anti-pattern, 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-qglobalstatic -Requested checks: connect-non-signal, connect-not-normalized, container-anti-pattern, 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-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, 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 @@ -49,6 +52,7 @@ Available checks and FixIts: - temporary-iterator - unused-non-trivial-variable - writing-to-temporary + - wrong-qevent-cast - wrong-qglobalstatic - Checks from level1: @@ -72,11 +76,11 @@ Available checks and FixIts: - range-loop - returning-data-from-temporary - rule-of-two-soft + - skipped-base-method - virtual-signal - Checks from level2: - base-class-event - - container-inside-loop - copyable-polymorphic - ctor-missing-parent-argument - function-args-by-ref @@ -87,16 +91,17 @@ Available checks and FixIts: - missing-typeinfo - old-style-connect (fix-old-style-connect) - qstring-allocations (fix-qlatin1string-allocations,fix-fromLatin1_fromUtf8-allocations,fix-fromCharPtrAllocations) - - reserve-candidates - returning-void-expression - rule-of-three + - static-pmf - virtual-call-ctor - Checks from level3: - assert-with-side-effects - - bogus-dynamic-cast - detaching-member + - reserve-candidates - thread-with-slots + - unneeded-cast If nothing is specified, all checks from level0 and level1 will be run. @@ -114,9 +119,9 @@ To enable FixIts for a check, also set the env variable CLAZY_FIXIT, for example 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-non-signal, connect-not-normalized, container-anti-pattern, 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-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: implicit-casts Requested checks: implicit-casts -Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, wrong-qglobalstatic -Requested checks: connect-non-signal, connect-not-normalized, container-anti-pattern, 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-qglobalstatic -Requested checks: auto-unexpected-qstringbuilder, child-event-qobject-cast, connect-3arg-lambda, connect-non-signal, connect-not-normalized, const-signal-or-slot, container-anti-pattern, detaching-temporary, foreach, 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, strict-iterators, temporary-iterator, unused-non-trivial-variable, virtual-signal, writing-to-temporary, 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, 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/connect-3arg-lambda/main.cpp b/tests/connect-3arg-lambda/main.cpp index a9698bbb..ef36d9d3 100644 --- a/tests/connect-3arg-lambda/main.cpp +++ b/tests/connect-3arg-lambda/main.cpp @@ -1,6 +1,6 @@ #include <QtCore/QString> -#include <QtCore/QObject> - +#include <QtWidgets/QMenu> +#include <QtCore/QTimer> void another_global(); void test() { @@ -74,3 +74,22 @@ public: QObject *m_member; QObject *m_member2; }; + +void testTimer() +{ + QObject *o; + QTimer::singleShot(0, [] {}); // Warn + QTimer::singleShot(0, o, [] {}); // OK + + QTimer::singleShot(0, Qt::CoarseTimer, [] {}); // Warn + QTimer::singleShot(0, Qt::CoarseTimer, o, [] {}); // OK +} + +void testQMenu() +{ + MyObject o; + QMenu menu; + menu.addAction("foo", &o, &MyObject::test); // OK + menu.addAction("foo", &o, []{}); // OK + menu.addAction("foo", []{}); // Warn +} diff --git a/tests/connect-3arg-lambda/main.cpp.expected b/tests/connect-3arg-lambda/main.cpp.expected index 9f478035..b5229203 100644 --- a/tests/connect-3arg-lambda/main.cpp.expected +++ b/tests/connect-3arg-lambda/main.cpp.expected @@ -6,3 +6,6 @@ connect-3arg-lambda/main.cpp:46:9: warning: Pass a context object as 3rd connect connect-3arg-lambda/main.cpp:59:9: warning: Pass a context object as 3rd connect parameter [-Wclazy-connect-3arg-lambda] connect-3arg-lambda/main.cpp:63:9: warning: Pass a context object as 3rd connect parameter [-Wclazy-connect-3arg-lambda] connect-3arg-lambda/main.cpp:68:9: warning: Pass a context object as 3rd connect parameter [-Wclazy-connect-3arg-lambda] +connect-3arg-lambda/main.cpp:81:5: warning: Pass a context object as 2nd singleShot parameter [-Wclazy-connect-3arg-lambda] +connect-3arg-lambda/main.cpp:84:5: warning: Pass a context object as 3rd singleShot parameter [-Wclazy-connect-3arg-lambda] +connect-3arg-lambda/main.cpp:94:5: warning: Pass a context object as 2nd singleShot parameter [-Wclazy-connect-3arg-lambda] diff --git a/tests/connect-by-name/config.json b/tests/connect-by-name/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/connect-by-name/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/connect-by-name/main.cpp b/tests/connect-by-name/main.cpp new file mode 100644 index 00000000..92676447 --- /dev/null +++ b/tests/connect-by-name/main.cpp @@ -0,0 +1,25 @@ +#include <QtCore/QObject> + +class MyObj; // OK + +class MyObj : public QObject +{ +public Q_SLOTS: + void on_foo_bar(); // Warn + +public: + void on_foo2_bar2(); // OK + +signals: + void on_foo3_bar3(); // OK +}; + +class MyObj; // OK + +void MyObj::on_foo_bar() // OK +{ +} + +void MyObj::on_foo2_bar2() // OK +{ +} diff --git a/tests/connect-by-name/main.cpp.expected b/tests/connect-by-name/main.cpp.expected new file mode 100644 index 00000000..9f71e2e0 --- /dev/null +++ b/tests/connect-by-name/main.cpp.expected @@ -0,0 +1 @@ +connect-by-name/main.cpp:8:5: warning: Slots named on_foo_bar are error prone [-Wclazy-connect-by-name] diff --git a/tests/connect-non-signal/392441.cpp b/tests/connect-non-signal/392441.cpp new file mode 100644 index 00000000..084dbd44 --- /dev/null +++ b/tests/connect-non-signal/392441.cpp @@ -0,0 +1,7 @@ +#include <QtWidgets/QComboBox> + +void test_bug392441() +{ + QComboBox *combo; + QObject::connect(combo, QOverload<const QString &>::of(&QComboBox::currentIndexChanged), [] (QString) {}); // OK +} diff --git a/tests/connect-non-signal/392441.cpp.expected b/tests/connect-non-signal/392441.cpp.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/connect-non-signal/392441.cpp.expected diff --git a/tests/connect-non-signal/config.json b/tests/connect-non-signal/config.json index f8b966d3..f6c64c91 100644 --- a/tests/connect-non-signal/config.json +++ b/tests/connect-non-signal/config.json @@ -9,6 +9,10 @@ "minimum_qt_version" : 570 }, { + "filename" : "392441.cpp", + "minimum_qt_version" : 570 + }, + { "filename" : "bug375239.cpp" }, { diff --git a/tests/connect-non-signal/main.cpp b/tests/connect-non-signal/main.cpp index 4a71c688..e41c5c6f 100644 --- a/tests/connect-non-signal/main.cpp +++ b/tests/connect-non-signal/main.cpp @@ -48,6 +48,6 @@ public: void test3() { auto *o = new MyDerivedObj(); - QObject::connect(o, &MyDerivedObj::myVirtualSig, o, &MyObj::foo); // Warn, overriden but not a signal now + QObject::connect(o, &MyDerivedObj::myVirtualSig, o, &MyObj::foo); // Warn, overridden but not a signal now QObject::connect(o, &MyObj::myVirtualSig, o, &MyObj::foo); // OK } diff --git a/tests/copyable-polymorphic/main.cpp.expected b/tests/copyable-polymorphic/main.cpp.expected index 02d91c10..074b6ce5 100644 --- a/tests/copyable-polymorphic/main.cpp.expected +++ b/tests/copyable-polymorphic/main.cpp.expected @@ -1,3 +1,3 @@ -copyable-polymorphic/main.cpp:36:1: warning: Polymorphic class is copyable. Potential slicing. [-Wclazy-copyable-polymorphic] -copyable-polymorphic/main.cpp:43:1: warning: Polymorphic class is copyable. Potential slicing. [-Wclazy-copyable-polymorphic] -copyable-polymorphic/main.cpp:52:1: warning: Polymorphic class is copyable. Potential slicing. [-Wclazy-copyable-polymorphic] +copyable-polymorphic/main.cpp:36:1: warning: Polymorphic class PolymorphicClass4 is copyable. Potential slicing. [-Wclazy-copyable-polymorphic] +copyable-polymorphic/main.cpp:43:1: warning: Polymorphic class PolymorphicClass5 is copyable. Potential slicing. [-Wclazy-copyable-polymorphic] +copyable-polymorphic/main.cpp:52:1: warning: Polymorphic class DerivedFromCopyable is copyable. Potential slicing. [-Wclazy-copyable-polymorphic] diff --git a/tests/detaching-member/main.cpp b/tests/detaching-member/main.cpp index 15133eff..64efeefb 100644 --- a/tests/detaching-member/main.cpp +++ b/tests/detaching-member/main.cpp @@ -1,7 +1,7 @@ #include <QtCore/QList> #include <QtCore/QString> #include <QtCore/QMap> - +#include <QtCore/QVector> @@ -104,3 +104,13 @@ void test356755() S s; qSort(s.m_listOfPointers.begin(), s.m_listOfPointers.end()); } + +void testFill() { + struct Fill { + Fill() { + m_vector.fill(1); + } + QVector<int> m_vector; + }; + +} diff --git a/tests/docker/build-clazy.sh b/tests/docker/build-clazy.sh new file mode 100755 index 00000000..0ad1cb5e --- /dev/null +++ b/tests/docker/build-clazy.sh @@ -0,0 +1,26 @@ +# This is the script that runs inside the docker container and builds clazy + +BRANCH=$1 +J_FLAG=$2 +CLAZY_PREFIX=$3 +LLVM_ROOT=$3 + +if [ -z "$1" ] +then + exit 1; +fi + +if [ -z "$2" ] +then + exit 1; +fi + +if [ -z "$3" ] +then + exit 1; +fi + +export PATH=$CLAZY_PREFIX/bin:$PATH +export LD_LIBRARY_PATH=$CLAZY_PREFIX/lib:$CLAZY_PREFIX/lib64:$LD_LIBRARY_PATH + +cd /root/clazy && git fetch && git checkout origin/$BRANCH && cmake -DCMAKE_INSTALL_PREFIX=$CLAZY_PREFIX -DCMAKE_BUILD_TYPE=RelWithDebInfo . && make $J_FLAG && make install && cd tests && ./run_tests.py diff --git a/tests/docker/conf.json b/tests/docker/conf.json new file mode 100644 index 00000000..f2b2bc29 --- /dev/null +++ b/tests/docker/conf.json @@ -0,0 +1,35 @@ +{ + "tests" : [ + { + "name" : "ubuntu-17.04", + "url" : "iamsergio/clazy-ubuntu-17.04", + "prefix" : "/usr/" + }, + { + "name" : "ubuntu-16.04", + "url" : "iamsergio/clazy-ubuntu-16.04", + "prefix" : "/usr/" + }, + { + "name" : "opensuse-tumbleweed", + "url" : "iamsergio/clazy-opensuse-tumbleweed", + "prefix" : "/usr/" + }, + { + "name" : "archlinux", + "url" : "iamsergio/clazy-archlinux", + "prefix" : "/usr/" + }, + { + "name" : "debian-unstable-llvm6", + "url" : "iamsergio/clazy-debian-unstable", + "prefix" : "/usr/lib/llvm-6/" + }, + { + "name" : "debian-unstable-llvm7", + "url" : "iamsergio/clazy-debian-unstable", + "prefix" : "/usr/lib/llvm-7/", + "llvm_root" : "/usr/lib/llvm-7/" + } + ] +} diff --git a/tests/docker/test_docker.py b/tests/docker/test_docker.py new file mode 100755 index 00000000..bb005362 --- /dev/null +++ b/tests/docker/test_docker.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python2 + +import sys, os, json, argparse + +JSON_CONFIG_FILENAME = os.path.dirname(sys.argv[0]) + '/conf.json' +MAKEFLAGS = "-j12" +BRANCH = 'master' +BUILD_SCRIPT = '/root/clazy/tests/docker/build-clazy.sh' + +class DockerTest: + def __init__(self, name, url): + self.name = name + self.url = url + self.prefix = '/opt/clazy' + self.llvm_root = '' + +def read_json_config(): + dockerTests = [] + + if not os.path.exists(JSON_CONFIG_FILENAME): + print "File doesn't exist %s" % (JSON_CONFIG_FILENAME) + return [] + + f = open(JSON_CONFIG_FILENAME, 'r') + contents = f.read() + f.close() + decoded = json.loads(contents) + if 'tests' in decoded: + tests = decoded['tests'] + for test in tests: + if 'name' in test and 'url' in test: + dockerTest = DockerTest(test['name'], test['url']) + if 'prefix' in test: + dockerTest.prefix = test['prefix'] + if 'llvm_root' in test: + dockerTest.llvm_root = test['llvm_root'] + + dockerTests.append(dockerTest) + return dockerTests + + + +def run_test(dockerTest): + cmd = 'docker run -i -t %s sh %s %s %s %s %s' % (dockerTest.url, BUILD_SCRIPT, BRANCH, MAKEFLAGS, dockerTest.prefix, dockerTest.llvm_root) + print cmd + return os.system(cmd) == 0 + + +dockerTests = read_json_config() + + +parser = argparse.ArgumentParser() +parser.add_argument("-b", "--branch") +parser.add_argument("docker_names", nargs='*', help="Names of the containers to run. Defaults to running all docker containers.") + +args = parser.parse_args() + +if args.branch is None: + BRANCH = 'master' +else: + BRANCH = args.branch + +results = {} +success = True +for test in dockerTests: + if args.docker_names and test.name not in args.docker_names: + continue + + results[test.name] = run_test(test) + success = success and results[test.name] + +if success: + print "Success!" +else: + for testname in results.keys(): + if not results[testname]: + print "Test %s failed!" % testname + +sys.exit(0 if success else 1) diff --git a/tests/empty-qstringliteral/config.json b/tests/empty-qstringliteral/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/empty-qstringliteral/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/empty-qstringliteral/main.cpp b/tests/empty-qstringliteral/main.cpp new file mode 100644 index 00000000..0429205e --- /dev/null +++ b/tests/empty-qstringliteral/main.cpp @@ -0,0 +1,11 @@ +#include <QtCore/QObject> +#include <QtCore/QString> + +void test() +{ + QStringLiteral("foo"); + QStringLiteral(""); + QStringLiteral(); + + if ("foo" == QStringLiteral()) {} +} diff --git a/tests/empty-qstringliteral/main.cpp.expected b/tests/empty-qstringliteral/main.cpp.expected new file mode 100644 index 00000000..d4591f06 --- /dev/null +++ b/tests/empty-qstringliteral/main.cpp.expected @@ -0,0 +1,3 @@ +empty-qstringliteral/main.cpp:7:5: warning: Use QString instead of an empty QStringLiteral [-Wclazy-empty-qstringliteral] +empty-qstringliteral/main.cpp:8:5: warning: Use QString instead of an empty QStringLiteral [-Wclazy-empty-qstringliteral] +empty-qstringliteral/main.cpp:10:18: warning: Use QString instead of an empty QStringLiteral [-Wclazy-empty-qstringliteral] diff --git a/tests/fully-qualified-moc-types/config.json b/tests/fully-qualified-moc-types/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/fully-qualified-moc-types/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/fully-qualified-moc-types/main.cpp b/tests/fully-qualified-moc-types/main.cpp new file mode 100644 index 00000000..3f0859f3 --- /dev/null +++ b/tests/fully-qualified-moc-types/main.cpp @@ -0,0 +1,62 @@ +#include <QtCore/QObject> + +struct A {}; + +struct NonNamespacedGadget { + Q_GADGET +}; + +namespace NS { + struct MyType {}; + + struct NamespacedGadget { + Q_GADGET + }; + + enum EnumFoo { EnumFoo1 }; + + class MyObject : public QObject + { + Q_OBJECT + Q_PROPERTY(NS::MyType foo READ foo) // OK, not gadget + Q_PROPERTY(MyType foo1 READ foo) // OK, not gadget + Q_PROPERTY(EnumFoo enumFoo READ enumFoo CONSTANT) // OK + Q_PROPERTY(NamespacedGadget namespacedGadget READ namespacedGadget CONSTANT) // Warn, gadget + Q_PROPERTY(NS::NamespacedGadget namespacedGadget2 READ namespacedGadget2 CONSTANT) // OK + Q_PROPERTY(NonNamespacedGadget nonNamespacedGadget READ nonNamespacedGadget CONSTANT) // OK + Q_SIGNALS: + void mysig(NS::MyType); + void mysig2(MyType); // Warn + void mysig3(NS::MyType); + void mysig4(const NS::MyType &); + void mysig5(A); + void mysig6(const A); + void mysig7(const A *); + void mysig8(A *); + public Q_SLOTS: + void myslot1(NS::MyType); + void myslot2(MyType); // Warn + void myslot3(NS::MyType); + void myslot4(const NS::MyType &); + void myslot5(A); + void myslot6(const A); + void myslot7(const A *); + void myslot8(A *); + public: + Q_INVOKABLE void myinvokable1(NS::MyType); + Q_INVOKABLE void myinvokable2(MyType); // Warn + Q_INVOKABLE void myinvokable3(NS::MyType); + Q_INVOKABLE void myinvokable4(const NS::MyType &); + Q_INVOKABLE void myinvokable5(A); + Q_INVOKABLE void myinvokable6(const A); + Q_INVOKABLE void myinvokable7(const A *); + Q_INVOKABLE void myinvokable8(A *); + NS::MyType foo(); + NamespacedGadget namespacedGadget() const; + NamespacedGadget namespacedGadget2() const; + NonNamespacedGadget nonNamespacedGadget() const; + EnumFoo enumFoo() const; + }; +} + +#include "main.moc_" diff --git a/tests/fully-qualified-moc-types/main.cpp.expected b/tests/fully-qualified-moc-types/main.cpp.expected new file mode 100644 index 00000000..9fd52799 --- /dev/null +++ b/tests/fully-qualified-moc-types/main.cpp.expected @@ -0,0 +1,4 @@ +fully-qualified-moc-types/main.cpp:29:9: warning: signal arguments need to be fully-qualified (NS::MyType instead of MyType) [-Wclazy-fully-qualified-moc-types] +fully-qualified-moc-types/main.cpp:38:9: warning: slot arguments need to be fully-qualified (NS::MyType instead of MyType) [-Wclazy-fully-qualified-moc-types] +fully-qualified-moc-types/main.cpp:47:21: warning: invokable arguments need to be fully-qualified (NS::MyType instead of MyType) [-Wclazy-fully-qualified-moc-types] +fully-qualified-moc-types/main.cpp:18:5: warning: Q_PROPERTY of type NamespacedGadget should use full qualification (NS::NamespacedGadget) [-Wclazy-fully-qualified-moc-types] diff --git a/tests/fully-qualified-moc-types/main.moc_ b/tests/fully-qualified-moc-types/main.moc_ new file mode 100644 index 00000000..69ae3d05 --- /dev/null +++ b/tests/fully-qualified-moc-types/main.moc_ @@ -0,0 +1,472 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'main.cpp' +** +** Created by: The Qt Meta Object Compiler version 67 (Qt 5.9.5) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include <QtCore/qbytearray.h> +#include <QtCore/qmetatype.h> +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'main.cpp' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 67 +#error "This file was generated using the moc from 5.9.5. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED +struct qt_meta_stringdata_NonNamespacedGadget_t { + QByteArrayData data[1]; + char stringdata0[20]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + qptrdiff(offsetof(qt_meta_stringdata_NonNamespacedGadget_t, stringdata0) + ofs \ + - idx * sizeof(QByteArrayData)) \ + ) +static const qt_meta_stringdata_NonNamespacedGadget_t qt_meta_stringdata_NonNamespacedGadget = { + { +QT_MOC_LITERAL(0, 0, 19) // "NonNamespacedGadget" + + }, + "NonNamespacedGadget" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_NonNamespacedGadget[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 4, // flags + 0, // signalCount + + 0 // eod +}; + +const QMetaObject NonNamespacedGadget::staticMetaObject = { + { nullptr, qt_meta_stringdata_NonNamespacedGadget.data, + qt_meta_data_NonNamespacedGadget, nullptr, nullptr, nullptr} +}; + +struct qt_meta_stringdata_NS__NamespacedGadget_t { + QByteArrayData data[1]; + char stringdata0[21]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + qptrdiff(offsetof(qt_meta_stringdata_NS__NamespacedGadget_t, stringdata0) + ofs \ + - idx * sizeof(QByteArrayData)) \ + ) +static const qt_meta_stringdata_NS__NamespacedGadget_t qt_meta_stringdata_NS__NamespacedGadget = { + { +QT_MOC_LITERAL(0, 0, 20) // "NS::NamespacedGadget" + + }, + "NS::NamespacedGadget" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_NS__NamespacedGadget[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 4, // flags + 0, // signalCount + + 0 // eod +}; + +const QMetaObject NS::NamespacedGadget::staticMetaObject = { + { nullptr, qt_meta_stringdata_NS__NamespacedGadget.data, + qt_meta_data_NS__NamespacedGadget, nullptr, nullptr, nullptr} +}; + +struct qt_meta_stringdata_NS__MyObject_t { + QByteArrayData data[41]; + char stringdata0[407]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) \ + Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \ + qptrdiff(offsetof(qt_meta_stringdata_NS__MyObject_t, stringdata0) + ofs \ + - idx * sizeof(QByteArrayData)) \ + ) +static const qt_meta_stringdata_NS__MyObject_t qt_meta_stringdata_NS__MyObject = { + { +QT_MOC_LITERAL(0, 0, 12), // "NS::MyObject" +QT_MOC_LITERAL(1, 13, 5), // "mysig" +QT_MOC_LITERAL(2, 19, 0), // "" +QT_MOC_LITERAL(3, 20, 10), // "NS::MyType" +QT_MOC_LITERAL(4, 31, 6), // "mysig2" +QT_MOC_LITERAL(5, 38, 6), // "MyType" +QT_MOC_LITERAL(6, 45, 6), // "mysig3" +QT_MOC_LITERAL(7, 52, 6), // "mysig4" +QT_MOC_LITERAL(8, 59, 6), // "mysig5" +QT_MOC_LITERAL(9, 66, 1), // "A" +QT_MOC_LITERAL(10, 68, 6), // "mysig6" +QT_MOC_LITERAL(11, 75, 6), // "mysig7" +QT_MOC_LITERAL(12, 82, 8), // "const A*" +QT_MOC_LITERAL(13, 91, 6), // "mysig8" +QT_MOC_LITERAL(14, 98, 2), // "A*" +QT_MOC_LITERAL(15, 101, 7), // "myslot1" +QT_MOC_LITERAL(16, 109, 7), // "myslot2" +QT_MOC_LITERAL(17, 117, 7), // "myslot3" +QT_MOC_LITERAL(18, 125, 7), // "myslot4" +QT_MOC_LITERAL(19, 133, 7), // "myslot5" +QT_MOC_LITERAL(20, 141, 7), // "myslot6" +QT_MOC_LITERAL(21, 149, 7), // "myslot7" +QT_MOC_LITERAL(22, 157, 7), // "myslot8" +QT_MOC_LITERAL(23, 165, 12), // "myinvokable1" +QT_MOC_LITERAL(24, 178, 12), // "myinvokable2" +QT_MOC_LITERAL(25, 191, 12), // "myinvokable3" +QT_MOC_LITERAL(26, 204, 12), // "myinvokable4" +QT_MOC_LITERAL(27, 217, 12), // "myinvokable5" +QT_MOC_LITERAL(28, 230, 12), // "myinvokable6" +QT_MOC_LITERAL(29, 243, 12), // "myinvokable7" +QT_MOC_LITERAL(30, 256, 12), // "myinvokable8" +QT_MOC_LITERAL(31, 269, 3), // "foo" +QT_MOC_LITERAL(32, 273, 4), // "foo1" +QT_MOC_LITERAL(33, 278, 7), // "enumFoo" +QT_MOC_LITERAL(34, 286, 7), // "EnumFoo" +QT_MOC_LITERAL(35, 294, 16), // "namespacedGadget" +QT_MOC_LITERAL(36, 311, 16), // "NamespacedGadget" +QT_MOC_LITERAL(37, 328, 17), // "namespacedGadget2" +QT_MOC_LITERAL(38, 346, 20), // "NS::NamespacedGadget" +QT_MOC_LITERAL(39, 367, 19), // "nonNamespacedGadget" +QT_MOC_LITERAL(40, 387, 19) // "NonNamespacedGadget" + + }, + "NS::MyObject\0mysig\0\0NS::MyType\0mysig2\0" + "MyType\0mysig3\0mysig4\0mysig5\0A\0mysig6\0" + "mysig7\0const A*\0mysig8\0A*\0myslot1\0" + "myslot2\0myslot3\0myslot4\0myslot5\0myslot6\0" + "myslot7\0myslot8\0myinvokable1\0myinvokable2\0" + "myinvokable3\0myinvokable4\0myinvokable5\0" + "myinvokable6\0myinvokable7\0myinvokable8\0" + "foo\0foo1\0enumFoo\0EnumFoo\0namespacedGadget\0" + "NamespacedGadget\0namespacedGadget2\0" + "NS::NamespacedGadget\0nonNamespacedGadget\0" + "NonNamespacedGadget" +}; +#undef QT_MOC_LITERAL + +static const uint qt_meta_data_NS__MyObject[] = { + + // content: + 7, // revision + 0, // classname + 0, 0, // classinfo + 24, 14, // methods + 6, 206, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 8, // signalCount + + // signals: name, argc, parameters, tag, flags + 1, 1, 134, 2, 0x06 /* Public */, + 4, 1, 137, 2, 0x06 /* Public */, + 6, 1, 140, 2, 0x06 /* Public */, + 7, 1, 143, 2, 0x06 /* Public */, + 8, 1, 146, 2, 0x06 /* Public */, + 10, 1, 149, 2, 0x06 /* Public */, + 11, 1, 152, 2, 0x06 /* Public */, + 13, 1, 155, 2, 0x06 /* Public */, + + // slots: name, argc, parameters, tag, flags + 15, 1, 158, 2, 0x0a /* Public */, + 16, 1, 161, 2, 0x0a /* Public */, + 17, 1, 164, 2, 0x0a /* Public */, + 18, 1, 167, 2, 0x0a /* Public */, + 19, 1, 170, 2, 0x0a /* Public */, + 20, 1, 173, 2, 0x0a /* Public */, + 21, 1, 176, 2, 0x0a /* Public */, + 22, 1, 179, 2, 0x0a /* Public */, + + // methods: name, argc, parameters, tag, flags + 23, 1, 182, 2, 0x02 /* Public */, + 24, 1, 185, 2, 0x02 /* Public */, + 25, 1, 188, 2, 0x02 /* Public */, + 26, 1, 191, 2, 0x02 /* Public */, + 27, 1, 194, 2, 0x02 /* Public */, + 28, 1, 197, 2, 0x02 /* Public */, + 29, 1, 200, 2, 0x02 /* Public */, + 30, 1, 203, 2, 0x02 /* Public */, + + // signals: parameters + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 5, 2, + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 9, 2, + QMetaType::Void, 0x80000000 | 9, 2, + QMetaType::Void, 0x80000000 | 12, 2, + QMetaType::Void, 0x80000000 | 14, 2, + + // slots: parameters + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 5, 2, + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 9, 2, + QMetaType::Void, 0x80000000 | 9, 2, + QMetaType::Void, 0x80000000 | 12, 2, + QMetaType::Void, 0x80000000 | 14, 2, + + // methods: parameters + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 5, 2, + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 3, 2, + QMetaType::Void, 0x80000000 | 9, 2, + QMetaType::Void, 0x80000000 | 9, 2, + QMetaType::Void, 0x80000000 | 12, 2, + QMetaType::Void, 0x80000000 | 14, 2, + + // properties: name, type, flags + 31, 0x80000000 | 3, 0x00095009, + 32, 0x80000000 | 5, 0x00095009, + 33, 0x80000000 | 34, 0x00095409, + 35, 0x80000000 | 36, 0x00095409, + 37, 0x80000000 | 38, 0x00095409, + 39, 0x80000000 | 40, 0x00095409, + + 0 // eod +}; + +void NS::MyObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + if (_c == QMetaObject::InvokeMetaMethod) { + MyObject *_t = static_cast<MyObject *>(_o); + Q_UNUSED(_t) + switch (_id) { + case 0: _t->mysig((*reinterpret_cast< NS::MyType(*)>(_a[1]))); break; + case 1: _t->mysig2((*reinterpret_cast< MyType(*)>(_a[1]))); break; + case 2: _t->mysig3((*reinterpret_cast< NS::MyType(*)>(_a[1]))); break; + case 3: _t->mysig4((*reinterpret_cast< const NS::MyType(*)>(_a[1]))); break; + case 4: _t->mysig5((*reinterpret_cast< A(*)>(_a[1]))); break; + case 5: _t->mysig6((*reinterpret_cast< const A(*)>(_a[1]))); break; + case 6: _t->mysig7((*reinterpret_cast< const A*(*)>(_a[1]))); break; + case 7: _t->mysig8((*reinterpret_cast< A*(*)>(_a[1]))); break; + case 8: _t->myslot1((*reinterpret_cast< NS::MyType(*)>(_a[1]))); break; + case 9: _t->myslot2((*reinterpret_cast< MyType(*)>(_a[1]))); break; + case 10: _t->myslot3((*reinterpret_cast< NS::MyType(*)>(_a[1]))); break; + case 11: _t->myslot4((*reinterpret_cast< const NS::MyType(*)>(_a[1]))); break; + case 12: _t->myslot5((*reinterpret_cast< A(*)>(_a[1]))); break; + case 13: _t->myslot6((*reinterpret_cast< const A(*)>(_a[1]))); break; + case 14: _t->myslot7((*reinterpret_cast< const A*(*)>(_a[1]))); break; + case 15: _t->myslot8((*reinterpret_cast< A*(*)>(_a[1]))); break; + case 16: _t->myinvokable1((*reinterpret_cast< NS::MyType(*)>(_a[1]))); break; + case 17: _t->myinvokable2((*reinterpret_cast< MyType(*)>(_a[1]))); break; + case 18: _t->myinvokable3((*reinterpret_cast< NS::MyType(*)>(_a[1]))); break; + case 19: _t->myinvokable4((*reinterpret_cast< const NS::MyType(*)>(_a[1]))); break; + case 20: _t->myinvokable5((*reinterpret_cast< A(*)>(_a[1]))); break; + case 21: _t->myinvokable6((*reinterpret_cast< const A(*)>(_a[1]))); break; + case 22: _t->myinvokable7((*reinterpret_cast< const A*(*)>(_a[1]))); break; + case 23: _t->myinvokable8((*reinterpret_cast< A*(*)>(_a[1]))); break; + default: ; + } + } else if (_c == QMetaObject::IndexOfMethod) { + int *result = reinterpret_cast<int *>(_a[0]); + { + typedef void (MyObject::*_t)(NS::MyType ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig)) { + *result = 0; + return; + } + } + { + typedef void (MyObject::*_t)(MyType ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig2)) { + *result = 1; + return; + } + } + { + typedef void (MyObject::*_t)(NS::MyType ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig3)) { + *result = 2; + return; + } + } + { + typedef void (MyObject::*_t)(const NS::MyType & ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig4)) { + *result = 3; + return; + } + } + { + typedef void (MyObject::*_t)(A ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig5)) { + *result = 4; + return; + } + } + { + typedef void (MyObject::*_t)(const A ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig6)) { + *result = 5; + return; + } + } + { + typedef void (MyObject::*_t)(const A * ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig7)) { + *result = 6; + return; + } + } + { + typedef void (MyObject::*_t)(A * ); + if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&MyObject::mysig8)) { + *result = 7; + return; + } + } + } +#ifndef QT_NO_PROPERTIES + else if (_c == QMetaObject::ReadProperty) { + MyObject *_t = static_cast<MyObject *>(_o); + Q_UNUSED(_t) + void *_v = _a[0]; + switch (_id) { + case 0: *reinterpret_cast< NS::MyType*>(_v) = _t->foo(); break; + case 1: *reinterpret_cast< MyType*>(_v) = _t->foo(); break; + case 2: *reinterpret_cast< EnumFoo*>(_v) = _t->enumFoo(); break; + case 3: *reinterpret_cast< NamespacedGadget*>(_v) = _t->namespacedGadget(); break; + case 4: *reinterpret_cast< NS::NamespacedGadget*>(_v) = _t->namespacedGadget2(); break; + case 5: *reinterpret_cast< NonNamespacedGadget*>(_v) = _t->nonNamespacedGadget(); break; + default: break; + } + } else if (_c == QMetaObject::WriteProperty) { + } else if (_c == QMetaObject::ResetProperty) { + } +#endif // QT_NO_PROPERTIES +} + +const QMetaObject NS::MyObject::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_NS__MyObject.data, + qt_meta_data_NS__MyObject, qt_static_metacall, nullptr, nullptr} +}; + + +const QMetaObject *NS::MyObject::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject; +} + +void *NS::MyObject::qt_metacast(const char *_clname) +{ + if (!_clname) return nullptr; + if (!strcmp(_clname, qt_meta_stringdata_NS__MyObject.stringdata0)) + return static_cast<void*>(this); + return QObject::qt_metacast(_clname); +} + +int NS::MyObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + if (_id < 24) + qt_static_metacall(this, _c, _id, _a); + _id -= 24; + } else if (_c == QMetaObject::RegisterMethodArgumentMetaType) { + if (_id < 24) + *reinterpret_cast<int*>(_a[0]) = -1; + _id -= 24; + } +#ifndef QT_NO_PROPERTIES + else if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty + || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) { + qt_static_metacall(this, _c, _id, _a); + _id -= 6; + } else if (_c == QMetaObject::QueryPropertyDesignable) { + _id -= 6; + } else if (_c == QMetaObject::QueryPropertyScriptable) { + _id -= 6; + } else if (_c == QMetaObject::QueryPropertyStored) { + _id -= 6; + } else if (_c == QMetaObject::QueryPropertyEditable) { + _id -= 6; + } else if (_c == QMetaObject::QueryPropertyUser) { + _id -= 6; + } +#endif // QT_NO_PROPERTIES + return _id; +} + +// SIGNAL 0 +void NS::MyObject::mysig(NS::MyType _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} + +// SIGNAL 1 +void NS::MyObject::mysig2(MyType _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 1, _a); +} + +// SIGNAL 2 +void NS::MyObject::mysig3(NS::MyType _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 2, _a); +} + +// SIGNAL 3 +void NS::MyObject::mysig4(const NS::MyType & _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 3, _a); +} + +// SIGNAL 4 +void NS::MyObject::mysig5(A _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 4, _a); +} + +// SIGNAL 5 +void NS::MyObject::mysig6(const A _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 5, _a); +} + +// SIGNAL 6 +void NS::MyObject::mysig7(const A * _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 6, _a); +} + +// SIGNAL 7 +void NS::MyObject::mysig8(A * _t1) +{ + void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 7, _a); +} +QT_WARNING_POP +QT_END_MOC_NAMESPACE diff --git a/tests/function-args-by-ref/config.json b/tests/function-args-by-ref/config.json index 03f72844..173569d8 100644 --- a/tests/function-args-by-ref/config.json +++ b/tests/function-args-by-ref/config.json @@ -8,6 +8,10 @@ }, { "filename" : "sharedptrs.cpp" + }, + { + "filename" : "warn-for-overridden-methods.cpp", + "env" : { "CLAZY_EXTRA_OPTIONS" : "function-args-by-ref-warn-for-overridden-methods" } } ] } diff --git a/tests/function-args-by-ref/main.cpp b/tests/function-args-by-ref/main.cpp index 459e4a09..7fb7d5ba 100644 --- a/tests/function-args-by-ref/main.cpp +++ b/tests/function-args-by-ref/main.cpp @@ -194,10 +194,10 @@ void Derived::foo(const Trivial &) { } -struct QDBusMessage -{ - void createErrorReply(QString) {} -}; + + + + struct DeletedCtor // bug #360112 { @@ -228,3 +228,20 @@ struct Ctors3 NonTrivial m; int i; }; + + +// #381812 +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(NonTrivial) {}; // Warn + virtual void virtualMethod2(NonTrivial) {}; // Warn + void nonVirtualMethod(NonTrivial) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(NonTrivial) override {}; // OK + void virtualMethod2(NonTrivial) {}; // OK + void nonVirtualMethod(NonTrivial) {}; // Warn +}; diff --git a/tests/function-args-by-ref/main.cpp.expected b/tests/function-args-by-ref/main.cpp.expected index d4a2e8f1..de6c8fbb 100644 --- a/tests/function-args-by-ref/main.cpp.expected +++ b/tests/function-args-by-ref/main.cpp.expected @@ -1,3 +1,4 @@ +function-args-by-ref/main.cpp:245:10: warning: 'virtualMethod2' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] function-args-by-ref/main.cpp:28:10: warning: Missing reference on non-trivial type (const struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:34:10: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:39:10: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] @@ -9,3 +10,7 @@ function-args-by-ref/main.cpp:121:35: warning: Missing reference on non-trivial function-args-by-ref/main.cpp:145:39: warning: Missing reference on non-trivial type (struct DefaultButNotTrivialCopyable) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:214:12: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] function-args-by-ref/main.cpp:227:12: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:237:33: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:238:33: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:239:27: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/main.cpp:246:27: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] diff --git a/tests/function-args-by-ref/warn-for-overridden-methods.cpp b/tests/function-args-by-ref/warn-for-overridden-methods.cpp new file mode 100644 index 00000000..f4a7ee37 --- /dev/null +++ b/tests/function-args-by-ref/warn-for-overridden-methods.cpp @@ -0,0 +1,22 @@ +struct NonTrivial { + NonTrivial() {} + NonTrivial(const NonTrivial &) {} + void constFunction() const {}; + void nonConstFunction() {}; + int a; +}; + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(NonTrivial) {}; // Warn + virtual void virtualMethod2(NonTrivial) {}; // Warn + void nonVirtualMethod(NonTrivial) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(NonTrivial) override {}; // Warn + void virtualMethod2(NonTrivial) {}; // Warn + void nonVirtualMethod(NonTrivial) {}; // Warn +}; diff --git a/tests/function-args-by-ref/warn-for-overridden-methods.cpp.expected b/tests/function-args-by-ref/warn-for-overridden-methods.cpp.expected new file mode 100644 index 00000000..d9acacbd --- /dev/null +++ b/tests/function-args-by-ref/warn-for-overridden-methods.cpp.expected @@ -0,0 +1,7 @@ +function-args-by-ref/warn-for-overridden-methods.cpp:20:10: warning: 'virtualMethod2' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] +function-args-by-ref/warn-for-overridden-methods.cpp:12:33: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/warn-for-overridden-methods.cpp:13:33: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/warn-for-overridden-methods.cpp:14:27: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/warn-for-overridden-methods.cpp:19:25: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/warn-for-overridden-methods.cpp:20:25: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] +function-args-by-ref/warn-for-overridden-methods.cpp:21:27: warning: Missing reference on non-trivial type (struct NonTrivial) [-Wclazy-function-args-by-ref] diff --git a/tests/function-args-by-value/config.json b/tests/function-args-by-value/config.json index 6bb3f06e..6a13e6c6 100644 --- a/tests/function-args-by-value/config.json +++ b/tests/function-args-by-value/config.json @@ -15,6 +15,15 @@ }, { "filename" : "bug379342.cpp" + }, + { + "filename" : "warn-for-overridden-methods.cpp", + "env" : { "CLAZY_EXTRA_OPTIONS" : "function-args-by-value-warn-for-overridden-methods" } + }, + { + "filename" : "warn-for-overridden-methods.cpp_fixed.cpp", + "env" : { "CLAZY_EXTRA_OPTIONS" : "function-args-by-value-warn-for-overridden-methods" }, + "isFixedFile" : true } ] } diff --git a/tests/function-args-by-value/main.cpp b/tests/function-args-by-value/main.cpp index f4ab78c3..b8e85085 100644 --- a/tests/function-args-by-value/main.cpp +++ b/tests/function-args-by-value/main.cpp @@ -216,3 +216,20 @@ struct Ctors void trivialByConstRef(const int &t) {} // Warn void trivialByRef(int &t) {} // OK + +// #381812 + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(const Trivial &) {}; // Warn + virtual void virtualMethod2(const Trivial &) {}; // Warn + void nonVirtualMethod(const Trivial &) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(const Trivial &) override {}; // OK + void virtualMethod2(const Trivial &) {}; // OK + void nonVirtualMethod(const Trivial &) {}; // Warn +}; diff --git a/tests/function-args-by-value/main.cpp.expected b/tests/function-args-by-value/main.cpp.expected index f30cec0b..735e75c0 100644 --- a/tests/function-args-by-value/main.cpp.expected +++ b/tests/function-args-by-value/main.cpp.expected @@ -1,3 +1,4 @@ +function-args-by-value/main.cpp:233:10: warning: 'virtualMethod2' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] function-args-by-value/main.cpp:150:22: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:166:11: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:170:11: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] @@ -5,3 +6,7 @@ function-args-by-value/main.cpp:174:11: warning: Pass small and trivially-copyab function-args-by-value/main.cpp:184:16: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:193:19: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] function-args-by-value/main.cpp:217:24: warning: Pass small and trivially-copyable type by value (const int &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:225:33: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:226:33: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:227:27: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/main.cpp:234:27: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] diff --git a/tests/function-args-by-value/main.cpp_fixed.cpp.expected b/tests/function-args-by-value/main.cpp_fixed.cpp.expected index ffa772e3..a76ae555 100644 --- a/tests/function-args-by-value/main.cpp_fixed.cpp.expected +++ b/tests/function-args-by-value/main.cpp_fixed.cpp.expected @@ -216,3 +216,20 @@ struct Ctors void trivialByConstRef(int t) {} // Warn void trivialByRef(int &t) {} // OK + +// #381812 + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(const Trivial &) {}; // Warn + virtual void virtualMethod2(const Trivial &) {}; // Warn + void nonVirtualMethod(Trivial ) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(const Trivial &) override {}; // OK + void virtualMethod2(const Trivial &) {}; // OK + void nonVirtualMethod(Trivial ) {}; // Warn +}; diff --git a/tests/function-args-by-value/warn-for-overridden-methods.cpp b/tests/function-args-by-value/warn-for-overridden-methods.cpp new file mode 100644 index 00000000..37f84121 --- /dev/null +++ b/tests/function-args-by-value/warn-for-overridden-methods.cpp @@ -0,0 +1,16 @@ +struct Trivial {}; + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(const Trivial &) {}; // Warn + virtual void virtualMethod2(const Trivial &) {}; // Warn + void nonVirtualMethod(const Trivial &) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(const Trivial &) override {}; // Warn + void virtualMethod2(const Trivial &) {}; // Warn + void nonVirtualMethod(const Trivial &) {}; // Warn +}; diff --git a/tests/function-args-by-value/warn-for-overridden-methods.cpp.expected b/tests/function-args-by-value/warn-for-overridden-methods.cpp.expected new file mode 100644 index 00000000..52723a66 --- /dev/null +++ b/tests/function-args-by-value/warn-for-overridden-methods.cpp.expected @@ -0,0 +1,7 @@ +function-args-by-value/warn-for-overridden-methods.cpp:14:10: warning: 'virtualMethod2' overrides a member function but is not marked 'override' [-Winconsistent-missing-override] +function-args-by-value/warn-for-overridden-methods.cpp:6:33: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/warn-for-overridden-methods.cpp:7:33: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/warn-for-overridden-methods.cpp:8:27: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/warn-for-overridden-methods.cpp:13:25: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/warn-for-overridden-methods.cpp:14:25: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] +function-args-by-value/warn-for-overridden-methods.cpp:15:27: warning: Pass small and trivially-copyable type by value (const struct Trivial &) [-Wclazy-function-args-by-value] diff --git a/tests/function-args-by-value/warn-for-overridden-methods.cpp_fixed.cpp.expected b/tests/function-args-by-value/warn-for-overridden-methods.cpp_fixed.cpp.expected new file mode 100644 index 00000000..a451add3 --- /dev/null +++ b/tests/function-args-by-value/warn-for-overridden-methods.cpp_fixed.cpp.expected @@ -0,0 +1,16 @@ +struct Trivial {}; + +class BaseWithVirtuals +{ +public: + virtual void virtualMethod1(Trivial ) {}; // Warn + virtual void virtualMethod2(Trivial ) {}; // Warn + void nonVirtualMethod(Trivial ) {}; // Warn +}; + +class DerivedWithVirtuals : BaseWithVirtuals { +public: + void virtualMethod1(Trivial ) override {}; // Warn + void virtualMethod2(Trivial ) {}; // Warn + void nonVirtualMethod(Trivial ) {}; // Warn +}; diff --git a/tests/old-style-connect/main.cpp b/tests/old-style-connect/main.cpp index 2da8117f..4303fdff 100644 --- a/tests/old-style-connect/main.cpp +++ b/tests/old-style-connect/main.cpp @@ -6,8 +6,8 @@ #include <QtWidgets/QAction> #include <QtWidgets/QProgressDialog> #include <QtDBus/QDBusInterface> - - +#include <QtWidgets/QMenu> +#include <QtWidgets/QMessageBox> @@ -366,4 +366,21 @@ void testStatic() TestStatic::disconnect(test, SIGNAL(destroyed(QObject*)), test, SLOT(test(QObject*))); } +void test1ArgDisconnect() +{ + QObject *o; + o->disconnect(SIGNAL(destroyed())); +} + +void testQMenuAndQMessageBox() +{ + QMenu m; + OtherObj o; + m.addAction("test", &o, SLOT(otherSlot())); // Warn + m.addAction("test", &o, &OtherObj::otherSlot); // OK + QMessageBox box; + box.open(); // OK + box.open(&o,SLOT(otherSlot())); // Warn +} + #include "main.moc" diff --git a/tests/old-style-connect/main.cpp.expected b/tests/old-style-connect/main.cpp.expected index 25cceb7b..ba2387a6 100644 --- a/tests/old-style-connect/main.cpp.expected +++ b/tests/old-style-connect/main.cpp.expected @@ -74,3 +74,8 @@ old-style-connect/main.cpp:341:5: warning: Old Style Connect [-Wclazy-old-style- old-style-connect/main.cpp:341:43: warning: FixIt failed, requires manual intervention: No such method foo in class QDBusInterface [-Wclazy-old-style-connect] old-style-connect/main.cpp:365:5: warning: Old Style Connect [-Wclazy-old-style-connect] old-style-connect/main.cpp:366:5: warning: Old Style Connect [-Wclazy-old-style-connect] +old-style-connect/main.cpp:372:5: warning: Old Style Connect [-Wclazy-old-style-connect] +old-style-connect/main.cpp:372:5: warning: FixIt failed, requires manual intervention: Fix it not implemented for disconnect with 3 args [-Wclazy-old-style-connect] +old-style-connect/main.cpp:379:5: warning: Old Style Connect [-Wclazy-old-style-connect] +old-style-connect/main.cpp:383:5: warning: Old Style Connect [-Wclazy-old-style-connect] +old-style-connect/main.cpp:383:5: warning: FixIt failed, requires manual intervention: Fix it not implemented for QMessageBox::open() [-Wclazy-old-style-connect] diff --git a/tests/old-style-connect/main.cpp_fixed.cpp.expected b/tests/old-style-connect/main.cpp_fixed.cpp.expected index 9340c212..0fb736a3 100644 --- a/tests/old-style-connect/main.cpp_fixed.cpp.expected +++ b/tests/old-style-connect/main.cpp_fixed.cpp.expected @@ -6,8 +6,8 @@ #include <QtWidgets/QAction> #include <QtWidgets/QProgressDialog> #include <QtDBus/QDBusInterface> - - +#include <QtWidgets/QMenu> +#include <QtWidgets/QMessageBox> @@ -366,4 +366,21 @@ void testStatic() TestStatic::disconnect(test, SIGNAL(destroyed(QObject*)), test, SLOT(test(QObject*))); } +void test1ArgDisconnect() +{ + QObject *o; + o->disconnect(SIGNAL(destroyed())); +} + +void testQMenuAndQMessageBox() +{ + QMenu m; + OtherObj o; + m.addAction("test", &o, &OtherObj::otherSlot); // Warn + m.addAction("test", &o, &OtherObj::otherSlot); // OK + QMessageBox box; + box.open(); // OK + box.open(&o,SLOT(otherSlot())); // Warn +} + #include "main.moc" diff --git a/tests/old-style-connect/not-in-hierarchy.cpp b/tests/old-style-connect/not-in-hierarchy.cpp new file mode 100644 index 00000000..a562ddc3 --- /dev/null +++ b/tests/old-style-connect/not-in-hierarchy.cpp @@ -0,0 +1,22 @@ +#include <QtCore/QObject> + +class A : public QObject { +public slots: + void OnEvent() {} +}; + +class B : public QObject { +public slots: + void OnEvent() {} +}; + +class C : public QObject +{ + C(QObject *client) + { + connect(this, SIGNAL(mysig()), client, SLOT(OnEvent())); // TODO: Would be nice to not warn in this case + } + +signals: + void mysig(); +}; diff --git a/tests/qhash-with-char-pointer-key/config.json b/tests/qhash-with-char-pointer-key/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/qhash-with-char-pointer-key/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/qhash-with-char-pointer-key/main.cpp b/tests/qhash-with-char-pointer-key/main.cpp new file mode 100644 index 00000000..47a5a5c7 --- /dev/null +++ b/tests/qhash-with-char-pointer-key/main.cpp @@ -0,0 +1,12 @@ +#include <QtCore/QHash> +#include <QtCore/QString> + +void test() +{ + QHash<const char*, int> hash1; // Warn + QHash<char, int> hash2; + QHash<char**, int> hash3; + QHash<int, int> hash4; + QHash<char32_t, int> hash5; + QHash<const char32_t*, int> hash6; +} diff --git a/tests/qhash-with-char-pointer-key/main.cpp.expected b/tests/qhash-with-char-pointer-key/main.cpp.expected new file mode 100644 index 00000000..b7a786ba --- /dev/null +++ b/tests/qhash-with-char-pointer-key/main.cpp.expected @@ -0,0 +1 @@ +qhash-with-char-pointer-key/main.cpp:6:5: warning: Using QHash<const char *, T> is dangerous [-Wclazy-qhash-with-char-pointer-key] diff --git a/tests/qlatin1string-non-ascii/main.cpp.expected b/tests/qlatin1string-non-ascii/main.cpp.expected index 6d3a0d4b..06a49a37 100644 --- a/tests/qlatin1string-non-ascii/main.cpp.expected +++ b/tests/qlatin1string-non-ascii/main.cpp.expected @@ -1 +1 @@ -qlatin1string-non-ascii/main.cpp:5:19: warning: QStringLiteral with non-ascii literal [-Wclazy-qlatin1string-non-ascii] +qlatin1string-non-ascii/main.cpp:5:19: warning: QLatin1String with non-ascii literal [-Wclazy-qlatin1string-non-ascii] diff --git a/tests/qstring-arg/main.cpp b/tests/qstring-arg/main.cpp index f1d4f4ec..39e0ce75 100644 --- a/tests/qstring-arg/main.cpp +++ b/tests/qstring-arg/main.cpp @@ -1,9 +1,9 @@ #include <QtCore/QString> +#include <QtWidgets/QMessageBox> +#include <QtWidgets/QApplication> - - -void test() +void test(int argc, char**argv) { QString s; QString s1; @@ -27,4 +27,5 @@ void test() s5 = QString("%1 %2 %3 %4").arg(s).arg(s1).arg(s3, s4); // Warning QString().arg(s1, s2, s3, s4, s5).arg(s1, s2, s3, s4, s5); // OK QString().arg(s1, s2, s3, s4, s5).arg(s1, s2, s3, s4); // Warning + QT_REQUIRE_VERSION(argc, argv, "5.2.0"); // OK (bug #391851) } diff --git a/tests/qstring-varargs/config.json b/tests/qstring-varargs/config.json new file mode 100644 index 00000000..41d845f1 --- /dev/null +++ b/tests/qstring-varargs/config.json @@ -0,0 +1,8 @@ +{ + "tests" : [ + { + "filename" : "main.cpp", + "flags" : "-Wno-non-pod-varargs" + } + ] +} diff --git a/tests/qstring-varargs/main.cpp b/tests/qstring-varargs/main.cpp new file mode 100644 index 00000000..cdef4edf --- /dev/null +++ b/tests/qstring-varargs/main.cpp @@ -0,0 +1,18 @@ +#include <QtCore/QString> + +void simple_printf(const char* ...) +{ +} + + +void test() +{ + QString s; + printf("%s", s); // Warn + simple_printf("%s", s); // Warn + const char *foo = "f"; + printf("%s", foo); + + QByteArray b; + printf("%s", b); // Warn +} diff --git a/tests/qstring-varargs/main.cpp.expected b/tests/qstring-varargs/main.cpp.expected new file mode 100644 index 00000000..7eba19aa --- /dev/null +++ b/tests/qstring-varargs/main.cpp.expected @@ -0,0 +1,3 @@ +qstring-varargs/main.cpp:11:18: warning: Passing QString to variadic function [-Wclazy-qstring-varargs] +qstring-varargs/main.cpp:12:25: warning: Passing QString to variadic function [-Wclazy-qstring-varargs] +qstring-varargs/main.cpp:17:18: warning: Passing QByteArray to variadic function [-Wclazy-qstring-varargs] diff --git a/tests/qt-keywords/config.json b/tests/qt-keywords/config.json new file mode 100644 index 00000000..acdbadf4 --- /dev/null +++ b/tests/qt-keywords/config.json @@ -0,0 +1,14 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + }, + { + "filename" : "main.cpp_fixed.cpp", + "isFixedFile" : true + }, + { + "filename" : "no_keywords.cpp" + } + ] +} diff --git a/tests/qt-keywords/main.cpp b/tests/qt-keywords/main.cpp new file mode 100644 index 00000000..4c01592c --- /dev/null +++ b/tests/qt-keywords/main.cpp @@ -0,0 +1,25 @@ +#include <QtCore/QObject> +#include <QtCore/QString> + +class MyObj : public QObject +{ +public slots: + void slot1(); + +public Q_SLOTS: + void slot2(); +signals: + void signal1(); +Q_SIGNALS: + void signal2(); +public: + Q_SLOT void slot3(); + Q_SIGNAL void signal3(); + void test() + { + emit signal1(); + QList<int> l; + foreach(int i, l) {} + } +}; + diff --git a/tests/qt-keywords/main.cpp.expected b/tests/qt-keywords/main.cpp.expected new file mode 100644 index 00000000..1c8e00e1 --- /dev/null +++ b/tests/qt-keywords/main.cpp.expected @@ -0,0 +1,4 @@ +qt-keywords/main.cpp:6:8: warning: Using a Qt keyword (slots) [-Wclazy-qt-keywords] +qt-keywords/main.cpp:11:1: warning: Using a Qt keyword (signals) [-Wclazy-qt-keywords] +qt-keywords/main.cpp:20:9: warning: Using a Qt keyword (emit) [-Wclazy-qt-keywords] +qt-keywords/main.cpp:22:9: warning: Using a Qt keyword (foreach) [-Wclazy-qt-keywords] diff --git a/tests/qt-keywords/main.cpp_fixed.cpp.expected b/tests/qt-keywords/main.cpp_fixed.cpp.expected new file mode 100644 index 00000000..4d0e3ff4 --- /dev/null +++ b/tests/qt-keywords/main.cpp_fixed.cpp.expected @@ -0,0 +1,25 @@ +#include <QtCore/QObject> +#include <QtCore/QString> + +class MyObj : public QObject +{ +public Q_SLOTS: + void slot1(); + +public Q_SLOTS: + void slot2(); +Q_SIGNALS: + void signal1(); +Q_SIGNALS: + void signal2(); +public: + Q_SLOT void slot3(); + Q_SIGNAL void signal3(); + void test() + { + Q_EMIT signal1(); + QList<int> l; + Q_FOREACH(int i, l) {} + } +}; + diff --git a/tests/qt-keywords/no_keywords.cpp b/tests/qt-keywords/no_keywords.cpp new file mode 100644 index 00000000..34f018e0 --- /dev/null +++ b/tests/qt-keywords/no_keywords.cpp @@ -0,0 +1,8 @@ +#define QT_NO_KEYWORDS +#include <QtCore/QObject> +#define emit test(); + +void test() +{ + emit // OK +} diff --git a/tests/qt-keywords/no_keywords.cpp.expected b/tests/qt-keywords/no_keywords.cpp.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/qt-keywords/no_keywords.cpp.expected diff --git a/tests/raw-environment-function/config.json b/tests/raw-environment-function/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/raw-environment-function/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/raw-environment-function/main.cpp b/tests/raw-environment-function/main.cpp new file mode 100644 index 00000000..836366eb --- /dev/null +++ b/tests/raw-environment-function/main.cpp @@ -0,0 +1,16 @@ +#include <QtCore/QObject> +#include <QtCore/QString> + +#include <stdlib.h> + +void test() +{ + QByteArray array = qgetenv("foo"); // OK + qputenv("foo", "bar"); // OK + + const char *array2 = getenv("foo"); // Warn + char *var; + int i = putenv(var); // Warn + + const char *array3 = ::getenv("foo"); // Warn +} diff --git a/tests/raw-environment-function/main.cpp.expected b/tests/raw-environment-function/main.cpp.expected new file mode 100644 index 00000000..8afdf377 --- /dev/null +++ b/tests/raw-environment-function/main.cpp.expected @@ -0,0 +1,3 @@ +raw-environment-function/main.cpp:11:26: warning: Prefer using qgetenv instead of getenv [-Wclazy-raw-environment-function] +raw-environment-function/main.cpp:13:13: warning: Prefer using qputenv instead of putenv [-Wclazy-raw-environment-function] +raw-environment-function/main.cpp:15:26: warning: Prefer using qgetenv instead of getenv [-Wclazy-raw-environment-function] diff --git a/tests/returning-data-from-temporary/config.json b/tests/returning-data-from-temporary/config.json index e7e6e0cb..f299093c 100644 --- a/tests/returning-data-from-temporary/config.json +++ b/tests/returning-data-from-temporary/config.json @@ -1,7 +1,8 @@ { "tests" : [ { - "filename" : "main.cpp" + "filename" : "main.cpp", + "expects_failure" : true } ] } diff --git a/tests/returning-data-from-temporary/main.cpp b/tests/returning-data-from-temporary/main.cpp index 1b84fc14..3643efe9 100644 --- a/tests/returning-data-from-temporary/main.cpp +++ b/tests/returning-data-from-temporary/main.cpp @@ -131,6 +131,7 @@ void testAssignment() const char *c8 = str.toUtf8(); // Warn const char *c9 = str.toLatin1(); // Warn const char *c10 = returnsByteArray(); // Warn + const char* buffer = QByteArray("Hello").constData(); // Warn } const char * testByParam(QByteArray &ba, QString &foo, QByteArray ba2) diff --git a/tests/returning-data-from-temporary/main.cpp.expected b/tests/returning-data-from-temporary/main.cpp.expected index d3ffcc53..68e0c88c 100644 --- a/tests/returning-data-from-temporary/main.cpp.expected +++ b/tests/returning-data-from-temporary/main.cpp.expected @@ -18,5 +18,6 @@ returning-data-from-temporary/main.cpp:130:22: warning: Returning data of tempor returning-data-from-temporary/main.cpp:131:22: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] returning-data-from-temporary/main.cpp:132:22: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] returning-data-from-temporary/main.cpp:133:23: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] -returning-data-from-temporary/main.cpp:141:12: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] +returning-data-from-temporary/main.cpp:134:23: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] returning-data-from-temporary/main.cpp:142:12: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] +returning-data-from-temporary/main.cpp:143:12: warning: Returning data of temporary QByteArray [-Wclazy-returning-data-from-temporary] diff --git a/tests/run_tests.py b/tests/run_tests.py index 3d6c365c..00c8c33f 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -11,15 +11,16 @@ 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") + 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 = 999999 + self.maximum_qt_version = 59999 self.minimum_clang_version = 380 self.compare_everything = False self.isFixedFile = False @@ -35,6 +36,8 @@ class Test: 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") @@ -62,8 +65,9 @@ class Check: self.name = name self.minimum_clang_version = 380 # clang 3.8.0 self.minimum_qt_version = 500 - self.maximum_qt_version = 999999 + self.maximum_qt_version = 59999 self.enabled = True + self.clazy_standalone_only = False self.tests = [] #------------------------------------------------------------------------------- # utility functions #1 @@ -101,6 +105,9 @@ def load_json(check_name): 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'] @@ -151,6 +158,10 @@ def load_json(check_name): 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) @@ -170,9 +181,13 @@ def find_qt_installation(major_version, 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 - installation.int_version = int(qmake_version_str.replace(".", "")) + 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 @@ -217,16 +232,23 @@ def clazy_standalone_command(test, qt): 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: + 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: - result = "clang -Xclang -load -Xclang " + libraryName() + " -Xclang -add-plugin -Xclang clang-lazy " + more_clazy_args() + qt.compiler_flags() + clang = os.getenv('CLANGXX', 'clang') + result = clang + " -Xclang -load -Xclang " + libraryName() + " -Xclang -add-plugin -Xclang clang-lazy " + more_clazy_args() + qt.compiler_flags() if test.qt4compat: result = result + " -Xclang -plugin-arg-clang-lazy -Xclang qt4-compat " @@ -250,12 +272,12 @@ def clazy_command(qt, test, 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.filename + 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 'clang' + return os.getenv('CLANGXX', 'clang') #------------------------------------------------------------------------------- # Get clang version @@ -371,7 +393,16 @@ def extract_word(word, in_file, out_file): in_f.close() out_f.close() +def print_file(filename): + f = open(filename, 'r') + print f.read() + f.close() + + 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: @@ -380,9 +411,13 @@ def run_unit_test(test, is_standalone): 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 @@ -412,6 +447,10 @@ def run_unit_test(test, is_standalone): 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 @@ -529,4 +568,9 @@ else: for thread in threads: thread.join() -sys.exit(0 if _was_successful else -1) +if _was_successful: + print "SUCCESS" + sys.exit(0) +else: + print "FAIL" + sys.exit(-1) diff --git a/tests/skipped-base-method/config.json b/tests/skipped-base-method/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/skipped-base-method/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/skipped-base-method/main.cpp b/tests/skipped-base-method/main.cpp new file mode 100644 index 00000000..9bdb7945 --- /dev/null +++ b/tests/skipped-base-method/main.cpp @@ -0,0 +1,27 @@ +#include <QtCore/QString> + +class Base { +public: + virtual void method1() {} + virtual void method2() {} + virtual void method3() {} +}; + +class Derived : public Base { +public: + virtual void method1() override {} + void method3() override = 0; +}; + +class DerivedDerived : public Derived { +public: + void test() + { + Base::method1(); // Warn + Derived::method1(); // OK + method1(); + + Base::method2(); // OK + Base::method3(); // OK + } +}; diff --git a/tests/skipped-base-method/main.cpp.expected b/tests/skipped-base-method/main.cpp.expected new file mode 100644 index 00000000..bfa8cb79 --- /dev/null +++ b/tests/skipped-base-method/main.cpp.expected @@ -0,0 +1 @@ +skipped-base-method/main.cpp:20:9: warning: Maybe you meant to call Derived::method1() instead [-Wclazy-skipped-base-method] diff --git a/tests/static-pmf/config.json b/tests/static-pmf/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/static-pmf/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/static-pmf/main.cpp b/tests/static-pmf/main.cpp new file mode 100644 index 00000000..4051c636 --- /dev/null +++ b/tests/static-pmf/main.cpp @@ -0,0 +1,12 @@ +#include <QtCore/QObject> + +struct NonQObject { + void method() {} +}; + +void test() +{ + static auto f = &QObject::destroyed; // Warn + auto f2 = &QObject::destroyed; // OK + static auto f4 = &NonQObject::method; // OK +} diff --git a/tests/static-pmf/main.cpp.expected b/tests/static-pmf/main.cpp.expected new file mode 100644 index 00000000..6d3f3dbd --- /dev/null +++ b/tests/static-pmf/main.cpp.expected @@ -0,0 +1 @@ +static-pmf/main.cpp:9:5: warning: Static pointer to member has portability issues [-Wclazy-static-pmf] diff --git a/tests/bogus-dynamic-cast/config.json b/tests/unneeded-cast/config.json index f6adf90f..b39132d9 100644 --- a/tests/bogus-dynamic-cast/config.json +++ b/tests/unneeded-cast/config.json @@ -4,10 +4,10 @@ "filename" : "main.cpp" }, { - "filename" : "qobjectcast.cpp", + "filename" : "dynamic_cast_over_qobjectcast.cpp", "env" : { - "CLAZY_EXTRA_OPTIONS" : "bogus-dynamic-cast-qobject" + "CLAZY_EXTRA_OPTIONS" : "unneeded-cast-prefer-dynamic-cast-over-qobject" } } ] diff --git a/tests/bogus-dynamic-cast/qobjectcast.cpp b/tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp index 11e0ed62..9cb0256d 100644 --- a/tests/bogus-dynamic-cast/qobjectcast.cpp +++ b/tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp @@ -4,5 +4,5 @@ struct MyObj : public QObject {}; void testQObjectCast(QObject *o) { - dynamic_cast<MyObj*>(o); // Warn + dynamic_cast<MyObj*>(o); // OK } diff --git a/tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp.expected b/tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp.expected diff --git a/tests/unneeded-cast/main.cpp b/tests/unneeded-cast/main.cpp new file mode 100644 index 00000000..808e1a87 --- /dev/null +++ b/tests/unneeded-cast/main.cpp @@ -0,0 +1,97 @@ +#include <QtCore/QObject> + + + + + + + + + + + + + + + +struct A +{ + virtual void foo() { } +}; + +struct B : public A +{ + +}; + +void test() +{ + A *a = new A(); + B *b = new B(); + + dynamic_cast<A*>(b); // warning: Casting to base + dynamic_cast<A*>(a); // warning: Casting to itself + dynamic_cast<B*>(a); // OK + dynamic_cast<B*>(b); // warning: Casting to itself + static_cast<A*>(b); // warning: Casting to base + static_cast<A*>(a); // warning: Casting to itself + static_cast<B*>(a); // OK + static_cast<B*>(b); // warning: Casting to itself +} + +class MyObj : public QObject +{ + Q_OBJECT +public: +}; + +void testQObjectCast(QObject *o) +{ + dynamic_cast<MyObj*>(o); // Warn + qobject_cast<QObject*>(o); // Warn + MyObj myobj; + qobject_cast<QObject*>(&myobj); // Warn + qobject_cast<MyObj*>(o); // OK +} + +class FwdDeclared; + +class MyObj2 : public QObject +{ +public: + Q_DECLARE_PUBLIC(QObject) // OK, in macro + MyObj2 *q_ptr; +}; + +struct Base2 {}; +class Base1 : public QObject {}; + +class Derived : public Base1, public Base2 +{ +public: + void test() + { + static_cast<Base1*>(this); // OK + static_cast<Base2*>(this); // OK + } +}; + +void test2() +{ + static_cast<MyObj*>(nullptr); // OK + static_cast<MyObj*>(0); // OK + + MyObj *o1; + MyObj2 *o2; + true ? static_cast<QObject*>(o1) : static_cast<QObject*>(o2); // Ok +} + +class MyObj4 : public QObject +{ + Q_OBJECT +public: + void test() + { + qobject_cast<MyObj4*>(sender()); // OK + } +}; diff --git a/tests/unneeded-cast/main.cpp.expected b/tests/unneeded-cast/main.cpp.expected new file mode 100644 index 00000000..448e714c --- /dev/null +++ b/tests/unneeded-cast/main.cpp.expected @@ -0,0 +1,15 @@ +unneeded-cast/main.cpp:32:5: warning: explicitly casting to base is unnecessary [-Wclazy-unneeded-cast] + dynamic_cast<A*>(b); // warning: Casting to base +unneeded-cast/main.cpp:33:5: warning: Casting to itself [-Wclazy-unneeded-cast] + dynamic_cast<A*>(a); // warning: Casting to itself +unneeded-cast/main.cpp:35:5: warning: Casting to itself [-Wclazy-unneeded-cast] + dynamic_cast<B*>(b); // warning: Casting to itself +unneeded-cast/main.cpp:36:5: warning: explicitly casting to base is unnecessary [-Wclazy-unneeded-cast] + static_cast<A*>(b); // warning: Casting to base +unneeded-cast/main.cpp:37:5: warning: Casting to itself [-Wclazy-unneeded-cast] + static_cast<A*>(a); // warning: Casting to itself +unneeded-cast/main.cpp:39:5: warning: Casting to itself [-Wclazy-unneeded-cast] + static_cast<B*>(b); // warning: Casting to itself +unneeded-cast/main.cpp:50:5: warning: Use qobject_cast rather than dynamic_cast [-Wclazy-unneeded-cast] +unneeded-cast/main.cpp:51:5: warning: Casting to itself [-Wclazy-unneeded-cast] +unneeded-cast/main.cpp:53:5: warning: explicitly casting to base is unnecessary [-Wclazy-unneeded-cast] diff --git a/tests/unused-non-trivial-variable/config.json b/tests/unused-non-trivial-variable/config.json index e7e6e0cb..7066b559 100644 --- a/tests/unused-non-trivial-variable/config.json +++ b/tests/unused-non-trivial-variable/config.json @@ -1,7 +1,14 @@ { "tests" : [ { - "filename" : "main.cpp" + "filename" : "main.cpp", + "env" :{ "CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_WHITELIST" : "MyWhitelistedType" } + }, + { + "filename" : "no-whitelist.cpp", + "env" : { "CLAZY_EXTRA_OPTIONS" : "unused-non-trivial-variable-no-whitelist", + "CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_BLACKLIST" : "MyBlacklistedType" + } } ] } diff --git a/tests/unused-non-trivial-variable/main.cpp b/tests/unused-non-trivial-variable/main.cpp index 12febdab..850c6b8d 100644 --- a/tests/unused-non-trivial-variable/main.cpp +++ b/tests/unused-non-trivial-variable/main.cpp @@ -30,7 +30,7 @@ struct MyRAII void testRAII() { - MyRAII m; // OK + MyRAII m; // OK, not whitelisted } void testFor() @@ -53,3 +53,14 @@ void test4() FOO(QRect) r2; // OK r2.setX(0); } + +struct MyWhitelistedType +{ + MyWhitelistedType(); + ~MyWhitelistedType(); +}; + +void testUserWhitelist() +{ + MyWhitelistedType m; // OK, whitelisted +} diff --git a/tests/unused-non-trivial-variable/main.cpp.expected b/tests/unused-non-trivial-variable/main.cpp.expected index f6400390..0177a663 100644 --- a/tests/unused-non-trivial-variable/main.cpp.expected +++ b/tests/unused-non-trivial-variable/main.cpp.expected @@ -4,3 +4,4 @@ unused-non-trivial-variable/main.cpp:49:5: warning: unused QList<int> [-Wclazy-u unused-non-trivial-variable/main.cpp:50:5: warning: unused QVector<int> [-Wclazy-unused-non-trivial-variable] unused-non-trivial-variable/main.cpp:51:5: warning: unused QByteArray [-Wclazy-unused-non-trivial-variable] unused-non-trivial-variable/main.cpp:52:5: warning: unused QRect [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/main.cpp:65:5: warning: unused MyWhitelistedType [-Wclazy-unused-non-trivial-variable] diff --git a/tests/unused-non-trivial-variable/no-whitelist.cpp b/tests/unused-non-trivial-variable/no-whitelist.cpp new file mode 100644 index 00000000..9cb7ee18 --- /dev/null +++ b/tests/unused-non-trivial-variable/no-whitelist.cpp @@ -0,0 +1,73 @@ +#include <QtCore/QString> +#include <QtCore/QList> +#include <QtCore/QVector> +#include <QtCore/QByteArray> +#include <QtCore/QRect> +#include <QtCore/QMutex> +#include <QtCore/QScopedPointer> +#include "other.h" + + + +extern void external(QString); + +QString test() +{ + QString s; // Warning + QString s1, s2; // Warning for s2 + QString s3; // OK + external(s1); + + return s3; + return {}; +} + +struct MyRAII +{ + MyRAII(); + ~MyRAII(); +}; + +void testRAII() +{ + MyRAII m; // Warn, not blacklisted +} + +void testFor() +{ + QStringList l; + for (QString s : l) // OK + s; + + foreach (QString s, l) // OK + s; +} + + +void test4() +{ + QList<int> l; //Warn + QVector<int> v; //Warn + QByteArray b; //Warn + QRect r; // Warn + FOO(QRect) r2; // OK + r2.setX(0); +} + +void mutex() +{ + QMutex m; + QMutexLocker ml(&m); // OK, is uninteresting + QScopedPointer<QMutex> p(&m); // OK, is uninteresting +} + +struct MyBlacklistedType +{ + MyBlacklistedType(); + ~MyBlacklistedType(); +}; + +void testUserWhitelist() +{ + MyBlacklistedType m; // OK, blacklisted +} diff --git a/tests/unused-non-trivial-variable/no-whitelist.cpp.expected b/tests/unused-non-trivial-variable/no-whitelist.cpp.expected new file mode 100644 index 00000000..0303caf2 --- /dev/null +++ b/tests/unused-non-trivial-variable/no-whitelist.cpp.expected @@ -0,0 +1,7 @@ +unused-non-trivial-variable/no-whitelist.cpp:16:5: warning: unused QString [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/no-whitelist.cpp:17:5: warning: unused QString [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/no-whitelist.cpp:33:5: warning: unused MyRAII [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/no-whitelist.cpp:49:5: warning: unused QList<int> [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/no-whitelist.cpp:50:5: warning: unused QVector<int> [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/no-whitelist.cpp:51:5: warning: unused QByteArray [-Wclazy-unused-non-trivial-variable] +unused-non-trivial-variable/no-whitelist.cpp:52:5: warning: unused QRect [-Wclazy-unused-non-trivial-variable] diff --git a/tests/virtual-signal/config.json b/tests/virtual-signal/config.json index e7e6e0cb..57be525c 100644 --- a/tests/virtual-signal/config.json +++ b/tests/virtual-signal/config.json @@ -2,6 +2,9 @@ "tests" : [ { "filename" : "main.cpp" + }, + { + "filename" : "non-qobject-base.cpp" } ] } diff --git a/tests/virtual-signal/non-qobject-base.cpp b/tests/virtual-signal/non-qobject-base.cpp new file mode 100644 index 00000000..b800d404 --- /dev/null +++ b/tests/virtual-signal/non-qobject-base.cpp @@ -0,0 +1,19 @@ +#include <QtCore/QObject> + +struct IFoo { + ~IFoo() {}; + + virtual void fooChanged(int); // Will be a signal in derived classes + int getFoo() const; + void setFoo(int) {} + +private: + int m_foo; +}; + +struct MyObject : QObject, IFoo { + Q_OBJECT + +signals: + void fooChanged(int) override; +}; diff --git a/tests/virtual-signal/non-qobject-base.cpp.expected b/tests/virtual-signal/non-qobject-base.cpp.expected new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/virtual-signal/non-qobject-base.cpp.expected diff --git a/tests/writing-to-temporary/main.cpp b/tests/writing-to-temporary/main.cpp index f5f1106d..8ca21b6a 100644 --- a/tests/writing-to-temporary/main.cpp +++ b/tests/writing-to-temporary/main.cpp @@ -2,7 +2,7 @@ #include <QtGui/QColor> #include <QtGui/QTextTableCell> #include <QtXml/QDomNode> - +#include <QtGui/QTextCursor> diff --git a/tests/wrong-qevent-cast/config.json b/tests/wrong-qevent-cast/config.json new file mode 100644 index 00000000..e7e6e0cb --- /dev/null +++ b/tests/wrong-qevent-cast/config.json @@ -0,0 +1,7 @@ +{ + "tests" : [ + { + "filename" : "main.cpp" + } + ] +} diff --git a/tests/wrong-qevent-cast/main.cpp b/tests/wrong-qevent-cast/main.cpp new file mode 100644 index 00000000..a212c6cd --- /dev/null +++ b/tests/wrong-qevent-cast/main.cpp @@ -0,0 +1,41 @@ +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QEvent> +#include <QtGui/QKeyEvent> + +void test(QEvent *ev) +{ + + switch (ev->type()) { + case QEvent::MouseMove: { + auto a = static_cast<QKeyEvent*>(ev); // Warn + auto b = static_cast<QMouseEvent*>(ev); // OK + break; + } + case QEvent::KeyPress: { + auto a = static_cast<QKeyEvent*>(ev); // OK + auto b = static_cast<QMouseEvent*>(ev); // Warn + + int val = 0; + switch (val) { // unrelated switch + case 1000: { + auto a = static_cast<QKeyEvent*>(ev); // OK + auto b = static_cast<QMouseEvent*>(ev); // Warn + } + } + break; + } + + case QEvent::Paint: + case QEvent::MetaCall: { + if (ev->type() == QEvent::Paint) + auto pe = static_cast<QPaintEvent*>(ev); // OK + break; + } + + default: + break; + } + + +} diff --git a/tests/wrong-qevent-cast/main.cpp.expected b/tests/wrong-qevent-cast/main.cpp.expected new file mode 100644 index 00000000..aaf8e0a0 --- /dev/null +++ b/tests/wrong-qevent-cast/main.cpp.expected @@ -0,0 +1,3 @@ +wrong-qevent-cast/main.cpp:11:22: warning: Cast from a QEvent::MouseMove event to QKeyEvent looks suspicious. [-Wclazy-wrong-qevent-cast] +wrong-qevent-cast/main.cpp:17:22: warning: Cast from a QEvent::KeyPress event to QMouseEvent looks suspicious. [-Wclazy-wrong-qevent-cast] +wrong-qevent-cast/main.cpp:23:30: warning: Cast from a QEvent::KeyPress event to QMouseEvent looks suspicious. [-Wclazy-wrong-qevent-cast] |