[PATCH] D87244: [clang] Add fix-it for -Wreorder-ctor.

2021-03-06 Thread Jeroen Van Antwerpen via Phabricator via cfe-commits
Jeroen added inline comments.



Comment at: clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp:171
+  // code can't do this.
+  Bar(int x) : c(x), /*foo*/ a(2), b(3) {} // expected-warning {{field 'c' 
will be initialized after field 'a'}}
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]

What would be the behavior for `:c(x), b(3), a(2)`?
Does that get fixed in 1 go, or should this be done in 2 steps?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87244

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


[PATCH] D87244: [clang] Add fix-it for -Wreorder-ctor.

2020-09-09 Thread Kadir Cetinkaya via Phabricator via cfe-commits
kadircet added a comment.

thanks, comments around some implementations. the only high level question i 
have is about the choice of location for fix-it (see the detailed comment 
inline)




Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5198
+static bool RangeContainsComments(Sema , SourceLocation Begin,
+  SourceLocation End) {
+  auto  = SemaRef.getSourceManager();

nit: assert for fileids of begin and end at the beginning.



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5208
+  while (!Lex.LexFromRawLexer(Tok) &&
+ SrcMgr.isBeforeInTranslationUnit(Tok.getLocation(), End)) {
+if (Tok.is(tok::comment))

since Tok and End are in the same file you can just use `<` operator.



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5298
+  if (IdealIndex == NumIdealInits) {
+// This initializer is not in the ideal list at all. This can happen in
+// a class with dependent base, when we cannot tell if initializer is

i don't really follow when this happens. comments mentions a dependent base, 
but test case only has a templated constructor. why do we think it is safe to 
continue in this case, while we bail out in case of a dependent class? i would 
suggest just bailing out at the beginning of the function if constructor is 
dependent too (in addition to it's class being dependent)



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5311
+// previous (valid) initializer, emit a warning.
+if (IsOutOfOrder && !InitsWithIdealIndex.empty()) {
+  // Emit previous diagnostic, if any.

why not move this into previous if block i.e.

```
if (isoutoforder) {
 if(!Initswithidealindex.empty()) { ... }
 if(IdealIndex== NumIdealInits) { ..}
 if(!InitsWithIdealIndex.empty() {
// emit diag
  }
}
```



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5337
+
+  // If possible, add fix to the last diagnostic. The fix will reorder all
+  // initializers. This is preferable to attaching overlapping fixes for each

I totally agree with having a single fix-it to re-order all of the 
initializers, but I am not sure if last initializer is the best location for 
that fixit, especially for interactive tools (it is definitely the right 
behaviour for command line tools like clang and clang-tidy tho, as they'll 
either print the fix multiple times or try to apply conflicting fixes)

e.g. if user sees 3 out-of-order initializer warnings, they'll likely hover 
over one of them and say, "aaah" and fix the code themselves, if fixit wasn't 
available with that one.
Moreover when there's only 1 out-of-order initialzier warning, they'll see the 
fixit attached no matter what.
Next time, when there are multiple warnings they won't see it and will likely 
file bugs saying that "clang(d) doesn't generate fixes all the time".

But I also don't have any better suggestions, as having it multiple times will 
definitely be helpful for interactive tools but will regress the others.. Maybe 
attaching it to the first initializer might be better overall?



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5348
+  for (const auto  : Inits) {
+if (Init->getSourceLocation().isMacroID() ||
+!SrcMgr.isWrittenInSameFile(Init->getSourceLocation(),

can we have some tests for this case?



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5355
+  }
+  if (ShouldEmitFix)
+ShouldEmitFix =

nit: `ShouldEmitFix = ShouldEmitFix && !Range...`



Comment at: clang/lib/Sema/SemaDeclCXX.cpp:5358
+!RangeContainsComments(SemaRef, Inits.front()->getSourceLocation(),
+   Inits.back()->getSourceLocation());
 

what if there are comments after last initializer ? I think we should rather 
use LBraceLoc for the constructor in here.



Comment at: clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp:139
+struct Y {
+  template  Y(T x) : a(), X(x) {}
+  Foo a;

i think it is enough to have `int a;` as a member do we really need a separate 
type? + why don't we just merge this with the previous case?


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87244

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


[PATCH] D87244: [clang] Add fix-it for -Wreorder-ctor.

2020-09-07 Thread Adam Czachorowski via Phabricator via cfe-commits
adamcz updated this revision to Diff 290313.
adamcz added a comment.

Add a missing const


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87244

Files:
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp

Index: clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
===
--- clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
+++ clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1  -fsyntax-only -Wreorder -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -Wreorder %s 2>&1 | FileCheck %s
 
 struct BB {};
 
@@ -122,6 +123,9 @@
 }
 
 namespace PR7179 {
+struct Foo {
+  Foo();
+};
   struct X
   {
 struct Y
@@ -129,6 +133,13 @@
   template  Y(T x) : X(x) { }
 };
   };
+
+  struct X2 {
+struct Y {
+  template  Y(T x) : a(), X(x) {}
+  Foo a;
+};
+  };
 }
 
 namespace test3 {
@@ -141,3 +152,23 @@
 }
   };
 }
+
+namespace fix {
+struct Foo {
+  Foo(int) {}
+};
+struct Bar {
+  Foo a, b, c;
+
+  Bar() : c(1), a(2), b(3) {} // expected-warning {{field 'c' will be initialized after field 'a'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:17}:"a(2)"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:23}:"b(3)"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:25-[[@LINE-3]]:29}:"c(1)"
+
+  // Check that we do not generate fix it when comments are present. Ideally
+  // we would, but comments would need to be reordered as well and the current
+  // code can't do this.
+  Bar(int x) : c(x), /*foo*/ a(2), b(3) {} // expected-warning {{field 'c' will be initialized after field 'a'}}
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+};
+} // namespace fix
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -5192,6 +5192,26 @@
   return Member->getAnyMember()->getCanonicalDecl();
 }
 
+// Returns true iff there are comments between Begin and End. Expects that both
+// Begin and End are in the same file.
+static bool RangeContainsComments(Sema , SourceLocation Begin,
+  SourceLocation End) {
+  auto  = SemaRef.getSourceManager();
+  auto BeginLocInfo = SrcMgr.getDecomposedLoc(Begin);
+  StringRef File = SrcMgr.getBufferData(BeginLocInfo.first);
+  Lexer Lex(SrcMgr.getLocForStartOfFile(BeginLocInfo.first),
+SemaRef.getLangOpts(), File.begin(),
+File.begin() + BeginLocInfo.second, File.end());
+  Lex.SetCommentRetentionState(true);
+  Token Tok;
+  while (!Lex.LexFromRawLexer(Tok) &&
+ SrcMgr.isBeforeInTranslationUnit(Tok.getLocation(), End)) {
+if (Tok.is(tok::comment))
+  return true;
+  }
+  return false;
+}
+
 static void DiagnoseBaseOrMemInitializerOrder(
 Sema , const CXXConstructorDecl *Constructor,
 ArrayRef Inits) {
@@ -5241,7 +5261,20 @@
   unsigned NumIdealInits = IdealInitKeys.size();
   unsigned IdealIndex = 0;
 
-  CXXCtorInitializer *PrevInit = nullptr;
+  // Instead of emitting diagnostic right away, we keep it here and only emit
+  // when we find a new one. The last one is emitted outside of this loop, with
+  // a FixIt attached.
+  PartialDiagnosticAt PDiag = {SourceLocation(),
+   PartialDiagnostic::NullDiagnostic()};
+  // Pairs {IdealIndex, pointer-to-initializer}, valid (i.e. found in
+  // IdealInitKeys) initializers only. Last value is used to generate
+  // diagnostics, the index is then used to sort valid initializers in
+  // initialization order.
+  SmallVector, 32>
+  InitsWithIdealIndex;
+  // MissingInIdeal[I] == true iff Inits[I] is not found in ideal list.
+  llvm::SmallBitVector MissingInIdeal(Inits.size());
+
   for (unsigned InitIndex = 0; InitIndex != Inits.size(); ++InitIndex) {
 CXXCtorInitializer *Init = Inits[InitIndex];
 const void *InitKey = GetKeyForMember(SemaRef.Context, Init);
@@ -5252,35 +5285,100 @@
   if (InitKey == IdealInitKeys[IdealIndex])
 break;
 
-// If we didn't find this initializer, it must be because we
-// scanned past it on a previous iteration.  That can only
-// happen if we're out of order;  emit a warning.
-if (IdealIndex == NumIdealInits && PrevInit) {
-  Sema::SemaDiagnosticBuilder D =
-SemaRef.Diag(PrevInit->getSourceLocation(),
- diag::warn_initializer_out_of_order);
+const bool IsOutOfOrder = IdealIndex == NumIdealInits;
+if (IsOutOfOrder) {
+  if (!InitsWithIdealIndex.empty()) {
+// This is not the first initializer we process. It's either not in the
+// ideal list at all, or it's out of order. Scan from the beginning.
+for (IdealIndex = 0; IdealIndex != NumIdealInits; ++IdealIndex)
+  if (InitKey == 

[PATCH] D87244: [clang] Add fix-it for -Wreorder-ctor.

2020-09-07 Thread Adam Czachorowski via Phabricator via cfe-commits
adamcz updated this revision to Diff 290312.
adamcz added a comment.

fixed comment typo


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D87244

Files:
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp

Index: clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
===
--- clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
+++ clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1  -fsyntax-only -Wreorder -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -Wreorder %s 2>&1 | FileCheck %s
 
 struct BB {};
 
@@ -122,6 +123,9 @@
 }
 
 namespace PR7179 {
+struct Foo {
+  Foo();
+};
   struct X
   {
 struct Y
@@ -129,6 +133,13 @@
   template  Y(T x) : X(x) { }
 };
   };
+
+  struct X2 {
+struct Y {
+  template  Y(T x) : a(), X(x) {}
+  Foo a;
+};
+  };
 }
 
 namespace test3 {
@@ -141,3 +152,23 @@
 }
   };
 }
+
+namespace fix {
+struct Foo {
+  Foo(int) {}
+};
+struct Bar {
+  Foo a, b, c;
+
+  Bar() : c(1), a(2), b(3) {} // expected-warning {{field 'c' will be initialized after field 'a'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:17}:"a(2)"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:23}:"b(3)"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:25-[[@LINE-3]]:29}:"c(1)"
+
+  // Check that we do not generate fix it when comments are present. Ideally
+  // we would, but comments would need to be reordered as well and the current
+  // code can't do this.
+  Bar(int x) : c(x), /*foo*/ a(2), b(3) {} // expected-warning {{field 'c' will be initialized after field 'a'}}
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+};
+} // namespace fix
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -5192,6 +5192,26 @@
   return Member->getAnyMember()->getCanonicalDecl();
 }
 
+// Returns true iff there are comments between Begin and End. Expects that both
+// Begin and End are in the same file.
+static bool RangeContainsComments(Sema , SourceLocation Begin,
+  SourceLocation End) {
+  auto  = SemaRef.getSourceManager();
+  auto BeginLocInfo = SrcMgr.getDecomposedLoc(Begin);
+  StringRef File = SrcMgr.getBufferData(BeginLocInfo.first);
+  Lexer Lex(SrcMgr.getLocForStartOfFile(BeginLocInfo.first),
+SemaRef.getLangOpts(), File.begin(),
+File.begin() + BeginLocInfo.second, File.end());
+  Lex.SetCommentRetentionState(true);
+  Token Tok;
+  while (!Lex.LexFromRawLexer(Tok) &&
+ SrcMgr.isBeforeInTranslationUnit(Tok.getLocation(), End)) {
+if (Tok.is(tok::comment))
+  return true;
+  }
+  return false;
+}
+
 static void DiagnoseBaseOrMemInitializerOrder(
 Sema , const CXXConstructorDecl *Constructor,
 ArrayRef Inits) {
@@ -5241,7 +5261,20 @@
   unsigned NumIdealInits = IdealInitKeys.size();
   unsigned IdealIndex = 0;
 
-  CXXCtorInitializer *PrevInit = nullptr;
+  // Instead of emitting diagnostic right away, we keep it here and only emit
+  // when we find a new one. The last one is emitted outside of this loop, with
+  // a FixIt attached.
+  PartialDiagnosticAt PDiag = {SourceLocation(),
+   PartialDiagnostic::NullDiagnostic()};
+  // Pairs {IdealIndex, pointer-to-initializer}, valid (i.e. found in
+  // IdealInitKeys) initializers only. Last value is used to generate
+  // diagnostics, the index is then used to sort valid initializers in
+  // initialization order.
+  SmallVector, 32>
+  InitsWithIdealIndex;
+  // MissingInIdeal[I] == true iff Inits[I] is not found in ideal list.
+  llvm::SmallBitVector MissingInIdeal(Inits.size());
+
   for (unsigned InitIndex = 0; InitIndex != Inits.size(); ++InitIndex) {
 CXXCtorInitializer *Init = Inits[InitIndex];
 const void *InitKey = GetKeyForMember(SemaRef.Context, Init);
@@ -5252,35 +5285,100 @@
   if (InitKey == IdealInitKeys[IdealIndex])
 break;
 
-// If we didn't find this initializer, it must be because we
-// scanned past it on a previous iteration.  That can only
-// happen if we're out of order;  emit a warning.
-if (IdealIndex == NumIdealInits && PrevInit) {
-  Sema::SemaDiagnosticBuilder D =
-SemaRef.Diag(PrevInit->getSourceLocation(),
- diag::warn_initializer_out_of_order);
+bool IsOutOfOrder = IdealIndex == NumIdealInits;
+if (IsOutOfOrder) {
+  if (!InitsWithIdealIndex.empty()) {
+// This is not the first initializer we process. It's either not in the
+// ideal list at all, or it's out of order. Scan from the beginning.
+for (IdealIndex = 0; IdealIndex != NumIdealInits; ++IdealIndex)
+  if (InitKey == 

[PATCH] D87244: [clang] Add fix-it for -Wreorder-ctor.

2020-09-07 Thread Adam Czachorowski via Phabricator via cfe-commits
adamcz created this revision.
Herald added subscribers: cfe-commits, mgrang.
Herald added a project: clang.
adamcz requested review of this revision.

This version is very limited. It does not work when comments are present
anywhere in the initializer list, since we do not have a good way to
associate them to specific initalizer and move them around.

The fix-it is associated with the last ctor-order diagnostic and
reorders all initializers. This is better than providing fix for each
diagnostic generated, since user would have to apply them one-by-one.

This also fixes a bug that would trigger an assert() in dependent
classes, when initializer present in code might not be found in the
idealized list of initializers. That is related to PR7179.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D87244

Files:
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp

Index: clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
===
--- clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
+++ clang/test/SemaCXX/warn-reorder-ctor-initialization.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1  -fsyntax-only -Wreorder -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -Wreorder %s 2>&1 | FileCheck %s
 
 struct BB {};
 
@@ -122,6 +123,9 @@
 }
 
 namespace PR7179 {
+struct Foo {
+  Foo();
+};
   struct X
   {
 struct Y
@@ -129,6 +133,13 @@
   template  Y(T x) : X(x) { }
 };
   };
+
+  struct X2 {
+struct Y {
+  template  Y(T x) : a(), X(x) {}
+  Foo a;
+};
+  };
 }
 
 namespace test3 {
@@ -141,3 +152,23 @@
 }
   };
 }
+
+namespace fix {
+struct Foo {
+  Foo(int) {}
+};
+struct Bar {
+  Foo a, b, c;
+
+  Bar() : c(1), a(2), b(3) {} // expected-warning {{field 'c' will be initialized after field 'a'}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:17}:"a(2)"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:19-[[@LINE-2]]:23}:"b(3)"
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:25-[[@LINE-3]]:29}:"c(1)"
+
+  // Check that we do not generate fix it when comments are present. Ideally
+  // we would, but comments would need to be reordered as well and the current
+  // code can't do this.
+  Bar(int x) : c(x), /*foo*/ a(2), b(3) {} // expected-warning {{field 'c' will be initialized after field 'a'}}
+  // CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]
+};
+} // namespace fix
Index: clang/lib/Sema/SemaDeclCXX.cpp
===
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -5192,6 +5192,26 @@
   return Member->getAnyMember()->getCanonicalDecl();
 }
 
+// Returns true iff there are comments between Begin and End. Expects that both
+// Begin and End are in the same file.
+static bool RangeContainsComments(Sema , SourceLocation Begin,
+  SourceLocation End) {
+  auto  = SemaRef.getSourceManager();
+  auto BeginLocInfo = SrcMgr.getDecomposedLoc(Begin);
+  StringRef File = SrcMgr.getBufferData(BeginLocInfo.first);
+  Lexer Lex(SrcMgr.getLocForStartOfFile(BeginLocInfo.first),
+SemaRef.getLangOpts(), File.begin(),
+File.begin() + BeginLocInfo.second, File.end());
+  Lex.SetCommentRetentionState(true);
+  Token Tok;
+  while (!Lex.LexFromRawLexer(Tok) &&
+ SrcMgr.isBeforeInTranslationUnit(Tok.getLocation(), End)) {
+if (Tok.is(tok::comment))
+  return true;
+  }
+  return false;
+}
+
 static void DiagnoseBaseOrMemInitializerOrder(
 Sema , const CXXConstructorDecl *Constructor,
 ArrayRef Inits) {
@@ -5241,7 +5261,20 @@
   unsigned NumIdealInits = IdealInitKeys.size();
   unsigned IdealIndex = 0;
 
-  CXXCtorInitializer *PrevInit = nullptr;
+  // Instead of emitting diagnostic right away, we keep it here and only emit
+  // when we find a new one. The last one is emitted outsdie of this loop, with
+  // a FixIt attached.
+  PartialDiagnosticAt PDiag = {SourceLocation(),
+   PartialDiagnostic::NullDiagnostic()};
+  // Pairs {IdealIndex, pointer-to-initializer}, valid (i.e. found in
+  // IdealInitKeys) initializers only. Last value is used to generate
+  // diagnostics, the index is then used to sort valid initializers in
+  // initialization order.
+  SmallVector, 32>
+  InitsWithIdealIndex;
+  // MissingInIdeal[I] == true iff Inits[I] is not found in ideal list.
+  llvm::SmallBitVector MissingInIdeal(Inits.size());
+
   for (unsigned InitIndex = 0; InitIndex != Inits.size(); ++InitIndex) {
 CXXCtorInitializer *Init = Inits[InitIndex];
 const void *InitKey = GetKeyForMember(SemaRef.Context, Init);
@@ -5252,35 +5285,100 @@
   if (InitKey == IdealInitKeys[IdealIndex])
 break;
 
-// If we didn't find this initializer, it must be because we
-// scanned past it on a previous iteration.  That can only
-// happen if we're out of order;  emit a warning.
-if