зеркало из https://github.com/microsoft/clang-1.git
Initial steps to improve diagnostics when there is a NULL and
a non-pointer on the two sides of a conditional expression. Patch by Stephen Hines and Mihai Rusu. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@125995 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
0656e5b9aa
Коммит
82214a80c0
|
@ -437,6 +437,22 @@ public:
|
||||||
/// EvaluateAsLValue - Evaluate an expression to see if it's a lvalue.
|
/// EvaluateAsLValue - Evaluate an expression to see if it's a lvalue.
|
||||||
bool EvaluateAsAnyLValue(EvalResult &Result, const ASTContext &Ctx) const;
|
bool EvaluateAsAnyLValue(EvalResult &Result, const ASTContext &Ctx) const;
|
||||||
|
|
||||||
|
/// \brief Enumeration used to describe the kind of Null pointer constant
|
||||||
|
/// returned from \c isNullPointerConstant().
|
||||||
|
enum NullPointerConstantKind {
|
||||||
|
/// \brief Expression is not a Null pointer constant.
|
||||||
|
NPCK_NotNull = 0,
|
||||||
|
|
||||||
|
/// \brief Expression is a Null pointer constant built from a zero integer.
|
||||||
|
NPCK_ZeroInteger,
|
||||||
|
|
||||||
|
/// \brief Expression is a C++0X nullptr.
|
||||||
|
NPCK_CXX0X_nullptr,
|
||||||
|
|
||||||
|
/// \brief Expression is a GNU-style __null constant.
|
||||||
|
NPCK_GNUNull
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Enumeration used to describe how \c isNullPointerConstant()
|
/// \brief Enumeration used to describe how \c isNullPointerConstant()
|
||||||
/// should cope with value-dependent expressions.
|
/// should cope with value-dependent expressions.
|
||||||
enum NullPointerConstantValueDependence {
|
enum NullPointerConstantValueDependence {
|
||||||
|
@ -452,11 +468,12 @@ public:
|
||||||
NPC_ValueDependentIsNotNull
|
NPC_ValueDependentIsNotNull
|
||||||
};
|
};
|
||||||
|
|
||||||
/// isNullPointerConstant - C99 6.3.2.3p3 - Return true if this is either an
|
/// isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to
|
||||||
/// integer constant expression with the value zero, or if this is one that is
|
/// a Null pointer constant. The return value can further distinguish the
|
||||||
/// cast to void*.
|
/// kind of NULL pointer constant that was detected.
|
||||||
bool isNullPointerConstant(ASTContext &Ctx,
|
NullPointerConstantKind isNullPointerConstant(
|
||||||
NullPointerConstantValueDependence NPC) const;
|
ASTContext &Ctx,
|
||||||
|
NullPointerConstantValueDependence NPC) const;
|
||||||
|
|
||||||
/// isOBJCGCCandidate - Return true if this expression may be used in a read/
|
/// isOBJCGCCandidate - Return true if this expression may be used in a read/
|
||||||
/// write barrier.
|
/// write barrier.
|
||||||
|
|
|
@ -3147,6 +3147,8 @@ def err_incomplete_type_used_in_type_trait_expr : Error<
|
||||||
"incomplete type %0 used in type trait expression">;
|
"incomplete type %0 used in type trait expression">;
|
||||||
def err_expected_ident_or_lparen : Error<"expected identifier or '('">;
|
def err_expected_ident_or_lparen : Error<"expected identifier or '('">;
|
||||||
|
|
||||||
|
def err_typecheck_cond_incompatible_operands_null : Error<
|
||||||
|
"non-pointer operand type %0 incompatible with %select{NULL|nullptr}1">;
|
||||||
} // End of general sema category.
|
} // End of general sema category.
|
||||||
|
|
||||||
// inline asm.
|
// inline asm.
|
||||||
|
|
|
@ -4774,6 +4774,9 @@ public:
|
||||||
QualType FindCompositeObjCPointerType(Expr *&LHS, Expr *&RHS,
|
QualType FindCompositeObjCPointerType(Expr *&LHS, Expr *&RHS,
|
||||||
SourceLocation questionLoc);
|
SourceLocation questionLoc);
|
||||||
|
|
||||||
|
bool DiagnoseConditionalForNull(Expr *LHS, Expr *RHS,
|
||||||
|
SourceLocation QuestionLoc);
|
||||||
|
|
||||||
/// type checking for vector binary operators.
|
/// type checking for vector binary operators.
|
||||||
QualType CheckVectorOperands(SourceLocation l, Expr *&lex, Expr *&rex);
|
QualType CheckVectorOperands(SourceLocation l, Expr *&lex, Expr *&rex);
|
||||||
QualType CheckVectorCompareOperands(Expr *&lex, Expr *&rx,
|
QualType CheckVectorCompareOperands(Expr *&lex, Expr *&rx,
|
||||||
|
|
|
@ -2132,11 +2132,14 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const {
|
||||||
return isEvaluatable(Ctx);
|
return isEvaluatable(Ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// isNullPointerConstant - C99 6.3.2.3p3 - Return true if this is either an
|
/// isNullPointerConstant - C99 6.3.2.3p3 - Return whether this is a null
|
||||||
/// integer constant expression with the value zero, or if this is one that is
|
/// pointer constant or not, as well as the specific kind of constant detected.
|
||||||
/// cast to void*.
|
/// Null pointer constants can be integer constant expressions with the
|
||||||
bool Expr::isNullPointerConstant(ASTContext &Ctx,
|
/// value zero, casts of zero to void*, nullptr (C++0X), or __null
|
||||||
NullPointerConstantValueDependence NPC) const {
|
/// (a GNU extension).
|
||||||
|
Expr::NullPointerConstantKind
|
||||||
|
Expr::isNullPointerConstant(ASTContext &Ctx,
|
||||||
|
NullPointerConstantValueDependence NPC) const {
|
||||||
if (isValueDependent()) {
|
if (isValueDependent()) {
|
||||||
switch (NPC) {
|
switch (NPC) {
|
||||||
case NPC_NeverValueDependent:
|
case NPC_NeverValueDependent:
|
||||||
|
@ -2144,10 +2147,13 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
|
||||||
// If the unthinkable happens, fall through to the safest alternative.
|
// If the unthinkable happens, fall through to the safest alternative.
|
||||||
|
|
||||||
case NPC_ValueDependentIsNull:
|
case NPC_ValueDependentIsNull:
|
||||||
return isTypeDependent() || getType()->isIntegralType(Ctx);
|
if (isTypeDependent() || getType()->isIntegralType(Ctx))
|
||||||
|
return NPCK_ZeroInteger;
|
||||||
|
else
|
||||||
|
return NPCK_NotNull;
|
||||||
|
|
||||||
case NPC_ValueDependentIsNotNull:
|
case NPC_ValueDependentIsNotNull:
|
||||||
return false;
|
return NPCK_NotNull;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2176,12 +2182,12 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
|
||||||
return DefaultArg->getExpr()->isNullPointerConstant(Ctx, NPC);
|
return DefaultArg->getExpr()->isNullPointerConstant(Ctx, NPC);
|
||||||
} else if (isa<GNUNullExpr>(this)) {
|
} else if (isa<GNUNullExpr>(this)) {
|
||||||
// The GNU __null extension is always a null pointer constant.
|
// The GNU __null extension is always a null pointer constant.
|
||||||
return true;
|
return NPCK_GNUNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// C++0x nullptr_t is always a null pointer constant.
|
// C++0x nullptr_t is always a null pointer constant.
|
||||||
if (getType()->isNullPtrType())
|
if (getType()->isNullPtrType())
|
||||||
return true;
|
return NPCK_CXX0X_nullptr;
|
||||||
|
|
||||||
if (const RecordType *UT = getType()->getAsUnionType())
|
if (const RecordType *UT = getType()->getAsUnionType())
|
||||||
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>())
|
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>())
|
||||||
|
@ -2193,12 +2199,14 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
|
||||||
// This expression must be an integer type.
|
// This expression must be an integer type.
|
||||||
if (!getType()->isIntegerType() ||
|
if (!getType()->isIntegerType() ||
|
||||||
(Ctx.getLangOptions().CPlusPlus && getType()->isEnumeralType()))
|
(Ctx.getLangOptions().CPlusPlus && getType()->isEnumeralType()))
|
||||||
return false;
|
return NPCK_NotNull;
|
||||||
|
|
||||||
// If we have an integer constant expression, we need to *evaluate* it and
|
// If we have an integer constant expression, we need to *evaluate* it and
|
||||||
// test for the value 0.
|
// test for the value 0.
|
||||||
llvm::APSInt Result;
|
llvm::APSInt Result;
|
||||||
return isIntegerConstantExpr(Result, Ctx) && Result == 0;
|
bool IsNull = isIntegerConstantExpr(Result, Ctx) && Result == 0;
|
||||||
|
|
||||||
|
return (IsNull ? NPCK_ZeroInteger : NPCK_NotNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief If this expression is an l-value for an Objective C
|
/// \brief If this expression is an l-value for an Objective C
|
||||||
|
|
|
@ -5229,6 +5229,46 @@ ExprResult Sema::ActOnParenOrParenListExpr(SourceLocation L,
|
||||||
return Owned(expr);
|
return Owned(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Emit a specialized diagnostic when one expression is a null pointer
|
||||||
|
/// constant and the other is not a pointer.
|
||||||
|
bool Sema::DiagnoseConditionalForNull(Expr *LHS, Expr *RHS,
|
||||||
|
SourceLocation QuestionLoc) {
|
||||||
|
Expr *NullExpr = LHS;
|
||||||
|
Expr *NonPointerExpr = RHS;
|
||||||
|
Expr::NullPointerConstantKind NullKind =
|
||||||
|
NullExpr->isNullPointerConstant(Context,
|
||||||
|
Expr::NPC_ValueDependentIsNotNull);
|
||||||
|
|
||||||
|
if (NullKind == Expr::NPCK_NotNull) {
|
||||||
|
NullExpr = RHS;
|
||||||
|
NonPointerExpr = LHS;
|
||||||
|
NullKind =
|
||||||
|
NullExpr->isNullPointerConstant(Context,
|
||||||
|
Expr::NPC_ValueDependentIsNotNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NullKind == Expr::NPCK_NotNull)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (NullKind == Expr::NPCK_ZeroInteger) {
|
||||||
|
// In this case, check to make sure that we got here from a "NULL"
|
||||||
|
// string in the source code.
|
||||||
|
NullExpr = NullExpr->IgnoreParenImpCasts();
|
||||||
|
SourceManager& SM = Context.getSourceManager();
|
||||||
|
SourceLocation Loc = SM.getInstantiationLoc(NullExpr->getExprLoc());
|
||||||
|
unsigned Len =
|
||||||
|
Lexer::MeasureTokenLength(Loc, SM, Context.getLangOptions());
|
||||||
|
if (Len != 4 || memcmp(SM.getCharacterData(Loc), "NULL", 4))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DiagType = (NullKind == Expr::NPCK_CXX0X_nullptr);
|
||||||
|
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands_null)
|
||||||
|
<< NonPointerExpr->getType() << DiagType
|
||||||
|
<< NonPointerExpr->getSourceRange();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Note that lhs is not null here, even if this is the gnu "x ?: y" extension.
|
/// Note that lhs is not null here, even if this is the gnu "x ?: y" extension.
|
||||||
/// In that case, lhs = cond.
|
/// In that case, lhs = cond.
|
||||||
/// C99 6.5.15
|
/// C99 6.5.15
|
||||||
|
@ -5471,6 +5511,12 @@ QualType Sema::CheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS,
|
||||||
return LHSTy;
|
return LHSTy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Emit a better diagnostic if one of the expressions is a null pointer
|
||||||
|
// constant and the other is not a pointer type. In this case, the user most
|
||||||
|
// likely forgot to take the address of the other expression.
|
||||||
|
if (DiagnoseConditionalForNull(LHS, RHS, QuestionLoc))
|
||||||
|
return QualType();
|
||||||
|
|
||||||
// Otherwise, the operands are not compatible.
|
// Otherwise, the operands are not compatible.
|
||||||
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
|
Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
|
||||||
<< LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange();
|
<< LHSTy << RHSTy << LHS->getSourceRange() << RHS->getSourceRange();
|
||||||
|
|
|
@ -2874,13 +2874,14 @@ static bool TryClassUnification(Sema &Self, Expr *From, Expr *To,
|
||||||
/// value operand is a class type, overload resolution is used to find a
|
/// value operand is a class type, overload resolution is used to find a
|
||||||
/// conversion to a common type.
|
/// conversion to a common type.
|
||||||
static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
|
static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
|
||||||
SourceLocation Loc) {
|
SourceLocation QuestionLoc) {
|
||||||
Expr *Args[2] = { LHS, RHS };
|
Expr *Args[2] = { LHS, RHS };
|
||||||
OverloadCandidateSet CandidateSet(Loc);
|
OverloadCandidateSet CandidateSet(QuestionLoc);
|
||||||
Self.AddBuiltinOperatorCandidates(OO_Conditional, Loc, Args, 2, CandidateSet);
|
Self.AddBuiltinOperatorCandidates(OO_Conditional, QuestionLoc, Args, 2,
|
||||||
|
CandidateSet);
|
||||||
|
|
||||||
OverloadCandidateSet::iterator Best;
|
OverloadCandidateSet::iterator Best;
|
||||||
switch (CandidateSet.BestViableFunction(Self, Loc, Best)) {
|
switch (CandidateSet.BestViableFunction(Self, QuestionLoc, Best)) {
|
||||||
case OR_Success:
|
case OR_Success:
|
||||||
// We found a match. Perform the conversions on the arguments and move on.
|
// We found a match. Perform the conversions on the arguments and move on.
|
||||||
if (Self.PerformImplicitConversion(LHS, Best->BuiltinTypes.ParamTypes[0],
|
if (Self.PerformImplicitConversion(LHS, Best->BuiltinTypes.ParamTypes[0],
|
||||||
|
@ -2891,13 +2892,20 @@ static bool FindConditionalOverload(Sema &Self, Expr *&LHS, Expr *&RHS,
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case OR_No_Viable_Function:
|
case OR_No_Viable_Function:
|
||||||
Self.Diag(Loc, diag::err_typecheck_cond_incompatible_operands)
|
|
||||||
|
// Emit a better diagnostic if one of the expressions is a null pointer
|
||||||
|
// constant and the other is a pointer type. In this case, the user most
|
||||||
|
// likely forgot to take the address of the other expression.
|
||||||
|
if (Self.DiagnoseConditionalForNull(LHS, RHS, QuestionLoc))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Self.Diag(QuestionLoc, diag::err_typecheck_cond_incompatible_operands)
|
||||||
<< LHS->getType() << RHS->getType()
|
<< LHS->getType() << RHS->getType()
|
||||||
<< LHS->getSourceRange() << RHS->getSourceRange();
|
<< LHS->getSourceRange() << RHS->getSourceRange();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case OR_Ambiguous:
|
case OR_Ambiguous:
|
||||||
Self.Diag(Loc, diag::err_conditional_ambiguous_ovl)
|
Self.Diag(QuestionLoc, diag::err_conditional_ambiguous_ovl)
|
||||||
<< LHS->getType() << RHS->getType()
|
<< LHS->getType() << RHS->getType()
|
||||||
<< LHS->getSourceRange() << RHS->getSourceRange();
|
<< LHS->getSourceRange() << RHS->getSourceRange();
|
||||||
// FIXME: Print the possible common types by printing the return types of
|
// FIXME: Print the possible common types by printing the return types of
|
||||||
|
|
|
@ -75,3 +75,16 @@ int f2(int x) {
|
||||||
// We can suppress this because the immediate context wants an int.
|
// We can suppress this because the immediate context wants an int.
|
||||||
return (x != 0) ? 0U : x;
|
return (x != 0) ? 0U : x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define NULL (void*)0
|
||||||
|
|
||||||
|
void PR9236() {
|
||||||
|
struct A {int i;} A1;
|
||||||
|
(void)(1 ? A1 : NULL); // expected-error{{non-pointer operand type 'struct A' incompatible with NULL}}
|
||||||
|
(void)(1 ? NULL : A1); // expected-error{{non-pointer operand type 'struct A' incompatible with NULL}}
|
||||||
|
(void)(1 ? 0 : A1); // expected-error{{incompatible operand types}}
|
||||||
|
(void)(1 ? (void*)0 : A1); // expected-error{{incompatible operand types}}
|
||||||
|
(void)(1 ? A1: (void*)0); // expected-error{{incompatible operand types}}
|
||||||
|
(void)(1 ? A1 : (NULL)); // expected-error{{non-pointer operand type 'struct A' incompatible with NULL}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,3 +12,10 @@ void f() {
|
||||||
// Verify that null is evaluated as 0.
|
// Verify that null is evaluated as 0.
|
||||||
int b[__null ? -1 : 1];
|
int b[__null ? -1 : 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct A {};
|
||||||
|
|
||||||
|
void g() {
|
||||||
|
(void)(0 ? __null : A()); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
|
||||||
|
(void)(0 ? A(): __null); // expected-error {{non-pointer operand type 'A' incompatible with NULL}}
|
||||||
|
}
|
||||||
|
|
|
@ -307,3 +307,15 @@ namespace PR7598 {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace PR9236 {
|
||||||
|
#define NULL 0L
|
||||||
|
void f() {
|
||||||
|
(void)(true ? A() : NULL); // expected-error{{non-pointer operand type 'A' incompatible with NULL}}
|
||||||
|
(void)(true ? NULL : A()); // expected-error{{non-pointer operand type 'A' incompatible with NULL}}
|
||||||
|
(void)(true ? 0 : A()); // expected-error{{incompatible operand types}}
|
||||||
|
(void)(true ? nullptr : A()); // expected-error{{non-pointer operand type 'A' incompatible with nullptr}}
|
||||||
|
(void)(true ? __null : A()); // expected-error{{non-pointer operand type 'A' incompatible with NULL}}
|
||||||
|
(void)(true ? (void*)0 : A()); // expected-error{{incompatible operand types}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ nullptr_t f(nullptr_t null)
|
||||||
(void)(1 + nullptr); // expected-error {{invalid operands to binary expression}}
|
(void)(1 + nullptr); // expected-error {{invalid operands to binary expression}}
|
||||||
(void)(0 ? nullptr : 0); // expected-error {{incompatible operand types}}
|
(void)(0 ? nullptr : 0); // expected-error {{incompatible operand types}}
|
||||||
(void)(0 ? nullptr : (void*)0);
|
(void)(0 ? nullptr : (void*)0);
|
||||||
|
(void)(0 ? nullptr : A()); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
|
||||||
|
(void)(0 ? A() : nullptr); // expected-error {{non-pointer operand type 'A' incompatible with nullptr}}
|
||||||
|
|
||||||
// Overloading
|
// Overloading
|
||||||
int t = o1(nullptr);
|
int t = o1(nullptr);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче