This is an archive of the discontinued LLVM Phabricator instance.

Extend MoveConstructorInitCheck to also flag constructor arguments passed by value and can be moved assigned to fields.
ClosedPublic

Authored by flx on Sep 14 2015, 8:03 AM.

Details

Summary

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.

Diff Detail

Event Timeline

flx updated this revision to Diff 34670.Sep 14 2015, 8:03 AM
flx retitled this revision from to Extend MoveConstructorInitCheck to also flag constructor arguments passed by value and can be moved assigned to fields..
flx updated this object.
flx added reviewers: alexfh, sbenza, aaron.ballman.
flx added a subscriber: cfe-commits.

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.

106

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

alexfh edited edge metadata.Sep 15 2015, 5:24 PM

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.

86

nit: It's common to omit braces around single-line statements in LLVM/Clang code.

106

nit: Please remove the trailing period.

106

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
flx updated this revision to Diff 35204.Sep 20 2015, 10:43 AM
flx edited edge metadata.
flx marked 10 inline comments as done.
flx added inline comments.
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?

106

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.

aaron.ballman added inline comments.Sep 21 2015, 6:40 AM
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?

alexfh added inline comments.Sep 22 2015, 6:56 AM
clang-tidy/misc/MoveConstructorInitCheck.cpp
106

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.

flx updated this revision to Diff 35826.Sep 27 2015, 2:30 PM
flx marked 7 inline comments as done.

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?

In D12839#254384, @flx wrote:

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
110

/*IsAngled=*/true)) {

clang-tidy/modernize/PassByValueCheck.cpp
121–122

clang-format?

clang-tidy/modernize/ReplaceAutoPtrCheck.cpp
191–192

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.

flx updated this revision to Diff 36049.Sep 29 2015, 4:25 PM
flx marked 3 inline comments as done.

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.

alexfh accepted this revision.Sep 29 2015, 6:12 PM
alexfh edited edge metadata.

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?

This revision is now accepted and ready to land.Sep 29 2015, 6:12 PM
In D12839#256179, @flx wrote:

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.

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.

flx updated this revision to Diff 36151.Sep 30 2015, 3:21 PM
flx edited edge metadata.
flx marked an inline comment as done.

Thanks for catching the last issues, Alex. I rebuilt and ran all tests using llvm-lit -v . for clang-tidy and they pass.

alexfh added a comment.Oct 1 2015, 2:48 AM

The patch doesn't apply cleanly. Please update it.

flx updated this revision to Diff 36433.Oct 3 2015, 10:18 AM

Updated patch to new cxx* matcher variants.

aaron.ballman accepted this revision.Oct 6 2015, 8:53 AM
aaron.ballman edited edge metadata.

LGTM!

Do you have commit access, or would you like me to commit on your behalf?

flx added a comment.Oct 6 2015, 9:15 AM

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.

106

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.

aaron.ballman closed this revision.Oct 6 2015, 9:29 AM

Thank you for the patch! I've commit in r249429.

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?

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.

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:

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!