Skip to content

[clang-tidy][abseil-string-find-startswith] Add string_view to default string-like classes #72283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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