chrisbazley created this revision. Herald added subscribers: steakhal, martong. Herald added a reviewer: NoQ. Herald added a project: All. chrisbazley requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
The existing nullability checker is so limited when applied to top-level functions that a casual observer might assume it doesn't work at all: void test1(int *_Nullable x) { *x = 5; // no warning! } Added a NullabilityChecker::checkBeginFunction method to honour any _Nullable annotation of parameters in a top-level call. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D142743 Files: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
Index: clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -26,12 +26,13 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/AnyCall.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Path.h" @@ -78,10 +79,11 @@ }; class NullabilityChecker - : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, - check::PostCall, check::PostStmt<ExplicitCastExpr>, - check::PostObjCMessage, check::DeadSymbols, eval::Assume, - check::Location, check::Event<ImplicitNullDerefEvent>> { + : public Checker<check::Bind, check::BeginFunction, check::PreCall, + check::PreStmt<ReturnStmt>, check::PostCall, + check::PostStmt<ExplicitCastExpr>, check::PostObjCMessage, + check::DeadSymbols, eval::Assume, check::Location, + check::Event<ImplicitNullDerefEvent>> { public: // If true, the checker will not diagnose nullabilility issues for calls @@ -104,6 +106,7 @@ CheckerContext &C) const; ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, bool Assumption) const; + void checkBeginFunction(CheckerContext &C) const; void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const override; @@ -1344,6 +1347,59 @@ } } +/// We want to trust developer annotations and consider all 'nullable' +/// parameters as potentially null. Each marked parameter will get a +/// corresponding constraint. +/// +/// \code +/// void foo(int *_Nullable x) { +/// // . . . +/// *x = 42; // we want to consider this as an error... +/// // . . . +/// } +/// \endcode +void NullabilityChecker::checkBeginFunction(CheckerContext &Context) const { + // Planned assumption makes sense only for top-level functions. + if (!Context.inTopFrame()) + return; + + const LocationContext *LocContext = Context.getLocationContext(); + + const Decl *FD = LocContext->getDecl(); + // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl + // separately and aggregates interfaces of these classes. + auto AbstractCall = AnyCall::forDecl(FD); + if (!AbstractCall) + return; + + ProgramStateRef State = Context.getState(); + + for (const ParmVarDecl *Parameter : AbstractCall->parameters()) { + Nullability RequiredNullability = + getNullabilityAnnotation(Parameter->getType()); + + if (RequiredNullability != Nullability::Nullable) + continue; + + Loc ParameterLoc = State->getLValue(Parameter, LocContext); + // We never consider top-level function parameters undefined. + auto StoredVal = + State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>(); + + const MemRegion *Region = getTrackRegion(StoredVal); + if (!Region) + continue; + + const Stmt *NullabilitySource = Parameter->getBody(); + if (ProgramStateRef NewState = State->set<NullabilityMap>( + Region, NullabilityState(RequiredNullability, NullabilitySource))) { + State = NewState; + } + } + + Context.addTransition(State); +} + void ento::registerNullabilityBase(CheckerManager &mgr) { mgr.registerChecker<NullabilityChecker>(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits