diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/Sema/Sema.cpp | 13 | ||||
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 179 | ||||
-rw-r--r-- | clang/lib/Sema/SemaOverload.cpp | 2 |
3 files changed, 138 insertions, 56 deletions
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 46fec7324bc..db1251f097f 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -356,6 +356,19 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, assert((VK == VK_RValue || !E->isRValue()) && "can't cast rvalue to lvalue"); #endif + // Check whether we're implicitly casting from a nullable type to a nonnull + // type. + if (auto exprNullability = E->getType()->getNullability(Context)) { + if (*exprNullability == NullabilityKind::Nullable) { + if (auto typeNullability = Ty->getNullability(Context)) { + if (*typeNullability == NullabilityKind::NonNull) { + Diag(E->getLocStart(), diag::warn_nullability_lost) + << E->getType() << Ty; + } + } + } + } + QualType ExprTy = Context.getCanonicalType(E->getType()); QualType TypeTy = Context.getCanonicalType(Ty); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 20140525bb3..f76727cad88 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1115,6 +1115,13 @@ bool Sema::getFormatStringInfo(const FormatAttr *Format, bool IsCXXMember, /// \brief Returns true if the value evaluates to null. static bool CheckNonNullExpr(Sema &S, const Expr *Expr) { + // If the expression has non-null type, it doesn't evaluate to null. + if (auto nullability + = Expr->IgnoreImplicit()->getType()->getNullability(S.Context)) { + if (*nullability == NullabilityKind::NonNull) + return false; + } + // As a special case, transparent unions initialized with zero are // considered null for the purposes of the nonnull attribute. if (const RecordType *UT = Expr->getType()->getAsUnionType()) { @@ -1190,56 +1197,111 @@ DiagnoseCStringFormatDirectiveInCFAPI(Sema &S, } } +/// Determine whether the given type has a non-null nullability annotation. +static bool isNonNullType(ASTContext &ctx, QualType type) { + if (auto nullability = type->getNullability(ctx)) + return *nullability == NullabilityKind::NonNull; + + return false; +} + static void CheckNonNullArguments(Sema &S, const NamedDecl *FDecl, + const FunctionProtoType *Proto, ArrayRef<const Expr *> Args, SourceLocation CallSiteLoc) { + assert((FDecl || Proto) && "Need a function declaration or prototype"); + // Check the attributes attached to the method/function itself. llvm::SmallBitVector NonNullArgs; - for (const auto *NonNull : FDecl->specific_attrs<NonNullAttr>()) { - if (!NonNull->args_size()) { - // Easy case: all pointer arguments are nonnull. - for (const auto *Arg : Args) - if (S.isValidPointerAttrType(Arg->getType())) - CheckNonNullArgument(S, Arg, CallSiteLoc); - return; - } + if (FDecl) { + // Handle the nonnull attribute on the function/method declaration itself. + for (const auto *NonNull : FDecl->specific_attrs<NonNullAttr>()) { + if (!NonNull->args_size()) { + // Easy case: all pointer arguments are nonnull. + for (const auto *Arg : Args) + if (S.isValidPointerAttrType(Arg->getType())) + CheckNonNullArgument(S, Arg, CallSiteLoc); + return; + } - for (unsigned Val : NonNull->args()) { - if (Val >= Args.size()) - continue; - if (NonNullArgs.empty()) - NonNullArgs.resize(Args.size()); - NonNullArgs.set(Val); + for (unsigned Val : NonNull->args()) { + if (Val >= Args.size()) + continue; + if (NonNullArgs.empty()) + NonNullArgs.resize(Args.size()); + NonNullArgs.set(Val); + } } } - // Check the attributes on the parameters. - ArrayRef<ParmVarDecl*> parms; - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl)) - parms = FD->parameters(); - else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(FDecl)) - parms = MD->parameters(); - - unsigned ArgIndex = 0; - for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end(); - I != E; ++I, ++ArgIndex) { - const ParmVarDecl *PVD = *I; - if (PVD->hasAttr<NonNullAttr>() || - (ArgIndex < NonNullArgs.size() && NonNullArgs[ArgIndex])) - CheckNonNullArgument(S, Args[ArgIndex], CallSiteLoc); + if (FDecl && (isa<FunctionDecl>(FDecl) || isa<ObjCMethodDecl>(FDecl))) { + // Handle the nonnull attribute on the parameters of the + // function/method. + ArrayRef<ParmVarDecl*> parms; + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FDecl)) + parms = FD->parameters(); + else + parms = cast<ObjCMethodDecl>(FDecl)->parameters(); + + unsigned ParamIndex = 0; + for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end(); + I != E; ++I, ++ParamIndex) { + const ParmVarDecl *PVD = *I; + if (PVD->hasAttr<NonNullAttr>() || + isNonNullType(S.Context, PVD->getType())) { + if (NonNullArgs.empty()) + NonNullArgs.resize(Args.size()); + + NonNullArgs.set(ParamIndex); + } + } + } else { + // If we have a non-function, non-method declaration but no + // function prototype, try to dig out the function prototype. + if (!Proto) { + if (const ValueDecl *VD = dyn_cast<ValueDecl>(FDecl)) { + QualType type = VD->getType().getNonReferenceType(); + if (auto pointerType = type->getAs<PointerType>()) + type = pointerType->getPointeeType(); + else if (auto blockType = type->getAs<BlockPointerType>()) + type = blockType->getPointeeType(); + // FIXME: data member pointers? + + // Dig out the function prototype, if there is one. + Proto = type->getAs<FunctionProtoType>(); + } + } + + // Fill in non-null argument information from the nullability + // information on the parameter types (if we have them). + if (Proto) { + unsigned Index = 0; + for (auto paramType : Proto->getParamTypes()) { + if (isNonNullType(S.Context, paramType)) { + if (NonNullArgs.empty()) + NonNullArgs.resize(Args.size()); + + NonNullArgs.set(Index); + } + + ++Index; + } + } } - // In case this is a variadic call, check any remaining arguments. - for (/**/; ArgIndex < NonNullArgs.size(); ++ArgIndex) + // Check for non-null arguments. + for (unsigned ArgIndex = 0, ArgIndexEnd = NonNullArgs.size(); + ArgIndex != ArgIndexEnd; ++ArgIndex) { if (NonNullArgs[ArgIndex]) CheckNonNullArgument(S, Args[ArgIndex], CallSiteLoc); + } } /// Handles the checks for format strings, non-POD arguments to vararg /// functions, and NULL arguments passed to non-NULL parameters. -void Sema::checkCall(NamedDecl *FDecl, ArrayRef<const Expr *> Args, - unsigned NumParams, bool IsMemberFunction, +void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto, + ArrayRef<const Expr *> Args, bool IsMemberFunction, SourceLocation Loc, SourceRange Range, VariadicCallType CallType) { // FIXME: We should check as much as we can in the template definition. @@ -1261,6 +1323,13 @@ void Sema::checkCall(NamedDecl *FDecl, ArrayRef<const Expr *> Args, // Refuse POD arguments that weren't caught by the format string // checks above. if (CallType != VariadicDoesNotApply) { + unsigned NumParams = Proto ? Proto->getNumParams() + : FDecl && isa<FunctionDecl>(FDecl) + ? cast<FunctionDecl>(FDecl)->getNumParams() + : FDecl && isa<ObjCMethodDecl>(FDecl) + ? cast<ObjCMethodDecl>(FDecl)->param_size() + : 0; + for (unsigned ArgIdx = NumParams; ArgIdx < Args.size(); ++ArgIdx) { // Args[ArgIdx] can be null in malformed code. if (const Expr *Arg = Args[ArgIdx]) { @@ -1270,12 +1339,14 @@ void Sema::checkCall(NamedDecl *FDecl, ArrayRef<const Expr *> Args, } } - if (FDecl) { - CheckNonNullArguments(*this, FDecl, Args, Loc); + if (FDecl || Proto) { + CheckNonNullArguments(*this, FDecl, Proto, Args, Loc); // Type safety checking. - for (const auto *I : FDecl->specific_attrs<ArgumentWithTypeTagAttr>()) - CheckArgumentWithTypeTag(I, Args.data()); + if (FDecl) { + for (const auto *I : FDecl->specific_attrs<ArgumentWithTypeTagAttr>()) + CheckArgumentWithTypeTag(I, Args.data()); + } } } @@ -1287,8 +1358,8 @@ void Sema::CheckConstructorCall(FunctionDecl *FDecl, SourceLocation Loc) { VariadicCallType CallType = Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply; - checkCall(FDecl, Args, Proto->getNumParams(), - /*IsMemberFunction=*/true, Loc, SourceRange(), CallType); + checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(), + CallType); } /// CheckFunctionCall - Check a direct function call for various correctness @@ -1301,7 +1372,6 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, IsMemberOperatorCall; VariadicCallType CallType = getVariadicCallType(FDecl, Proto, TheCall->getCallee()); - unsigned NumParams = Proto ? Proto->getNumParams() : 0; Expr** Args = TheCall->getArgs(); unsigned NumArgs = TheCall->getNumArgs(); if (IsMemberOperatorCall) { @@ -1311,7 +1381,7 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, ++Args; --NumArgs; } - checkCall(FDecl, llvm::makeArrayRef(Args, NumArgs), NumParams, + checkCall(FDecl, Proto, llvm::makeArrayRef(Args, NumArgs), IsMemberFunction, TheCall->getRParenLoc(), TheCall->getCallee()->getSourceRange(), CallType); @@ -1345,9 +1415,9 @@ bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac, VariadicCallType CallType = Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply; - checkCall(Method, Args, Method->param_size(), - /*IsMemberFunction=*/false, - lbrac, Method->getSourceRange(), CallType); + checkCall(Method, nullptr, Args, + /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), + CallType); return false; } @@ -1356,13 +1426,14 @@ bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall, const FunctionProtoType *Proto) { QualType Ty; if (const auto *V = dyn_cast<VarDecl>(NDecl)) - Ty = V->getType(); + Ty = V->getType().getNonReferenceType(); else if (const auto *F = dyn_cast<FieldDecl>(NDecl)) - Ty = F->getType(); + Ty = F->getType().getNonReferenceType(); else return false; - if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType()) + if (!Ty->isBlockPointerType() && !Ty->isFunctionPointerType() && + !Ty->isFunctionProtoType()) return false; VariadicCallType CallType; @@ -1373,11 +1444,10 @@ bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall, } else { // Ty->isFunctionPointerType() CallType = VariadicFunction; } - unsigned NumParams = Proto ? Proto->getNumParams() : 0; - checkCall(NDecl, llvm::makeArrayRef(TheCall->getArgs(), - TheCall->getNumArgs()), - NumParams, /*IsMemberFunction=*/false, TheCall->getRParenLoc(), + checkCall(NDecl, Proto, + llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()), + /*IsMemberFunction=*/false, TheCall->getRParenLoc(), TheCall->getCallee()->getSourceRange(), CallType); return false; @@ -1388,11 +1458,9 @@ bool Sema::CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall, bool Sema::CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto) { VariadicCallType CallType = getVariadicCallType(/*FDecl=*/nullptr, Proto, TheCall->getCallee()); - unsigned NumParams = Proto ? Proto->getNumParams() : 0; - - checkCall(/*FDecl=*/nullptr, + checkCall(/*FDecl=*/nullptr, Proto, llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()), - NumParams, /*IsMemberFunction=*/false, TheCall->getRParenLoc(), + /*IsMemberFunction=*/false, TheCall->getRParenLoc(), TheCall->getCallee()->getSourceRange(), CallType); return false; @@ -5680,7 +5748,8 @@ Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType, CheckReturnStackAddr(*this, RetValExp, lhsType, ReturnLoc); // Check if the return value is null but should not be. - if (Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs) && + if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) || + (!isObjCMethod && isNonNullType(Context, lhsType))) && CheckNonNullExpr(*this, RetValExp)) Diag(ReturnLoc, diag::warn_null_ret) << (isObjCMethod ? 1 : 0) << RetValExp->getSourceRange(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index f8610e009bd..a0fdcd78e59 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -11260,7 +11260,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc, if (Op == OO_Equal) DiagnoseSelfMove(Args[0], Args[1], OpLoc); - checkCall(FnDecl, ArgsArray, 0, isa<CXXMethodDecl>(FnDecl), OpLoc, + checkCall(FnDecl, nullptr, ArgsArray, isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(), VariadicDoesNotApply); return MaybeBindToTemporary(TheCall); |