Skip to content

Commit 2c779f3

Browse files
committed
[analyzer] Refer to macro names in diagnostics for macros representing a literal
When a macro expending to a literal is used in a comparison, use the macro name in the diagnostic rather than the literal. This improves readability of path notes. Added tests for various macro literals that could occur. Only BOOl, Int, and NULL tests have changed behavior with this patch. Differential Revision: https://reviews.llvm.org/D27726 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@289884 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 29a3ca1 commit 2c779f3

File tree

6 files changed

+143
-6
lines changed

6 files changed

+143
-6
lines changed

include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ class ConditionBRVisitor final
245245
const ExplodedNode *N);
246246

247247
bool patternMatch(const Expr *Ex,
248+
const Expr *ParentEx,
248249
raw_ostream &Out,
249250
BugReporterContext &BRC,
250251
BugReport &R,

lib/StaticAnalyzer/Core/BugReporterVisitors.cpp

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/AST/Expr.h"
1616
#include "clang/AST/ExprObjC.h"
1717
#include "clang/Analysis/CFGStmtMap.h"
18+
#include "clang/Lex/Lexer.h"
1819
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
1920
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
2021
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -1372,14 +1373,57 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
13721373
return Event;
13731374
}
13741375

1375-
bool ConditionBRVisitor::patternMatch(const Expr *Ex, raw_ostream &Out,
1376+
bool ConditionBRVisitor::patternMatch(const Expr *Ex,
1377+
const Expr *ParentEx,
1378+
raw_ostream &Out,
13761379
BugReporterContext &BRC,
13771380
BugReport &report,
13781381
const ExplodedNode *N,
13791382
Optional<bool> &prunable) {
13801383
const Expr *OriginalExpr = Ex;
13811384
Ex = Ex->IgnoreParenCasts();
13821385

1386+
// Use heuristics to determine if Ex is a macro expending to a literal and
1387+
// if so, use the macro's name.
1388+
SourceLocation LocStart = Ex->getLocStart();
1389+
SourceLocation LocEnd = Ex->getLocEnd();
1390+
if (LocStart.isMacroID() && LocEnd.isMacroID() &&
1391+
(isa<GNUNullExpr>(Ex) ||
1392+
isa<ObjCBoolLiteralExpr>(Ex) ||
1393+
isa<CXXBoolLiteralExpr>(Ex) ||
1394+
isa<IntegerLiteral>(Ex) ||
1395+
isa<FloatingLiteral>(Ex))) {
1396+
1397+
StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart,
1398+
BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
1399+
StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd,
1400+
BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
1401+
bool beginAndEndAreTheSameMacro = StartName.equals(EndName);
1402+
1403+
bool partOfParentMacro = false;
1404+
if (ParentEx->getLocStart().isMacroID()) {
1405+
StringRef PName = Lexer::getImmediateMacroNameForDiagnostics(
1406+
ParentEx->getLocStart(), BRC.getSourceManager(),
1407+
BRC.getASTContext().getLangOpts());
1408+
partOfParentMacro = PName.equals(StartName);
1409+
}
1410+
1411+
if (beginAndEndAreTheSameMacro && !partOfParentMacro ) {
1412+
// Get the location of the macro name as written by the caller.
1413+
SourceLocation Loc = LocStart;
1414+
while (LocStart.isMacroID()) {
1415+
Loc = LocStart;
1416+
LocStart = BRC.getSourceManager().getImmediateMacroCallerLoc(LocStart);
1417+
}
1418+
StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
1419+
Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
1420+
1421+
// Return the macro name.
1422+
Out << MacroName;
1423+
return false;
1424+
}
1425+
}
1426+
13831427
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) {
13841428
const bool quotes = isa<VarDecl>(DR->getDecl());
13851429
if (quotes) {
@@ -1440,10 +1484,10 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond,
14401484
SmallString<128> LhsString, RhsString;
14411485
{
14421486
llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
1443-
const bool isVarLHS = patternMatch(BExpr->getLHS(), OutLHS, BRC, R, N,
1444-
shouldPrune);
1445-
const bool isVarRHS = patternMatch(BExpr->getRHS(), OutRHS, BRC, R, N,
1446-
shouldPrune);
1487+
const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS,
1488+
BRC, R, N, shouldPrune);
1489+
const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS,
1490+
BRC, R, N, shouldPrune);
14471491

14481492
shouldInvert = !isVarLHS && isVarRHS;
14491493
}

test/Analysis/Inputs/system-header-simulator-objc.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ typedef unsigned long NSUInteger;
1717
typedef unsigned short unichar;
1818
typedef UInt16 UniChar;
1919

20-
#define NULL ((void *)0)
20+
#ifndef NULL
21+
#define __DARWIN_NULL ((void *)0)
22+
#define NULL __DARWIN_NULL
23+
#endif
24+
2125
#define nil ((id)0)
2226

2327
enum {
@@ -54,6 +58,7 @@ typedef struct _NSZone NSZone;
5458
- (oneway void)release;
5559
- (id)autorelease;
5660
- (id)init;
61+
@property (readonly, copy) NSString *description;
5762
@end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone;
5863
@end @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone;
5964
@end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder;

test/Analysis/Inputs/system-header-simulator.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,11 @@ void exit(int status) __attribute__ ((__noreturn__));
102102
void _exit(int status) __attribute__ ((__noreturn__));
103103
void _Exit(int status) __attribute__ ((__noreturn__));
104104

105+
#define UINT32_MAX 4294967295U
106+
#define INT64_MIN (-INT64_MAX-1)
107+
#define __DBL_MAX__ 1.7976931348623157e+308
108+
#define DBL_MAX __DBL_MAX__
109+
#ifndef NULL
110+
#define __DARWIN_NULL 0
111+
#define NULL __DARWIN_NULL
112+
#endif

test/Analysis/diagnostics/macros.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=core,osx -analyzer-output=text -verify %s
2+
3+
#include "../Inputs/system-header-simulator.h"
4+
#include "../Inputs/system-header-simulator-cxx.h"
5+
6+
void testIntMacro(unsigned int i) {
7+
if (i == UINT32_MAX) { // expected-note {{Assuming 'i' is equal to UINT32_MAX}}
8+
// expected-note@-1 {{Taking true branch}}
9+
char *p = NULL; // expected-note {{'p' initialized to a null pointer value}}
10+
*p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
11+
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}}
12+
}
13+
}
14+
15+
void testNULLMacro(int *p) {
16+
if (p == NULL) { // expected-note {{Assuming 'p' is equal to NULL}}
17+
// expected-note@-1 {{Taking true branch}}
18+
*p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
19+
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}}
20+
}
21+
}
22+
23+
void testnullptrMacro(int *p) {
24+
if (p == nullptr) { // expected-note {{Assuming pointer value is null}}
25+
// expected-note@-1 {{Taking true branch}}
26+
*p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
27+
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}}
28+
}
29+
}
30+
31+
// There are no path notes on the comparison to float types.
32+
void testDoubleMacro(double d) {
33+
if (d == DBL_MAX) { // expected-note {{Taking true branch}}
34+
35+
char *p = NULL; // expected-note {{'p' initialized to a null pointer value}}
36+
*p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
37+
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}}
38+
}
39+
}
40+
41+
void testboolMacro(bool b, int *p) {
42+
p = nullptr; // expected-note {{Null pointer value stored to 'p'}}
43+
if (b == false) { // expected-note {{Assuming the condition is true}}
44+
// expected-note@-1 {{Taking true branch}}
45+
*p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
46+
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}}
47+
}
48+
}

