]> granicus.if.org Git - clang/commitdiff
[analyzer][UninitializedObjectChecker] Fixed captured lambda variable name
authorKristof Umann <dkszelethus@gmail.com>
Fri, 13 Jul 2018 12:54:47 +0000 (12:54 +0000)
committerKristof Umann <dkszelethus@gmail.com>
Fri, 13 Jul 2018 12:54:47 +0000 (12:54 +0000)
Differential Revision: https://reviews.llvm.org/D48291

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336995 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
test/Analysis/cxx-uninitialized-object.cpp

index a44ae9cd8d3497ac5a9face31f23529a5e3f37c6..5c94a5bd3371105e88e914cfc331dd73903607a8 100644 (file)
@@ -232,6 +232,10 @@ static bool isPrimitiveType(const QualType &T) {
 static void printNoteMessage(llvm::raw_ostream &Out,
                              const FieldChainInfo &Chain);
 
+/// Returns with Field's name. This is a helper function to get the correct name
+/// even if Field is a captured lambda variable.
+static StringRef getVariableName(const FieldDecl *Field);
+
 //===----------------------------------------------------------------------===//
 //                  Methods for UninitializedObjectChecker.
 //===----------------------------------------------------------------------===//
@@ -581,22 +585,6 @@ const FieldDecl *FieldChainInfo::getEndOfChain() const {
 // "uninitialized field 'this->x'", but we can't refer to 'x' directly,
 // we need an explicit namespace resolution whether the uninit field was
 // 'D1::x' or 'D2::x'.
-//
-// TODO: If a field in the fieldchain is a captured lambda parameter, this
-// function constructs an empty string for it:
-//
-//   template <class Callable> struct A {
-//     Callable c;
-//     A(const Callable &c, int) : c(c) {}
-//   };
-//
-//   int b; // say that this isn't zero initialized
-//   auto alwaysTrue = [&b](int a) { return true; };
-//
-// A call with these parameters: A<decltype(alwaysTrue)>::A(alwaysTrue, int())
-// will emit a note with the message "uninitialized field: 'this->c.'". If
-// possible, the lambda parameter name should be retrieved or be replaced with a
-// "<lambda parameter>" or something similar.
 void FieldChainInfo::print(llvm::raw_ostream &Out) const {
   if (Chain.isEmpty())
     return;
@@ -604,7 +592,7 @@ void FieldChainInfo::print(llvm::raw_ostream &Out) const {
   const llvm::ImmutableListImpl<const FieldRegion *> *L =
       Chain.getInternalPointer();
   printTail(Out, L->getTail());
-  Out << L->getHead()->getDecl()->getNameAsString();
+  Out << getVariableName(L->getHead()->getDecl());
 }
 
 void FieldChainInfo::printTail(
@@ -615,7 +603,7 @@ void FieldChainInfo::printTail(
 
   printTail(Out, L->getTail());
   const FieldDecl *Field = L->getHead()->getDecl();
-  Out << Field->getNameAsString();
+  Out << getVariableName(Field);
   Out << (Field->getType()->isPointerType() ? "->" : ".");
 }
 
@@ -676,6 +664,21 @@ static void printNoteMessage(llvm::raw_ostream &Out,
   Out << "'";
 }
 
+static StringRef getVariableName(const FieldDecl *Field) {
+  // If Field is a captured lambda variable, Field->getName() will return with
+  // an empty string. We can however acquire it's name from the lambda's
+  // captures.
+  const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
+
+  if (CXXParent && CXXParent->isLambda()) {
+    assert(CXXParent->captures_begin());
+    auto It = CXXParent->captures_begin() + Field->getFieldIndex();
+    return It->getCapturedVar()->getName();
+  }
+
+  return Field->getName();
+}
+
 void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
   auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
   Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
index 617ac78f40a5af86bc8db59f0e5651595fb18426..39d4a7f80183139be1243ca5e500d4e82cd7aa91 100644 (file)
@@ -736,6 +736,22 @@ void fMemsetTest2() {
 // Lambda tests.
 //===----------------------------------------------------------------------===//
 
+template <class Callable>
+struct LambdaThisTest {
+  Callable functor;
+
+  LambdaThisTest(const Callable &functor, int) : functor(functor) {
+    // All good!
+  }
+};
+
+struct HasCapturableThis {
+  void fLambdaThisTest() {
+    auto isEven = [this](int a) { return a % 2 == 0; }; // no-crash
+    LambdaThisTest<decltype(isEven)>(isEven, int());
+  }
+};
+
 template <class Callable>
 struct LambdaTest1 {
   Callable functor;
@@ -760,7 +776,7 @@ struct LambdaTest2 {
 
 void fLambdaTest2() {
   int b;
-  auto equals = [&b](int a) { return a == b; }; // expected-note{{uninitialized field 'this->functor.'}}
+  auto equals = [&b](int a) { return a == b; }; // expected-note{{uninitialized field 'this->functor.b'}}
   LambdaTest2<decltype(equals)>(equals, int());
 }
 #else
@@ -782,8 +798,8 @@ void fLambdaTest2() {
 namespace LT3Detail {
 
 struct RecordType {
-  int x; // expected-note{{uninitialized field 'this->functor..x'}}
-  int y; // expected-note{{uninitialized field 'this->functor..y'}}
+  int x; // expected-note{{uninitialized field 'this->functor.rec1.x'}}
+  int y; // expected-note{{uninitialized field 'this->functor.rec1.y'}}
 };
 
 } // namespace LT3Detail
@@ -826,6 +842,35 @@ void fLambdaTest3() {
 }
 #endif //PEDANTIC
 
+template <class Callable>
+struct MultipleLambdaCapturesTest1 {
+  Callable functor;
+  int dontGetFilteredByNonPedanticMode = 0;
+
+  MultipleLambdaCapturesTest1(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized field}}
+};
+
+void fMultipleLambdaCapturesTest1() {
+  int b1, b2 = 3, b3;
+  auto equals = [&b1, &b2, &b3](int a) { return a == b1 == b2 == b3; }; // expected-note{{uninitialized field 'this->functor.b1'}}
+  // expected-note@-1{{uninitialized field 'this->functor.b3'}}
+  MultipleLambdaCapturesTest1<decltype(equals)>(equals, int());
+}
+
+template <class Callable>
+struct MultipleLambdaCapturesTest2 {
+  Callable functor;
+  int dontGetFilteredByNonPedanticMode = 0;
+
+  MultipleLambdaCapturesTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}}
+};
+
+void fMultipleLambdaCapturesTest2() {
+  int b1, b2 = 3, b3;
+  auto equals = [b1, &b2, &b3](int a) { return a == b1 == b2 == b3; }; // expected-note{{uninitialized field 'this->functor.b3'}}
+  MultipleLambdaCapturesTest2<decltype(equals)>(equals, int());
+}
+
 //===----------------------------------------------------------------------===//
 // System header tests.
 //===----------------------------------------------------------------------===//