[Clang][Sema] Implement GCC -Wcast-function-type
``` Warn when a function pointer is cast to an incompatible function pointer. In a cast involving function types with a variable argument list only the types of initial arguments that are provided are considered. Any parameter of pointer-type matches any other pointer-type. Any benign differences in integral types are ignored, like int vs. long on ILP32 targets. Likewise type qualifiers are ignored. The function type void (*) (void) is special and matches everything, which can be used to suppress this warning. In a cast involving pointer to member types this warning warns whenever the type cast is changing the pointer to member type. This warning is enabled by -Wextra. ``` Reviewed By: rsmith Differential Revision: https://reviews.llvm.org/D97831
This commit is contained in:
parent
ef69aa961d
commit
217f0f735a
|
@ -851,6 +851,13 @@ This diagnostic is enabled by default.
|
||||||
|:warning:`warning:` |nbsp| :diagtext:`cast from function call of type` |nbsp| :placeholder:`A` |nbsp| :diagtext:`to non-matching type` |nbsp| :placeholder:`B`|
|
|:warning:`warning:` |nbsp| :diagtext:`cast from function call of type` |nbsp| :placeholder:`A` |nbsp| :diagtext:`to non-matching type` |nbsp| :placeholder:`B`|
|
||||||
+--------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
-Wcast-function-type
|
||||||
|
-------------------
|
||||||
|
**Diagnostic text:**
|
||||||
|
|
||||||
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|:warning:`warning:` |nbsp| :diagtext:`cast from` |nbsp| :placeholder:`A` |nbsp| :diagtext:`to` |nbsp| :placeholder:`B` |nbsp| :diagtext:`converts to incompatible function types`|
|
||||||
|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
-Wbinary-literal
|
-Wbinary-literal
|
||||||
----------------
|
----------------
|
||||||
|
|
|
@ -499,6 +499,7 @@ def PrivateExtern : DiagGroup<"private-extern">;
|
||||||
def SelTypeCast : DiagGroup<"cast-of-sel-type">;
|
def SelTypeCast : DiagGroup<"cast-of-sel-type">;
|
||||||
def FunctionDefInObjCContainer : DiagGroup<"function-def-in-objc-container">;
|
def FunctionDefInObjCContainer : DiagGroup<"function-def-in-objc-container">;
|
||||||
def BadFunctionCast : DiagGroup<"bad-function-cast">;
|
def BadFunctionCast : DiagGroup<"bad-function-cast">;
|
||||||
|
def CastFunctionType : DiagGroup<"cast-function-type">;
|
||||||
def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
|
def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
|
||||||
def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
|
def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
|
||||||
def ObjCPropertyAssignOnObjectType : DiagGroup<"objc-property-assign-on-object-type">;
|
def ObjCPropertyAssignOnObjectType : DiagGroup<"objc-property-assign-on-object-type">;
|
||||||
|
|
|
@ -8386,6 +8386,9 @@ def note_change_calling_conv_fixit : Note<
|
||||||
def warn_bad_function_cast : Warning<
|
def warn_bad_function_cast : Warning<
|
||||||
"cast from function call of type %0 to non-matching type %1">,
|
"cast from function call of type %0 to non-matching type %1">,
|
||||||
InGroup<BadFunctionCast>, DefaultIgnore;
|
InGroup<BadFunctionCast>, DefaultIgnore;
|
||||||
|
def warn_cast_function_type : Warning<
|
||||||
|
"cast from %0 to %1 converts to incompatible function types">,
|
||||||
|
InGroup<CastFunctionType>, DefaultIgnore;
|
||||||
def err_cast_pointer_to_non_pointer_int : Error<
|
def err_cast_pointer_to_non_pointer_int : Error<
|
||||||
"pointer cannot be cast to type %0">;
|
"pointer cannot be cast to type %0">;
|
||||||
def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">;
|
def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">;
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "clang/Sema/SemaInternal.h"
|
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
|
#include "clang/AST/ASTStructuralEquivalence.h"
|
||||||
#include "clang/AST/CXXInheritance.h"
|
#include "clang/AST/CXXInheritance.h"
|
||||||
#include "clang/AST/ExprCXX.h"
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/ExprObjC.h"
|
#include "clang/AST/ExprObjC.h"
|
||||||
|
@ -23,6 +23,7 @@
|
||||||
#include "clang/Basic/TargetInfo.h"
|
#include "clang/Basic/TargetInfo.h"
|
||||||
#include "clang/Lex/Preprocessor.h"
|
#include "clang/Lex/Preprocessor.h"
|
||||||
#include "clang/Sema/Initialization.h"
|
#include "clang/Sema/Initialization.h"
|
||||||
|
#include "clang/Sema/SemaInternal.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include <set>
|
#include <set>
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
@ -1035,6 +1036,90 @@ static void DiagnoseReinterpretUpDownCast(Sema &Self, const Expr *SrcExpr,
|
||||||
<< FixItHint::CreateReplacement(BeginLoc, "static_cast");
|
<< FixItHint::CreateReplacement(BeginLoc, "static_cast");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool argTypeIsABIEquivalent(QualType SrcType, QualType DestType,
|
||||||
|
ASTContext &Context) {
|
||||||
|
if (SrcType->isPointerType() && DestType->isPointerType())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Allow integral type mismatch if their size are equal.
|
||||||
|
if (SrcType->isIntegralType(Context) && DestType->isIntegralType(Context))
|
||||||
|
if (Context.getTypeInfoInChars(SrcType).Width ==
|
||||||
|
Context.getTypeInfoInChars(DestType).Width)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return Context.hasSameUnqualifiedType(SrcType, DestType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkCastFunctionType(Sema &Self, const ExprResult &SrcExpr,
|
||||||
|
QualType DestType) {
|
||||||
|
if (Self.Diags.isIgnored(diag::warn_cast_function_type,
|
||||||
|
SrcExpr.get()->getExprLoc()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
QualType SrcType = SrcExpr.get()->getType();
|
||||||
|
const FunctionType *SrcFTy = nullptr;
|
||||||
|
const FunctionType *DstFTy = nullptr;
|
||||||
|
if (((SrcType->isBlockPointerType() || SrcType->isFunctionPointerType()) &&
|
||||||
|
DestType->isFunctionPointerType()) ||
|
||||||
|
(SrcType->isMemberFunctionPointerType() &&
|
||||||
|
DestType->isMemberFunctionPointerType())) {
|
||||||
|
SrcFTy = SrcType->getPointeeType()->castAs<FunctionType>();
|
||||||
|
DstFTy = DestType->getPointeeType()->castAs<FunctionType>();
|
||||||
|
} else if (SrcType->isFunctionType() && DestType->isFunctionReferenceType()) {
|
||||||
|
SrcFTy = SrcType->castAs<FunctionType>();
|
||||||
|
DstFTy = DestType.getNonReferenceType()->castAs<FunctionType>();
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assert(SrcFTy && DstFTy);
|
||||||
|
|
||||||
|
auto IsVoidVoid = [](const FunctionType *T) {
|
||||||
|
if (!T->getReturnType()->isVoidType())
|
||||||
|
return false;
|
||||||
|
if (const auto *PT = T->getAs<FunctionProtoType>())
|
||||||
|
return !PT->isVariadic() && PT->getNumParams() == 0;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip if either function type is void(*)(void)
|
||||||
|
if (IsVoidVoid(SrcFTy) || IsVoidVoid(DstFTy))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check return type.
|
||||||
|
if (!argTypeIsABIEquivalent(SrcFTy->getReturnType(), DstFTy->getReturnType(),
|
||||||
|
Self.Context))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if either has unspecified number of parameters
|
||||||
|
if (SrcFTy->isFunctionNoProtoType() || DstFTy->isFunctionNoProtoType())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Check parameter types.
|
||||||
|
|
||||||
|
const auto *SrcFPTy = cast<FunctionProtoType>(SrcFTy);
|
||||||
|
const auto *DstFPTy = cast<FunctionProtoType>(DstFTy);
|
||||||
|
|
||||||
|
// In a cast involving function types with a variable argument list only the
|
||||||
|
// types of initial arguments that are provided are considered.
|
||||||
|
unsigned NumParams = SrcFPTy->getNumParams();
|
||||||
|
unsigned DstNumParams = DstFPTy->getNumParams();
|
||||||
|
if (NumParams > DstNumParams) {
|
||||||
|
if (!DstFPTy->isVariadic())
|
||||||
|
return false;
|
||||||
|
NumParams = DstNumParams;
|
||||||
|
} else if (NumParams < DstNumParams) {
|
||||||
|
if (!SrcFPTy->isVariadic())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < NumParams; ++i)
|
||||||
|
if (!argTypeIsABIEquivalent(SrcFPTy->getParamType(i),
|
||||||
|
DstFPTy->getParamType(i), Self.Context))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// CheckReinterpretCast - Check that a reinterpret_cast\<DestType\>(SrcExpr) is
|
/// CheckReinterpretCast - Check that a reinterpret_cast\<DestType\>(SrcExpr) is
|
||||||
/// valid.
|
/// valid.
|
||||||
/// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code
|
/// Refer to C++ 5.2.10 for details. reinterpret_cast is typically used in code
|
||||||
|
@ -1072,6 +1157,10 @@ void CastOperation::CheckReinterpretCast() {
|
||||||
if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers())
|
if (Self.getLangOpts().allowsNonTrivialObjCLifetimeQualifiers())
|
||||||
checkObjCConversion(Sema::CCK_OtherCast);
|
checkObjCConversion(Sema::CCK_OtherCast);
|
||||||
DiagnoseReinterpretUpDownCast(Self, SrcExpr.get(), DestType, OpRange);
|
DiagnoseReinterpretUpDownCast(Self, SrcExpr.get(), DestType, OpRange);
|
||||||
|
|
||||||
|
if (!checkCastFunctionType(Self, SrcExpr, DestType))
|
||||||
|
Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
|
||||||
|
<< SrcExpr.get()->getType() << DestType << OpRange;
|
||||||
} else {
|
} else {
|
||||||
SrcExpr = ExprError();
|
SrcExpr = ExprError();
|
||||||
}
|
}
|
||||||
|
@ -2645,6 +2734,11 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
|
||||||
if (isValidCast(tcr)) {
|
if (isValidCast(tcr)) {
|
||||||
if (Kind == CK_BitCast)
|
if (Kind == CK_BitCast)
|
||||||
checkCastAlign();
|
checkCastAlign();
|
||||||
|
|
||||||
|
if (!checkCastFunctionType(Self, SrcExpr, DestType))
|
||||||
|
Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
|
||||||
|
<< SrcExpr.get()->getType() << DestType << OpRange;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
SrcExpr = ExprError();
|
SrcExpr = ExprError();
|
||||||
}
|
}
|
||||||
|
@ -2957,6 +3051,10 @@ void CastOperation::CheckCStyleCast() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!checkCastFunctionType(Self, SrcExpr, DestType))
|
||||||
|
Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
|
||||||
|
<< SrcType << DestType << OpRange;
|
||||||
|
|
||||||
DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType);
|
DiagnoseCastOfObjCSEL(Self, SrcExpr, DestType);
|
||||||
DiagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange);
|
DiagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange);
|
||||||
DiagnoseBadFunctionCast(Self, SrcExpr, DestType);
|
DiagnoseBadFunctionCast(Self, SrcExpr, DestType);
|
||||||
|
|
29
clang/test/Sema/warn-cast-function-type.c
Normal file
29
clang/test/Sema/warn-cast-function-type.c
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// RUN: %clang_cc1 -x c %s -fsyntax-only -Wcast-function-type -triple x86_64-- -verify
|
||||||
|
|
||||||
|
int x(long);
|
||||||
|
|
||||||
|
typedef int (f1)(long);
|
||||||
|
typedef int (f2)(void*);
|
||||||
|
typedef int (f3)();
|
||||||
|
typedef void (f4)();
|
||||||
|
typedef void (f5)(void);
|
||||||
|
typedef int (f6)(long, int);
|
||||||
|
typedef int (f7)(long,...);
|
||||||
|
|
||||||
|
f1 *a;
|
||||||
|
f2 *b;
|
||||||
|
f3 *c;
|
||||||
|
f4 *d;
|
||||||
|
f5 *e;
|
||||||
|
f6 *f;
|
||||||
|
f7 *g;
|
||||||
|
|
||||||
|
void foo(void) {
|
||||||
|
a = (f1 *)x;
|
||||||
|
b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function types}} */
|
||||||
|
c = (f3 *)x;
|
||||||
|
d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)()') converts to incompatible function types}} */
|
||||||
|
e = (f5 *)x;
|
||||||
|
f = (f6 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function types}} */
|
||||||
|
g = (f7 *)x;
|
||||||
|
}
|
47
clang/test/Sema/warn-cast-function-type.cpp
Normal file
47
clang/test/Sema/warn-cast-function-type.cpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// RUN: %clang_cc1 -x c++ %s -fblocks -fsyntax-only -Wcast-function-type -triple x86_64-- -verify
|
||||||
|
|
||||||
|
int x(long);
|
||||||
|
|
||||||
|
typedef int (f1)(long);
|
||||||
|
typedef int (f2)(void*);
|
||||||
|
typedef int (f3)(...);
|
||||||
|
typedef void (f4)(...);
|
||||||
|
typedef void (f5)(void);
|
||||||
|
typedef int (f6)(long, int);
|
||||||
|
typedef int (f7)(long,...);
|
||||||
|
typedef int (&f8)(long, int);
|
||||||
|
|
||||||
|
f1 *a;
|
||||||
|
f2 *b;
|
||||||
|
f3 *c;
|
||||||
|
f4 *d;
|
||||||
|
f5 *e;
|
||||||
|
f6 *f;
|
||||||
|
f7 *g;
|
||||||
|
|
||||||
|
struct S
|
||||||
|
{
|
||||||
|
void foo (int*);
|
||||||
|
void bar (int);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (S::*mf)(int);
|
||||||
|
|
||||||
|
void foo() {
|
||||||
|
a = (f1 *)x;
|
||||||
|
b = (f2 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function types}} */
|
||||||
|
b = reinterpret_cast<f2 *>(x); /* expected-warning {{cast from 'int (*)(long)' to 'f2 *' (aka 'int (*)(void *)') converts to incompatible function types}} */
|
||||||
|
c = (f3 *)x;
|
||||||
|
d = (f4 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f4 *' (aka 'void (*)(...)') converts to incompatible function types}} */
|
||||||
|
e = (f5 *)x;
|
||||||
|
f = (f6 *)x; /* expected-warning {{cast from 'int (*)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function types}} */
|
||||||
|
g = (f7 *)x;
|
||||||
|
|
||||||
|
mf p1 = (mf)&S::foo; /* expected-warning {{cast from 'void (S::*)(int *)' to 'mf' (aka 'void (S::*)(int)') converts to incompatible function types}} */
|
||||||
|
|
||||||
|
f8 f2 = (f8)x; /* expected-warning {{cast from 'int (long)' to 'f8' (aka 'int (&)(long, int)') converts to incompatible function types}} */
|
||||||
|
(void)f2;
|
||||||
|
|
||||||
|
int (^y)(long);
|
||||||
|
f = (f6 *)y; /* expected-warning {{cast from 'int (^)(long)' to 'f6 *' (aka 'int (*)(long, int)') converts to incompatible function types}} */
|
||||||
|
}
|
Loading…
Reference in a new issue