Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -309,6 +309,9 @@ removed in the future once clang supports all such operations. - Added the ``-print-diagnostic-options`` option, which prints a list of warnings the compiler supports. +- Added the ``-Warray-parameter`` warning. It detects function redefinition, + where different definition involve argument type that decay to the same + pointer type from different array types. Deprecated Compiler Flags ------------------------- Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -31,6 +31,7 @@ def GNUAutoType : DiagGroup<"gnu-auto-type">; def ArrayBounds : DiagGroup<"array-bounds">; def ArrayBoundsPointerArithmetic : DiagGroup<"array-bounds-pointer-arithmetic">; +def ArrayParameter : DiagGroup<"array-parameter">; def AutoDisableVptrSanitizer : DiagGroup<"auto-disable-vptr-sanitizer">; def Availability : DiagGroup<"availability">; def Section : DiagGroup<"section">; @@ -978,6 +979,7 @@ ]>; def Most : DiagGroup<"most", [ + ArrayParameter, BoolOperation, CharSubscript, Comment, Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9393,6 +9393,12 @@ def note_array_declared_here : Note< "array %0 declared here">; +def warn_inconsistent_array_form : Warning< + "argument '%0' of type '%1' with mismatched bound">, + InGroup, DefaultIgnore; +def note_previous_declaration_as : Note< + "previously declared as %0 here">; + def warn_printf_insufficient_data_args : Warning< "more '%%' conversions than data arguments">, InGroup; def warn_printf_data_arg_not_used : Warning< Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -3209,6 +3209,36 @@ if (!foundAny) newDecl->dropAttrs(); } +static bool EquivalentArrayTypes(QualType Old, QualType New) { + auto NoSizeInfo = [](QualType Ty) { + return Ty->isIncompleteArrayType() || Ty->isPointerType() || + (Ty->isVariableArrayType() && + cast(Ty)->getSizeModifier() == + ArrayType::ArraySizeModifier::Star); + }; + + // `type[]` is equivalent to `type *` and `type[*]` + if (NoSizeInfo(Old) && NoSizeInfo(New)) + return true; + + // Don't try to compare VLA sizes, unless one of them has the star modifier + if (Old->isVariableArrayType() && New->isVariableArrayType()) { + const auto *OldVAT = cast(Old); + const auto *NewVAT = cast(New); + if ((OldVAT->getSizeModifier() == ArrayType::ArraySizeModifier::Star) ^ + (NewVAT->getSizeModifier() == ArrayType::ArraySizeModifier::Star)) + return false; + return true; + } + + // Only compare size, ignore Size modifiers and CVR + if (Old->isConstantArrayType() && New->isConstantArrayType()) + return cast(Old)->getSize() == + cast(New)->getSize(); + + return Old == New; +} + static void mergeParamDeclTypes(ParmVarDecl *NewParam, const ParmVarDecl *OldParam, Sema &S) { @@ -3234,6 +3264,20 @@ NewParam->setType(NewT); } } + const auto *OldParamDT = dyn_cast(OldParam->getType()); + const auto *NewParamDT = dyn_cast(NewParam->getType()); + if (OldParamDT && NewParamDT && + OldParamDT->getPointeeType() == NewParamDT->getPointeeType()) { + QualType OldParamOT = OldParamDT->getOriginalType(); + QualType NewParamOT = NewParamDT->getOriginalType(); + if (!EquivalentArrayTypes(OldParamOT, NewParamOT)) { + S.Diag(NewParam->getLocation(), diag::warn_inconsistent_array_form) + << NewParam->getName() + << NewParamOT->getCanonicalTypeInternal().getAsString(); + S.Diag(OldParam->getLocation(), diag::note_previous_declaration_as) + << OldParamOT->getCanonicalTypeInternal().getAsString(); + } + } } namespace { Index: clang/test/Misc/warning-wall.c =================================================================== --- clang/test/Misc/warning-wall.c +++ clang/test/Misc/warning-wall.c @@ -3,6 +3,7 @@ CHECK:-Wall CHECK-NEXT: -Wmost +CHECK-NEXT: -Warray-parameter CHECK-NEXT: -Wbool-operation CHECK-NEXT: -Wbitwise-instead-of-logical CHECK-NEXT: -Wchar-subscripts Index: clang/test/Sema/array-parameter.c =================================================================== --- /dev/null +++ clang/test/Sema/array-parameter.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -Warray-parameter -verify %s +void f0(int a[]); +void f0(int *a); // no warning + +void f1(int a[]); // expected-note {{previously declared as int[] here}} +void f1(int a[2]); // expected-warning {{argument 'a' of type 'int[2]' with mismatched bound}} + +void f2(int a[3]); // expected-note {{previously declared as int[3] here}} +void f2(int a[2]); // expected-warning {{argument 'a' of type 'int[2]' with mismatched bound}} + +void f3(int a[const 2]); +void f3(int a[2]); // no warning + +void f4(int a[static 2]); +void f4(int a[2]); // no warning + +void f5(int a[restrict 2]); +void f5(int a[2]); // no warning + +void f6(int a[*]); +void f6(int a[]); // no warning + +void f7(int n, int a[*]); // expected-note {{previously declared as int[*] here}} +void f7(int n, int a[n]); // expected-warning {{argument 'a' of type 'int[n]' with mismatched bound}} + +void f8(int *a); +void f8(int a[2]); +void f8(int a[]); // expected-warning {{argument 'a' of type 'int[]' with mismatched bound}} + // expected-note@-2 {{previously declared as int[2] here}} +void f8(int a[2]) // expected-warning {{argument 'a' of type 'int[2]' with mismatched bound}} + // expected-note@-3 {{previously declared as int[] here}} +{}