[PATCH] D138031: Add clang-tidy check for missing move constructors

2022-11-15 Thread Martin Bidlingmaier via Phabricator via cfe-commits
mbid created this revision.
Herald added a subscriber: carlosgalvezp.
Herald added a reviewer: njames93.
Herald added a project: All.
mbid requested review of this revision.
Herald added projects: clang, clang-tools-extra.
Herald added a subscriber: cfe-commits.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D138031

Files:
  clang-tools-extra/clang-tidy/performance/CMakeLists.txt
  clang-tools-extra/clang-tidy/performance/MissingMoveConstructorCheck.cpp
  clang-tools-extra/clang-tidy/performance/MissingMoveConstructorCheck.h
  clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/docs/clang-tidy/checks/performance/missing-move-constructor.rst
  
clang-tools-extra/test/clang-tidy/checkers/performance/missing-move-constructor.cpp
  clang/include/clang/Lex/Token.h

Index: clang/include/clang/Lex/Token.h
===
--- clang/include/clang/Lex/Token.h
+++ clang/include/clang/Lex/Token.h
@@ -96,9 +96,7 @@
   /// "if (Tok.is(tok::l_brace)) {...}".
   bool is(tok::TokenKind K) const { return Kind == K; }
   bool isNot(tok::TokenKind K) const { return Kind != K; }
-  bool isOneOf(tok::TokenKind K1, tok::TokenKind K2) const {
-return is(K1) || is(K2);
-  }
+  bool isOneOf() const { return false; }
   template  bool isOneOf(tok::TokenKind K1, Ts... Ks) const {
 return is(K1) || isOneOf(Ks...);
   }
Index: clang-tools-extra/test/clang-tidy/checkers/performance/missing-move-constructor.cpp
===
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/performance/missing-move-constructor.cpp
@@ -0,0 +1,99 @@
+// RUN: %check_clang_tidy %s performance-missing-move-constructor %t
+
+class OffendingDtor  {
+public:
+  ~OffendingDtor() = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit destructor prevents generation of implicit move constructor and move assignment operator [performance-missing-move-constructor]
+};
+
+class OffendingCopyCtor  {
+public:
+  OffendingCopyCtor(const OffendingCopyCtor &) = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit copy constructor prevents generation of implicit move constructor and move assignment operator [performance-missing-move-constructor]
+};
+
+class OffendingMoveCtor  {
+public:
+  OffendingMoveCtor(OffendingMoveCtor &&) = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit move constructor prevents generation of implicit move assignment operator [performance-missing-move-constructor]
+
+  // Copy assignment operator is not implicitly defined because of the explicit
+  // move constructor.
+  OffendingMoveCtor &operator=(const OffendingMoveCtor &) = default;
+};
+
+class OffendingCopyAssign  {
+public:
+  OffendingCopyAssign &operator=(const OffendingCopyAssign &) = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit copy assignment operator prevents generation of implicit move constructor and move assignment operator [performance-missing-move-constructor]
+};
+
+class OffendingMoveAssign  {
+public:
+  // Copy constructor is not implicitly defined because of the explicit move
+  // assignment operator. This means that the warning appears here, because the
+  // copy constructor has higher priority in where we print than the move
+  // assignment operator.
+  OffendingMoveAssign(const OffendingMoveAssign &) = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit copy constructor prevents generation of implicit move constructor [performance-missing-move-constructor]
+
+  OffendingMoveAssign &operator=(OffendingMoveAssign &&) = default;
+};
+
+
+class InlineFix {
+public:
+  InlineFix(const InlineFix &) = default;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit copy constructor prevents generation of implicit move constructor and move assignment operator [performance-missing-move-constructor]
+  // CHECK-FIXES: InlineFix(InlineFix &&) = default;
+
+  InlineFix& operator=(const InlineFix &) = default;
+  // CHECK-FIXES: InlineFix &operator=(InlineFix &&) = default;
+};
+
+namespace N {
+
+class OutOfLineFix {
+public:
+  OutOfLineFix(const OutOfLineFix &);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: explicit copy constructor prevents generation of implicit move constructor and move assignment operator [performance-missing-move-constructor]
+  // CHECK-FIXES: OutOfLineFix(OutOfLineFix &&);
+
+  OutOfLineFix& operator=(const OutOfLineFix &);
+  // CHECK-FIXES: OutOfLineFix &operator=(OutOfLineFix &&);
+};
+
+}
+
+N::OutOfLineFix::OutOfLineFix(const OutOfLineFix &) = default;
+// CHECK-FIXES: N::OutOfLineFix::OutOfLineFix(OutOfLineFix &&) = default;
+
+N::OutOfLineFix &N::OutOfLineFix::operator=(const OutOfLineFix &) = default;
+// CHECK-FIXES: N::OutOfLineFix &N::OutOfLineFix::operator=(OutOfLineFix &&) = default;
+
+class InlineFix

