diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -211,6 +211,22 @@ (Tag ? Tag : Location.getTag())); } + /// Generate a transition to a node that will be used to report + /// an error. This node will be a sink. That is, it will stop exploration of + /// the given path. + /// + /// @param State The state of the generated node. + /// @param Pred The transition will be generated from the specified Pred node + /// to the newly generated node. + /// @param Tag The tag to uniquely identify the creation site. If null, + /// the default tag for the checker will be used. + ExplodedNode *generateErrorNode(ProgramStateRef State, + ExplodedNode *Pred, + const ProgramPointTag *Tag = nullptr) { + return generateSink(State, Pred, + (Tag ? Tag : Location.getTag())); + } + /// Generate a transition to a node that will be used to report /// an error. This node will not be a sink. That is, exploration will /// continue along this path. diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -951,7 +951,7 @@ Constraint->negate()->apply(NewState, Call, Summary, C); // The argument constraint is not satisfied. if (FailureSt && !SuccessSt) { - if (ExplodedNode *N = C.generateErrorNode(NewState)) + if (ExplodedNode *N = C.generateErrorNode(NewState, NewNode)) reportBug(Call, N, Constraint.get(), Summary, C); break; } diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c --- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c @@ -20,6 +20,7 @@ // RUN: -verify=bugpath void clang_analyzer_eval(int); +void clang_analyzer_warnIfReached(); int glob; @@ -215,6 +216,20 @@ // bugpath-note{{}} \ // bugpath-note{{Function argument constraint is not satisfied}} } +void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) { + if (fp) // \ + // bugpath-note{{Assuming 'fp' is null}} \ + // bugpath-note{{Taking false branch}} + return; + size_t ret = fread(buf, size, n, fp); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // report-note{{}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + clang_analyzer_warnIfReached(); // not reachable +} + typedef __WCHAR_TYPE__ wchar_t; // This is one test case for the ARR38-C SEI-CERT rule. void ARR38_C_F(FILE *file) {