Add matcher that identifies arguments that are passed by value, are copy assigned to a class field and have a non-deleted move constructor. If the type is also expensive to copy issue diagnostic message that the field can be move constructed.
Details
Diff Detail
Event Timeline
There is a -Wpessimizing-move frontend warning that Richard added not too long ago, which tells the user to remove calls to std::move() because they pessimize the code. The new functionality you are looking to add is basically the opposite: it tells the user to add std::move() because the code is currently pessimized due to copies. Given how general that concept is (it doesn't just appertain to constructor initializations), I wonder if this makes more sense as a frontend warning like -Wpessimizing-copy.
Richard (both of you), what do you think?
~Aaron
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
36 | Elide braces, here and elsewhere. | |
40 | Can turn this into a return statement directly instead of an if. | |
42 | Why do you need classHasTrivialCopyAndDestroy() when you're already checking if it's a trivially copyable type? | |
48 | Should return unsigned, please. | |
54 | You don't actually need Matches, you can call match().size() instead. | |
56 | Should be *Initializer instead of * Initializer. | |
108 | Perhaps: "argument can be moved to avoid a copy" instead? | |
test/clang-tidy/misc-move-constructor-init.cpp | ||
7 | Formatting | |
8 | Why not = default? | |
36 | Should also have a test for scalars |
Oops, forgot to hit "Submit" yesterday. Sorry for the delay.
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
32 | These helper functions can (and will) be useful for other checks as well. Let's move them to clang-tidy/utils/ . We can add TypeTraits.h for functions like classHasTrivialCopyAndDestroy, isExpensiveTypeToCopy, etc. And a separate header (Matchers.h, for example) for the matchers that are not suitable for the ASTMatchers.h. | |
88 | nit: It's common to omit braces around single-line statements in LLVM/Clang code. | |
108 | nit: Please remove the trailing period. | |
108 | Does anything stop us from suggesting fixes here (inserting "std::move()" around the argument and #include <utility>, if it's no there yet)? | |
clang-tidy/misc/MoveConstructorInitCheck.h | ||
24 | I suggest updating the corresponding .rst file instead and adding the URL of the documentation in the comment along with a short summary of what the check does. For example: /// Flag missing move opportunities in constructor initializer lists. /// /// For the user-facing documentation see: /// http://clang.llvm.org/extra/clang-tidy/checks/misc-move-constructor-init.html |
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
42 | We also want to catch types that have non-trivial destructors which would be executed when the temporary copy goes out of scope. | |
48 | Done. Is this an llvm convention? | |
108 | How would I tread in the IncludeOrder style (i.e. Google vs LLVM)? Should this be a flag shared by all of ClangTidy or specific to this check? | |
test/clang-tidy/misc-move-constructor-init.cpp | ||
8 | We need to make the type non-trivially copyable by our definition above. | |
36 | Added. |
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
11 | Errr, I'm not certain that we typically use relative paths for this sort of thing. I see we do use relative paths for other things in clang-tidy, but this is definitely an uncommon thing in the rest of the code base (outside of test or example code). Not saying you should change anything here, but we may want to consider changing the paths at some point so we are more in line with the rest of the LLVM projects in not using relative include paths. | |
clang-tidy/utils/Matchers.h | ||
2 | You are missing header include guards. | |
clang-tidy/utils/TypeTraits.cpp | ||
30 | This isn't needed because Type.isTriviallyCopyableType() already returns false for this case. | |
33 | No need to check for isScalarType() because isTriviallyCopyableType() already does that correctly. | |
clang-tidy/utils/TypeTraits.h | ||
23 | I think this is an implementation detail more than a trait we want to expose. | |
test/clang-tidy/misc-move-constructor-init.cpp | ||
37 | Sorry to have not caught this earlier, but can you also add a test case for dependent types to make sure they remain negative? |
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
108 | Other checks have separate options for the include style (see PassByValueCheck.cpp, for example), so this one should have its own option too. If we ever decide to introduce a common option instead, it will be easier to do this, if all usages of the IncludeInserter do the same thing with the include style. One thing I would change though, is to add the IncludeStyle <-> string conversion functions instead of doing this in each check. |
I changed the check to also produce a fix that wraps the argument in std::move().
When I modified the test include -isystem %S/Inputs/Headers it broke and only produces warnings but no fixes anymore. Is there a subtle difference in how tests with includes need to be invoked?
Did you add the files you're including (j.h, s.h)? If not, this will result in compilation errors and clang-tidy by default won't apply fixes, if the code doesn't compile. In any case, they should be present in the patch as well.
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
112 | /*IsAngled=*/true)) { | |
clang-tidy/modernize/PassByValueCheck.cpp | ||
121–122 | clang-format? | |
clang-tidy/modernize/ReplaceAutoPtrCheck.cpp | ||
192–193 | clang-format? | |
clang-tidy/utils/IncludeSorter.h | ||
29 | nit: The current name gives no hints at what is being converted to IncludeStyle, but parseIncludeStyle would make it clear that the source is a string. |
I had to convert the line endings of mis-move-constructor-init.cpp to unix style otherwise the test would not correctly work. This took a long time to debug since the checks failed complaining that error messages that look exactly the same didn't match.
Let me know if this needs to be converted back to DOS line endings, but it looks like most other tests have unix line endings.
Looks good. Please fix the patch and I can commit it for you.
clang-tidy/utils/IncludeSorter.cpp | ||
---|---|---|
289 | Did you forget to update the name here? |
All source and test files should have svn:eol-style=native property set to avoid this kind of an issue. I have no idea why is it not done on all llvm/clang sources.
Thanks for catching the last issues, Alex. I rebuilt and ran all tests using llvm-lit -v . for clang-tidy and they pass.
I don't have access, so that'd be great if you or Alex could submit the patch, thanks!
clang-tidy/misc/MoveConstructorInitCheck.cpp | ||
---|---|---|
11 | Leaving this as is for now as all other ClangTidy specific includes I could find were relative as well. Not sure if non-relative includes require extra work in the build rules. | |
108 | Done. I moved the conversion into IncludeSorter where the the enum is defined. | |
clang-tidy/modernize/PassByValueCheck.cpp | ||
121–122 | This is how clang-format formatted it. I used the following invocation: clang-format --style=LLVM MoveConstructorInitCheck.cpp > formatted And then diffed to only update format changes in code I added. | |
clang-tidy/utils/IncludeSorter.h | ||
29 | Done, although the argument type uniquely identifies a function. | |
clang-tidy/utils/Matchers.h | ||
2 | Thanks for catching that. Done. | |
clang-tidy/utils/TypeTraits.cpp | ||
33 | Done. Also demorganized the expression to make it easier to understand. | |
clang-tidy/utils/TypeTraits.h | ||
23 | Removed from header. |
The modernize-pass-by-value check does the same thing:
test/clang-tidy/misc-move-constructor-init.cpp:98:12: warning: pass by value and use std::move [modernize-pass-by-value] Positive(Movable M) : M_(M) {} ^ std::move( ) test/clang-tidy/misc-move-constructor-init.cpp:98:28: warning: value argument 'M' can be moved to avoid copy [misc-move-constructor-init] Positive(Movable M) : M_(M) {} ^
Do we need two checks for this?
This overlap is unfortunate. misc-move-constructor-init is for move constructor initializer lists which accidentally initialize a member or base through a copy constructor rather than a move constructor. Consider:
struct B { B(B&&); B(const B&); }; struct D { D(D &&d) : B(d) {} // Oops, calling B(const B&) not B(B&&) D(const D &d) : B(d) {}
modernize-pass-by-value is slightly different in that it is concerned with the parameter of an arbitrary function being something that is movable if you pass it by value instead of passing it by reference.
So misc-move-constructor-init cares about move constructor initializer lists while modernize-pass-by-value cares about the parameters of a function.
The main check is, but the additional sub-check added in this patch is already handled by modernize-pass-by-value.
modernize-pass-by-value is slightly different in that it is concerned with the parameter of an arbitrary function being something that is movable if you pass it by value instead of passing it by reference.
The doc for modernize-pass-by-value says:
Currently, only constructors are transformed to make use of pass-by-value. Contributions that handle other situations are welcome!
I suggest updating the corresponding .rst file instead and adding the URL of the documentation in the comment along with a short summary of what the check does.
For example: