Skip to content

[C] Add new -Wimplicit-int-enum-cast to -Wc++-compat #137658

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,15 @@ C Language Changes
- Added ``-Wimplicit-void-ptr-cast``, grouped under ``-Wc++-compat``, which
diagnoses implicit conversion from ``void *`` to another pointer type as
being incompatible with C++. (#GH17792)
- Added ``-Wimplicit-int-enum-cast``, grouped under ``-Wc++-compat``, which
diagnoses implicit conversion from integer types to an enumeration type in C,
which is not compatible with C++. #GH37027
- Split "implicit conversion from enum type to different enum type" diagnostic
from ``-Wenum-conversion`` into its own diagnostic group,
``-Wimplicit-enum-enum-cast``, which is grouped under both
``-Wenum-conversion`` and ``-Wimplicit-int-enum-cast``. This conversion is an
int-to-enum conversion because the enumeration on the right-hand side is
promoted to ``int`` before the assignment.

C2y Feature Support
^^^^^^^^^^^^^^^^^^^
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/Basic/DiagnosticGroups.td
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ def AnonEnumEnumConversion : DiagGroup<"anon-enum-enum-conversion",
[DeprecatedAnonEnumEnumConversion]>;
def EnumEnumConversion : DiagGroup<"enum-enum-conversion",
[DeprecatedEnumEnumConversion]>;
def ImplicitEnumEnumCast : DiagGroup<"implicit-enum-enum-cast">;
def EnumFloatConversion : DiagGroup<"enum-float-conversion",
[DeprecatedEnumFloatConversion]>;
def EnumConversion : DiagGroup<"enum-conversion",
[EnumEnumConversion,
ImplicitEnumEnumCast,
EnumFloatConversion,
EnumCompareConditional]>;
def DeprecatedOFast : DiagGroup<"deprecated-ofast">;
Expand Down Expand Up @@ -157,7 +159,10 @@ def : DiagGroup<"c2x-compat", [C23Compat]>;
def DefaultConstInitUnsafe : DiagGroup<"default-const-init-unsafe">;
def DefaultConstInit : DiagGroup<"default-const-init", [DefaultConstInitUnsafe]>;
def ImplicitVoidPtrCast : DiagGroup<"implicit-void-ptr-cast">;
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit]>;
def ImplicitIntToEnumCast : DiagGroup<"implicit-int-enum-cast",
[ImplicitEnumEnumCast]>;
def CXXCompat: DiagGroup<"c++-compat", [ImplicitVoidPtrCast, DefaultConstInit,
ImplicitIntToEnumCast]>;

def ExternCCompat : DiagGroup<"extern-c-compat">;
def KeywordCompat : DiagGroup<"keyword-compat">;
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -4310,7 +4310,10 @@ def warn_impcast_string_literal_to_bool : Warning<
InGroup<StringConversion>, DefaultIgnore;
def warn_impcast_different_enum_types : Warning<
"implicit conversion from enumeration type %0 to different enumeration type "
"%1">, InGroup<EnumConversion>;
"%1">, InGroup<ImplicitEnumEnumCast>;
def warn_impcast_int_to_enum : Warning<
"implicit conversion from %0 to enumeration type %1 is invalid in C++">,
InGroup<ImplicitIntToEnumCast>, DefaultIgnore;
def warn_impcast_bool_to_null_pointer : Warning<
"initialization of pointer of type %0 to null from a constant boolean "
"expression">, InGroup<BoolConversion>;
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12268,13 +12268,19 @@ void Sema::CheckImplicitConversion(Expr *E, QualType T, SourceLocation CC,
*ICContext = true;
}

return DiagnoseImpCast(*this, E, T, CC, DiagID);
DiagnoseImpCast(*this, E, T, CC, DiagID);
}

// If we're implicitly converting from an integer into an enumeration, that
// is valid in C but invalid in C++.
QualType SourceType = E->getEnumCoercedType(Context);
const BuiltinType *CoercedSourceBT = SourceType->getAs<BuiltinType>();
if (CoercedSourceBT && CoercedSourceBT->isInteger() && isa<EnumType>(Target))
return DiagnoseImpCast(*this, E, T, CC, diag::warn_impcast_int_to_enum);

