aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h25
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h10
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp55
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp10
-rw-r--r--test/Analysis/diagnostics/deref-track-symbolic-region.c197
-rw-r--r--test/Analysis/inlining/inline-defensive-checks.c99
-rw-r--r--test/Analysis/inlining/path-notes.c1004
7 files changed, 530 insertions, 870 deletions
diff --git a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
index bef4b30a35..c1b5594209 100644
--- a/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
+++ b/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitor.h
@@ -280,6 +280,31 @@ public:
BugReport &BR);
};
+class SuppressInlineDefensiveChecksVisitor
+: public BugReporterVisitorImpl<SuppressInlineDefensiveChecksVisitor>
+{
+ // The symbolic value for which we are tracking constraints.
+ // This value is constrained to null in the end of path.
+ DefinedSVal V;
+
+ // Track if we found the node where the constraint was first added.
+ bool IsSatisfied;
+
+public:
+ SuppressInlineDefensiveChecksVisitor(DefinedSVal Val, const ExplodedNode *N);
+
+ void Profile(llvm::FoldingSetNodeID &ID) const;
+
+ /// Return the tag associated with this visitor. This tag will be used
+ /// to make all PathDiagnosticPieces created by this visitor.
+ static const char *getTag();
+
+ PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR);
+};
+
namespace bugreporter {
/// Attempts to add visitors to trace a null or undefined value back to its
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index eab248bac2..798090735b 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -173,8 +173,8 @@ public:
ProgramStateRef assume(DefinedOrUnknownSVal cond, bool assumption) const;
/// This method assumes both "true" and "false" for 'cond', and
- /// returns both corresponding states. It's shorthand for doing
- /// 'assume' twice.
+ /// returns both corresponding states. It's shorthand for doing
+ /// 'assume' twice.
std::pair<ProgramStateRef , ProgramStateRef >
assume(DefinedOrUnknownSVal cond) const;
@@ -182,7 +182,11 @@ public:
DefinedOrUnknownSVal upperBound,
bool assumption,
QualType IndexType = QualType()) const;
-
+
+ /// \brief Check if the given SVal is constrained to zero or is a zero
+ /// constant.
+ ConditionTruthVal isNull(SVal V) const;
+
/// Utility method for getting regions.
const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const;
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index bb20c0d3c6..b4ba2d4789 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -27,6 +27,8 @@
using namespace clang;
using namespace ento;
+using llvm::FoldingSetNodeID;
+
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
@@ -663,6 +665,49 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
return NULL;
}
+SuppressInlineDefensiveChecksVisitor::
+SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
+ : V(Value), IsSatisfied(false) {
+
+ assert(N->getState()->isNull(V).isConstrainedTrue() &&
+ "The visitor only tracks the cases where V is constrained to 0");
+}
+
+void SuppressInlineDefensiveChecksVisitor::Profile(FoldingSetNodeID &ID) const {
+ static int id = 0;
+ ID.AddPointer(&id);
+ ID.Add(V);
+}
+
+const char *SuppressInlineDefensiveChecksVisitor::getTag() {
+ return "IDCVisitor";
+}
+
+PathDiagnosticPiece *
+SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ if (IsSatisfied)
+ return 0;
+
+ // Check if in the previous state it was feasible for this value
+ // to *not* be null.
+ if (PrevN->getState()->assume(V, true)) {
+ IsSatisfied = true;
+
+ // TODO: Investigate if missing the transition point, where V
+ // is non-null in N could lead to false negatives.
+
+ // Check if this is inline defensive checks.
+ const LocationContext *CurLC = PrevN->getLocationContext();
+ const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext();
+ if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC))
+ BR.markInvalid("Suppress IDC", CurLC);
+ }
+ return 0;
+}
+
bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S,
BugReport &report, bool IsArg) {
if (!S || !N)
@@ -772,8 +817,16 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S,
// If the contents are symbolic, find out when they became null.
if (V.getAsLocSymbol()) {
BugReporterVisitor *ConstraintTracker =
- new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false);
+ new TrackConstraintBRVisitor(V.castAs<DefinedSVal>(), false);
report.addVisitor(ConstraintTracker);
+
+ // Add visitor, which will suppress inline defensive checks.
+ if (N->getState()->isNull(V).isConstrainedTrue()) {
+ BugReporterVisitor *IDCSuppressor =
+ new SuppressInlineDefensiveChecksVisitor(V.castAs<DefinedSVal>(),
+ N);
+ report.addVisitor(IDCSuppressor);
+ }
}
if (Optional<KnownSVal> KV = V.getAs<KnownSVal>())
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 400569e49a..64205f8d99 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -324,6 +324,16 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption);
}
+ConditionTruthVal ProgramState::isNull(SVal V) const {
+ if (V.isZeroConstant())
+ return true;
+
+ SymbolRef Sym = V.getAsSymbol();
+ if (!Sym)
+ return false;
+ return getStateManager().ConstraintMgr->isNull(this, Sym);
+}
+
ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) {
ProgramState State(this,
EnvMgr.getInitialEnvironment(),
diff --git a/test/Analysis/diagnostics/deref-track-symbolic-region.c b/test/Analysis/diagnostics/deref-track-symbolic-region.c
index e2ec8fc5f3..94774dd61d 100644
--- a/test/Analysis/diagnostics/deref-track-symbolic-region.c
+++ b/test/Analysis/diagnostics/deref-track-symbolic-region.c
@@ -9,22 +9,19 @@ struct S {
int *foo();
-void inlined(struct S *s, int m) {
- if (s->x)
+void test(struct S syz, int *pp) {
+ int m = 0;
+ syz.x = foo(); // expected-note{{Value assigned to 'syz.x'}}
+
+ struct S *ps = &syz;
+ if (ps->x)
//expected-note@-1{{Taking false branch}}
//expected-note@-2{{Assuming pointer value is null}}
m++;
-}
-void test(struct S syz, int *pp) {
- int m = 0;
- syz.x = foo(); // expected-note{{Value assigned to 'syz.x'}}
- inlined(&syz, m);
- // expected-note@-1{{Calling 'inlined'}}
- // expected-note@-2{{Returning from 'inlined'}}
m += *syz.x; // expected-warning{{Dereference of null pointer (loaded from field 'x')}}
- // expected-note@-1{{Dereference of null pointer (loaded from field 'x')}}
+ // expected-note@-1{{Dereference of null pointer (loaded from field 'x')}}
}
// CHECK: <key>diagnostics</key>
@@ -40,12 +37,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>21</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>21</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -53,12 +50,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -70,7 +67,7 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -78,12 +75,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>15</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -103,12 +100,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>22</integer>
+// CHECK-NEXT: <key>line</key><integer>14</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -116,89 +113,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>9</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>kind</key><string>event</string>
-// CHECK-NEXT: <key>location</key>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <key>ranges</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>18</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: <key>depth</key><integer>0</integer>
-// CHECK-NEXT: <key>extended_message</key>
-// CHECK-NEXT: <string>Calling &apos;inlined&apos;</string>
-// CHECK-NEXT: <key>message</key>
-// CHECK-NEXT: <string>Calling &apos;inlined&apos;</string>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>kind</key><string>event</string>
-// CHECK-NEXT: <key>location</key>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>12</integer>
-// CHECK-NEXT: <key>col</key><integer>1</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <key>depth</key><integer>1</integer>
-// CHECK-NEXT: <key>extended_message</key>
-// CHECK-NEXT: <string>Entered call from &apos;test&apos;</string>
-// CHECK-NEXT: <key>message</key>
-// CHECK-NEXT: <string>Entered call from &apos;test&apos;</string>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>kind</key><string>control</string>
-// CHECK-NEXT: <key>edges</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>start</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>12</integer>
-// CHECK-NEXT: <key>col</key><integer>1</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>12</integer>
-// CHECK-NEXT: <key>col</key><integer>4</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: <key>end</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>4</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -214,12 +134,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>4</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -227,13 +147,13 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
-// CHECK-NEXT: <key>col</key><integer>7</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
+// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@@ -244,7 +164,7 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -252,53 +172,24 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>13</integer>
-// CHECK-NEXT: <key>col</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
+// CHECK-NEXT: <key>col</key><integer>11</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
-// CHECK-NEXT: <key>depth</key><integer>1</integer>
+// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
// CHECK-NEXT: <string>Assuming pointer value is null</string>
// CHECK-NEXT: <key>message</key>
// CHECK-NEXT: <string>Assuming pointer value is null</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>kind</key><string>event</string>
-// CHECK-NEXT: <key>location</key>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <key>ranges</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>18</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: <key>depth</key><integer>1</integer>
-// CHECK-NEXT: <key>extended_message</key>
-// CHECK-NEXT: <string>Returning from &apos;inlined&apos;</string>
-// CHECK-NEXT: <key>message</key>
-// CHECK-NEXT: <string>Returning from &apos;inlined&apos;</string>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
@@ -306,25 +197,25 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
+// CHECK-NEXT: <key>col</key><integer>7</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>23</integer>
-// CHECK-NEXT: <key>col</key><integer>9</integer>
+// CHECK-NEXT: <key>line</key><integer>17</integer>
+// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -340,12 +231,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -353,12 +244,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -370,7 +261,7 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -378,12 +269,12 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>13</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>13</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -401,12 +292,14 @@ void test(struct S syz, int *pp) {
// CHECK-NEXT: <key>type</key><string>Dereference of null pointer</string>
// CHECK-NEXT: <key>issue_context_kind</key><string>function</string>
// CHECK-NEXT: <key>issue_context</key><string>test</string>
-// CHECK-NEXT: <key>issue_hash</key><string>6</string>
+// CHECK-NEXT: <key>issue_hash</key><string>11</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>26</integer>
+// CHECK-NEXT: <key>line</key><integer>23</integer>
// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </plist>
diff --git a/test/Analysis/inlining/inline-defensive-checks.c b/test/Analysis/inlining/inline-defensive-checks.c
new file mode 100644
index 0000000000..a91d6a3daf
--- /dev/null
+++ b/test/Analysis/inlining/inline-defensive-checks.c
@@ -0,0 +1,99 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify %s
+
+// Perform inline defensive checks.
+void idc(int *p) {
+ if (p)
+ ;
+}
+
+int test01(int *p) {
+ if (p)
+ ;
+ return *p; // expected-warning {{Dereference of null pointer}}
+}
+
+int test02(int *p, int *x) {
+ if (p)
+ ;
+ idc(p);
+ if (x)
+ ;
+ return *p; // expected-warning {{Dereference of null pointer}}
+}
+
+int test03(int *p, int *x) {
+ idc(p);
+ if (p)
+ ;
+ return *p; // False negative
+}
+
+int deref04(int *p) {
+ return *p; // expected-warning {{Dereference of null pointer}}
+}
+
+int test04(int *p) {
+ if (p)
+ ;
+ idc(p);
+ return deref04(p);
+}
+
+int test11(int *q, int *x) {
+ int *p = q;
+ if (q)
+ ;
+ if (x)
+ ;
+ return *p; // expected-warning{{Dereference of null pointer}}
+}
+
+int test12(int *q) {
+ int *p = q;
+ idc(q);
+ return *p;
+}
+
+int test13(int *q) {
+ int *p = q;
+ idc(p);
+ return *p;
+}
+
+int test21(int *q, int *x) {
+ if (q)
+ ;
+ if (x)
+ ;
+ int *p = q;
+ return *p; // expected-warning{{Dereference of null pointer}}
+}
+
+int test22(int *q, int *x) {
+ idc(q);
+ if (x)
+ ;
+ int *p = q;
+ return *p;
+}
+
+int test23(int *q, int *x) {
+ idc(q);
+ if (x)
+ ;
+ int *p = q;
+ if (!p)
+ ;
+ return *p; // False negative
+}
+
+void use(char *p) {
+ if (!p)
+ return;
+ p[0] = 'a';
+}
+
+void test24(char *buffer) {
+ use(buffer);
+ buffer[1] = 'b';
+}
diff --git a/test/Analysis/inlining/path-notes.c b/test/Analysis/inlining/path-notes.c
index a2d603c00a..b128aabf7e 100644
--- a/test/Analysis/inlining/path-notes.c
+++ b/test/Analysis/inlining/path-notes.c
@@ -15,20 +15,12 @@ void testZero(int *a) {
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
}
-
-void check(int *p) {
- if (p) {
- // expected-note@-1 + {{Assuming 'p' is null}}
+void testCheck(int *a) {
+ if (a) {
+ // expected-note@-1 + {{Assuming 'a' is null}}
// expected-note@-2 + {{Taking false branch}}
- return;
+ ;
}
- return;
-}
-
-void testCheck(int *a) {
- check(a);
- // expected-note@-1 {{Calling 'check'}}
- // expected-note@-2 {{Returning from 'check'}}
*a = 1; // expected-warning{{Dereference of null pointer}}
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
}
@@ -39,9 +31,11 @@ int *getPointer();
void testInitCheck() {
int *a = getPointer();
// expected-note@-1 {{'a' initialized here}}
- check(a);
- // expected-note@-1 {{Calling 'check'}}
- // expected-note@-2 {{Returning from 'check'}}
+ if (a) {
+ // expected-note@-1 + {{Assuming 'a' is null}}
+ // expected-note@-2 + {{Taking false branch}}
+ ;
+ }
*a = 1; // expected-warning{{Dereference of null pointer}}
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
}
@@ -49,9 +43,11 @@ void testInitCheck() {
void testStoreCheck(int *a) {
a = getPointer();
// expected-note@-1 {{Value assigned to 'a'}}
- check(a);
- // expected-note@-1 {{Calling 'check'}}
- // expected-note@-2 {{Returning from 'check'}}
+ if (a) {
+ // expected-note@-1 + {{Assuming 'a' is null}}
+ // expected-note@-2 + {{Taking false branch}}
+ ;
+ }
*a = 1; // expected-warning{{Dereference of null pointer}}
// expected-note@-1 {{Dereference of null pointer (loaded from variable 'a')}}
}
@@ -332,49 +328,6 @@ void testUseOfNullPointer() {
// CHECK-NEXT: <key>path</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>kind</key><string>event</string>
-// CHECK-NEXT: <key>location</key>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>29</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <key>ranges</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>29</integer>
-// CHECK-NEXT: <key>col</key><integer>3</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>29</integer>
-// CHECK-NEXT: <key>col</key><integer>10</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: <key>depth</key><integer>0</integer>
-// CHECK-NEXT: <key>extended_message</key>
-// CHECK-NEXT: <string>Calling &apos;check&apos;</string>
-// CHECK-NEXT: <key>message</key>
-// CHECK-NEXT: <string>Calling &apos;check&apos;</string>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>kind</key><string>event</string>
-// CHECK-NEXT: <key>location</key>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>19</integer>
-// CHECK-NEXT: <key>col</key><integer>1</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <key>depth</key><integer>1</integer>
-// CHECK-NEXT: <key>extended_message</key>
-// CHECK-NEXT: <string>Entered call from &apos;testCheck&apos;</string>
-// CHECK-NEXT: <key>message</key>
-// CHECK-NEXT: <string>Entered call from &apos;testCheck&apos;</string>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
// CHECK-NEXT: <key>edges</key>
// CHECK-NEXT: <array>
@@ -383,45 +336,11 @@ void testUseOfNullPointer() {
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>19</integer>
-// CHECK-NEXT: <key>col</key><integer>1</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>19</integer>
-// CHECK-NEXT: <key>col</key><integer>4</integer>
-// CHECK-NEXT: <key>file</key><integer>0</integer>
-// CHECK-NEXT: </dict>
-// CHECK-NEXT: </array>
-// CHECK-NEXT: <key>end</key>
-// CHECK-NEXT: <array>
-// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>20</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer&g