This is an archive of the discontinued LLVM Phabricator instance.

[clang-tidy] misc-move-const-arg: Detect if result of std::move() is being passed as a const ref argument
ClosedPublic

Authored by mboehme on Jun 10 2016, 2:56 AM.

Details

Summary

Conceptually, this is very close to the existing functionality of misc-move-const-arg, which is why I'm adding it here and not creating a new check. For example, for a type A that is both movable and copyable, this

const A a1;
A a2(std::move(a1));

is not only a case where a const argument is being passed to std::move(), but the result of std::move() is also being passed as a const reference (due to overload resolution).

The new check typically triggers (exclusively) in cases where people think they're dealing with a movable type, but in fact the type is not movable.

Diff Detail

Event Timeline

mboehme updated this revision to Diff 60327.Jun 10 2016, 2:56 AM
mboehme retitled this revision from to [clang-tidy] misc-move-const-arg: Detect if result of std::move() is being passed as a const ref argument.
mboehme updated this object.
mboehme added reviewers: alexfh, hokein.
mboehme added a subscriber: cfe-commits.
aaron.ballman added a subscriber: aaron.ballman.
aaron.ballman added inline comments.
clang-tidy/misc/MoveConstantArgumentCheck.cpp
20

Instead of an unnamed namespace, you should use static functions per our coding standards (http://llvm.org/docs/CodingStandards.html#anonymous-namespaces).

22

Should pass Diag by reference rather than by pointer.

26

Should only use auto when the type is explicitly spelled out in the initialization (which this is not). Same elsewhere.

alexfh edited edge metadata.Jun 13 2016, 4:59 AM

Thank you for working on this! A few more comments in addition to what Aaron has written.

clang-tidy/misc/MoveConstantArgumentCheck.cpp
22

I'd prefer the parameter to be named Call instead of TheCallExpr. Would make it more consistent with the rest of clang-tidy code.

37

Alternatively, this function could return a llvm::SmallVector<FixItHint, 2> (either empty or with the fixes) and the caller could just feed it to the result of diag(). Not much difference though, just pointing to this possibility.

42

With two lines of implementation and just one caller, this function doesn't seem to add any value.

116

Is this equivalent to just ignoring all cases where CallMove->getLocStart().isMacroID()? If not, what's the subset of the cases that would be ignored by isMacroID(), but not with the current condition?

mboehme updated this revision to Diff 60506.Jun 13 2016, 5:26 AM
mboehme edited edge metadata.
mboehme updated this revision to Diff 60507.Jun 13 2016, 5:28 AM
mboehme updated this revision to Diff 60523.Jun 13 2016, 7:36 AM
mboehme marked 4 inline comments as done.
mboehme added inline comments.Jun 13 2016, 7:40 AM
clang-tidy/misc/MoveConstantArgumentCheck.cpp
37

Thanks for the suggestion! If it's OK with you, though, I'd like to keep this as-is -- I feel it makes the call-site clearer.

42

I've inlined the function. (I think I originally had two places where it got called, so I factored it out, but then one of those places went away.)

116

Actually, having looked into this some more, it seems that the macro cases are already handled by the check "if (!FileMoveRange.isValid())" above -- so I'm deleting this entire block of code.

(I think I added the isMacroBodyExpansion() and isMacroArgExpansion() checks first before I moved the FileMoveRange.isValid() check, which then made them unnecessary.)

aaron.ballman added inline comments.Jun 13 2016, 11:10 AM
test/clang-tidy/misc-move-const-arg.cpp
75–76

This type isn't non-moveable. For that, you need to explicitly delete the move constructor. Perhaps a better name is NonMoveConstructible?

Also, no need for the public access specifier.

116

No need for the public access specifier.

mboehme updated this revision to Diff 60671.Jun 14 2016, 6:11 AM
mboehme added inline comments.Jun 14 2016, 6:21 AM
test/clang-tidy/misc-move-const-arg.cpp
75–76

This type isn't non-moveable. For that, you need to explicitly delete the move constructor.

Can you expand on this?

The standard says: "If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if - X does not have a user-declared copy constructor [...]" (12.8/9).

Because I'm declaring a copy constructor, I would thus have expected not to get an implicitly-declared move constructor. Where am I going wrong here?

Perhaps a better name is NonMoveConstructible?

But the type _is_ move-constructible (in the sense of std::is_move_constructible<>).

Also, no need for the public access specifier.

Oops -- thanks for catching this.

Actually, I meant to make this a class -- not sure how the "struct" slipped in there.

116

Thanks for cathching this -- as above, I meant to make this a class.

aaron.ballman added inline comments.Jun 14 2016, 6:54 AM
test/clang-tidy/misc-move-const-arg.cpp
75–76

Can you expand on this?

The standard says: "If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if - X does not have a user-declared copy constructor [...]" (12.8/9).

Because I'm declaring a copy constructor, I would thus have expected not to get an implicitly-declared move constructor. Where am I going wrong here?

Move operations are optimized versions of a copy operation, so a failed move operation can fall back to perform a copy operation instead and achieve the same operational semantics but with slightly worse performance. Because you have a copy constructor, you do not get an implicitly-declared move constructor (so that's correct), but that doesn't mean the type cannot be used in a context where the user expects a move (i.e., calling std::move() on it) -- the operation just falls back on the copy constructor.

Basically, there's an operational difference between not having the implicitly-declared move constructor and having a deleted move constructor. In the former, fallback to copy happens and in the latter you get a diagnostic. So when I hear "non-moveable type", my brain assumes you mean "get a diagnostic when you try to move."

mboehme updated this revision to Diff 60686.Jun 14 2016, 7:35 AM
mboehme added inline comments.Jun 14 2016, 7:40 AM
test/clang-tidy/misc-move-const-arg.cpp
75–76

Ah, now I see what you mean.

I've renamed "NonMoveable" to "NoMoveSemantics" (and "Moveable" to "MoveSemantics" for consistency). What do you think?

(I don't like NonMoveConstructible because it's prone to the same ambiguity -- or even more so, because std::is_move_constructible<> uses exactly the same term and the type is move constructible in the sense of that trait.)

aaron.ballman accepted this revision.Jun 16 2016, 7:08 AM
aaron.ballman edited edge metadata.

LGTM, but you should wait for @alexfh to sign off as well since he had some comments.

This revision is now accepted and ready to land.Jun 16 2016, 7:08 AM
alexfh accepted this revision.Jun 16 2016, 7:22 AM
alexfh edited edge metadata.

LG. Thanks!

BTW, I'll commit the patch for you.

This revision was automatically updated to reflect the committed changes.