From: Kristof Umann Date: Fri, 13 Jul 2018 12:54:47 +0000 (+0000) Subject: [analyzer][UninitializedObjectChecker] Fixed captured lambda variable name X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=08b72986d5d53d57e461c87c49046f2728edcabe;p=clang [analyzer][UninitializedObjectChecker] Fixed captured lambda variable name Differential Revision: https://reviews.llvm.org/D48291 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336995 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp index a44ae9cd8d..5c94a5bd33 100644 --- a/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp @@ -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 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; @@ -604,7 +592,7 @@ void FieldChainInfo::print(llvm::raw_ostream &Out) const { const llvm::ImmutableListImpl *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(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(); Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption( diff --git a/test/Analysis/cxx-uninitialized-object.cpp b/test/Analysis/cxx-uninitialized-object.cpp index 617ac78f40..39d4a7f801 100644 --- a/test/Analysis/cxx-uninitialized-object.cpp +++ b/test/Analysis/cxx-uninitialized-object.cpp @@ -736,6 +736,22 @@ void fMemsetTest2() { // Lambda tests. //===----------------------------------------------------------------------===// +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 @@ 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(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 +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. //===----------------------------------------------------------------------===//