test/Analysis/diagnostics/macros.m

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -analyze -analyzer-checker=core,osx -fblocks -analyzer-output=text -verify %s
2+
3+
#include "../Inputs/system-header-simulator-objc.h"
4+
5+
@interface NSDictionary : NSObject
6+
- (NSUInteger)count;
7+
- (id)objectForKey:(id)aKey;
8+
- (NSEnumerator *)keyEnumerator;
9+
@end
10+
@interface NSMutableDictionary : NSDictionary
11+
- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
12+
@end
13+
14+
void testBOOLMacro(BOOL b) {
15+
if (b == YES) { // expected-note {{Assuming 'b' is equal to YES}}
16+
// expected-note@-1 {{Taking true branch}}
17+
char *p = NULL;// expected-note {{'p' initialized to a null pointer value}}
18+
*p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}}
19+
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'p')}}
20+
}
21+
}
22+
23+
void testNilMacro(NSMutableDictionary *d, NSObject *o) {
24+
if (o == nil) // expected-note {{Assuming 'o' is equal to nil}}
25+
// expected-note@-1 {{Taking true branch}}
26+
[d setObject:o forKey:[o description]]; // expected-warning {{Key argument to 'setObject:forKey:' cannot be nil}}
27+
// expected-note@-1 {{'description' not called because the receiver is nil}}
28+
// expected-note@-2 {{Key argument to 'setObject:forKey:' cannot be nil}}
29+
30+
return;
31+
}

0 commit comments

Comments
 (0)