This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] White List Option for performance-unnecessary-value-param, performance-unnecessary-copy-initialization and performance-for-range-copy
ClosedPublic

Authored by baloghadamsoftware on Oct 1 2018, 8:58 AM.

Details

Summary

New option added to these three checks to be able to silence false positives on types that are intentionally passed by value or copied. Such types are e.g. intrusive reference counting pointer types like llvm::IntrusiveRefCntPtr. The new option is named WhiteListTypes and can contain a semicolon-separated list of names of these types. Regular expressions are allowed. Default is empty.

Diff Detail

Repository
rL LLVM

Event Timeline

Thanks, that looks much better.
Please clang-format the patch.

This pattern is repeated at least 4 times now, i think some abstraction is needed.

clang-tidy/performance/ForRangeCopyCheck.cpp
49 ↗(On Diff #167746)

I'm not sure what is auto here, please spell QualType.

50–53 ↗(On Diff #167746)

llvm::any_of()

clang-tidy/performance/UnnecessaryCopyInitialization.cpp
96 ↗(On Diff #167746)

Same

clang-tidy/performance/UnnecessaryValueParamCheck.cpp
103 ↗(On Diff #167746)

Same

JonasToth added inline comments.
clang-tidy/performance/ForRangeCopyCheck.cpp
49 ↗(On Diff #167746)

And please elide const, as it is a value and values are not made const in llvm code (consistency for now)

50 ↗(On Diff #167746)

This configuration should be used in the matchers. Please see cppcoreguidelines-no-malloc for an example on how to do it in the matchers. Having it there usually improves performance and is clearer.

clang-tidy/performance/UnnecessaryValueParamCheck.cpp
110 ↗(On Diff #167746)

please remove the const

docs/clang-tidy/checks/performance-for-range-copy.rst
31 ↗(On Diff #167746)

Please give an example for regular expressions. There are many slightly different variations of them and not everyone might be familiar. I think one wildcard expression is enough.

The sentences miss some fill words i think. whitelisted types, The default is empty.

docs/clang-tidy/checks/performance-unnecessary-copy-initialization.rst
44 ↗(On Diff #167746)

Same as other doc, same below

test/clang-tidy/performance-for-range-copy.cpp
1 ↗(On Diff #167746)

I would prefer 2 test files. One with default configuration and one with the special whitelisting, same for the other checks

clang-tidy/performance/ForRangeCopyCheck.cpp
50 ↗(On Diff #167746)

google-runtime-references has this in the check() function. It seems there is no common agreement where to put this. cppcoreguidelines-no-malloc is not a good example since it simple compares strings instead of matching regular expressions. I think here we should allow regular expressions. Then we would need a new matcher called matchesAnyName. Should I create it?

JonasToth added inline comments.Oct 2 2018, 8:29 AM
clang-tidy/performance/ForRangeCopyCheck.cpp
50 ↗(On Diff #167746)

Yes, regex is not supported there, but the mechanism is the same. You can put the new matcher in this check for now, if it's generally usefully in other clang-tidy check we can easily migrate them later on.

My motiviation for wanting it in the matcher instead of the check is to reduce the amount of ineffective work. If the matcher already ignores these cases, we don't need to spend cycles doing the callbacks and all the machinery. Its not a strong opinion though, if you don't agree I am fine with it.

Do you want to provide many matchers? In principle one could make one big regex with (PATTERN1|PATTERN2|PATTERN3). What do you think?

lebedev.ri added inline comments.Oct 2 2018, 1:28 PM
clang-tidy/performance/ForRangeCopyCheck.cpp
50 ↗(On Diff #167746)

You can put the new matcher in this check for now, if it's generally usefully in other clang-tidy check we can easily migrate them later on.

Since it's now used in 4 places, i think it should be clang-tidy/utils/Matchers.h

True

Am 02.10.2018 um 22:28 schrieb Roman Lebedev via Phabricator:

lebedev.ri added inline comments.

Comment at: clang-tidy/performance/ForRangeCopyCheck.cpp:50
+ const auto VarType = Var->getType();
+ if (std::find_if(WhiteListTypes.begin(), WhiteListTypes.end(),

+ [&](llvm::StringRef WhiteListType) {

JonasToth wrote:

baloghadamsoftware wrote:

JonasToth wrote:

lebedev.ri wrote:

llvm::any_of()

This configuration should be used in the matchers. Please see cppcoreguidelines-no-malloc for an example on how to do it in the matchers. Having it there usually improves performance and is clearer.

google-runtime-references has this in the check() function. It seems there is no common agreement where to put this. cppcoreguidelines-no-malloc is not a good example since it simple compares strings instead of matching regular expressions. I think here we should allow regular expressions. Then we would need a new matcher called matchesAnyName. Should I create it?

Yes, regex is not supported there, but the mechanism is the same. You can put the new matcher in this check for now, if it's generally usefully in other clang-tidy check we can easily migrate them later on.

My motiviation for wanting it in the matcher instead of the check is to reduce the amount of ineffective work. If the matcher already ignores these cases, we don't need to spend cycles doing the callbacks and all the machinery. Its not a strong opinion though, if you don't agree I am fine with it.

Do you want to provide many matchers? In principle one could make one big regex with (PATTERN1|PATTERN2|PATTERN3). What do you think?
You can put the new matcher in this check for now, if it's generally usefully in other clang-tidy check we can easily migrate them later on.

Since it's now used in 4 places, i think it should be clang-tidy/utils/Matchers.h

Repository:

rCTE Clang Tools Extra

https://reviews.llvm.org/D52727

aaron.ballman added inline comments.Oct 3 2018, 12:53 PM
clang-tidy/performance/ForRangeCopyCheck.h
43 ↗(On Diff #167746)

Nit: can you name it AllowedTypes rather than WhiteListTypes? Use similar nomenclature ("allow" instead of "white") elsewhere as well.

Updated according to the comments.

baloghadamsoftware marked 5 inline comments as done.Oct 4 2018, 5:37 AM

Thanks looks even better, getting there.
There are some spurious whiteline changes.

clang-tidy/performance/ForRangeCopyCheck.cpp
53 ↗(On Diff #168274)

Spurious newline

clang-tidy/utils/Matchers.cpp
20 ↗(On Diff #168274)
if(NameList.empty())
  return false;
29 ↗(On Diff #168274)

Why do you need to 'concatenate' all the regexes?
Why not simply match in a loop?

clang-tidy/utils/Matchers.cpp
20 ↗(On Diff #168274)

false? But this functions returns Matcher<NamedDecl>.

29 ↗(On Diff #168274)

This is what @JonasToth suggested. How to match them in a loop if the function returns a Matcher<NamedDecl>?

lebedev.ri added inline comments.Oct 4 2018, 5:54 AM
clang-tidy/utils/Matchers.cpp
20 ↗(On Diff #168274)

Hm, then unless(anything()), or unless(anything()).matches(Node, Finder, Builder).

lebedev.ri added inline comments.Oct 4 2018, 5:59 AM
clang-tidy/utils/Matchers.cpp
18–19 ↗(On Diff #168274)

Actually wait, what is this?
It should be something like

AST_MATCHER_P(NamedDecl, matchesAnyListedName, std::vector<std::string> &, NameList) {
...
20 ↗(On Diff #168274)

Are you *sure* it doesn't work? Have you tried?

29 ↗(On Diff #168274)

Are you *sure* it doesn't work? Have you tried?

lebedev.ri added inline comments.Oct 4 2018, 6:05 AM
clang-tidy/utils/Matchers.cpp
1 ↗(On Diff #168274)

Also, why is this not in a header?
I suspect that heavily pessimizes inlining.

clang-tidy/utils/Matchers.cpp
18–19 ↗(On Diff #168274)

Taken from cppcoreguidelines-no-malloc as @JonasToth suggested.

lebedev.ri added inline comments.Oct 4 2018, 6:20 AM
clang-tidy/utils/Matchers.cpp
18–19 ↗(On Diff #168274)

Ok, but as you can see in clang-tidy/utils/Matchers.h (and ASTMatchers.h), it's not really correct prototype.

JonasToth added inline comments.Oct 4 2018, 6:30 AM
clang-tidy/utils/Matchers.cpp
18–19 ↗(On Diff #168274)

This is not a full matcher, but a utility function creating one. You can make a full matcher-class with the AST_MATCHER macros as well.
Given this now is a public (within clang-tidy) matcher, it should probably be a AST_MATCHER based matcher. The cppcoreguidelines-check can then be migrated.

Updated according to the comments.

baloghadamsoftware marked 5 inline comments as done.Oct 8 2018, 5:31 AM

That's it!
Hopefully last portion of nits.

clang-tidy/performance/UnnecessaryCopyInitialization.cpp
65–66 ↗(On Diff #168646)

Does it matter whether we are calling matchers::isExpensiveToCopy() on the type, or on the canonical type?

clang-tidy/performance/UnnecessaryValueParamCheck.cpp
83–86 ↗(On Diff #168646)

This is inconsistent with the rest of the matchers here.
I think you want to check isExpensiveToCopy() first, because that should be less expensive than regex.

clang-tidy/utils/Matchers.h
51 ↗(On Diff #168646)

Please double-check that this does not result in a copy, and std::vector is passed as a reference.

53 ↗(On Diff #168646)
for (const std::string &Name : NameList) {
53 ↗(On Diff #168646)

Do we want to be explicit about early-return here?

JonasToth added inline comments.Oct 9 2018, 2:34 AM
clang-tidy/performance/UnnecessaryCopyInitialization.cpp
65–66 ↗(On Diff #168646)

the canonical type does resolve all typedefs, which is what is desirable in this case.

clang-tidy/utils/Matchers.h
51 ↗(On Diff #168646)

its const&, see clang/include/clang/ASTMatchers/ASTMatchersMacros.h line 139

53 ↗(On Diff #168646)

it looks like a return llvm::any_of(NameList, []() { /* Match regex */});

Updated according to the comments.

baloghadamsoftware marked 2 inline comments as done.Oct 9 2018, 6:13 AM
baloghadamsoftware added inline comments.
clang-tidy/performance/UnnecessaryCopyInitialization.cpp
65–66 ↗(On Diff #168646)

The real question is whether we want to match the canonical type to the list of allowed type names. I am not sure.

JonasToth added inline comments.Oct 9 2018, 6:40 AM
clang-tidy/performance/UnnecessaryCopyInitialization.cpp
65–66 ↗(On Diff #168646)

Well, very long template names are impractical, even for something "simple" as std::string the name gets very long without the typedef. I would say the typedefs should be respected

LG other than the hasCanonicalType() vs hasType() question.

Matching of allowed types happens now on the top-level type, not the canonical one.

lebedev.ri accepted this revision.Oct 10 2018, 7:02 AM
lebedev.ri added a reviewer: JonasToth.

LG unless @JonasToth or @aaron.ballman has any further comments.

This revision is now accepted and ready to land.Oct 10 2018, 7:02 AM

LG in principle, just the SmallVec thing could be done if you agree. I don't insist on it, but it looks like a performance benefit to me.

clang-tidy/performance/UnnecessaryCopyInitialization.h
41 ↗(On Diff #168993)

I think these lists for the allowed types could even be SmallVectors, as I wouldn't expect so many entries.

LG in principle, just the SmallVec thing could be done if you agree. I don't insist on it, but it looks like a performance benefit to me.

I principally agree, but then I also have to duplicate the string list parser function in the options to make it work on SmallVector as well.

I see, probably not worth it. Its one-time effort anyway right?
LGTM

JonasToth accepted this revision.Oct 12 2018, 6:01 AM
This revision was automatically updated to reflect the committed changes.

Maybe it should be done in a separate patch together for all checks using that utility function. So there would be no need for duplication but the original functions should be changed instead.