Skip to content

Commit

Permalink
[clang-tidy][abseil-string-find-startswith] Add string_view to defaul…
Browse files Browse the repository at this point in the history
…t string-like classes (#72283)

As per title. A small improvement to this check, `string_view` should
work out of the box.
  • Loading branch information
nicovank authored and PiotrZSL committed Nov 14, 2023
1 parent dae3c44 commit 2aec866
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ using namespace clang::ast_matchers;

namespace clang::tidy::abseil {

const auto DefaultStringLikeClasses =
"::std::basic_string;::std::basic_string_view";

StringFindStartswithCheck::StringFindStartswithCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
StringLikeClasses(utils::options::parseStringList(
Options.get("StringLikeClasses", "::std::basic_string"))),
Options.get("StringLikeClasses", DefaultStringLikeClasses))),
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
utils::IncludeSorter::IS_LLVM),
areDiagsSelfContained()),
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^

- Improved :doc:`abseil-string-find-startswith
<clang-tidy/checks/abseil/string-find-startswith>` check to also consider
``std::basic_string_view`` in addition to ``std::basic_string`` by default.

- Improved :doc:`bugprone-dangling-handle
<clang-tidy/checks/bugprone/dangling-handle>` check to support functional
casting during type conversions at variable initialization, now with improved
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
abseil-string-find-startswith
=============================

Checks whether a ``std::string::find()`` or ``std::string::rfind()`` result is
compared with 0, and suggests replacing with ``absl::StartsWith()``. This is
both a readability and performance issue.
Checks whether a ``std::string::find()`` or ``std::string::rfind()`` (and
corresponding ``std::string_view`` methods) result is compared with 0, and
suggests replacing with ``absl::StartsWith()``. This is both a readability and
performance issue.

.. code-block:: c++

Expand All @@ -28,9 +29,9 @@ Options

.. option:: StringLikeClasses

Semicolon-separated list of names of string-like classes. By default only
``std::basic_string`` is considered. The list of methods to considered is
fixed.
Semicolon-separated list of names of string-like classes. By default both
``std::basic_string`` and ``std::basic_string_view`` are considered. The list
of methods to be considered is fixed.

.. option:: IncludeStyle

Expand Down
22 changes: 22 additions & 0 deletions clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/string
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ struct basic_string {
size_type find(const C* s, size_type pos = 0) const;
size_type find(const C* s, size_type pos, size_type n) const;

size_type rfind(const _Type& str, size_type pos = npos) const;
size_type rfind(const C* s, size_type pos, size_type count) const;
size_type rfind(const C* s, size_type pos = npos) const;
size_type rfind(C ch, size_type pos = npos) const;

_Type& insert(size_type pos, const _Type& str);
_Type& insert(size_type pos, const C* s);
_Type& insert(size_type pos, const C* s, size_type n);
Expand All @@ -54,6 +59,8 @@ struct basic_string {
_Type& operator+=(const C* s);
_Type& operator=(const _Type& str);
_Type& operator=(const C* s);

static constexpr size_t npos = -1;
};

typedef basic_string<char> string;
Expand All @@ -63,8 +70,23 @@ typedef basic_string<char32> u32string;

template <typename C, typename T = char_traits<C>>
struct basic_string_view {
typedef size_t size_type;
typedef basic_string_view<C, T> _Type;

const C *str;
constexpr basic_string_view(const C* s) : str(s) {}

size_type find(_Type v, size_type pos = 0) const;
size_type find(C ch, size_type pos = 0) const;
size_type find(const C* s, size_type pos, size_type count) const;
size_type find(const C* s, size_type pos = 0) const;

size_type rfind(_Type v, size_type pos = npos) const;
size_type rfind(C ch, size_type pos = npos) const;
size_type rfind(const C* s, size_type pos, size_type count) const;
size_type rfind(const C* s, size_type pos = npos) const;

static constexpr size_t npos = -1;
};
typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
// RUN: %check_clang_tidy %s abseil-string-find-startswith %t -- \
// RUN: -config="{CheckOptions: {abseil-string-find-startswith.StringLikeClasses: '::std::basic_string;::basic_string'}}"
// RUN: -config="{CheckOptions: \
// RUN: {abseil-string-find-startswith.StringLikeClasses: \
// RUN: '::std::basic_string;::std::basic_string_view;::basic_string'}}" \
// RUN: -- -isystem %clang_tidy_headers

#include <string>

using size_t = decltype(sizeof(int));

namespace std {
template <typename T> class allocator {};
template <typename T> class char_traits {};
template <typename C, typename T = std::char_traits<C>,
typename A = std::allocator<C>>
struct basic_string {
basic_string();
basic_string(const basic_string &);
basic_string(const C *, const A &a = A());
~basic_string();
int find(basic_string<C> s, int pos = 0);
int find(const char *s, int pos = 0);
int rfind(basic_string<C> s, int pos = npos);
int rfind(const char *s, int pos = npos);
static constexpr size_t npos = -1;
};
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;

struct cxx_string {
int find(const char *s, int pos = 0);
int rfind(const char *s, int pos = npos);
Expand All @@ -39,7 +26,7 @@ std::string bar();

#define A_MACRO(x, y) ((x) == (y))

void tests(std::string s, global_string s2) {
void tests(std::string s, global_string s2, std::string_view sv) {
s.find("a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith instead of find() == 0 [abseil-string-find-startswith]
// CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s, "a");{{$}}
Expand Down Expand Up @@ -96,6 +83,14 @@ void tests(std::string s, global_string s2) {
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith
// CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(s2, "a");{{$}}

sv.find("a") == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StartsWith
// CHECK-FIXES: {{^[[:space:]]*}}absl::StartsWith(sv, "a");{{$}}

sv.rfind("a", 0) != 0;
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StartsWith
// CHECK-FIXES: {{^[[:space:]]*}}!absl::StartsWith(sv, "a");{{$}}

// expressions that don't trigger the check are here.
A_MACRO(s.find("a"), 0);
A_MACRO(s.rfind("a", 0), 0);
Expand Down

0 comments on commit 2aec866

Please sign in to comment.