aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cmake_has_regex_test.cpp (renamed from cmake_has_regex_test.cpp)0
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt245
-rw-r--r--Changelog25
-rw-r--r--CheckSources.cmake83
-rw-r--r--ClazySources.cmake72
-rw-r--r--README.md191
-rwxr-xr-xartwork/clazy.pngbin0 -> 15271 bytes
-rwxr-xr-xartwork/clazy.svgzbin0 -> 21936 bytes
-rwxr-xr-xartwork/ico1024.pngbin0 -> 117048 bytes
-rwxr-xr-xartwork/ico128.pngbin0 -> 5143 bytes
-rw-r--r--checks.json329
-rwxr-xr-xdev-scripts/generate.py548
-rw-r--r--dev-scripts/templates/check-readme.md1
-rw-r--r--dev-scripts/templates/check.cpp45
-rw-r--r--dev-scripts/templates/check.h40
-rw-r--r--dev-scripts/templates/test-config.json (renamed from tests/config.json)0
-rw-r--r--dev-scripts/templates/test-main.cpp6
-rwxr-xr-xdev-scripts/test_build_on_distros.sh21
-rw-r--r--docs/checks/README-assert-with-side-effects.md (renamed from src/checks/level3/README-assert-with-side-effects.md)0
-rw-r--r--docs/checks/README-auto-unexpected-qstringbuilder.md (renamed from src/checks/level1/README-auto-unexpected-qstringbuilder.md)1
-rw-r--r--docs/checks/README-base-class-event.md (renamed from src/checks/level2/README-base-class-event.md)0
-rw-r--r--docs/checks/README-child-event-qobject-cast.md (renamed from src/checks/level1/README-child-event-qobject-cast.md)0
-rw-r--r--docs/checks/README-connect-3arg-lambda.md (renamed from src/checks/level1/README-connect-3arg-lambda.md)9
-rw-r--r--docs/checks/README-connect-by-name.md10
-rw-r--r--docs/checks/README-connect-non-signal.md (renamed from src/checks/level0/README-connect-non-signal.md)0
-rw-r--r--docs/checks/README-connect-not-normalized.md (renamed from src/checks/level0/README-connect-not-normalized.md)0
-rw-r--r--docs/checks/README-const-signal-or-slot.md (renamed from src/checks/level1/README-const-signal-or-slot.md)7
-rw-r--r--docs/checks/README-container-anti-pattern.md (renamed from src/checks/level0/README-container-anti-pattern.md)0
-rw-r--r--docs/checks/README-container-inside-loop.md (renamed from src/checks/level2/README-container-inside-loop.md)0
-rw-r--r--docs/checks/README-copyable-polymorphic.md (renamed from src/checks/level2/README-copyable-polymorphic.md)0
-rw-r--r--docs/checks/README-ctor-missing-parent-argument.md (renamed from src/checks/level1/README-ctor-missing-parent-argument.md)0
-rw-r--r--docs/checks/README-detaching-member.md (renamed from src/checks/level3/README-detaching-member.md)0
-rw-r--r--docs/checks/README-detaching-temporary.md (renamed from src/checks/level1/README-detaching-temporary.md)0
-rw-r--r--docs/checks/README-empty-qstringliteral.md6
-rw-r--r--docs/checks/README-foreach.md (renamed from src/checks/level1/README-foreach.md)0
-rw-r--r--docs/checks/README-fully-qualified-moc-types.md26
-rw-r--r--docs/checks/README-function-args-by-ref.md (renamed from src/checks/level2/README-function-args-by-ref.md)0
-rw-r--r--docs/checks/README-function-args-by-value.md (renamed from src/checks/level2/README-function-args-by-value.md)0
-rw-r--r--docs/checks/README-global-const-char-pointer.md (renamed from src/checks/level2/README-global-const-char-pointer.md)0
-rw-r--r--docs/checks/README-implicit-casts.md (renamed from src/checks/level2/README-implicit-casts.md)0
-rw-r--r--docs/checks/README-incorrect-emit.md (renamed from src/checks/level1/README-incorrect-emit.md)0
-rw-r--r--docs/checks/README-inefficient-qlist-soft.md (renamed from src/checks/level1/README-inefficient-qlist-soft.md)0
-rw-r--r--docs/checks/README-inefficient-qlist.md (renamed from src/checks/hiddenlevel/README-inefficient-qlist.md)0
-rw-r--r--docs/checks/README-install-event-filter.md (renamed from src/checks/level1/README-install-event-filter.md)0
-rw-r--r--docs/checks/README-isempty-vs-count.md (renamed from src/checks/hiddenlevel/README-isempty-vs-count.md)0
-rw-r--r--docs/checks/README-lambda-in-connect.md (renamed from src/checks/level0/README-lambda-in-connect.md)0
-rw-r--r--docs/checks/README-lambda-unique-connection.md (renamed from src/checks/level0/README-lambda-unique-connection.md)0
-rw-r--r--docs/checks/README-missing-qobject-macro.md (renamed from src/checks/level2/README-missing-qobject-macro.md)0
-rw-r--r--docs/checks/README-missing-typeinfo.md (renamed from src/checks/level2/README-missing-typeinfo.md)0
-rw-r--r--docs/checks/README-mutable-container-key.md (renamed from src/checks/level0/README-mutable-container-key.md)0
-rw-r--r--docs/checks/README-non-pod-global-static.md (renamed from src/checks/level1/README-non-pod-global-static.md)0
-rw-r--r--docs/checks/README-old-style-connect.md (renamed from src/checks/level2/README-old-style-connect.md)0
-rw-r--r--docs/checks/README-overridden-signal.md (renamed from src/checks/level1/README-overridden-signal.md)2
-rw-r--r--docs/checks/README-post-event.md (renamed from src/checks/level1/README-post-event.md)0
-rw-r--r--docs/checks/README-qcolor-from-literal.md (renamed from src/checks/level0/README-qcolor-from-literal.md)0
-rw-r--r--docs/checks/README-qdatetime-utc.md (renamed from src/checks/level0/README-qdatetime-utc.md)0
-rw-r--r--docs/checks/README-qdeleteall.md (renamed from src/checks/level1/README-qdeleteall.md)0
-rw-r--r--docs/checks/README-qenums.md (renamed from src/checks/level0/README-qenums.md)0
-rw-r--r--docs/checks/README-qfileinfo-exists.md (renamed from src/checks/level0/README-qfileinfo-exists.md)0
-rw-r--r--docs/checks/README-qgetenv.md (renamed from src/checks/level0/README-qgetenv.md)0
-rw-r--r--docs/checks/README-qhash-namespace.md (renamed from src/checks/level1/README-qhash-namespace.md)0
-rw-r--r--docs/checks/README-qhash-with-char-pointer-key.md6
-rw-r--r--docs/checks/README-qlatin1string-non-ascii.md (renamed from src/checks/level1/README-qlatin1string-non-ascii.md)0
-rw-r--r--docs/checks/README-qmap-with-pointer-key.md (renamed from src/checks/level0/README-qmap-with-pointer-key.md)0
-rw-r--r--docs/checks/README-qproperty-without-notify.md7
-rw-r--r--docs/checks/README-qstring-allocations.md (renamed from src/checks/level2/README-qstring-allocations.md)0
-rw-r--r--docs/checks/README-qstring-arg.md (renamed from src/checks/level0/README-qstring-arg.md)0
-rw-r--r--docs/checks/README-qstring-insensitive-allocation.md (renamed from src/checks/level0/README-qstring-insensitive-allocation.md)0
-rw-r--r--docs/checks/README-qstring-left.md (renamed from src/checks/level1/README-qstring-left.md)0
-rw-r--r--docs/checks/README-qstring-ref.md (renamed from src/checks/level0/README-qstring-ref.md)0
-rw-r--r--docs/checks/README-qstring-varargs.md13
-rw-r--r--docs/checks/README-qt-keywords.md12
-rw-r--r--docs/checks/README-qt-macros.md (renamed from src/checks/level0/README-qt-macros.md)0
-rw-r--r--docs/checks/README-qt4-qstring-from-array.md (renamed from src/checks/hiddenlevel/README-qt4-qstring-from-array.md)0
-rw-r--r--docs/checks/README-qvariant-template-instantiation.md (renamed from src/checks/level0/README-qvariant-template-instantiation.md)0
-rw-r--r--docs/checks/README-range-loop.md (renamed from src/checks/level1/README-range-loop.md)0
-rw-r--r--docs/checks/README-raw-environment-function.md5
-rw-r--r--docs/checks/README-reserve-candidates.md (renamed from src/checks/level2/README-reserve-candidates.md)0
-rw-r--r--docs/checks/README-returning-data-from-temporary.md (renamed from src/checks/level1/README-returning-data-from-temporary.md)0
-rw-r--r--docs/checks/README-returning-void-expression.md (renamed from src/checks/level2/README-returning-void-expression.md)0
-rw-r--r--docs/checks/README-rule-of-three.md (renamed from src/checks/level2/README-rule-of-three.md)0
-rw-r--r--docs/checks/README-rule-of-two-soft.md (renamed from src/checks/level1/README-rule-of-two-soft.md)0
-rw-r--r--docs/checks/README-skipped-base-method.md18
-rw-r--r--docs/checks/README-static-pmf.md9
-rw-r--r--docs/checks/README-strict-iterators.md (renamed from src/checks/level0/README-strict-iterators.md)0
-rw-r--r--docs/checks/README-temporary-iterator.md (renamed from src/checks/level0/README-temporary-iterator.md)0
-rw-r--r--docs/checks/README-thread-with-slots.md (renamed from src/checks/level3/README-thread-with-slots.md)2
-rw-r--r--docs/checks/README-tr-non-literal.md (renamed from src/checks/hiddenlevel/README-tr-non-literal.md)0
-rw-r--r--docs/checks/README-unneeded-cast.md21
-rw-r--r--docs/checks/README-unused-non-trivial-variable.md16
-rw-r--r--docs/checks/README-virtual-call-ctor.md (renamed from src/checks/level2/README-virtual-call-ctor.md)0
-rw-r--r--docs/checks/README-virtual-signal.md (renamed from src/checks/level1/README-virtual-signal.md)0
-rw-r--r--docs/checks/README-writing-to-temporary.md (renamed from src/checks/level0/README-writing-to-temporary.md)0
-rw-r--r--docs/checks/README-wrong-qevent-cast.md11
-rw-r--r--docs/checks/README-wrong-qglobalstatic.md (renamed from src/checks/level0/README-wrong-qglobalstatic.md)0
-rw-r--r--docs/man/clazy.pod4
-rw-r--r--readmes.cmake92
-rwxr-xr-xscripts/fix_json_database.py4
-rw-r--r--src/AccessSpecifierManager.cpp50
-rw-r--r--src/AccessSpecifierManager.h8
-rw-r--r--src/Checks.h206
-rw-r--r--src/Clazy.cpp139
-rw-r--r--src/Clazy.h25
-rw-r--r--src/ClazyAnchorHeader.h4
-rw-r--r--src/ClazyContext.cpp13
-rw-r--r--src/ClazyContext.h68
-rw-r--r--src/ClazyStandaloneMain.cpp18
-rw-r--r--src/ContextUtils.cpp16
-rw-r--r--src/ContextUtils.h4
-rw-r--r--src/FixItUtils.cpp68
-rw-r--r--src/FixItUtils.h4
-rw-r--r--src/FunctionUtils.h25
-rw-r--r--src/HierarchyUtils.h32
-rw-r--r--src/LoopUtils.cpp40
-rw-r--r--src/LoopUtils.h2
-rw-r--r--src/MacroUtils.h13
-rw-r--r--src/NormalizedSignatureUtils.h2
-rw-r--r--src/PreProcessorVisitor.cpp9
-rw-r--r--src/PreProcessorVisitor.h4
-rw-r--r--src/QtUtils.cpp109
-rw-r--r--src/QtUtils.h77
-rw-r--r--src/SourceCompatibilityHelpers.h59
-rw-r--r--src/StmtBodyRange.h2
-rw-r--r--src/StringUtils.cpp12
-rw-r--r--src/StringUtils.h90
-rw-r--r--src/SuppressionManager.cpp6
-rw-r--r--src/TemplateUtils.cpp12
-rw-r--r--src/TemplateUtils.h2
-rw-r--r--src/TypeUtils.cpp14
-rw-r--r--src/TypeUtils.h16
-rw-r--r--src/Utils.cpp178
-rw-r--r--src/Utils.h26
-rw-r--r--src/checkbase.cpp59
-rw-r--r--src/checkbase.h43
-rw-r--r--src/checkmanager.cpp84
-rw-r--r--src/checkmanager.h31
-rw-r--r--src/checks/detachingbase.cpp14
-rw-r--r--src/checks/detachingbase.h10
-rw-r--r--src/checks/inefficientqlistbase.cpp14
-rw-r--r--src/checks/level0/README-unused-non-trivial-variable.md8
-rw-r--r--src/checks/level0/connect-by-name.cpp64
-rw-r--r--src/checks/level0/connect-by-name.h38
-rw-r--r--src/checks/level0/connect-non-signal.cpp19
-rw-r--r--src/checks/level0/connect-not-normalized.cpp23
-rw-r--r--src/checks/level0/container-anti-pattern.cpp24
-rw-r--r--src/checks/level0/empty-qstringliteral.cpp63
-rw-r--r--src/checks/level0/empty-qstringliteral.h38
-rw-r--r--src/checks/level0/fully-qualified-moc-types.cpp150
-rw-r--r--src/checks/level0/fully-qualified-moc-types.h44
-rw-r--r--src/checks/level0/lambda-in-connect.cpp16
-rw-r--r--src/checks/level0/lambda-unique-connection.cpp11
-rw-r--r--src/checks/level0/mutable-container-key.cpp18
-rw-r--r--src/checks/level0/qcolor-from-literal.cpp10
-rw-r--r--src/checks/level0/qdatetime-utc.cpp (renamed from src/checks/level0/qdatetimeutc.cpp)20
-rw-r--r--src/checks/level0/qdatetime-utc.h (renamed from src/checks/level0/qdatetimeutc.h)0
-rw-r--r--src/checks/level0/qenums.cpp9
-rw-r--r--src/checks/level0/qenums.h7
-rw-r--r--src/checks/level0/qfileinfo-exists.cpp15
-rw-r--r--src/checks/level0/qgetenv.cpp32
-rw-r--r--src/checks/level0/qmap-with-pointer-key.cpp5
-rw-r--r--src/checks/level0/qstring-arg.cpp (renamed from src/checks/level0/qstringarg.cpp)69
-rw-r--r--src/checks/level0/qstring-arg.h (renamed from src/checks/level0/qstringarg.h)5
-rw-r--r--src/checks/level0/qstring-insensitive-allocation.cpp12
-rw-r--r--src/checks/level0/qstring-ref.cpp (renamed from src/checks/level0/qstringref.cpp)74
-rw-r--r--src/checks/level0/qstring-ref.h (renamed from src/checks/level0/qstringref.h)0
-rw-r--r--src/checks/level0/qt-macros.cpp7
-rw-r--r--src/checks/level0/qvariant-template-instantiation.cpp32
-rw-r--r--src/checks/level0/strict-iterators.cpp31
-rw-r--r--src/checks/level0/temporary-iterator.cpp (renamed from src/checks/level0/temporaryiterator.cpp)39
-rw-r--r--src/checks/level0/temporary-iterator.h (renamed from src/checks/level0/temporaryiterator.h)2
-rw-r--r--src/checks/level0/unused-non-trivial-variable.cpp112
-rw-r--r--src/checks/level0/unused-non-trivial-variable.h3
-rw-r--r--src/checks/level0/writing-to-temporary.cpp (renamed from src/checks/level0/writingtotemporary.cpp)25
-rw-r--r--src/checks/level0/writing-to-temporary.h (renamed from src/checks/level0/writingtotemporary.h)2
-rw-r--r--src/checks/level0/wrong-qevent-cast.cpp273
-rw-r--r--src/checks/level0/wrong-qevent-cast.h39
-rw-r--r--src/checks/level0/wrong-qglobalstatic.cpp11
-rw-r--r--src/checks/level1/README-qproperty-without-notify.md3
-rw-r--r--src/checks/level1/auto-unexpected-qstringbuilder.cpp (renamed from src/checks/level1/autounexpectedqstringbuilder.cpp)61
-rw-r--r--src/checks/level1/auto-unexpected-qstringbuilder.h (renamed from src/checks/level1/autounexpectedqstringbuilder.h)1
-rw-r--r--src/checks/level1/child-event-qobject-cast.cpp21
-rw-r--r--src/checks/level1/child-event-qobject-cast.h4
-rw-r--r--src/checks/level1/connect-3arg-lambda.cpp78
-rw-r--r--src/checks/level1/connect-3arg-lambda.h6
-rw-r--r--src/checks/level1/const-signal-or-slot.cpp10
-rw-r--r--src/checks/level1/detaching-temporary.cpp (renamed from src/checks/level1/detachingtemporary.cpp)49
-rw-r--r--src/checks/level1/detaching-temporary.h (renamed from src/checks/level1/detachingtemporary.h)2
-rw-r--r--src/checks/level1/foreach.cpp40
-rw-r--r--src/checks/level1/incorrect-emit.cpp24
-rw-r--r--src/checks/level1/incorrect-emit.h2
-rw-r--r--src/checks/level1/inefficient-qlist-soft.cpp9
-rw-r--r--src/checks/level1/inefficient-qlist-soft.h1
-rw-r--r--src/checks/level1/install-event-filter.cpp8
-rw-r--r--src/checks/level1/non-pod-global-static.cpp (renamed from src/checks/level1/nonpodstatic.cpp)40
-rw-r--r--src/checks/level1/non-pod-global-static.h (renamed from src/checks/level1/nonpodstatic.h)4
-rw-r--r--src/checks/level1/overridden-signal.cpp19
-rw-r--r--src/checks/level1/post-event.cpp9
-rw-r--r--src/checks/level1/qdeleteall.cpp19
-rw-r--r--src/checks/level1/qhash-namespace.cpp23
-rw-r--r--src/checks/level1/qhash-namespace.h4
-rw-r--r--src/checks/level1/qlatin1string-non-ascii.cpp9
-rw-r--r--src/checks/level1/qproperty-without-notify.cpp9
-rw-r--r--src/checks/level1/qproperty-without-notify.h2
-rw-r--r--src/checks/level1/qstring-left.cpp9
-rw-r--r--src/checks/level1/range-loop.cpp21
-rw-r--r--src/checks/level1/returning-data-from-temporary.cpp15
-rw-r--r--src/checks/level1/rule-of-two-soft.cpp (renamed from src/checks/level1/ruleoftwosoft.cpp)9
-rw-r--r--src/checks/level1/rule-of-two-soft.h (renamed from src/checks/level1/ruleoftwosoft.h)0
-rw-r--r--src/checks/level1/skipped-base-method.cpp66
-rw-r--r--src/checks/level1/skipped-base-method.h39
-rw-r--r--src/checks/level1/virtual-signal.cpp21
-rw-r--r--src/checks/level2/base-class-event.cpp20
-rw-r--r--src/checks/level2/copyable-polymorphic.cpp23
-rw-r--r--src/checks/level2/ctor-missing-parent-argument.cpp (renamed from src/checks/level1/ctor-missing-parent-argument.cpp)16
-rw-r--r--src/checks/level2/ctor-missing-parent-argument.h (renamed from src/checks/level1/ctor-missing-parent-argument.h)0
-rw-r--r--src/checks/level2/function-args-by-ref.cpp52
-rw-r--r--src/checks/level2/function-args-by-value.cpp66
-rw-r--r--src/checks/level2/global-const-char-pointer.cpp (renamed from src/checks/level2/globalconstcharpointer.cpp)9
-rw-r--r--src/checks/level2/global-const-char-pointer.h (renamed from src/checks/level2/globalconstcharpointer.h)0
-rw-r--r--src/checks/level2/implicit-casts.cpp (renamed from src/checks/level2/implicitcasts.cpp)46
-rw-r--r--src/checks/level2/implicit-casts.h (renamed from src/checks/level2/implicitcasts.h)1
-rw-r--r--src/checks/level2/missing-qobject-macro.cpp18
-rw-r--r--src/checks/level2/missing-qobject-macro.h6
-rw-r--r--src/checks/level2/missing-typeinfo.cpp (renamed from src/checks/level2/missing-type-info.cpp)25
-rw-r--r--src/checks/level2/missing-typeinfo.h (renamed from src/checks/level2/missing-type-info.h)4
-rw-r--r--src/checks/level2/old-style-connect.cpp (renamed from src/checks/level2/oldstyleconnect.cpp)144
-rw-r--r--src/checks/level2/old-style-connect.h (renamed from src/checks/level2/oldstyleconnect.h)2
-rw-r--r--src/checks/level2/qstring-allocations.cpp166
-rw-r--r--src/checks/level2/qstring-allocations.h2
-rw-r--r--src/checks/level2/returning-void-expression.cpp10
-rw-r--r--src/checks/level2/rule-of-three.cpp (renamed from src/checks/level2/ruleofthree.cpp)35
-rw-r--r--src/checks/level2/rule-of-three.h (renamed from src/checks/level2/ruleofthree.h)0
-rw-r--r--src/checks/level2/static-pmf.cpp58
-rw-r--r--src/checks/level2/static-pmf.h39
-rw-r--r--src/checks/level2/virtual-call-ctor.cpp (renamed from src/checks/level2/virtualcallsfromctor.cpp)44
-rw-r--r--src/checks/level2/virtual-call-ctor.h (renamed from src/checks/level2/virtualcallsfromctor.h)8
-rw-r--r--src/checks/level3/README-bogus-dynamic-cast.md13
-rw-r--r--src/checks/level3/assert-with-side-effects.cpp (renamed from src/checks/level3/assertwithsideeffects.cpp)47
-rw-r--r--src/checks/level3/assert-with-side-effects.h (renamed from src/checks/level3/assertwithsideeffects.h)0
-rw-r--r--src/checks/level3/detaching-member.cpp (renamed from src/checks/level3/detachingmember.cpp)39
-rw-r--r--src/checks/level3/detaching-member.h (renamed from src/checks/level3/detachingmember.h)0
-rw-r--r--src/checks/level3/dynamic_cast.cpp66
-rw-r--r--src/checks/level3/reserve-candidates.cpp (renamed from src/checks/level2/reservecandidates.cpp)60
-rw-r--r--src/checks/level3/reserve-candidates.h (renamed from src/checks/level2/reservecandidates.h)0
-rw-r--r--src/checks/level3/thread-with-slots.cpp11
-rw-r--r--src/checks/level3/unneeded-cast.cpp117
-rw-r--r--src/checks/level3/unneeded-cast.h (renamed from src/checks/level3/dynamic_cast.h)21
-rw-r--r--src/checks/manuallevel/container-inside-loop.cpp (renamed from src/checks/level2/container-inside-loop.cpp)12
-rw-r--r--src/checks/manuallevel/container-inside-loop.h (renamed from src/checks/level2/container-inside-loop.h)0
-rw-r--r--src/checks/manuallevel/inefficient-qlist.cpp (renamed from src/checks/hiddenlevel/inefficientqlist.cpp)5
-rw-r--r--src/checks/manuallevel/inefficient-qlist.h (renamed from src/checks/hiddenlevel/inefficientqlist.h)0
-rw-r--r--src/checks/manuallevel/isempty-vs-count.cpp (renamed from src/checks/hiddenlevel/isempty-vs-count.cpp)16
-rw-r--r--src/checks/manuallevel/isempty-vs-count.h (renamed from src/checks/hiddenlevel/isempty-vs-count.h)0
-rw-r--r--src/checks/manuallevel/qhash-with-char-pointer-key.cpp56
-rw-r--r--src/checks/manuallevel/qhash-with-char-pointer-key.h39
-rw-r--r--src/checks/manuallevel/qstring-varargs.cpp61
-rw-r--r--src/checks/manuallevel/qstring-varargs.h39
-rw-r--r--src/checks/manuallevel/qt-keywords.cpp74
-rw-r--r--src/checks/manuallevel/qt-keywords.h40
-rw-r--r--src/checks/manuallevel/qt4-qstring-from-array.cpp (renamed from src/checks/hiddenlevel/qt4-qstring-from-array.cpp)77
-rw-r--r--src/checks/manuallevel/qt4-qstring-from-array.h (renamed from src/checks/hiddenlevel/qt4-qstring-from-array.h)4
-rw-r--r--src/checks/manuallevel/raw-environment-function.cpp55
-rw-r--r--src/checks/manuallevel/raw-environment-function.h38
-rw-r--r--src/checks/manuallevel/tr-non-literal.cpp (renamed from src/checks/hiddenlevel/tr-non-literal.cpp)8
-rw-r--r--src/checks/manuallevel/tr-non-literal.h (renamed from src/checks/hiddenlevel/tr-non-literal.h)0
-rw-r--r--src/checks/requiredresults.cpp88
-rw-r--r--src/checks/ruleofbase.cpp6
-rw-r--r--src/clazy_stl.h58
-rw-r--r--tests/auto-unexpected-qstringbuilder/main.cpp8
-rw-r--r--tests/auto-unexpected-qstringbuilder/main.cpp.expected1
-rw-r--r--tests/auto-unexpected-qstringbuilder/main.cpp_fixed.cpp.expected8
-rw-r--r--tests/bogus-dynamic-cast/main.cpp46
-rw-r--r--tests/bogus-dynamic-cast/main.cpp.expected6
-rw-r--r--tests/bogus-dynamic-cast/qobjectcast.cpp.expected1
-rw-r--r--tests/clazy-standalone/config.json30
-rw-r--r--tests/clazy-standalone/fileToIgnore.cpp7
-rw-r--r--tests/clazy-standalone/fileToIgnore.cpp.expected2
-rw-r--r--tests/clazy-standalone/fileToNotIgnore.cpp6
-rw-r--r--tests/clazy-standalone/fileToNotIgnore.cpp.expected1
-rw-r--r--tests/clazy-standalone/header_filter.cpp7
-rw-r--r--tests/clazy-standalone/header_filter.cpp.expected3
-rw-r--r--tests/clazy-standalone/header_filter2.cpp7
-rw-r--r--tests/clazy-standalone/header_filter2.cpp.expected2
-rw-r--r--tests/clazy-standalone/header_filter_bar.h6
-rw-r--r--tests/clazy-standalone/header_filter_foo.h6
-rw-r--r--tests/clazy/test_requested_checks.sh.expected43
-rw-r--r--tests/connect-3arg-lambda/main.cpp23
-rw-r--r--tests/connect-3arg-lambda/main.cpp.expected3
-rw-r--r--tests/connect-by-name/config.json7
-rw-r--r--tests/connect-by-name/main.cpp25
-rw-r--r--tests/connect-by-name/main.cpp.expected1
-rw-r--r--tests/connect-non-signal/392441.cpp7
-rw-r--r--tests/connect-non-signal/392441.cpp.expected0
-rw-r--r--tests/connect-non-signal/config.json4
-rw-r--r--tests/connect-non-signal/main.cpp2
-rw-r--r--tests/copyable-polymorphic/main.cpp.expected6
-rw-r--r--tests/detaching-member/main.cpp12
-rwxr-xr-xtests/docker/build-clazy.sh26
-rw-r--r--tests/docker/conf.json35
-rwxr-xr-xtests/docker/test_docker.py79
-rw-r--r--tests/empty-qstringliteral/config.json7
-rw-r--r--tests/empty-qstringliteral/main.cpp11
-rw-r--r--tests/empty-qstringliteral/main.cpp.expected3
-rw-r--r--tests/fully-qualified-moc-types/config.json7
-rw-r--r--tests/fully-qualified-moc-types/main.cpp62
-rw-r--r--tests/fully-qualified-moc-types/main.cpp.expected4
-rw-r--r--tests/fully-qualified-moc-types/main.moc_472
-rw-r--r--tests/function-args-by-ref/config.json4
-rw-r--r--tests/function-args-by-ref/main.cpp25
-rw-r--r--tests/function-args-by-ref/main.cpp.expected5
-rw-r--r--tests/function-args-by-ref/warn-for-overridden-methods.cpp22
-rw-r--r--tests/function-args-by-ref/warn-for-overridden-methods.cpp.expected7
-rw-r--r--tests/function-args-by-value/config.json9
-rw-r--r--tests/function-args-by-value/main.cpp17
-rw-r--r--tests/function-args-by-value/main.cpp.expected5
-rw-r--r--tests/function-args-by-value/main.cpp_fixed.cpp.expected17
-rw-r--r--tests/function-args-by-value/warn-for-overridden-methods.cpp16
-rw-r--r--tests/function-args-by-value/warn-for-overridden-methods.cpp.expected7
-rw-r--r--tests/function-args-by-value/warn-for-overridden-methods.cpp_fixed.cpp.expected16
-rw-r--r--tests/old-style-connect/main.cpp21
-rw-r--r--tests/old-style-connect/main.cpp.expected5
-rw-r--r--tests/old-style-connect/main.cpp_fixed.cpp.expected21
-rw-r--r--tests/old-style-connect/not-in-hierarchy.cpp22
-rw-r--r--tests/qhash-with-char-pointer-key/config.json7
-rw-r--r--tests/qhash-with-char-pointer-key/main.cpp12
-rw-r--r--tests/qhash-with-char-pointer-key/main.cpp.expected1
-rw-r--r--tests/qlatin1string-non-ascii/main.cpp.expected2
-rw-r--r--tests/qstring-arg/main.cpp7
-rw-r--r--tests/qstring-varargs/config.json8
-rw-r--r--tests/qstring-varargs/main.cpp18
-rw-r--r--tests/qstring-varargs/main.cpp.expected3
-rw-r--r--tests/qt-keywords/config.json14
-rw-r--r--tests/qt-keywords/main.cpp25
-rw-r--r--tests/qt-keywords/main.cpp.expected4
-rw-r--r--tests/qt-keywords/main.cpp_fixed.cpp.expected25
-rw-r--r--tests/qt-keywords/no_keywords.cpp8
-rw-r--r--tests/qt-keywords/no_keywords.cpp.expected0
-rw-r--r--tests/raw-environment-function/config.json7
-rw-r--r--tests/raw-environment-function/main.cpp16
-rw-r--r--tests/raw-environment-function/main.cpp.expected3
-rw-r--r--tests/returning-data-from-temporary/config.json3
-rw-r--r--tests/returning-data-from-temporary/main.cpp1
-rw-r--r--tests/returning-data-from-temporary/main.cpp.expected3
-rwxr-xr-xtests/run_tests.py62
-rw-r--r--tests/skipped-base-method/config.json7
-rw-r--r--tests/skipped-base-method/main.cpp27
-rw-r--r--tests/skipped-base-method/main.cpp.expected1
-rw-r--r--tests/static-pmf/config.json7
-rw-r--r--tests/static-pmf/main.cpp12
-rw-r--r--tests/static-pmf/main.cpp.expected1
-rw-r--r--tests/unneeded-cast/config.json (renamed from tests/bogus-dynamic-cast/config.json)4
-rw-r--r--tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp (renamed from tests/bogus-dynamic-cast/qobjectcast.cpp)2
-rw-r--r--tests/unneeded-cast/dynamic_cast_over_qobjectcast.cpp.expected0
-rw-r--r--tests/unneeded-cast/main.cpp97
-rw-r--r--tests/unneeded-cast/main.cpp.expected15
-rw-r--r--tests/unused-non-trivial-variable/config.json9
-rw-r--r--tests/unused-non-trivial-variable/main.cpp13
-rw-r--r--tests/unused-non-trivial-variable/main.cpp.expected1
-rw-r--r--tests/unused-non-trivial-variable/no-whitelist.cpp73
-rw-r--r--tests/unused-non-trivial-variable/no-whitelist.cpp.expected7
-rw-r--r--tests/virtual-signal/config.json3
-rw-r--r--tests/virtual-signal/non-qobject-base.cpp19
-rw-r--r--tests/virtual-signal/non-qobject-base.cpp.expected0
-rw-r--r--tests/writing-to-temporary/main.cpp2
-rw-r--r--tests/wrong-qevent-cast/config.json7
-rw-r--r--tests/wrong-qevent-cast/main.cpp41
-rw-r--r--tests/wrong-qevent-cast/main.cpp.expected3
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
diff --git a/.gitignore b/.gitignore
index 1c932810..d59b1b7a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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()
diff --git a/Changelog b/Changelog
index a2a1290d..e881c82b 100644
--- a/Changelog
+++ b/Changelog
@@ -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}
diff --git a/README.md b/README.md
index b9d55a26..232d629e 100644
--- a/README.md
+++ b/README.md
@@ -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
new file mode 100755
index 00000000..cd473b96
--- /dev/null
+++ b/artwork/clazy.png
Binary files differ
diff --git a/artwork/clazy.svgz b/artwork/clazy.svgz
new file mode 100755
index 00000000..32ed0df5
--- /dev/null
+++ b/artwork/clazy.svgz
Binary files differ
diff --git a/artwork/ico1024.png b/artwork/ico1024.png
new file mode 100755
index 00000000..e55bdf41
--- /dev/null
+++ b/artwork/ico1024.png
Binary files differ
diff --git a/artwork/ico128.png b/artwork/ico128.png
new file mode 100755
index 00000000..ecff717e
--- /dev/null
+++ b/artwork/ico128.png
Binary files differ
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> &regex, 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 &macroName)
+inline bool isPredefined(const clang::PreprocessorOptions &ppOpts, const llvm::StringRef &macroName)
{
const auto &macros = 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 &macroName)
+inline bool isInMacro(const clang::ASTContext *context, clang::SourceLocation loc, const llvm::StringRef &macroName)
{
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> &macroNames)
+inline bool isInAnyMacro(const clang::ASTContext *context, clang::SourceLocation loc, const std::vector<llvm::StringRef> &macroNames)
{
- return clazy_std::any_of(macroNames, [context, loc](const std::string &macroName) {
+ return clazy::any_of(macroNames, [context, loc](const llvm::StringRef &macroName) {
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 &paramType, bool &ok, int &numCtors)
+bool clazy::recordHasCtorWithParam(clang::CXXRecordDecl *record, const std::string &paramType, 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 &macroNameTok, const MacroDefinition &,
+void ClazyPreprocessorCallbacks::MacroExpands(const Token &macroNameTok, const MacroDefinition &md,
SourceRange range, const MacroArgs *)
{
- check->VisitMacroExpands(macroNameTok, range);
+ check->VisitMacroExpands(macroNameTok, range, md.getMacroInfo());
}
void ClazyPreprocessorCallbacks::Defined(const Token &macroNameTok, const MacroDefinition &, SourceRange range)
@@ -65,14 +65,14 @@ void ClazyPreprocessorCallbacks::MacroDefined(const Token &macroNameTok, 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 &macroNameTok, const clang::SourceRange &);
+protected:
+ virtual void VisitMacroExpands(const clang::Token &macroNameTok, const clang::SourceRange &, const clang::MacroInfo *minfo = nullptr);
virtual void VisitMacroDefined(const clang::Token &macroNameTok);
virtual void VisitDefined(const clang::Token &macroNameTok, const clang::SourceRange &);
virtual void VisitIfdef(clang::SourceLocation, const clang::Token &macroNameTok);
@@ -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 &macroNameTok, 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 &macroNameTok)
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 &macroNameTok, const SourceRange &range)
+void OldStyleConnect::VisitMacroExpands(const Token &macroNameTok, 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 &macroName) 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 &macroNameTok, const clang::SourceRange &) override;
+ void VisitMacroExpands(const clang::Token &macroNameTok, 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 &macroNameTok, 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]