Index: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp +++ lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp @@ -230,6 +230,10 @@ /// Constructs a note message for a given FieldChainInfo object. 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. +StringRef getVariableName(const FieldDecl *Field); + } // end of anonymous namespace //===----------------------------------------------------------------------===// @@ -604,22 +608,6 @@ // "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 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::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 -// "" or something similar. void FieldChainInfo::print(llvm::raw_ostream &Out) const { if (Chain.isEmpty()) return; @@ -627,7 +615,7 @@ const llvm::ImmutableListImpl *L = Chain.getInternalPointer(); printTail(Out, L->getTail()); - Out << L->getHead()->getDecl()->getNameAsString(); + Out << getVariableName(L->getHead()->getDecl()); } void FieldChainInfo::printTail( @@ -638,7 +626,7 @@ printTail(Out, L->getTail()); const FieldDecl *Field = L->getHead()->getDecl(); - Out << Field->getNameAsString(); + Out << getVariableName(Field); Out << (Field->getType()->isPointerType() ? "->" : "."); } @@ -700,6 +688,23 @@ Out << "'"; } +StringRef getVariableName(const FieldDecl *Field) { + // If \p Field is a captured lambda variable, Field->getName() will return + // with an empty string. We can however acquire it's name by iterating over + // the lambda object's captures to find the corresponding variable. + const auto *CXXParent = dyn_cast(Field->getParent()); + + if (CXXParent && CXXParent->isLambda()) { + for (const LambdaCapture &L : CXXParent->captures()) { + if (L.getLocation() == Field->getLocation()) + return L.getCapturedVar()->getName(); + } + llvm_unreachable("Failed to find the captured lambda variable!"); + } + + return Field->getName(); +} + } // end of anonymous namespace void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { Index: test/Analysis/cxx-uninitialized-object.cpp =================================================================== --- test/Analysis/cxx-uninitialized-object.cpp +++ test/Analysis/cxx-uninitialized-object.cpp @@ -737,6 +737,22 @@ //===----------------------------------------------------------------------===// template +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(isEven, int()); + } +}; + +template struct LambdaTest1 { Callable functor; @@ -760,7 +776,7 @@ 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(equals, int()); } #else @@ -782,8 +798,8 @@ 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 @@ } #endif //PEDANTIC +template +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(equals, int()); +} + +template +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(equals, int()); +} + //===----------------------------------------------------------------------===// // System header tests. //===----------------------------------------------------------------------===//