37 #include "llvm/ADT/StringExtras.h" 38 #include "llvm/Support/Path.h" 40 using namespace clang;
52 std::min(static_cast<char>(Lhs), static_cast<char>(Rhs)));
55 const char *getNullabilityString(
Nullability Nullab) {
58 return "contradicted";
66 llvm_unreachable(
"Unexpected enumeration.");
75 NullableAssignedToNonnull,
76 NullableReturnedToNonnull,
78 NullablePassedToNonnull
81 class NullabilityChecker
82 :
public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
83 check::PostCall, check::PostStmt<ExplicitCastExpr>,
84 check::PostObjCMessage, check::DeadSymbols,
85 check::Event<ImplicitNullDerefEvent>> {
86 mutable std::unique_ptr<BugType> BT;
107 const char *Sep)
const override;
109 struct NullabilityChecksFilter {
117 CheckName CheckNameNullReturnedFromNonnull;
119 CheckName CheckNameNullablePassedToNonnull;
120 CheckName CheckNameNullableReturnedFromNonnull;
123 NullabilityChecksFilter Filter;
133 NullabilityBugVisitor(
const MemRegion *M) : Region(M) {}
135 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
138 ID.AddPointer(Region);
141 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
156 void reportBugIfInvariantHolds(StringRef Msg,
ErrorKind Error,
159 const Stmt *ValueExpr =
nullptr,
160 bool SuppressPath =
false)
const;
164 const Stmt *ValueExpr =
nullptr)
const {
168 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
170 R->markInteresting(Region);
171 R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region));
174 R->addRange(ValueExpr->getSourceRange());
175 if (Error == ErrorKind::NilAssignedToNonnull ||
176 Error == ErrorKind::NilPassedToNonnull ||
177 Error == ErrorKind::NilReturnedToNonnull)
178 bugreporter::trackNullOrUndefValue(N, ValueExpr, *R);
186 bool CheckSuperRegion =
false)
const;
190 bool isDiagnosableCall(
const CallEvent &Call)
const {
198 class NullabilityState {
201 : Nullab(Nullab), Source(Source) {}
203 const Stmt *getNullabilitySource()
const {
return Source; }
207 void Profile(llvm::FoldingSetNodeID &
ID)
const {
208 ID.AddInteger(static_cast<char>(Nullab));
209 ID.AddPointer(Source);
212 void print(raw_ostream &Out)
const {
213 Out << getNullabilityString(Nullab) <<
"\n";
225 bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
226 return Lhs.getValue() == Rhs.getValue() &&
227 Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
261 enum class NullConstraint { IsNull, IsNotNull, Unknown };
267 return NullConstraint::IsNotNull;
269 return NullConstraint::IsNull;
274 NullabilityChecker::getTrackRegion(
SVal Val,
bool CheckSuperRegion)
const {
282 const MemRegion *Region = RegionSVal->getRegion();
284 if (CheckSuperRegion) {
288 return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
294 std::shared_ptr<PathDiagnosticPiece>
295 NullabilityChecker::NullabilityBugVisitor::VisitNode(
const ExplodedNode *N,
302 const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region);
303 const NullabilityState *TrackedNullabPrev =
304 StatePrev->get<NullabilityMap>(Region);
308 if (TrackedNullabPrev &&
309 TrackedNullabPrev->getValue() == TrackedNullab->getValue())
313 const Stmt *S = TrackedNullab->getNullabilitySource();
321 std::string InfoText =
322 (llvm::Twine(
"Nullability '") +
323 getNullabilityString(TrackedNullab->getValue()) +
"' is inferred")
329 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText,
true,
345 State->getSVal(RegionVal->getRegion()).getAs<DefinedOrUnknownSVal>();
359 for (
const auto *ParamDecl : Params) {
360 if (ParamDecl->isParameterPack())
363 SVal LV = State->getLValue(ParamDecl, LocCtxt);
365 ParamDecl->getType())) {
376 if (!MD || !MD->isInstanceMethod())
383 SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt));
394 for (
const auto *IvarDecl : ID->
ivars()) {
395 SVal LV = State->getLValue(IvarDecl, SelfVal);
405 if (State->get<InvariantViolated>())
414 if (
const auto *BD = dyn_cast<BlockDecl>(D))
415 Params = BD->parameters();
416 else if (
const auto *FD = dyn_cast<FunctionDecl>(D))
417 Params = FD->parameters();
418 else if (
const auto *MD = dyn_cast<ObjCMethodDecl>(D))
419 Params = MD->parameters();
432 void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg,
440 OriginalState = OriginalState->set<InvariantViolated>(
true);
448 void NullabilityChecker::checkDeadSymbols(
SymbolReaper &SR,
454 NullabilityMapTy Nullabilities = State->get<NullabilityMap>();
455 for (NullabilityMapTy::iterator I = Nullabilities.begin(),
456 E = Nullabilities.end();
459 assert(Region &&
"Non-symbolic region is tracked.");
460 if (SR.
isDead(Region->getSymbol())) {
461 State = State->remove<NullabilityMap>(I->first);
481 getTrackRegion(Event.
Location,
true);
486 const NullabilityState *TrackedNullability =
487 State->get<NullabilityMap>(Region);
489 if (!TrackedNullability)
492 if (Filter.CheckNullableDereferenced &&
498 reportBug(
"Nullable pointer is dereferenced",
499 ErrorKind::NullableDereferenced, Event.
SinkNode, Region, BR);
501 reportBug(
"Nullable pointer is passed to a callee that requires a " 502 "non-null", ErrorKind::NullablePassedToNonnull,
515 while (
auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
516 E = ICE->getSubExpr();
524 void NullabilityChecker::checkPreStmt(
const ReturnStmt *S,
530 if (!RetExpr->getType()->isAnyPointerType())
534 if (State->get<InvariantViolated>())
541 bool InSuppressedMethodFamily =
false;
547 if (
auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
554 InSuppressedMethodFamily =
true;
556 RequiredRetType = MD->getReturnType();
557 }
else if (
auto *FD = dyn_cast<FunctionDecl>(D)) {
558 RequiredRetType = FD->getReturnType();
576 Nullness == NullConstraint::IsNull);
577 if (Filter.CheckNullReturnedFromNonnull &&
578 NullReturnedFromNonNull &&
580 !InSuppressedMethodFamily &&
588 llvm::raw_svector_ostream OS(SBuf);
589 OS << (RetExpr->getType()->isObjCObjectPointerType() ?
"nil" :
"Null");
591 " that is expected to return a non-null value";
592 reportBugIfInvariantHolds(OS.str(),
593 ErrorKind::NilReturnedToNonnull, N,
nullptr, C,
600 if (NullReturnedFromNonNull) {
601 State = State->set<InvariantViolated>(
true);
606 const MemRegion *Region = getTrackRegion(*RetSVal);
610 const NullabilityState *TrackedNullability =
611 State->get<NullabilityMap>(Region);
612 if (TrackedNullability) {
613 Nullability TrackedNullabValue = TrackedNullability->getValue();
614 if (Filter.CheckNullableReturnedFromNonnull &&
615 Nullness != NullConstraint::IsNotNull &&
622 llvm::raw_svector_ostream OS(SBuf);
624 " that is expected to return a non-null value";
626 reportBugIfInvariantHolds(OS.str(),
627 ErrorKind::NullableReturnedToNonnull, N,
633 State = State->set<NullabilityMap>(Region,
634 NullabilityState(RequiredNullability,
642 void NullabilityChecker::checkPreCall(
const CallEvent &Call,
648 if (State->get<InvariantViolated>())
655 if (Param->isParameterPack())
666 if (!Param->getType()->isAnyPointerType() &&
667 !Param->getType()->isReferenceType())
677 unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
679 if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull &&
682 isDiagnosableCall(Call)) {
688 llvm::raw_svector_ostream OS(SBuf);
689 OS << (Param->getType()->isObjCObjectPointerType() ?
"nil" :
"Null");
690 OS <<
" passed to a callee that requires a non-null " << ParamIdx
691 << llvm::getOrdinalSuffix(ParamIdx) <<
" parameter";
692 reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N,
698 const MemRegion *Region = getTrackRegion(*ArgSVal);
702 const NullabilityState *TrackedNullability =
703 State->get<NullabilityMap>(Region);
705 if (TrackedNullability) {
706 if (Nullness == NullConstraint::IsNotNull ||
710 if (Filter.CheckNullablePassedToNonnull &&
712 isDiagnosableCall(Call)) {
715 llvm::raw_svector_ostream OS(SBuf);
716 OS <<
"Nullable pointer is passed to a callee that requires a non-null " 717 << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) <<
" parameter";
718 reportBugIfInvariantHolds(OS.str(),
719 ErrorKind::NullablePassedToNonnull, N,
720 Region, C, ArgExpr,
true);
723 if (Filter.CheckNullableDereferenced &&
724 Param->getType()->isReferenceType()) {
726 reportBugIfInvariantHolds(
"Nullable pointer is dereferenced",
727 ErrorKind::NullableDereferenced, N, Region,
736 State = State->set<NullabilityMap>(
737 Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr));
739 if (State != OrigState)
744 void NullabilityChecker::checkPostCall(
const CallEvent &Call,
759 if (State->get<InvariantViolated>())
770 if (llvm::sys::path::filename(FilePath).startswith(
"CG")) {
776 const NullabilityState *TrackedNullability =
777 State->get<NullabilityMap>(Region);
779 if (!TrackedNullability &&
799 if (Nullness == NullConstraint::IsNotNull)
803 if (ValueRegionSVal) {
804 const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
807 const NullabilityState *TrackedSelfNullability =
808 State->get<NullabilityMap>(SelfRegion);
809 if (TrackedSelfNullability)
810 return TrackedSelfNullability->getValue();
818 void NullabilityChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
828 if (State->get<InvariantViolated>())
831 const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue());
835 auto Interface =
Decl->getClassInterface();
836 auto Name = Interface ? Interface->getName() :
"";
840 if (Name.startswith(
"NS")) {
857 if (Name.contains(
"Array") &&
858 (FirstSelectorSlot ==
"firstObject" ||
859 FirstSelectorSlot ==
"lastObject")) {
870 if (Name.contains(
"String")) {
872 if (Param->getName() ==
"encoding") {
873 State = State->set<NullabilityMap>(ReturnRegion,
885 const NullabilityState *NullabilityOfReturn =
886 State->get<NullabilityMap>(ReturnRegion);
888 if (NullabilityOfReturn) {
892 Nullability RetValTracked = NullabilityOfReturn->getValue();
894 getMostNullable(RetValTracked, SelfNullability);
895 if (ComputedNullab != RetValTracked &&
897 const Stmt *NullabilitySource =
898 ComputedNullab == RetValTracked
899 ? NullabilityOfReturn->getNullabilitySource()
901 State = State->set<NullabilityMap>(
902 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
918 Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
920 const Stmt *NullabilitySource = ComputedNullab == RetNullability
923 State = State->set<NullabilityMap>(
924 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
943 if (State->get<InvariantViolated>())
954 const MemRegion *Region = getTrackRegion(*RegionSVal);
961 if (Nullness == NullConstraint::IsNull) {
968 const NullabilityState *TrackedNullability =
969 State->get<NullabilityMap>(Region);
971 if (!TrackedNullability) {
974 State = State->set<NullabilityMap>(Region,
975 NullabilityState(DestNullability, CE));
980 if (TrackedNullability->getValue() != DestNullability &&
991 if (
auto *BinOp = dyn_cast<BinaryOperator>(S)) {
992 if (BinOp->getOpcode() == BO_Assign)
993 return BinOp->getRHS();
997 if (
auto *DS = dyn_cast<DeclStmt>(S)) {
998 if (DS->isSingleDecl()) {
999 auto *VD = dyn_cast<
VarDecl>(DS->getSingleDecl());
1003 if (
const Expr *Init = VD->getInit())
1032 if (!DS || !DS->isSingleDecl())
1035 auto *VD = dyn_cast<
VarDecl>(DS->getSingleDecl());
1040 if(!VD->getType().getQualifiers().hasObjCLifetime())
1043 const Expr *Init = VD->getInit();
1044 assert(Init &&
"ObjC local under ARC without initializer");
1047 if (!isa<ImplicitValueInitExpr>(Init))
1055 void NullabilityChecker::checkBind(
SVal L,
SVal V,
const Stmt *S,
1058 dyn_cast_or_null<TypedValueRegion>(L.
getAsRegion());
1067 if (State->get<InvariantViolated>())
1071 if (!ValDefOrUnknown)
1077 if (
SymbolRef Sym = ValDefOrUnknown->getAsSymbol())
1087 ValueExprTypeLevelNullability =
1092 RhsNullness == NullConstraint::IsNull);
1093 if (Filter.CheckNullPassedToNonnull &&
1094 NullAssignedToNonNull &&
1104 const Stmt *ValueStmt = S;
1106 ValueStmt = ValueExpr;
1109 llvm::raw_svector_ostream OS(SBuf);
1111 OS <<
" assigned to a pointer which is expected to have non-null value";
1112 reportBugIfInvariantHolds(OS.str(),
1113 ErrorKind::NilAssignedToNonnull, N,
nullptr, C,
1120 if (NullAssignedToNonNull) {
1121 State = State->set<InvariantViolated>(
true);
1129 const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown);
1133 const NullabilityState *TrackedNullability =
1134 State->get<NullabilityMap>(ValueRegion);
1136 if (TrackedNullability) {
1137 if (RhsNullness == NullConstraint::IsNotNull ||
1140 if (Filter.CheckNullablePassedToNonnull &&
1144 reportBugIfInvariantHolds(
"Nullable pointer is assigned to a pointer " 1145 "which is expected to have non-null value",
1146 ErrorKind::NullableAssignedToNonnull, N,
1157 const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S;
1158 State = State->set<NullabilityMap>(
1159 ValueRegion, NullabilityState(ValNullability, NullabilitySource));
1165 const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S;
1166 State = State->set<NullabilityMap>(
1167 ValueRegion, NullabilityState(LocNullability, NullabilitySource));
1173 const char *NL,
const char *Sep)
const {
1175 NullabilityMapTy B = State->get<NullabilityMap>();
1182 for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
1183 Out << I->first <<
" : ";
1184 I->second.print(Out);
1189 #define REGISTER_CHECKER(name, trackingRequired) \ 1190 void ento::register##name##Checker(CheckerManager &mgr) { \ 1191 NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \ 1192 checker->Filter.Check##name = true; \ 1193 checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ 1194 checker->NeedTracking = checker->NeedTracking || trackingRequired; \ 1195 checker->NoDiagnoseCallsToSystemHeaders = \ 1196 checker->NoDiagnoseCallsToSystemHeaders || \ 1197 mgr.getAnalyzerOptions().getBooleanOption( \ 1198 "NoDiagnoseCallsToSystemHeaders", false, checker, true); \ bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
static bool checkParamsForPreconditionViolation(ArrayRef< ParmVarDecl *> Params, ProgramStateRef State, const LocationContext *LocCtxt)
TypedValueRegion - An abstract class representing regions having a typed value.
A (possibly-)qualified type.
MemRegion - The root abstract class for all memory regions.
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
const char *const MemoryError
bool operator==(CanQual< T > x, CanQual< U > y)
Stmt - This represents one statement.
FunctionType - C99 6.7.5.3 - Function Declarators.
A helper class which wraps a boolean value set to false by default.
static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, SVal LV, QualType T)
Returns true when the value stored at the given location is null and the passed in type is nonnnull...
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
virtual QualType getValueType() const =0
Decl - This represents one declaration (or definition), e.g.
StringRef getDeclDescription(const Decl *D)
Returns the word that should be used to refer to the declaration in the report.
static Nullability getReceiverNullability(const ObjCMethodCall &M, ProgramStateRef State)
const ProgramStateRef & getState() const
Represents a variable declaration or definition.
ObjCMethodDecl - Represents an instance or class method declaration.
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
SVal getSVal(const Stmt *S) const
Get the value of arbitrary expressions at this point in the path.
Represents a parameter to a function.
const bool wasInlined
If we are post visiting a call, this flag will be set if the call was inlined.
static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, ProgramStateRef State)
ObjCMethodFamily
A family of Objective-C methods.
AnalysisDeclContext contains the context data for the function or method under analysis.
const Expr * getRetValue() const
virtual const Expr * getArgExpr(unsigned Index) const
Returns the expression associated with a given argument.
BugReporter & getBugReporter()
Represents any expression that calls an Objective-C method.
void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
virtual Kind getKind() const =0
Returns the kind of call this is.
const ImplicitParamDecl * getSelfDecl() const
const LocationContext * getLocationContext() const
virtual bool inTopFrame() const
Return true if the current LocationContext has no caller context.
A builtin binary operation expression such as "x + y" or "x <= y".
SVal getReturnValue() const
Returns the return value of the call.
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
Represents an ObjC class declaration.
We dereferenced a location that may be null.
virtual ArrayRef< ParmVarDecl * > parameters() const =0
Return call's formal parameters.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
ArrayRef< ParmVarDecl * > parameters() const override
const FunctionType * getFunctionType(bool BlocksToo=true) const
Looks through the Decl's underlying type to extract a FunctionType when possible. ...
bool hasDeadSymbols() const
const RegionTy * getAs() const
SymbolicRegion - A special, "non-concrete" region.
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
Expr - This represents one expression.
bool isInSystemHeader() const
Returns true if the callee is known to be from a system header.
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
static SVal getValue(SVal val, SValBuilder &svalBuilder)
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
An expression that sends a message to the given Objective-C object or class.
#define REGISTER_CHECKER(name, trackingRequired)
REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, NullabilityState) enum class NullConstraint
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
BugReporter is a utility class for generating PathDiagnostics for analysis.
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
SourceLocation getLocStart() const LLVM_READONLY
StringRef getFilename(SourceLocation SpellingLoc) const
Return the filename of the file containing a SourceLocation.
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy...
QualType getReturnType() const
static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S)
Returns true if.
const MemRegion * getAsRegion() const
SourceLocation getLocStart() const LLVM_READONLY
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
static const Expr * lookThroughImplicitCasts(const Expr *E)
Find the outermost subexpression of E that is not an implicit cast.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
const Decl * getDecl() const
bool isObjCObjectPointerType() const
bool isAnyPointerType() const
static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, CheckerContext &C)
A class responsible for cleaning up unused symbols.
static const Expr * matchValueExprForBind(const Stmt *S)
For a given statement performing a bind, attempt to syntactically match the expression resulting in t...
const ObjCMethodDecl * getDecl() const override
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
virtual const ObjCMessageExpr * getOriginExpr() const
Selector getSelector() const
Dataflow Directional Tag Classes.
ASTContext & getASTContext()
Nullability getNullabilityAnnotation(QualType Type)
Get nullability annotation for a given type.
Represents an abstract call to a function or method along a particular path.
ExplicitCastExpr - An explicit cast written in the source code.
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
const Decl * getDecl() const
Represents a pointer to an Objective C object.
bool isInstanceMessage() const
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface...
const ProgramStateRef & getState() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
SourceManager & getSourceManager()
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
static bool checkSelfIvarsForInvariantViolation(ProgramStateRef State, const LocationContext *LocCtxt)
ElementRegin is used to represent both array elements and casts.
__DEVICE__ int min(int __a, int __b)
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
This class provides an interface through which checkers can create individual bug reports...
AnalysisDeclContext * getAnalysisDeclContext() const
const LocationContext * getLocationContext() const
const LangOptions & getLangOpts() const
This class handles loading and caching of source files into memory.
SourceManager & getSourceManager()