forked from OSchip/llvm-project
[analyzer] Add the type of the leaked object to the diagnostic message
If the object is a temporary, and there is no variable it binds to, let's at least print out the object name in order to help differentiate it from other temporaries. rdar://45175098 Differential Revision: https://reviews.llvm.org/D55033 llvm-svn: 347943
This commit is contained in:
parent
a1c3bb88ee
commit
f893ea1592
|
@ -28,6 +28,17 @@ static bool isNumericLiteralExpression(const Expr *E) {
|
||||||
isa<CXXBoolLiteralExpr>(E);
|
isa<CXXBoolLiteralExpr>(E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If type represents a pointer to CXXRecordDecl,
|
||||||
|
/// and is not a typedef, return the decl name.
|
||||||
|
/// Otherwise, return the serialization of type.
|
||||||
|
static StringRef getPrettyTypeName(QualType QT) {
|
||||||
|
QualType PT = QT->getPointeeType();
|
||||||
|
if (!PT.isNull() && !QT->getAs<TypedefType>())
|
||||||
|
if (const auto *RD = PT->getAsCXXRecordDecl())
|
||||||
|
return RD->getName();
|
||||||
|
return QT.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
/// Write information about the type state change to {@code os},
|
/// Write information about the type state change to {@code os},
|
||||||
/// return whether the note should be generated.
|
/// return whether the note should be generated.
|
||||||
static bool shouldGenerateNote(llvm::raw_string_ostream &os,
|
static bool shouldGenerateNote(llvm::raw_string_ostream &os,
|
||||||
|
@ -193,7 +204,7 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N,
|
||||||
<< Sym->getType().getAsString() << " with a ";
|
<< Sym->getType().getAsString() << " with a ";
|
||||||
} else if (CurrV.getObjKind() == RetEffect::OS) {
|
} else if (CurrV.getObjKind() == RetEffect::OS) {
|
||||||
os << " returns an OSObject of type "
|
os << " returns an OSObject of type "
|
||||||
<< Sym->getType().getAsString() << " with a ";
|
<< getPrettyTypeName(Sym->getType()) << " with a ";
|
||||||
} else if (CurrV.getObjKind() == RetEffect::Generalized) {
|
} else if (CurrV.getObjKind() == RetEffect::Generalized) {
|
||||||
os << " returns an object of type " << Sym->getType().getAsString()
|
os << " returns an object of type " << Sym->getType().getAsString()
|
||||||
<< " with a ";
|
<< " with a ";
|
||||||
|
@ -432,7 +443,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
|
||||||
if (RegionDescription) {
|
if (RegionDescription) {
|
||||||
os << "object allocated and stored into '" << *RegionDescription << '\'';
|
os << "object allocated and stored into '" << *RegionDescription << '\'';
|
||||||
} else {
|
} else {
|
||||||
os << "allocated object";
|
os << "allocated object of type " << getPrettyTypeName(Sym->getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the retain count.
|
// Get the retain count.
|
||||||
|
@ -472,10 +483,10 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
|
||||||
" Foundation";
|
" Foundation";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
os << " is not referenced later in this execution path and has a retain "
|
os << " is not referenced later in this execution path and has a retain "
|
||||||
"count of +" << RV->getCount();
|
"count of +" << RV->getCount();
|
||||||
|
}
|
||||||
|
|
||||||
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
|
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
|
||||||
}
|
}
|
||||||
|
@ -555,6 +566,10 @@ void CFRefLeakReport::createDescription(CheckerContext &Ctx,
|
||||||
FullSourceLoc SL(AllocStmt->getBeginLoc(), Ctx.getSourceManager());
|
FullSourceLoc SL(AllocStmt->getBeginLoc(), Ctx.getSourceManager());
|
||||||
os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
|
os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// If we can't figure out the name, just supply the type information.
|
||||||
|
os << " of type " << getPrettyTypeName(Sym->getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,12 +141,14 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class CFRefReport : public BugReport {
|
class CFRefReport : public BugReport {
|
||||||
|
protected:
|
||||||
|
SymbolRef Sym;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CFRefReport(CFRefBug &D, const LangOptions &LOpts,
|
CFRefReport(CFRefBug &D, const LangOptions &LOpts,
|
||||||
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
|
const SummaryLogTy &Log, ExplodedNode *n, SymbolRef sym,
|
||||||
bool registerVisitor = true)
|
bool registerVisitor = true)
|
||||||
: BugReport(D, D.getDescription(), n) {
|
: BugReport(D, D.getDescription(), n), Sym(sym) {
|
||||||
if (registerVisitor)
|
if (registerVisitor)
|
||||||
addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log));
|
addVisitor(llvm::make_unique<CFRefReportVisitor>(sym, Log));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1727,12 +1727,12 @@
|
||||||
</dict>
|
</dict>
|
||||||
<key>depth</key><integer>0</integer>
|
<key>depth</key><integer>0</integer>
|
||||||
<key>extended_message</key>
|
<key>extended_message</key>
|
||||||
<string>Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1</string>
|
<string>Object leaked: allocated object of type CFStringRef is not referenced later in this execution path and has a retain count of +1</string>
|
||||||
<key>message</key>
|
<key>message</key>
|
||||||
<string>Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1</string>
|
<string>Object leaked: allocated object of type CFStringRef is not referenced later in this execution path and has a retain count of +1</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>description</key><string>Potential leak of an object</string>
|
<key>description</key><string>Potential leak of an object of type CFStringRef</string>
|
||||||
<key>category</key><string>Memory (Core Foundation/Objective-C)</string>
|
<key>category</key><string>Memory (Core Foundation/Objective-C)</string>
|
||||||
<key>type</key><string>Leak</string>
|
<key>type</key><string>Leak</string>
|
||||||
<key>check_name</key><string>osx.cocoa.RetainCount</string>
|
<key>check_name</key><string>osx.cocoa.RetainCount</string>
|
||||||
|
|
|
@ -3834,12 +3834,12 @@
|
||||||
</array>
|
</array>
|
||||||
<key>depth</key><integer>0</integer>
|
<key>depth</key><integer>0</integer>
|
||||||
<key>extended_message</key>
|
<key>extended_message</key>
|
||||||
<string>Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1</string>
|
<string>Object leaked: allocated object of type MyObj * is not referenced later in this execution path and has a retain count of +1</string>
|
||||||
<key>message</key>
|
<key>message</key>
|
||||||
<string>Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1</string>
|
<string>Object leaked: allocated object of type MyObj * is not referenced later in this execution path and has a retain count of +1</string>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>description</key><string>Potential leak of an object</string>
|
<key>description</key><string>Potential leak of an object of type MyObj *</string>
|
||||||
<key>category</key><string>Memory (Core Foundation/Objective-C)</string>
|
<key>category</key><string>Memory (Core Foundation/Objective-C)</string>
|
||||||
<key>type</key><string>Leak</string>
|
<key>type</key><string>Leak</string>
|
||||||
<key>check_name</key><string>osx.cocoa.RetainCount</string>
|
<key>check_name</key><string>osx.cocoa.RetainCount</string>
|
||||||
|
|
|
@ -1315,12 +1315,12 @@ void runTest() {
|
||||||
// CHECK: </array>
|
// CHECK: </array>
|
||||||
// CHECK: <key>depth</key><integer>4</integer>
|
// CHECK: <key>depth</key><integer>4</integer>
|
||||||
// CHECK: <key>extended_message</key>
|
// CHECK: <key>extended_message</key>
|
||||||
// CHECK: <string>Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1</string>
|
// CHECK: <string>Object leaked: allocated object of type NSNumber * is not referenced later in this execution path and has a retain count of +1</string>
|
||||||
// CHECK: <key>message</key>
|
// CHECK: <key>message</key>
|
||||||
// CHECK: <string>Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1</string>
|
// CHECK: <string>Object leaked: allocated object of type NSNumber * is not referenced later in this execution path and has a retain count of +1</string>
|
||||||
// CHECK: </dict>
|
// CHECK: </dict>
|
||||||
// CHECK: </array>
|
// CHECK: </array>
|
||||||
// CHECK: <key>description</key><string>Potential leak of an object</string>
|
// CHECK: <key>description</key><string>Potential leak of an object of type NSNumber *</string>
|
||||||
// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
|
// CHECK: <key>category</key><string>Memory (Core Foundation/Objective-C)</string>
|
||||||
// CHECK: <key>type</key><string>Leak</string>
|
// CHECK: <key>type</key><string>Leak</string>
|
||||||
// CHECK: <key>location</key>
|
// CHECK: <key>location</key>
|
||||||
|
|
|
@ -61,13 +61,13 @@ void check_custom_iterator_rule(OSArray *arr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_no_invalidation() {
|
void check_no_invalidation() {
|
||||||
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
|
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
|
||||||
OtherStruct::doNothingToArray(arr);
|
OtherStruct::doNothingToArray(arr);
|
||||||
} // expected-warning{{Potential leak of an object stored into 'arr'}}
|
} // expected-warning{{Potential leak of an object stored into 'arr'}}
|
||||||
// expected-note@-1{{Object leaked}}
|
// expected-note@-1{{Object leaked}}
|
||||||
|
|
||||||
void check_no_invalidation_other_struct() {
|
void check_no_invalidation_other_struct() {
|
||||||
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
|
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
|
||||||
OtherStruct other(arr); // expected-warning{{Potential leak}}
|
OtherStruct other(arr); // expected-warning{{Potential leak}}
|
||||||
// expected-note@-1{{Object leaked}}
|
// expected-note@-1{{Object leaked}}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,8 @@ struct ArrayOwner : public OSObject {
|
||||||
};
|
};
|
||||||
|
|
||||||
OSArray *generateArray() {
|
OSArray *generateArray() {
|
||||||
return OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
|
return OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
|
||||||
|
// expected-note@-1{{Call to function 'withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int check_leak_good_error_message() {
|
unsigned int check_leak_good_error_message() {
|
||||||
|
@ -109,7 +110,10 @@ unsigned int check_leak_good_error_message() {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int check_leak_msg_temporary() {
|
unsigned int check_leak_msg_temporary() {
|
||||||
return generateArray()->getCount();
|
return generateArray()->getCount(); // expected-warning{{Potential leak of an object}}
|
||||||
|
// expected-note@-1{{Calling 'generateArray'}}
|
||||||
|
// expected-note@-2{{Returning from 'generateArray'}}
|
||||||
|
// expected-note@-3{{Object leaked: allocated object of type OSArray is not referenced later in this execution path and has a retain count of +1}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_confusing_getters() {
|
void check_confusing_getters() {
|
||||||
|
@ -178,14 +182,14 @@ void check_dynamic_cast_null_check() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void use_after_release() {
|
void use_after_release() {
|
||||||
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
|
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
|
||||||
arr->release(); // expected-note{{Object released}}
|
arr->release(); // expected-note{{Object released}}
|
||||||
arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
|
arr->getCount(); // expected-warning{{Reference-counted object is used after it is released}}
|
||||||
// expected-note@-1{{Reference-counted object is used after it is released}}
|
// expected-note@-1{{Reference-counted object is used after it is released}}
|
||||||
}
|
}
|
||||||
|
|
||||||
void potential_leak() {
|
void potential_leak() {
|
||||||
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type struct OSArray * with a +1 retain count}}
|
OSArray *arr = OSArray::withCapacity(10); // expected-note{{Call to function 'withCapacity' returns an OSObject of type OSArray with a +1 retain count}}
|
||||||
arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
|
arr->retain(); // expected-note{{Reference count incremented. The object now has a +2 retain count}}
|
||||||
arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
|
arr->release(); // expected-note{{Reference count decremented. The object now has a +1 retain count}}
|
||||||
arr->getCount();
|
arr->getCount();
|
||||||
|
@ -236,7 +240,7 @@ unsigned int no_warn_ok_release(ArrayOwner *owner) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
|
unsigned int warn_on_overrelease_with_unknown_source(ArrayOwner *owner) {
|
||||||
OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{function call returns an OSObject of type struct OSArray * with a +0 retain count}}
|
OSArray *arr = owner->getArraySourceUnknown(); // expected-note{{function call returns an OSObject of type OSArray with a +0 retain count}}
|
||||||
arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
arr->release(); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||||
// expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
// expected-note@-1{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||||
return arr->getCount();
|
return arr->getCount();
|
||||||
|
|
|
@ -227,7 +227,7 @@ static int Cond;
|
||||||
// expected-note@-1 {{Method returns an instance of MyObj with a +1 retain count}}
|
// expected-note@-1 {{Method returns an instance of MyObj with a +1 retain count}}
|
||||||
// expected-note@-2 {{Calling 'initX'}}
|
// expected-note@-2 {{Calling 'initX'}}
|
||||||
// expected-note@-3 {{Returning from 'initX'}}
|
// expected-note@-3 {{Returning from 'initX'}}
|
||||||
// expected-note@-4 {{Object leaked: allocated object is not referenced later in this execution path and has a retain count of +1}}
|
// expected-note@-4 {{Object leaked: allocated object of type MyObj * is not referenced later in this execution path and has a retain count of +1}}
|
||||||
// initI is inlined because the allocation happens within initY
|
// initI is inlined because the allocation happens within initY
|
||||||
id y = [[MyObj alloc] initY];
|
id y = [[MyObj alloc] initY];
|
||||||
// expected-note@-1 {{Calling 'initY'}}
|
// expected-note@-1 {{Calling 'initY'}}
|
||||||
|
|
|
@ -19,7 +19,7 @@ void foo(CFAllocatorRef allocator) {
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
CFTypeRef* values = (CFTypeRef[]){
|
CFTypeRef* values = (CFTypeRef[]){
|
||||||
CFNumberCreate(allocator, kCFNumberSInt32Type, &width), //expected-warning-re{{Potential leak of an object{{$}}}}
|
CFNumberCreate(allocator, kCFNumberSInt32Type, &width), //expected-warning{{Potential leak of an object of type CFNumberRef}}
|
||||||
CFNumberCreate(allocator, kCFNumberSInt32Type, &height), //expected-warning-re{{Potential leak of an object{{$}}}}
|
CFNumberCreate(allocator, kCFNumberSInt32Type, &height), //expected-warning{{Potential leak of an object of type CFNumberRef}}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue