diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp --- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp @@ -21,8 +21,9 @@ #include "NoInternalDependenciesCheck.h" #include "NoNamespaceCheck.h" #include "RedundantStrcatCallsCheck.h" -#include "StringFindStartswithCheck.h" #include "StrCatAppendCheck.h" +#include "StringFindStartswithCheck.h" +#include "StringFindStrContainsCheck.h" #include "TimeComparisonCheck.h" #include "TimeSubtractionCheck.h" #include "UpgradeDurationConversionsCheck.h" @@ -61,6 +62,8 @@ "abseil-str-cat-append"); CheckFactories.registerCheck( "abseil-string-find-startswith"); + CheckFactories.registerCheck( + "abseil-string-find-str-contains"); CheckFactories.registerCheck( "abseil-time-comparison"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -20,6 +20,7 @@ RedundantStrcatCallsCheck.cpp StrCatAppendCheck.cpp StringFindStartswithCheck.cpp + StringFindStrContainsCheck.cpp TimeComparisonCheck.cpp TimeSubtractionCheck.cpp UpgradeDurationConversionsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.h b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.h @@ -0,0 +1,38 @@ +//===--- StringFindStrContainsCheck.h - clang-tidy---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTRCONTAINSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTRCONTAINSCHECK_H + +#include "../ClangTidy.h" +#include "../utils/IncludeInserter.h" +#include "../utils/TransformerClangTidyCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include +#include +#include + +namespace clang { +namespace tidy { +namespace abseil { + +/// Find string.find(...) == npos comparisons and suggest replacing with +/// absl::StrContains. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-string-find-str-contains.html +class StringFindStrContainsCheck : public utils::TransformerClangTidyCheck { +public: + StringFindStrContainsCheck(StringRef Name, ClangTidyContext *Context); +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRINGFINDSTRCONTAINSCHECK_H diff --git a/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp @@ -0,0 +1,95 @@ +//===--- StringFindStrContainsCheck.cc - clang-tidy---------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "StringFindStrContainsCheck.h" + +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Tooling/Transformer/RewriteRule.h" +#include "clang/Tooling/Transformer/Stencil.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +using ::clang::transformer::applyFirst; +using ::clang::transformer::cat; +using ::clang::transformer::change; +using ::clang::transformer::makeRule; +using ::clang::transformer::node; + +namespace { + +llvm::Optional +MakeRule(const LangOptions &LangOpts, + const ClangTidyCheck::OptionsView &Options) { + // Only support C++. + if (!LangOpts.CPlusPlus) + return llvm::None; + + // Parse options. + const std::vector StringLikeClassNames = + utils::options::parseStringList(Options.get("StringLikeClasses", + "::std::basic_string;" + "::std::basic_string_view;" + "::absl::string_view")); + const std::string AbseilStringsMatchHeaderOption( + Options.get("AbseilStringsMatchHeader", "absl/strings/match.h")); + + auto StringLikeClass = cxxRecordDecl(hasAnyName(SmallVector( + StringLikeClassNames.begin(), StringLikeClassNames.end()))); + auto StringType = + hasUnqualifiedDesugaredType(recordType(hasDeclaration(StringLikeClass))); + auto CharStarType = + hasUnqualifiedDesugaredType(pointerType(pointee(isAnyCharacter()))); + auto StringNpos = declRefExpr( + to(varDecl(hasName("npos"), hasDeclContext(StringLikeClass)))); + auto StringFind = cxxMemberCallExpr( + callee(cxxMethodDecl( + hasName("find"), + hasParameter(0, parmVarDecl(anyOf(hasType(StringType), + hasType(CharStarType)))))), + on(hasType(StringType)), hasArgument(0, expr().bind("parameter_to_find")), + anyOf(hasArgument(1, integerLiteral(equals(0))), + hasArgument(1, cxxDefaultArgExpr())), + onImplicitObjectArgument(expr().bind("string_being_searched"))); + + tooling::RewriteRule rule = applyFirst( + {makeRule( + binaryOperator(hasOperatorName("=="), + hasEitherOperand(ignoringParenImpCasts(StringNpos)), + hasEitherOperand(ignoringParenImpCasts(StringFind))), + change(cat("!absl::StrContains(", node("string_being_searched"), + ", ", node("parameter_to_find"), ")")), + cat("use !absl::StrContains instead of find() == npos")), + makeRule( + binaryOperator(hasOperatorName("!="), + hasEitherOperand(ignoringParenImpCasts(StringNpos)), + hasEitherOperand(ignoringParenImpCasts(StringFind))), + change(cat("absl::StrContains(", node("string_being_searched"), ", ", + node("parameter_to_find"), ")")), + cat("use absl::StrContains instead of find() != npos"))}); + addInclude(rule, AbseilStringsMatchHeaderOption); + return rule; +} + +} // namespace + +StringFindStrContainsCheck::StringFindStrContainsCheck( + StringRef Name, ClangTidyContext *Context) + : TransformerClangTidyCheck(&MakeRule, Name, Context) {} + +} // namespace abseil +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -75,6 +75,12 @@ New checks ^^^^^^^^^^ +- New :doc:`abseil-string-find-str-contains + ` check. + + Finds string.find(...) == npos comparisons and suggests replacing with + absl::StrContains. + - New :doc:`cppcoreguidelines-avoid-non-const-global-variables ` check. Finds non-const global variables as described in check I.2 of C++ Core diff --git a/clang-tools-extra/docs/clang-tidy/checks/abseil-string-find-str-contains.rst b/clang-tools-extra/docs/clang-tidy/checks/abseil-string-find-str-contains.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/abseil-string-find-str-contains.rst @@ -0,0 +1,51 @@ +.. title:: clang-tidy - abseil-string-find-str-contains + +abseil-string-find-str-contains +============================= + +Checks whether ``find()`` on a string-like type is compared with ``npos`` on +a string-like type, and suggests replacing with ``absl::StrContains()``. This +improves readability and reduces the likelihood of accidentally mixing +``find()`` and ``npos`` from different string-like types. + +By default, "string-like types" includes ``::std::basic_string``, +``::std::basic_string_view``, and ``::absl::string_view``. See the +StringLikeClasses option to change this. + +.. code-block:: c++ + + std::string s = "..."; + if (s.find("Hello World") == std::string::npos) { /* do something */ } + + absl::string_view a = "..."; + if (absl::string_view::npos != a.find("Hello World")) { /* do something */ } + +becomes + +.. code-block:: c++ + + string s = "..."; + if (!absl::StrContains(s, "Hello World")) { /* do something */ } + + absl::string_view a = "..."; + if (absl::StrContains(a, "Hello World")) { /* do something */ } + + +Options +------- + +.. option:: StringLikeClasses + + Semicolon-separated list of names of string-like classes. By default includes + ``::std::basic_string``, ``::std::basic_string_view``, and + ``::absl::string_view``. + +.. option:: IncludeStyle + + A string specifying which include-style is used, `llvm` or `google`. Default + is `llvm`. + +.. option:: AbseilStringsMatchHeader + + The location of Abseil's ``strings/match.h``. Defaults to + ``absl/strings/match.h``. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -12,27 +12,28 @@ .. csv-table:: :header: "Name", "Offers fixes" - `abseil-duration-addition `_, "Yes" - `abseil-duration-comparison `_, "Yes" - `abseil-duration-conversion-cast `_, "Yes" - `abseil-duration-division `_, "Yes" - `abseil-duration-factory-float `_, "Yes" - `abseil-duration-factory-scale `_, "Yes" - `abseil-duration-subtraction `_, "Yes" - `abseil-duration-unnecessary-conversion `_, "Yes" - `abseil-faster-strsplit-delimiter `_, "Yes" + `abseil-duration-addition `_, + `abseil-duration-comparison `_, + `abseil-duration-conversion-cast `_, + `abseil-duration-division `_, + `abseil-duration-factory-float `_, + `abseil-duration-factory-scale `_, + `abseil-duration-subtraction `_, + `abseil-duration-unnecessary-conversion `_, + `abseil-faster-strsplit-delimiter `_, `abseil-no-internal-dependencies `_, `abseil-no-namespace `_, - `abseil-redundant-strcat-calls `_, "Yes" - `abseil-str-cat-append `_, "Yes" - `abseil-string-find-startswith `_, "Yes" - `abseil-time-comparison `_, "Yes" - `abseil-time-subtraction `_, "Yes" - `abseil-upgrade-duration-conversions `_, "Yes" - `android-cloexec-accept `_, "Yes" + `abseil-redundant-strcat-calls `_, + `abseil-str-cat-append `_, + `abseil-string-find-startswith `_, + `abseil-string-find-str-contains `_, + `abseil-time-comparison `_, + `abseil-time-subtraction `_, + `abseil-upgrade-duration-conversions `_, + `android-cloexec-accept `_, `android-cloexec-accept4 `_, - `android-cloexec-creat `_, "Yes" - `android-cloexec-dup `_, "Yes" + `android-cloexec-creat `_, + `android-cloexec-dup `_, `android-cloexec-epoll-create `_, `android-cloexec-epoll-create1 `_, `android-cloexec-fopen `_, @@ -40,63 +41,63 @@ `android-cloexec-inotify-init1 `_, `android-cloexec-memfd-create `_, `android-cloexec-open `_, - `android-cloexec-pipe `_, "Yes" + `android-cloexec-pipe `_, `android-cloexec-pipe2 `_, `android-cloexec-socket `_, `android-comparison-in-temp-failure-retry `_, - `boost-use-to-string `_, "Yes" - `bugprone-argument-comment `_, "Yes" + `boost-use-to-string `_, + `bugprone-argument-comment `_, `bugprone-assert-side-effect `_, `bugprone-bad-signal-to-kill-thread `_, - `bugprone-bool-pointer-implicit-conversion `_, "Yes" + `bugprone-bool-pointer-implicit-conversion `_, `bugprone-branch-clone `_, - `bugprone-copy-constructor-init `_, "Yes" + `bugprone-copy-constructor-init `_, `bugprone-dangling-handle `_, `bugprone-dynamic-static-initializers `_, `bugprone-exception-escape `_, `bugprone-fold-init-type `_, `bugprone-forward-declaration-namespace `_, `bugprone-forwarding-reference-overload `_, - `bugprone-inaccurate-erase `_, "Yes" + `bugprone-inaccurate-erase `_, `bugprone-incorrect-roundings `_, `bugprone-infinite-loop `_, `bugprone-integer-division `_, `bugprone-lambda-function-name `_, - `bugprone-macro-parentheses `_, "Yes" + `bugprone-macro-parentheses `_, `bugprone-macro-repeated-side-effects `_, - `bugprone-misplaced-operator-in-strlen-in-alloc `_, "Yes" - `bugprone-misplaced-pointer-arithmetic-in-alloc `_, "Yes" + `bugprone-misplaced-operator-in-strlen-in-alloc `_, + `bugprone-misplaced-pointer-arithmetic-in-alloc `_, `bugprone-misplaced-widening-cast `_, - `bugprone-move-forwarding-reference `_, "Yes" + `bugprone-move-forwarding-reference `_, `bugprone-multiple-statement-macro `_, - `bugprone-not-null-terminated-result `_, "Yes" - `bugprone-parent-virtual-call `_, "Yes" - `bugprone-posix-return `_, "Yes" - `bugprone-reserved-identifier `_, "Yes" + `bugprone-not-null-terminated-result `_, + `bugprone-parent-virtual-call `_, + `bugprone-posix-return `_, + `bugprone-reserved-identifier `_, `bugprone-signed-char-misuse `_, `bugprone-sizeof-container `_, `bugprone-sizeof-expression `_, `bugprone-spuriously-wake-up-functions `_, - `bugprone-string-constructor `_, "Yes" - `bugprone-string-integer-assignment `_, "Yes" + `bugprone-string-constructor `_, + `bugprone-string-integer-assignment `_, `bugprone-string-literal-with-embedded-nul `_, `bugprone-suspicious-enum-usage `_, `bugprone-suspicious-include `_, - `bugprone-suspicious-memset-usage `_, "Yes" + `bugprone-suspicious-memset-usage `_, `bugprone-suspicious-missing-comma `_, - `bugprone-suspicious-semicolon `_, "Yes" - `bugprone-suspicious-string-compare `_, "Yes" - `bugprone-swapped-arguments `_, "Yes" - `bugprone-terminating-continue `_, "Yes" + `bugprone-suspicious-semicolon `_, + `bugprone-suspicious-string-compare `_, + `bugprone-swapped-arguments `_, + `bugprone-terminating-continue `_, `bugprone-throw-keyword-missing `_, `bugprone-too-small-loop-variable `_, `bugprone-undefined-memory-manipulation `_, `bugprone-undelegated-constructor `_, `bugprone-unhandled-self-assignment `_, - `bugprone-unused-raii `_, "Yes" + `bugprone-unused-raii `_, `bugprone-unused-return-value `_, `bugprone-use-after-move `_, - `bugprone-virtual-near-miss `_, "Yes" + `bugprone-virtual-near-miss `_, `cert-dcl21-cpp `_, `cert-dcl50-cpp `_, `cert-dcl58-cpp `_, @@ -132,28 +133,29 @@ `clang-analyzer-valist.Uninitialized `_, `clang-analyzer-valist.Unterminated `_, `cppcoreguidelines-avoid-goto `_, - `cppcoreguidelines-init-variables `_, "Yes" + `cppcoreguidelines-avoid-non-const-global-variables `_, + `cppcoreguidelines-init-variables `_, `cppcoreguidelines-interfaces-global-init `_, `cppcoreguidelines-macro-usage `_, `cppcoreguidelines-narrowing-conversions `_, `cppcoreguidelines-no-malloc `_, `cppcoreguidelines-owning-memory `_, `cppcoreguidelines-pro-bounds-array-to-pointer-decay `_, - `cppcoreguidelines-pro-bounds-constant-array-index `_, "Yes" + `cppcoreguidelines-pro-bounds-constant-array-index `_, `cppcoreguidelines-pro-bounds-pointer-arithmetic `_, `cppcoreguidelines-pro-type-const-cast `_, - `cppcoreguidelines-pro-type-cstyle-cast `_, "Yes" - `cppcoreguidelines-pro-type-member-init `_, "Yes" + `cppcoreguidelines-pro-type-cstyle-cast `_, + `cppcoreguidelines-pro-type-member-init `_, `cppcoreguidelines-pro-type-reinterpret-cast `_, - `cppcoreguidelines-pro-type-static-cast-downcast `_, "Yes" + `cppcoreguidelines-pro-type-static-cast-downcast `_, `cppcoreguidelines-pro-type-union-access `_, `cppcoreguidelines-pro-type-vararg `_, `cppcoreguidelines-slicing `_, `cppcoreguidelines-special-member-functions `_, `darwin-avoid-spinlock `_, - `darwin-dispatch-once-nonstatic `_, "Yes" + `darwin-dispatch-once-nonstatic `_, `fuchsia-default-arguments-calls `_, - `fuchsia-default-arguments-declarations `_, "Yes" + `fuchsia-default-arguments-declarations `_, `fuchsia-multiple-inheritance `_, `fuchsia-overloaded-operator `_, `fuchsia-statically-constructed-objects `_, @@ -163,7 +165,7 @@ `google-build-namespaces `_, `google-build-using-namespace `_, `google-default-arguments `_, - `google-explicit-constructor `_, "Yes" + `google-explicit-constructor `_, `google-global-names-in-headers `_, `google-objc-avoid-nsobject-new `_, `google-objc-avoid-throwing-exception `_, @@ -175,7 +177,7 @@ `google-runtime-int `_, `google-runtime-operator `_, `google-runtime-references `_, - `google-upgrade-googletest-case `_, "Yes" + `google-upgrade-googletest-case `_, `hicpp-avoid-goto `_, `hicpp-exception-baseclass `_, `hicpp-multiway-paths-covered `_, @@ -183,121 +185,121 @@ `hicpp-signed-bitwise `_, `linuxkernel-must-use-errs `_, `llvm-header-guard `_, - `llvm-include-order `_, "Yes" + `llvm-include-order `_, `llvm-namespace-comment `_, - `llvm-prefer-isa-or-dyn-cast-in-conditionals `_, "Yes" - `llvm-prefer-register-over-unsigned `_, "Yes" - `llvm-twine-local `_, "Yes" - `llvmlibc-callee-namespace `_, + `llvm-prefer-isa-or-dyn-cast-in-conditionals `_, + `llvm-prefer-register-over-unsigned `_, + `llvm-twine-local `_, + `llvmlibc-callee-namespace `_, `llvmlibc-implementation-in-namespace `_, - `llvmlibc-restrict-system-libc-headers `_, "Yes" - `misc-definitions-in-headers `_, "Yes" + `llvmlibc-restrict-system-libc-headers `_, + `misc-definitions-in-headers `_, `misc-misplaced-const `_, `misc-new-delete-overloads `_, `misc-no-recursion `_, `misc-non-copyable-objects `_, `misc-non-private-member-variables-in-classes `_, - `misc-redundant-expression `_, "Yes" - `misc-static-assert `_, "Yes" + `misc-redundant-expression `_, + `misc-static-assert `_, `misc-throw-by-value-catch-by-reference `_, `misc-unconventional-assign-operator `_, - `misc-uniqueptr-reset-release `_, "Yes" - `misc-unused-alias-decls `_, "Yes" - `misc-unused-parameters `_, "Yes" - `misc-unused-using-decls `_, "Yes" - `modernize-avoid-bind `_, "Yes" + `misc-uniqueptr-reset-release `_, + `misc-unused-alias-decls `_, + `misc-unused-parameters `_, + `misc-unused-using-decls `_, + `modernize-avoid-bind `_, `modernize-avoid-c-arrays `_, - `modernize-concat-nested-namespaces `_, "Yes" - `modernize-deprecated-headers `_, "Yes" - `modernize-deprecated-ios-base-aliases `_, "Yes" - `modernize-loop-convert `_, "Yes" - `modernize-make-shared `_, "Yes" - `modernize-make-unique `_, "Yes" - `modernize-pass-by-value `_, "Yes" - `modernize-raw-string-literal `_, "Yes" - `modernize-redundant-void-arg `_, "Yes" - `modernize-replace-auto-ptr `_, "Yes" - `modernize-replace-random-shuffle `_, "Yes" - `modernize-return-braced-init-list `_, "Yes" - `modernize-shrink-to-fit `_, "Yes" - `modernize-unary-static-assert `_, "Yes" - `modernize-use-auto `_, "Yes" - `modernize-use-bool-literals `_, "Yes" - `modernize-use-default-member-init `_, "Yes" - `modernize-use-emplace `_, "Yes" - `modernize-use-equals-default `_, "Yes" - `modernize-use-equals-delete `_, "Yes" - `modernize-use-nodiscard `_, "Yes" - `modernize-use-noexcept `_, "Yes" - `modernize-use-nullptr `_, "Yes" - `modernize-use-override `_, "Yes" - `modernize-use-trailing-return-type `_, "Yes" - `modernize-use-transparent-functors `_, "Yes" - `modernize-use-uncaught-exceptions `_, "Yes" - `modernize-use-using `_, "Yes" - `mpi-buffer-deref `_, "Yes" - `mpi-type-mismatch `_, "Yes" + `modernize-concat-nested-namespaces `_, + `modernize-deprecated-headers `_, + `modernize-deprecated-ios-base-aliases `_, + `modernize-loop-convert `_, + `modernize-make-shared `_, + `modernize-make-unique `_, + `modernize-pass-by-value `_, + `modernize-raw-string-literal `_, + `modernize-redundant-void-arg `_, + `modernize-replace-auto-ptr `_, + `modernize-replace-random-shuffle `_, + `modernize-return-braced-init-list `_, + `modernize-shrink-to-fit `_, + `modernize-unary-static-assert `_, + `modernize-use-auto `_, + `modernize-use-bool-literals `_, + `modernize-use-default-member-init `_, + `modernize-use-emplace `_, + `modernize-use-equals-default `_, + `modernize-use-equals-delete `_, + `modernize-use-nodiscard `_, + `modernize-use-noexcept `_, + `modernize-use-nullptr `_, + `modernize-use-override `_, + `modernize-use-trailing-return-type `_, + `modernize-use-transparent-functors `_, + `modernize-use-uncaught-exceptions `_, + `modernize-use-using `_, + `mpi-buffer-deref `_, + `mpi-type-mismatch `_, `objc-avoid-nserror-init `_, `objc-dealloc-in-category `_, `objc-forbidden-subclassing `_, `objc-missing-hash `_, - `objc-nsinvocation-argument-lifetime `_, "Yes" - `objc-property-declaration `_, "Yes" - `objc-super-self `_, "Yes" + `objc-nsinvocation-argument-lifetime `_, + `objc-property-declaration `_, + `objc-super-self `_, `openmp-exception-escape `_, `openmp-use-default-none `_, - `performance-faster-string-find `_, "Yes" - `performance-for-range-copy `_, "Yes" + `performance-faster-string-find `_, + `performance-for-range-copy `_, `performance-implicit-conversion-in-loop `_, - `performance-inefficient-algorithm `_, "Yes" + `performance-inefficient-algorithm `_, `performance-inefficient-string-concatenation `_, - `performance-inefficient-vector-operation `_, "Yes" - `performance-move-const-arg `_, "Yes" - `performance-move-constructor-init `_, "Yes" + `performance-inefficient-vector-operation `_, + `performance-move-const-arg `_, + `performance-move-constructor-init `_, `performance-no-automatic-move `_, - `performance-noexcept-move-constructor `_, "Yes" - `performance-trivially-destructible `_, "Yes" - `performance-type-promotion-in-math-fn `_, "Yes" + `performance-noexcept-move-constructor `_, + `performance-trivially-destructible `_, + `performance-type-promotion-in-math-fn `_, `performance-unnecessary-copy-initialization `_, - `performance-unnecessary-value-param `_, "Yes" - `portability-restrict-system-includes `_, "Yes" + `performance-unnecessary-value-param `_, + `portability-restrict-system-includes `_, `portability-simd-intrinsics `_, `readability-avoid-const-params-in-decls `_, - `readability-braces-around-statements `_, "Yes" - `readability-const-return-type `_, "Yes" - `readability-container-size-empty `_, "Yes" + `readability-braces-around-statements `_, + `readability-const-return-type `_, + `readability-container-size-empty `_, `readability-convert-member-functions-to-static `_, - `readability-delete-null-pointer `_, "Yes" + `readability-delete-null-pointer `_, `readability-deleted-default `_, - `readability-else-after-return `_, "Yes" + `readability-else-after-return `_, `readability-function-size `_, - `readability-identifier-naming `_, "Yes" - `readability-implicit-bool-conversion `_, "Yes" - `readability-inconsistent-declaration-parameter-name `_, "Yes" - `readability-isolate-declaration `_, "Yes" + `readability-identifier-naming `_, + `readability-implicit-bool-conversion `_, + `readability-inconsistent-declaration-parameter-name `_, + `readability-isolate-declaration `_, `readability-magic-numbers `_, - `readability-make-member-function-const `_, "Yes" + `readability-make-member-function-const `_, `readability-misleading-indentation `_, - `readability-misplaced-array-index `_, "Yes" - `readability-named-parameter `_, "Yes" - `readability-non-const-parameter `_, "Yes" - `readability-qualified-auto `_, "Yes" - `readability-redundant-access-specifiers `_, "Yes" - `readability-redundant-control-flow `_, "Yes" - `readability-redundant-declaration `_, "Yes" - `readability-redundant-function-ptr-dereference `_, "Yes" - `readability-redundant-member-init `_, "Yes" + `readability-misplaced-array-index `_, + `readability-named-parameter `_, + `readability-non-const-parameter `_, + `readability-qualified-auto `_, + `readability-redundant-access-specifiers `_, + `readability-redundant-control-flow `_, + `readability-redundant-declaration `_, + `readability-redundant-function-ptr-dereference `_, + `readability-redundant-member-init `_, `readability-redundant-preprocessor `_, - `readability-redundant-smartptr-get `_, "Yes" + `readability-redundant-smartptr-get `_, `readability-redundant-string-cstr `_, - `readability-redundant-string-init `_, "Yes" - `readability-simplify-boolean-expr `_, "Yes" - `readability-simplify-subscript-expr `_, "Yes" - `readability-static-accessed-through-instance `_, "Yes" - `readability-static-definition-in-anonymous-namespace `_, "Yes" - `readability-string-compare `_, "Yes" - `readability-uniqueptr-delete-release `_, "Yes" - `readability-uppercase-literal-suffix `_, "Yes" + `readability-redundant-string-init `_, + `readability-simplify-boolean-expr `_, + `readability-simplify-subscript-expr `_, + `readability-static-accessed-through-instance `_, + `readability-static-definition-in-anonymous-namespace `_, + `readability-string-compare `_, + `readability-uniqueptr-delete-release `_, + `readability-uppercase-literal-suffix `_, `zircon-temporary-objects `_, @@ -306,10 +308,10 @@ `cert-con36-c `_, `bugprone-spuriously-wake-up-functions `_, `cert-con54-cpp `_, `bugprone-spuriously-wake-up-functions `_, - `cert-dcl03-c `_, `misc-static-assert `_, "Yes" - `cert-dcl16-c `_, `readability-uppercase-literal-suffix `_, "Yes" - `cert-dcl37-c `_, `bugprone-reserved-identifier `_, "Yes" - `cert-dcl51-cpp `_, `bugprone-reserved-identifier `_, "Yes" + `cert-dcl03-c `_, `misc-static-assert `_, + `cert-dcl16-c `_, `readability-uppercase-literal-suffix `_, + `cert-dcl37-c `_, `bugprone-reserved-identifier `_, + `cert-dcl51-cpp `_, `bugprone-reserved-identifier `_, `cert-dcl54-cpp `_, `misc-new-delete-overloads `_, `cert-dcl59-cpp `_, `google-build-namespaces `_, `cert-err09-cpp `_, `misc-throw-by-value-catch-by-reference `_, @@ -317,7 +319,7 @@ `cert-fio38-c `_, `misc-non-copyable-objects `_, `cert-msc30-c `_, `cert-msc50-cpp `_, `cert-msc32-c `_, `cert-msc51-cpp `_, - `cert-oop11-cpp `_, `performance-move-constructor-init `_, "Yes" + `cert-oop11-cpp `_, `performance-move-constructor-init `_, `cert-oop54-cpp `_, `bugprone-unhandled-self-assignment `_, `cert-pos44-c `_, `bugprone-bad-signal-to-kill-thread `_, `cert-str34-c `_, `bugprone-signed-char-misuse `_, @@ -387,38 +389,36 @@ `clang-analyzer-unix.cstring.NullArg `_, `Clang Static Analyzer `_, `cppcoreguidelines-avoid-c-arrays `_, `modernize-avoid-c-arrays `_, `cppcoreguidelines-avoid-magic-numbers `_, `readability-magic-numbers `_, - `cppcoreguidelines-avoid-non-const-global-variables `_, , , "" `cppcoreguidelines-c-copy-assignment-signature `_, `misc-unconventional-assign-operator `_, - `cppcoreguidelines-explicit-virtual-functions `_, `modernize-use-override `_, "Yes" + `cppcoreguidelines-explicit-virtual-functions `_, `modernize-use-override `_, `cppcoreguidelines-non-private-member-variables-in-classes `_, `misc-non-private-member-variables-in-classes `_, `fuchsia-header-anon-namespaces `_, `google-build-namespaces `_, - `google-readability-braces-around-statements `_, `readability-braces-around-statements `_, "Yes" + `google-readability-braces-around-statements `_, `readability-braces-around-statements `_, `google-readability-function-size `_, `readability-function-size `_, `google-readability-namespace-comments `_, `llvm-namespace-comment `_, `hicpp-avoid-c-arrays `_, `modernize-avoid-c-arrays `_, - `hicpp-braces-around-statements `_, `readability-braces-around-statements `_, "Yes" - `hicpp-deprecated-headers `_, `modernize-deprecated-headers `_, "Yes" - `hicpp-explicit-conversions `_, `google-explicit-constructor `_, "Yes" + `hicpp-braces-around-statements `_, `readability-braces-around-statements `_, + `hicpp-deprecated-headers `_, `modernize-deprecated-headers `_, + `hicpp-explicit-conversions `_, `google-explicit-constructor `_, `hicpp-function-size `_, `readability-function-size `_, `hicpp-invalid-access-moved `_, `bugprone-use-after-move `_, - `hicpp-member-init `_, `cppcoreguidelines-pro-type-member-init `_, "Yes" - `hicpp-move-const-arg `_, `performance-move-const-arg `_, "Yes" - `hicpp-named-parameter `_, `readability-named-parameter `_, "Yes" + `hicpp-member-init `_, `cppcoreguidelines-pro-type-member-init `_, + `hicpp-move-const-arg `_, `performance-move-const-arg `_, + `hicpp-named-parameter `_, `readability-named-parameter `_, `hicpp-new-delete-operators `_, `misc-new-delete-overloads `_, `hicpp-no-array-decay `_, `cppcoreguidelines-pro-bounds-array-to-pointer-decay `_, `hicpp-no-malloc `_, `cppcoreguidelines-no-malloc `_, `hicpp-noexcept-move `_, `performance-noexcept-move-constructor `_, `hicpp-special-member-functions `_, `cppcoreguidelines-special-member-functions `_, - `hicpp-static-assert `_, `misc-static-assert `_, "Yes" + `hicpp-static-assert `_, `misc-static-assert `_, `hicpp-undelegated-constructor `_, `bugprone-undelegated-constructor `_, - `hicpp-uppercase-literal-suffix `_, `readability-uppercase-literal-suffix `_, "Yes" - `hicpp-use-auto `_, `modernize-use-auto `_, "Yes" - `hicpp-use-emplace `_, `modernize-use-emplace `_, "Yes" - `hicpp-use-equals-default `_, `modernize-use-equals-default `_, "Yes" - `hicpp-use-equals-delete `_, `modernize-use-equals-delete `_, "Yes" - `hicpp-use-noexcept `_, `modernize-use-noexcept `_, "Yes" - `hicpp-use-nullptr `_, `modernize-use-nullptr `_, "Yes" - `hicpp-use-override `_, `modernize-use-override `_, "Yes" + `hicpp-uppercase-literal-suffix `_, `readability-uppercase-literal-suffix `_, + `hicpp-use-auto `_, `modernize-use-auto `_, + `hicpp-use-emplace `_, `modernize-use-emplace `_, + `hicpp-use-equals-default `_, `modernize-use-equals-default `_, + `hicpp-use-equals-delete `_, `modernize-use-equals-delete `_, + `hicpp-use-noexcept `_, `modernize-use-noexcept `_, + `hicpp-use-nullptr `_, `modernize-use-nullptr `_, + `hicpp-use-override `_, `modernize-use-override `_, `hicpp-vararg `_, `cppcoreguidelines-pro-type-vararg `_, - `llvm-qualified-auto `_, `readability-qualified-auto `_, "Yes" - + `llvm-qualified-auto `_, `readability-qualified-auto `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-str-contains.cpp b/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-str-contains.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/abseil-string-find-str-contains.cpp @@ -0,0 +1,283 @@ +// RUN: %check_clang_tidy %s abseil-string-find-str-contains %t -- \ +// RUN: -config="{CheckOptions: []}" + +using size_t = decltype(sizeof(int)); + +namespace std { + +// Lightweight standin for std::string. +template +class basic_string { +public: + basic_string(); + basic_string(const basic_string &); + basic_string(const C *); + ~basic_string(); + int find(basic_string s, int pos = 0); + int find(const C *s, int pos = 0); + int find(char c, int pos = 0); + static constexpr size_t npos = -1; +}; +typedef basic_string string; + +// Lightweight standin for std::string_view. +template +class basic_string_view { +public: + basic_string_view(); + basic_string_view(const basic_string_view &); + basic_string_view(const C *); + ~basic_string_view(); + int find(basic_string_view s, int pos = 0); + int find(const C *s, int pos = 0); + int find(char c, int pos = 0); + static constexpr size_t npos = -1; +}; +typedef basic_string_view string_view; + +} // namespace std + +namespace absl { + +// Lightweight standin for absl::string_view. +class string_view { +public: + string_view(); + string_view(const string_view &); + string_view(const char *); + ~string_view(); + int find(string_view s, int pos = 0); + int find(const char *s, int pos = 0); + int find(char c, int pos = 0); + static constexpr size_t npos = -1; +}; + +} // namespace absl + +// Functions that take and return our various string-like types. +std::string foo_ss(std::string); +std::string_view foo_ssv(std::string_view); +absl::string_view foo_asv(absl::string_view); +std::string bar_ss(); +std::string_view bar_ssv(); +absl::string_view bar_asv(); + +// Confirms that find==npos and find!=npos work for each supported type, when +// npos comes from the correct type. +void basic_tests() { + std::string ss; + ss.find("a") == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of find() == npos + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "a");{{$}} + + ss.find("a") != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of find() != npos + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}} + + std::string::npos != ss.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}} + + std::string_view ssv; + ssv.find("a") == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "a");{{$}} + + ssv.find("a") != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}} + + std::string_view::npos != ssv.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}} + + absl::string_view asv; + asv.find("a") == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "a");{{$}} + + asv.find("a") != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}} + + absl::string_view::npos != asv.find("a"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}} +} + +// Confirms that it works even if you mix-and-match the type for find and for +// npos. (One of the reasons for this checker is to clean up cases that +// accidentally mix-and-match like this. absl::StrContains is less +// error-prone.) +void mismatched_npos() { + std::string ss; + ss.find("a") == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "a");{{$}} + + ss.find("a") != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}} + + std::string_view ssv; + ssv.find("a") == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "a");{{$}} + + ssv.find("a") != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}} + + absl::string_view asv; + asv.find("a") == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "a");{{$}} + + asv.find("a") != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}} +} + +// Confirms that it works even when the needle or the haystack are more +// complicated expressions. +void subexpression_tests() { + std::string ss, ss2; + foo_ss(ss).find(ss2) == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_ss(ss), ss2);{{$}} + + ss.find(foo_ss(ss2)) != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, foo_ss(ss2));{{$}} + + foo_ss(bar_ss()).find(foo_ss(ss2)) != std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_ss(bar_ss()), foo_ss(ss2));{{$}} + + std::string_view ssv, ssv2; + foo_ssv(ssv).find(ssv2) == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_ssv(ssv), ssv2);{{$}} + + ssv.find(foo_ssv(ssv2)) != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, foo_ssv(ssv2));{{$}} + + foo_ssv(bar_ssv()).find(foo_ssv(ssv2)) != std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_ssv(bar_ssv()), foo_ssv(ssv2));{{$}} + + absl::string_view asv, asv2; + foo_asv(asv).find(asv2) == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_asv(asv), asv2);{{$}} + + asv.find(foo_asv(asv2)) != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, foo_asv(asv2));{{$}} + + foo_asv(bar_asv()).find(foo_asv(asv2)) != absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_asv(bar_asv()), foo_asv(asv2));{{$}} +} + +// Confirms that it works with string literal, char* and const char* parameters. +void string_literal_and_char_ptr_tests() { + char *c = nullptr; + const char *cc = nullptr; + + std::string ss; + ss.find("c") == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "c");{{$}} + + ss.find(c) == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, c);{{$}} + + ss.find(cc) == std::string::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, cc);{{$}} + + std::string_view ssv; + ssv.find("c") == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "c");{{$}} + + ssv.find(c) == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, c);{{$}} + + ssv.find(cc) == std::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, cc);{{$}} + + absl::string_view asv; + asv.find("c") == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "c");{{$}} + + asv.find(c) == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, c);{{$}} + + asv.find(cc) == absl::string_view::npos; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of + // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, cc);{{$}} +} + +// Confirms that it does *not* match when the parameter to find() is a char, +// because absl::StrContains is not implemented for char. +void no_char_param_tests() { + std::string ss; + ss.find('c') == std::string::npos; + + std::string_view ssv; + ssv.find('c') == std::string_view::npos; + + absl::string_view asv; + asv.find('c') == absl::string_view::npos; +} + +#define COMPARE_MACRO(x, y) ((x) == (y)) +#define FIND_MACRO(x, y) ((x).find(y)) +#define FIND_COMPARE_MACRO(x, y, z) ((x).find(y) == (z)) + +// Confirms that it does not match when a macro is involved. +void no_macros() { + std::string s; + COMPARE_MACRO(s.find("a"), std::string::npos); + FIND_MACRO(s, "a") == std::string::npos; + FIND_COMPARE_MACRO(s, "a", std::string::npos); +} + +// Confirms that it does not match when the pos parameter is non-zero. +void no_nonzero_pos() { + std::string ss; + ss.find("a", 1) == std::string::npos; + + std::string_view ssv; + ssv.find("a", 2) == std::string_view::npos; + + absl::string_view asv; + asv.find("a", 3) == std::string_view::npos; +} + +// Confirms that it does not match when it's compared to something other than +// npos, even if the value is the same as npos. +void no_non_npos() { + std::string ss; + ss.find("a") == 0; + ss.find("a") == 1; + ss.find("a") == -1; + + std::string_view ssv; + ssv.find("a") == 0; + ssv.find("a") == 1; + ssv.find("a") == -1; + + absl::string_view asv; + asv.find("a") == 0; + asv.find("a") == 1; + asv.find("a") == -1; +}