This looks really great. Let's start getting bits of this checked in!

  I've marked a few pieces of this patch that I'd like to be checked in as 
separate pieces (to avoid the big monolithic change here). In addition to 
those, can you factor out the `CXXDefaultInitExpr` handling code into a 
separate patch, too?

  Additionally, now we've basically converged on the functional side, there's a 
couple of coding standard issues that should be sorted out prior to commit. 
Variable names should be capitalized (don't worry about pre-existing variables 
in this file, it's currently a big mess with regard to coding style issues), 
and you have a "* " in `ClassifyRefs::VisitMemberExpr`.

  After getting the side-pieces committed, we can circle back to the bulk of 
this patch. There's only really one substantive comment that I have there (and 
I think it's something you said you're already working on): it's hard to tell 
if `CXXThisExpr` is being handled appropriately in all cases, and I'd like for 
it to be tracked with a `ClassifyRefs`-based approach too (so if a 
`CXXThisExpr` escapes we can conservatively-correctly mark all fields as 
initialized). I don't know what you think about this -- do you think the patch 
is already handling this sufficiently reliably that it'd be OK to commit 
without this, and fix it up later? I know you've tested this against a lot of 
code already, but cases like passing `this` through an `ObjCMessageExpr` or a 
`CXXConstructExpr` (which may not have come up in your testing) don't look like 
they're handled here.


================
Comment at: lib/Analysis/UninitializedValues.cpp:391
@@ +390,3 @@
+            ? cast<DeclRefExpr>(E)->getDecl()
+            : isa<MemberExpr>(E) ? cast<MemberExpr>(E)->getMemberDecl() : 0;
+    if (!D || !isTracked(D))
----------------
Do you need the `isa` check here? This should only be called on `DeclRefExpr` 
and `MemberExpr`, as far as I can tell, so we should assert if something else 
gets passed in (or, ideally, not compile).

================
Comment at: lib/Analysis/UninitializedValues.cpp:413-437
@@ -361,12 +412,27 @@
   E = E->IgnoreParens();
-  if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(E)) {
-    const Expr *TrueExpr = CO->getTrueExpr();
+  if (const AbstractConditionalOperator *ACO =
+          dyn_cast<AbstractConditionalOperator>(E)) {
+    if (isa<BinaryConditionalOperator>(ACO))
+      classify(cast<BinaryConditionalOperator>(ACO)->getCommon(), Use);
+    const Expr *TrueExpr = ACO->getTrueExpr();
     if (!isa<OpaqueValueExpr>(TrueExpr))
       classify(TrueExpr, C);
-    classify(CO->getFalseExpr(), C);
+    classify(ACO->getFalseExpr(), C);
     return;
   }
 
-  FindVarResult Var = findVar(E, DC);
-  if (const DeclRefExpr *DRE = Var.getDeclRefExpr())
-    Classification[DRE] = std::max(Classification[DRE], C);
+  if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+    switch (BO->getOpcode()) {
+      case BO_PtrMemD:
+      case BO_PtrMemI:
+        classify(BO->getLHS(), C);
+        return;
+      case BO_Comma:
+        classify(BO->getLHS(), Ignore);
+        classify(BO->getRHS(), C);
+        return;
+      default:
+        return;
+    }
+  }
+
----------------
The improvements here are great, but they're orthogonal to handling class 
members. Please factor them out into a separate patch.

================
Comment at: lib/Analysis/UninitializedValues.cpp:393-394
@@ -392,4 +461,2 @@
     classify(BO->getLHS(), Use);
-  else if (BO->getOpcode() == BO_Assign)
-    classify(BO->getLHS(), Ignore);
 }
----------------
I'm concerned about this removal. The old model was:

  * Visiting the `DeclRefExpr` on the LHS of an assignment does not initialize 
the variable
  * Visiting the assignment expression itself initializes the variable on its 
LHS

The new model seems to be:

 * Visiting the `DeclRefExpr` or `MemberExpr` on the LHS of an assignment 
initializes the variable or field
 * Visiting the assignment expression itself *also* initializes the variable

I think this may be subtly incorrect. If we have:

  int x;
  x = x + 1;

... and the CFG happens to emit the LHS and then the RHS, we won't find the 
uninitialized use, because we'll think that `x` is already initialized.

================
Comment at: lib/Analysis/UninitializedValues.cpp:476-480
@@ -404,5 +475,7 @@
 void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
   // If a value is passed by const reference to a function, we should not 
assume
   // that it is initialized by the call, and we conservatively do not assume
   // that it is used.
+  // If the address of a value is passed to a function, we treat the value as
+  // initialized.
   for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();
----------------
This comment could describe what's going on here better. Usually if an address 
is taken, we treat the value as initialized; the thing we should call out here 
is that the value is *not* treated as initialized if passed to a const pointer 
parameter. Maybe just change the first comment to "If a value is passed by 
const pointer or const reference to a function, ..."?