[PATCH] D138031: Add clang-tidy check for missing move constructors

2022-11-15 Thread Martin Bidlingmaier via Phabricator via cfe-commits
mbid added inline comments.



Comment at: clang/include/clang/Lex/Token.h:101
-return is(K1) || is(K2);
-  }
   template  bool isOneOf(tok::TokenKind K1, Ts... Ks) const {

This change is necessary to make `isOneOf` work with a single argument (and 
also no arguments). isOneOf is called from findNextAnyTokenKind, which is used 
in this patch with a single token type.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D138031/new/

https://reviews.llvm.org/D138031

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D138035: Fix clang-tidy util findNextTokenSkippingComments

2022-11-15 Thread Martin Bidlingmaier via Phabricator via cfe-commits
mbid created this revision.
Herald added a subscriber: carlosgalvezp.
Herald added a reviewer: njames93.
Herald added a project: All.
mbid requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

The function did not update the Start source location, resulting in an
infinite loop if the first token was a comment token.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D138035

Files:
  clang-tools-extra/clang-tidy/utils/LexerUtils.cpp


Index: clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
===
--- clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
+++ clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
@@ -81,9 +81,13 @@
   const SourceManager &SM,
   const LangOptions &LangOpts) {
   Optional CurrentToken;
-  do {
+  while (true) {
 CurrentToken = Lexer::findNextToken(Start, SM, LangOpts);
-  } while (CurrentToken && CurrentToken->is(tok::comment));
+if (!CurrentToken || !CurrentToken->is(tok::comment)) {
+  break;
+}
+Start = CurrentToken->getLastLoc();
+  }
   return CurrentToken;
 }
 


Index: clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
===
--- clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
+++ clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
@@ -81,9 +81,13 @@
   const SourceManager &SM,
   const LangOptions &LangOpts) {
   Optional CurrentToken;
-  do {
+  while (true) {
 CurrentToken = Lexer::findNextToken(Start, SM, LangOpts);
-  } while (CurrentToken && CurrentToken->is(tok::comment));
+if (!CurrentToken || !CurrentToken->is(tok::comment)) {
+  break;
+}
+Start = CurrentToken->getLastLoc();
+  }
   return CurrentToken;
 }
 
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D138031: Add clang-tidy check for missing move constructors

2022-11-17 Thread Martin Bidlingmaier via Phabricator via cfe-commits
mbid added a comment.

Hi Nathan, thanks for looking into this. I did indeed not know about the 
existing check in cppcoreguidelines -- maybe I should've been more suspicious 
about assuming something so obvious supposedly doesn't exist yet :)

But perhaps the replacements could still be integrated into the existing check. 
Note that replacements in my patch are generated only if you have a defaulted 
copying version. So e.g. the defaulted move constructor is emitted only if the 
class has  a default copy constructor. This can still introduce bugs, but I 
think the probability for that is fairly low.

For context, I'm trying to run this on the chromium codebase, where copy 
constructors are required to be defined (even if = default) in the .cpp file 
for all non-trivial classes, which is why the implicit move constructors are 
often missing. I want to understand what the performance impact of that is.

> the replacements that this check generates would often not improve performance

Why would you expect there to be no improvement? If there's a default copy 
constructor and hence no (implicit) move constructor, than the more expensive 
copying version will be invoked when a move would be sufficient.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D138031/new/

https://reviews.llvm.org/D138031

___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D139122: Generalize clang-tidy modernize-pass-by-value

2022-12-01 Thread Martin Bidlingmaier via Phabricator via cfe-commits
mbid created this revision.
Herald added a subscriber: carlosgalvezp.
Herald added a reviewer: njames93.
Herald added a project: All.
mbid added a comment.
mbid published this revision for review.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

Hi Nathan,

