[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:
Yuanfang Chen 2021-03-24 16:03:13 -07:00
parent ef69aa961d
commit 217f0f735a
6 changed files with 186 additions and 1 deletions

View file

@ -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`|
+--------------------------------------------------------------------------------------------------------------------------------------------------------------+
-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
----------------

View file

@ -499,6 +499,7 @@ def PrivateExtern : DiagGroup<"private-extern">;
def SelTypeCast : DiagGroup<"cast-of-sel-type">;
def FunctionDefInObjCContainer : DiagGroup<"function-def-in-objc-container">;
def BadFunctionCast : DiagGroup<"bad-function-cast">;
def CastFunctionType : DiagGroup<"cast-function-type">;
def ObjCPropertyImpl : DiagGroup<"objc-property-implementation">;
def ObjCPropertyNoAttribute : DiagGroup<"objc-property-no-attribute">;
def ObjCPropertyAssignOnObjectType : DiagGroup<"objc-property-assign-on-object-type">;

View file

@ -8386,6 +8386,9 @@ def note_change_calling_conv_fixit : Note<
def warn_bad_function_cast : Warning<
"cast from function call of type %0 to non-matching type %1">,
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<
"pointer cannot be cast to type %0">;
def err_cast_to_bfloat16 : Error<"cannot type-cast to __bf16">;

View file

@ -13,8 +13,8 @@
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaInternal.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTStructuralEquivalence.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
@ -23,6 +23,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/SmallVector.h"
#include <set>
using namespace clang;
@ -1035,6 +1036,90 @@ static void DiagnoseReinterpretUpDownCast(Sema &Self, const Expr *SrcExpr,
<< 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
/// valid.
/// 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())
checkObjCConversion(Sema::CCK_OtherCast);
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 {
SrcExpr = ExprError();
}
@ -2645,6 +2734,11 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle,
if (isValidCast(tcr)) {
if (Kind == CK_BitCast)
checkCastAlign();
if (!checkCastFunctionType(Self, SrcExpr, DestType))
Self.Diag(OpRange.getBegin(), diag::warn_cast_function_type)
<< SrcExpr.get()->getType() << DestType << OpRange;
} else {
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);
DiagnoseCallingConvCast(Self, SrcExpr, DestType, OpRange);
DiagnoseBadFunctionCast(Self, SrcExpr, DestType);

View 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;
}

View 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}} */
}