================
Comment at: lib/Analysis/UninitializedValues.cpp:495-500
@@ -412,1 +494,8 @@
+  }
+  if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE)) {
+    if (const MemberExpr *ME =
+            dyn_cast<MemberExpr>(MCE->getImplicitObjectArgument())) {
+      classify(ME, Use);
+    }
+  }
 }
----------------
What happens here:

  int f();
  struct S {
    int a, b;
    int init() { a = b = 0; return f(); }
  };
  struct T {
    int k;
    S s;
    T() { s.init(); } // ok? warning?
    T(int) : k(s.init()) {} // ok? warning?
  };

I think both of these are fine: because `T::s` has trivial initialization, its 
lifetime begins when storage for it is obtained (per **[basic.life]p1**).

================
Comment at: lib/Analysis/UninitializedValues.cpp:519-523
@@ +518,7 @@
+    case CK_FunctionToPointerDecay:
+      if (const MemberExpr *ME = dyn_cast<MemberExpr>(CE->getSubExpr())) {
+        if (isa<CXXMethodDecl>(ME->getMemberDecl()) &&
+            cast<CXXMethodDecl>(ME->getMemberDecl())->isStatic())
+          classify(ME->getBase(), Ignore);
+      }
+      break;
----------------
Is this necessary? The `VisitMemberExpr` check seems like it should do the 
right thing here.

================
Comment at: lib/Analysis/UninitializedValues.cpp:541-544
@@ +540,6 @@
+  // If it's not a reference, it will be classified appropriately by a cast
+  if (LME == ME && LME->getMemberDecl()->getType()->isReferenceType())
+    classify(LME, Use);
+  else if (LME != ME)
+    classify(LME, isFieldOrNonStaticMethod(ME->getMemberDecl()) ? Use : 
Ignore);
+}
----------------
This doesn't look quite right (though I think the issue is in 
`findLeftmostMemberExpr`). Consider:

  this->member.static_member.non_static_member

When we visit this, I think we'll mark 'member' as `Use`. Suggestion: teach 
`findLeftmostMemberExpr` not to walk through `MemberExpr`s for static data 
members.

================
Comment at: lib/Analysis/UninitializedValues.cpp:547-550
@@ -425,2 +546,6 @@
+
+void ClassifyRefs::VisitCXXConstructExpr(CXXConstructExpr *CE) {
+  if (CE->getConstructor()->isCopyOrMoveConstructor())
+    classify(CE->getArg(0), Use);
 }
 
----------------
I think you should also handle the arguments the same way that `VisitCallExpr` 
does.

  struct A { A(const int&); };
  struct B {
    int x, y;
    B() { A a(x); y = x; } // should warn on this
  };

I think we won't warn here, because our usual handling for conservatively 
marking the `x` in `a(x)` as Ignore won't fire.

(Even if this is a copy or move constructor, it could still have additional 
arguments, so you should do this in that case too.)

That said, this is also orthogonal to handling of fields, and fixing this in a 
separate patch would be preferred.

================
Comment at: lib/Analysis/UninitializedValues.cpp:487-493
@@ +486,9 @@
+      continue;
+    } else if (IsPointerToConst((*I)->getType())) {
+      const Expr *Ex = stripCasts(DC->getParentASTContext(), *I);
+      const UnaryOperator *UO = dyn_cast<UnaryOperator>(Ex);
+      if (UO && UO->getOpcode() == UO_AddrOf)
+        Ex = UO->getSubExpr();
+      classify(Ex, Ignore);
+    }
+  }
----------------
This is orthogonal to field handling; please break this out into a separate 
patch.

================
Comment at: lib/Analysis/UninitializedValues.cpp:993
@@ +992,3 @@
+    else if (Optional<CFGInitializer> ci = I->getAs<CFGInitializer>()) {
+      tf.setReportConstructor(isInClassInitializer(ctor, ++initIdx));
+      FieldDecl *fd = ci->getInitializer()->getMember();
----------------
This counting approach seems rather complex to me, and I think we can get the 
same effect with something much simpler: remove the 'ReportConstructor' code 
entirely, and when you come to issue a diagnostic for an uninitialized use of a 
field, check whether the source location of the use is within the source range 
of the constructor. If not, emit the additional note pointing out which 
constructor it was.

================
Comment at: lib/Sema/AnalysisBasedWarnings.cpp:1672
@@ -1651,1 +1671,3 @@
+      .setAlwaysAdd(Stmt::MemberExprClass)
+      .setAlwaysAdd(Stmt::CXXDefaultInitExprClass)
       .setAlwaysAdd(Stmt::ImplicitCastExprClass)
----------------
I don't think you need this any more.


http://llvm-reviews.chandlerc.com/D2181
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to