[Sema] Make nullness warnings appear in C++.

Given the following code:

    int *_Nullable ptr;
    int *_Nonnull nn = ptr;

...In C, clang will warn you about `nn = ptr`, because you're assigning
a nonnull pointer to a nullable pointer. In C++, clang issues no such
warning. This patch helps ensure that clang doesn't ever miss an
opportunity to complain about C++ code.

N.B. Though this patch has a differential revision link, the actual
review took place over email.

Differential Revision: http://reviews.llvm.org/D14938

llvm-svn: 255556
This commit is contained in:
George Burgess IV 2015-12-14 22:00:49 +00:00
parent fa54acedd1
commit 8d141e0120
5 changed files with 61 additions and 13 deletions

View File

@ -3511,6 +3511,11 @@ public:
void DiagnoseSelfMove(const Expr *LHSExpr, const Expr *RHSExpr,
SourceLocation OpLoc);
/// \brief Warn if we're implicitly casting from a _Nullable pointer type to a
/// _Nonnull one.
void diagnoseNullableToNonnullConversion(QualType DstType, QualType SrcType,
SourceLocation Loc);
ParsingDeclState PushParsingDeclaration(sema::DelayedDiagnosticPool &pool) {
return DelayedDiagnostics.push(pool);
}

View File

@ -349,6 +349,20 @@ void Sema::PrintStats() const {
AnalysisWarnings.PrintStats();
}
void Sema::diagnoseNullableToNonnullConversion(QualType DstType,
QualType SrcType,
SourceLocation Loc) {
Optional<NullabilityKind> ExprNullability = SrcType->getNullability(Context);
if (!ExprNullability || *ExprNullability != NullabilityKind::Nullable)
return;
Optional<NullabilityKind> TypeNullability = DstType->getNullability(Context);
if (!TypeNullability || *TypeNullability != NullabilityKind::NonNull)
return;
Diag(Loc, diag::warn_nullability_lost) << SrcType << DstType;
}
/// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast.
/// If there is already an implicit cast, merge into the existing one.
/// The result is of the given category.
@ -372,18 +386,7 @@ 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;
}
}
}
}
diagnoseNullableToNonnullConversion(Ty, E->getType(), E->getLocStart());
QualType ExprTy = Context.getCanonicalType(E->getType());
QualType TypeTy = Context.getCanonicalType(Ty);

View File

@ -3118,6 +3118,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
ToType = ToAtomic->getValueType();
}
QualType InitialFromType = FromType;
// Perform the first implicit conversion.
switch (SCS.First) {
case ICK_Identity:
@ -3488,6 +3489,12 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
VK_RValue, nullptr, CCK).get();
}
// If this conversion sequence succeeded and involved implicitly converting a
// _Nullable type to a _Nonnull one, complain.
if (CCK == CCK_ImplicitConversion)
diagnoseNullableToNonnullConversion(ToType, InitialFromType,
From->getLocStart());
return From;
}

View File

@ -112,4 +112,7 @@ _Nonnull int *returns_int_ptr(int x) {
void nullable_to_nonnull(_Nullable int *ptr) {
int *a = ptr; // okay
_Nonnull int *b = ptr; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}}
b = ptr; // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}}
accepts_nonnull_1(ptr); // expected-warning{{implicit conversion from nullable pointer 'int * _Nullable' to non-nullable pointer type 'int * _Nonnull'}}
}

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wno-nullability-declspec %s -verify -Wnullable-to-nonnull-conversion
#if __has_feature(nullability)
#else
@ -67,3 +67,33 @@ void test_accepts_nonnull_null_pointer_literal_template() {
}
template void test_accepts_nonnull_null_pointer_literal_template<&accepts_nonnull_4>(); // expected-note{{instantiation of function template specialization}}
void TakeNonnull(void *_Nonnull);
// Check different forms of assignment to a nonull type from a nullable one.
void AssignAndInitNonNull() {
void *_Nullable nullable;
void *_Nonnull p(nullable); // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull p2{nullable}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull p3 = {nullable}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull p4 = nullable; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull nonnull;
nonnull = nullable; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
nonnull = {nullable}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
TakeNonnull(nullable); //expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull}}
TakeNonnull(nonnull); // OK
}
void *_Nullable ReturnNullable();
void AssignAndInitNonNullFromFn() {
void *_Nonnull p(ReturnNullable()); // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull p2{ReturnNullable()}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull p3 = {ReturnNullable()}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull p4 = ReturnNullable(); // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
void *_Nonnull nonnull;
nonnull = ReturnNullable(); // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
nonnull = {ReturnNullable()}; // expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull'}}
TakeNonnull(ReturnNullable()); //expected-warning{{implicit conversion from nullable pointer 'void * _Nullable' to non-nullable pointer type 'void * _Nonnull}}
}