// Diagnose conversions between different enumeration types.
// In C, we pretend that the type of an EnumConstantDecl is its enumeration
// type, to give us better diagnostics.
QualType SourceType = E->getEnumCoercedType(Context);
Source = Context.getCanonicalType(SourceType).getTypePtr();

if (const EnumType *SourceEnum = Source->getAs<EnumType>())
Expand Down
8 changes: 5 additions & 3 deletions clang/test/Misc/warning-flags-enabled.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@
// CHECK-NO-LEVELS-NOT: {{^F }}
// CHECK-NO-LEVELS: warn_objc_root_class_missing [-Wobjc-root-class]

// Test if EnumConversion is a subgroup of -Wconversion.
// Test if EnumConversion is a subgroup of -Wconversion. Because no diagnostics
// are grouped directly under -Wenum-conversion, we check for
// -Wimplicit-enum-enum-cast instead (which is itself under -Wenum-conversion).
// RUN: diagtool show-enabled --no-levels -Wno-conversion -Wenum-conversion %s | FileCheck --check-prefix CHECK-ENUM-CONVERSION %s
// RUN: diagtool show-enabled --no-levels %s | FileCheck --check-prefix CHECK-ENUM-CONVERSION %s
// RUN: diagtool show-enabled --no-levels -Wno-conversion %s | FileCheck --check-prefix CHECK-NO-ENUM-CONVERSION %s
//
// CHECK-ENUM-CONVERSION: -Wenum-conversion
// CHECK-NO-ENUM-CONVERSION-NOT: -Wenum-conversion
// CHECK-ENUM-CONVERSION: -Wimplicit-enum-enum-cast
// CHECK-NO-ENUM-CONVERSION-NOT: -Wimplicit-enum-enum-cast

// Test if -Wshift-op-parentheses is a subgroup of -Wparentheses
// RUN: diagtool show-enabled --no-levels -Wno-parentheses -Wshift-op-parentheses %s | FileCheck --check-prefix CHECK-SHIFT-OP-PARENTHESES %s
Expand Down
52 changes: 52 additions & 0 deletions clang/test/Sema/implicit-int-enum-conversion.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wimplicit-int-enum-cast %s
// RUN: %clang_cc1 -fsyntax-only -verify -Wc++-compat %s
// RUN: %clang_cc1 -fsyntax-only -verify=cxx -x c++ %s
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wno-implicit-enum-enum-cast %s
// RUN: %clang_cc1 -fsyntax-only -verify=good -Wc++-compat -Wno-implicit-enum-enum-cast -Wno-implicit-int-enum-cast %s
// good-no-diagnostics

enum E1 {
E1_Zero,
E1_One
};

enum E2 {
E2_Zero
};

struct S {
enum E1 e;
} s = { 12 }; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
cxx-error {{cannot initialize a member subobject of type 'enum E1' with an rvalue of type 'int'}}

enum E1 foo(void) {
int x;
enum E1 e = 12; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
cxx-error {{cannot initialize a variable of type 'enum E1' with an rvalue of type 'int'}}

// Enum to integer is fine.
x = e;

// Integer to enum is not fine.
e = x; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
cxx-error {{assigning to 'enum E1' from incompatible type 'int'}}
return x; // expected-warning {{implicit conversion from 'int' to enumeration type 'enum E1' is invalid in C++}} \
cxx-error {{cannot initialize return object of type 'enum E1' with an lvalue of type 'int'}}
}

// Returning with the correct types is fine.
enum E1 bar(void) {
return E1_Zero;
}

// Enum to different-enum conversion is also a C++ incompatibility, but is
// handled via a more general diagnostic, -Wimplicit-enum-enum-cast, which is
// on by default.
enum E1 quux(void) {
enum E1 e1 = E2_Zero; // expected-warning {{implicit conversion from enumeration type 'enum E2' to different enumeration type 'enum E1'}} \
cxx-error {{cannot initialize a variable of type 'enum E1' with an rvalue of type 'E2'}}
e1 = E2_Zero; // expected-warning {{implicit conversion from enumeration type 'enum E2' to different enumeration type 'enum E1'}} \
cxx-error {{assigning to 'enum E1' from incompatible type 'E2'}}
return E2_Zero; // expected-warning {{implicit conversion from enumeration type 'enum E2' to different enumeration type 'enum E1'}} \
cxx-error {{cannot initialize return object of type 'enum E1' with an rvalue of type 'E2'}}
}