This is an archive of the discontinued LLVM Phabricator instance.

'size' call that could be replaced with 'empty' on STL containers
ClosedPublic

Authored by xazax.hun on Jan 12 2015, 5:15 AM.

Details

Reviewers
klimek
alexfh
Summary

We are porting some of the checkers at a company we developed to the Clang Tidy infrastructure. We would like to open source the checkers that may be useful for the community as well. This patch is the first checker that is being ported to Clang Tidy. We also added fix-it hints, and applied them to LLVM: http://reviews.llvm.org/D6924

The code compiled and the unit tests are passed after the fixits was applied.

The documentation of the checker:

/ The emptiness of a container should be checked using the empty method
/ instead of the size method. It is not guaranteed that size is a
/ constant-time function, and it is generally more efficient and also shows
/ clearer intent to use empty. Furthermore some containers may implement the
/ empty method but not implement the size method. Using empty whenever
/ possible makes it easier to switch to another container in the future.

It also uses some custom ASTMatchers. In case you find them useful I can submit them as separate patches to clang. I will apply your suggestions to this patch.

Diff Detail

Event Timeline

xazax.hun updated this revision to Diff 18016.Jan 12 2015, 5:15 AM
xazax.hun retitled this revision from to 'size' call that could be replaced with 'empty' on STL containers.
xazax.hun updated this object.
xazax.hun edited the test plan for this revision. (Show Details)
xazax.hun added reviewers: klimek, alexfh.
xazax.hun added a subscriber: Unknown Object (MLST).
alexfh edited edge metadata.Jan 12 2015, 7:55 AM

The check seems to be rather useful. Thank you for working on contributing it to ClangTidy!

The code is mostly fine, but there are a bunch of style issues. Please see the comments inline.

clang-tidy/readability/ContainerSizeEmpty.cpp
23

I'm afraid this won't compile on one of the supported toolchains: MSVS 2013. But I can't verify this myself.

Also, you could use llvm::StringSet<> instead, as it's more efficient.

30

Please use StringRef instead of "const std::string &".

87

nit: Please remove the empty line.

88

Looks like a valid use case for "auto". The type is already spelled in the initializer.

88

"MC" is too cryptic. Maybe "Call" or "MemberCall"? The variable is only used twice, so this won't increase the code size a lot.

90

Maybe "BinaryOperator" or "BinaryOp"?

93

nit: Variable names should be capitalized.

102

nit: Here and in other places: please end sentences with a period.

104

Did you mean "ConstIsLHS"?

108

"LIT" doesn't seem to be an acronym here, so I'd better rename it to "Literal" or something like that.

142

nit: I'd move the comment to the next line.

clang-tidy/readability/ContainerSizeEmpty.h
20

nits:

  1. Sentences should end with a period.
  2. s/that //
  3. Please use Doxygen tags for function, class and variable names.

How about:

Checks whether a call to the \c size() method on a container can be replaced with a call to \c empty().
22

Please mark method names with the \c Doxygen tag:

... using the \c empty() method instead of the \c size() method.
modularize/ModuleAssistant.cpp
71 ↗(On Diff #18016)

Please send cleanups as a separate patch.

test/clang-tidy/readibility-container-size-empty.cpp
19

Please make the CHECK-FIXES lines as specific as possible to avoid incorrect matches. E.g.:

// CHECK-FIXES: {{^  }}if(vect.size() == 0);
21

You can truncate the message in all the CHECK-MESSAGES lines after the first one. E.g.:

// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
72

Please a couple of tests with templates and macros. I suspect that they aren't yet handled correctly.

E.g.:

template<typename T>
void f() {
  std::vector<T> v;
  if (v.size()) {}
}
void g() {
  f<int>();
  f<double>();
  f<char*>();
}
xazax.hun updated this revision to Diff 18080.Jan 13 2015, 2:51 AM
xazax.hun edited edge metadata.

Thank you for the detailed review.

I think I fixed all of the issues you mentioned. In case I forgot something I will include it in the fix in the next version of the patch.

Thanks for addressing the comments. There are still some issues left. See the comments inline.

clang-tidy/readability/ContainerSizeEmpty.cpp
23

Here and in other places:

"The name should be camel case, and start with an upper case letter (e.g. Leader or Boats)."
http://llvm.org/docs/CodingStandards.html#name-types-functions-variables-and-enumerators-properly

101

This change made MemberCall a constant (before it was a non-const pointer to a constant). Was this intentional or you meant "const auto *MemberCall"?

clang-tidy/readability/ContainerSizeEmpty.h
24

Please use "\c size()", "\c empty()" etc. in all other locations as well.

test/clang-tidy/readibility-container-size-empty.cpp
17

nit: If the lack of space between "if" and "(" is not intentional, I'd add a space to make the code closer to the LLVM style. Maybe just clang-format the test file?

19

I actually meant to make the CHECK-FIXES lines more specific, not CHECK-MESSAGES (they are specific enough due to line numbers). Each type of CHECK lines is checked independently, and the location of a CHECK line doesn't matter, only their order (unless the [[@LINE]] expression is used). So essentially, fixed output is verified against this set of checks:

// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect2.empty()
// CHECK-FIXES: vect3->empty()
// CHECK-FIXES: vect4.empty()
// CHECK-FIXES: !v.empty()

The only thing that is verified, is that there is an ordered subset of lines in the input that match the patterns above. As you can guess, this is not particularly useful, as any of the "vect.empty()" patterns would also match "if(!vect.empty());" line, for example.

So please specify full lines of the output together with the leading "{{^ }}" (the part in double curlies is treated as a regular expression).

Thanks!

97

The interesting part is whether the replacement is performed only once or additionally for each template instantiation, in which case the code will still contain the replacement text, but will otherwise be corrupted.

100

The interesting part here is to check that the macro _definition_ is not "fixed".

xazax.hun updated this revision to Diff 18145.Jan 14 2015, 3:18 AM

Thank you for the review. Unfortunately I am still getting used to the LLVM coding conventions. Hopefully less iteration might be required in the future. :)

One comment seems to have slipped through the cracks. And one more nit.

clang-tidy/readability/ContainerSizeEmpty.cpp
23

It looks like this variable would better be a static local variable inside isContainer.

That's a smart way to initialize a StringSet variable, btw ;)

test/clang-tidy/readibility-container-size-empty.cpp
19

Not sure if Phabricator failed to display the previous comment on this line or you just missed it. Repeating it here for convenience:


I actually meant to make the CHECK-FIXES lines more specific, not CHECK-MESSAGES (they are specific enough due to line numbers). Each type of CHECK lines is checked independently, and the location of a CHECK line doesn't matter, only their order (unless the [[@LINE]] expression is used). So essentially, fixed output is verified against this set of checks:

// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: vect.empty()
// CHECK-FIXES: !vect.empty()
// CHECK-FIXES: !vect2.empty()
// CHECK-FIXES: vect3->empty()
// CHECK-FIXES: vect4.empty()
// CHECK-FIXES: !v.empty()

The only thing that is verified, is that there is an ordered subset of lines in the input that match the patterns above. As you can guess, this is not particularly useful, as any of the "vect.empty()" patterns would also match "if(!vect.empty());" line, for example.

So please specify full lines of the output together with the leading "{{^ }}" (the part in double curlies is treated as a regular expression).

Thanks!


One clarification: I think, CHECK-MESSAGES lines checking code snippets in warning messages don't add any value and just complicate the test. You could safely remove them and instead make CHECK-FIXES lines verify whole lines.

xazax.hun updated this revision to Diff 18220.Jan 15 2015, 3:46 AM

Thank you, it looks like I missed that comment. I have altered the test according to your recommendations. I think I get it now how those check-fixes works :)

alexfh accepted this revision.Jan 15 2015, 5:21 AM
alexfh edited edge metadata.

This now looks good. Thank you for putting effort into open-sourcing this check!

I still suspect that macros and templates as seen in the test are handled correctly just by coincidence, but it should be easy to fix once any problems are found on the real code.

I'll commit the patch for you.

This revision is now accepted and ready to land.Jan 15 2015, 5:21 AM

There were some changes in clang-tools-extra and the patch doesn't apply cleanly. Please rebase the patch on top of the current HEAD.

xazax.hun updated this revision to Diff 18227.Jan 15 2015, 7:33 AM
xazax.hun edited edge metadata.

I have rebased it to the current head. Sorry for the inconvenience.

alexfh closed this revision.Jan 15 2015, 7:48 AM

No problem.

Committed revision 226172.