Could you have a look at whether something like this looks reasonable? The docs 
of modernize-pass-by-value currently say that a patch that generalizes this 
check is welcome. I'd polish the docs and add some more test cases before merge 
if this looks reasonable overall.

- Martin


Previously, the clang-tidy modernize-pass-by-value check would only fire
for arguments of constructors that are used exactly once and in a member
or base class initializers. This commit generalizes the check to general
functions and usages in the body of the function.

We now consider the control-flow graph of the function. If every
control-flow path must transition through one final usage of a given
parameter, we fire the diagnostic. We additionally require that the
variable is not referenced or pointed to by a local variable in the
function. There are still some false-positives, however, because we do
not try to figure out whether a pointer to the local variable escapes
through other means, e.g. here:

  struct HasStrPtr {
const std::string* str;
  };
  
  void set_ptr(HasStrPtr& has_ptr, const std::string& str) {
has_ptr.str = &str;
  }
  
  // Take argument by value.
  void foo(std::string);
  
  void foo(const std::string& str) {
HasStrPtr obj;
set_ptr(obj, str);
foo(str);
foo(*HasStrPtr.str);
  }

The example looks at first sight like we can std::move in the call to
foo, but this would change the value pointed to by the pointer in
HasStrPtr, so that the semantics of the program change.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D139122

Files:
  clang-tools-extra/clang-tidy/modernize/PassByValueCheck.cpp
  clang-tools-extra/clang-tidy/modernize/PassByValueCheck.h
  clang-tools-extra/docs/clang-tidy/checks/modernize/pass-by-value.rst
  clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value.cpp
===
--- clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value.cpp
+++ clang-tools-extra/test/clang-tidy/checkers/modernize/pass-by-value.cpp
@@ -11,6 +11,8 @@
   Movable() = default;
   Movable(const Movable &) {}
   Movable(Movable &&) {}
+  Movable& operator=(const Movable&) = default;
+  Movable& operator=(Movable&&) = default;
 };
 
 struct NotMovable {
@@ -19,12 +21,18 @@
   NotMovable(NotMovable &&) = delete;
   int a, b, c;
 };
+
+struct MovableConstructible {
+  MovableConstructible() = default;
+  MovableConstructible(Movable m) {}
+};
+
 }
 
 struct A {
-  A(const Movable &M) : M(M) {}
+  A(const Movable &MM) : M(MM) {}
   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value]
-  // CHECK-FIXES: A(Movable M) : M(std::move(M)) {}
+  // CHECK-FIXES: A(Movable MM) : M(std::move(MM)) {}
   Movable M;
 };
 
@@ -36,7 +44,88 @@
   Movable M;
 };
 
-// Test that a parameter with more than one reference to it won't be changed.
+// Tests a single usage in function with trivial control flow graph.
+void straightLineManual(const Movable &m) {
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: pass by value and use std::move [modernize-pass-by-value]
+  // CHECK-FIXES: void straightLineManual(Movable m) {
+  MovableConstructible mc{m};
+  // CHECK-FIXES: MovableConstructible mc{std::move(m)};
+}
+
+// Tests a unique usage in a return statement.
+Movable automatic(const Movable &m) {
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: pass by value and use std::move [modernize-pass-by-value]
+  // CHECK-FIXES: Movable automatic(Movable m) {
+  return (m);
+  // CHECK-FIXES: return (m);
+}
+
+// Tests a unique usage in a return statement with an implicit conversion.
+MovableConstructible automaticWithImplicitConversion(const Movable &m) {
+  // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: pass by value and use std::move [modernize-pass-by-value]
+  // CHECK-FIXES: MovableConstructible automaticWithImplicitConversion(Movable m) {
+  return m;
+  // CHECK-FIXES: return m;
+}
+
+// Tests that we don't emit a diagnostic if the variable is pointed to by a
+// local variable.
+const Movable& pointed(const Movable& m) {
+  // CHECK-FIXES: const Movable& pointed(const Movable& m) {
+  const Movable* ptr = &m;
+  MovableConstructible mc{m};
+  return *ptr;
+}
+
+// Same as `pointed` but with a reference.
+const Movable& referenced(const Movable& m) {
+  // CHECK-FIXES: const Movable& referenced(const Movable& m) {
+  const Movable& ref = m;
+  MovableConstructible mc{m};
+  return ref;
+}
+
+// Tests that we emit a diagnostic for more than one final usages due to `if` blocks.
+MovableConstructible branchingIf(co