Skip to content

[clang-tidy] support string::contains #110351

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

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

dl8sd11
Copy link
Contributor

@dl8sd11 dl8sd11 commented Sep 28, 2024

Starting from c++23, we can replace std::string::find() == std::string::npos with std::string.contains() . #109327

Currently, this is WIP because there are two limitations:

  1. False positive: SubStr type is const std::string& which does not match std::string::contains(basic_string_view) type.
std::string SubStr;
  if (Str.find(SubStr) != std::string::npos) {};
  1. Currently, the fixit for std::string::find("test", 0) is incorrect.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

Copy link
Contributor

@nicovank nicovank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO no need to update release notes:

Improved readability-container-contains check to let it work on any class that has a contains method.

But I think an example should be added to the check documentation.

@@ -32,7 +33,8 @@ void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {

const auto FindCall =
cxxMemberCallExpr(
argumentCountIs(1),
anyOf(argumentCountIs(1),
allOf(argumentCountIs(2), hasArgument(1, cxxDefaultArgExpr()))),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment here saying std::string and std::string_view take an optional pos argument on where to start the search. Also match ignoringParenImpCasts(integerLiteral(0))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to match the two arguments but I fail to remove the second argument in the Fixit hint. Could you suggest how to get the location after the first argument. I tried binding the first argument (e.g. "test"), but the source range seems to be the first character (") instead of the whole argument ("test").

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering str.find('a') != std::string::npos. Instead of deleting stuff from ) to npos, you could replace everything from after 'a' to the end of the comparison with ), that should work. I wrote something like this here:

// Remove possible arguments after search expression and ' [!=]= 0' suffix.
Diagnostic << FixItHint::CreateReplacement(
CharSourceRange::getTokenRange(
Lexer::getLocForEndOfToken(SearchExpr->getEndLoc(), 0,
*Result.SourceManager, getLangOpts()),
ComparisonExpr->getEndLoc()),
")");

@dl8sd11 dl8sd11 requested a review from nicovank October 5, 2024 04:35
Comment on lines +60 to +61
bool contains(const C *s) const;
bool contains(C s) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool contains(const C *s) const;
bool contains(C s) const;
bool contains(const C *s) const;
bool contains(C s) const;
bool contains(basic_string_view<C, T> sv) const;

Comment on lines +111 to +112
bool contains(const C *s) const;
bool contains(C s) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool contains(const C *s) const;
bool contains(C s) const;
bool contains(const C *s) const;
bool contains(C s) const;
bool contains(basic_string_view sv) const;

const auto FindCall =
cxxMemberCallExpr(
argumentCountIs(1),
anyOf(argumentCountIs(1),
allOf(argumentCountIs(2), // string::find takes two arguments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
allOf(argumentCountIs(2), // string::find takes two arguments
// `find` for string-like classes often has a second parameter,
// indicating where to start the search. We match if it's
// the default zero or explicitly equal to zero.
allOf(argumentCountIs(2),

I think a little more detail is good here.

@@ -50,12 +50,16 @@ struct basic_string {
size_type find(const _Type& str, size_type pos = 0) const;
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 find(C ch, size_type pos = 0) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
size_type find(C ch, size_type pos = 0) const;
size_type find(C ch, size_type pos = 0) const;
template<class StringViewLike>
size_type find(const StringViewLike& t, size_type pos = 0) const;

Let's cover them all if possible.
This one might cause issues, and is probably not all that common, but let's see.

@@ -453,3 +458,29 @@ void testOperandPermutations(std::map<int, int>& Map) {
// CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (!Map.contains(0)) {};
}

void testStringNops(std::string Str, std::string SubStr, std::string_view StrView) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void testStringNops(std::string Str, std::string SubStr, std::string_view StrView) {
void testStringNpos(std::string Str, std::string SubStr, std::string_view StrView) {

Just a typo.

@nicovank
Copy link
Contributor

nicovank commented Oct 5, 2024

Thanks!
Is there still an issue with type stuff? If so, what would be a test case that fails that shouldn't?

@nicovank
Copy link
Contributor

FYI I'm going to merge #110386 now which will cause conflicts (binaryOperator -> binaryOperation).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants