Skip to content
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

[clang][Sema] Add -Wswitch-default warning option #73077

Merged
merged 1 commit into from
Dec 7, 2023

Conversation

dongjianqiang2
Copy link
Contributor

Adds a warning, issued by the clang semantic analysis. The patch warns on switch which don't have the default branch.

This is a counterpart of gcc's Wswitch-default.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Nov 22, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Nov 22, 2023

@llvm/pr-subscribers-clang

Author: dong jianqiang (dongjianqiang2)

Changes

Adds a warning, issued by the clang semantic analysis. The patch warns on switch which don't have the default branch.

This is a counterpart of gcc's Wswitch-default.


Full diff: https://github.com/llvm/llvm-project/pull/73077.diff

4 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticGroups.td (+1-1)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
  • (modified) clang/lib/Sema/SemaStmt.cpp (+3)
  • (added) clang/test/Sema/switch-default.c (+17)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index ff028bbbf74261e..12b11527b30571a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -632,7 +632,7 @@ def ShadowAll : DiagGroup<"shadow-all", [Shadow, ShadowFieldInConstructor,
 def Shorten64To32 : DiagGroup<"shorten-64-to-32">;
 def : DiagGroup<"sign-promo">;
 def SignCompare : DiagGroup<"sign-compare">;
-def : DiagGroup<"switch-default">;
+def SwitchDefault  : DiagGroup<"switch-default">;
 def : DiagGroup<"synth">;
 def SizeofArrayArgument : DiagGroup<"sizeof-array-argument">;
 def SizeofArrayDecay : DiagGroup<"sizeof-array-decay">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 990692c06d7d3a8..17c9627910bb6ce 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10044,6 +10044,8 @@ def warn_missing_case : Warning<"%plural{"
   "3:enumeration values %1, %2, and %3 not handled in switch|"
   ":%0 enumeration values not handled in switch: %1, %2, %3...}0">,
   InGroup<Switch>;
+def warn_switch_default : Warning<"switch missing default case">,
+  InGroup<SwitchDefault>, DefaultIgnore;
 
 def warn_unannotated_fallthrough : Warning<
   "unannotated fall-through between switch labels">,
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 2b45aa5dff7be7c..63348d27a8c94a1 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -1327,6 +1327,9 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
     }
   }
 
+  if (!TheDefaultStmt)
+    Diag(SwitchLoc, diag::warn_switch_default);
+
   if (!HasDependentValue) {
     // If we don't have a default statement, check whether the
     // condition is constant.
diff --git a/clang/test/Sema/switch-default.c b/clang/test/Sema/switch-default.c
new file mode 100644
index 000000000000000..3f2e21693303378
--- /dev/null
+++ b/clang/test/Sema/switch-default.c
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wswitch-default %s
+
+int f1(int a) {
+  switch (a) {                // expected-warning {{switch missing default case}}
+    case 1: a++; break;
+    case 2: a += 2; break;
+  }
+  return a;
+}
+
+int f2(int a) {
+  switch (a) {                // no-warning
+    default:
+      ;
+  }
+  return a;
+}

@xgupta
Copy link
Contributor

xgupta commented Nov 22, 2023

There is one clang-tidy check (bugprone-switch-missing-default-case) also for this feature.

@dongjianqiang2
Copy link
Contributor Author

There is one clang-tidy check (bugprone-switch-missing-default-case) also for this feature.

Thank you for your reply. It may be a more convenient and straightforward way if can be identified during compile time.
On the other hand, it it more compatibile with GCC's hehavior. : )

@cor3ntin
Copy link
Contributor

I'd like other to weight in on that but even though GCC has that flag (which clang recognizes and ignores), I'm, a bit reluctant to add a warning for it.
We have a policy to avoid off-by-default warnings most of the time... but more importantly, whether switch statements
should have a default is not something the industry seem to agree on, so it would be a very opinionated warning.

For example, Folks at google advise against using default when switching over an enum https://abseil.io/tips/147.
Misra requires a default statement, but makes an exception for enums. (6.4.6 in misra 2008)

LLVM development heavily relies on -Wswitch-enum, for example.

Maybe the existing tidy check is the right tool for this.

@hstk30-hw
Copy link
Contributor

For example, Folks at google advise against using default when switching over an enum https://abseil.io/tips/147.
Misra requires a default statement, but makes an exception for enums. (6.4.6 in misra 2008)

I see it's easy to implement in LLVM like clang tidy (not warning for enum and constant expr).

The clang tidy may seperate the switch warning ( I have to use tidy to see switch-default, but I can see switch-enum switch-bool in compiler).

IMAO, if we can implement it together in compiler, why not?

@cor3ntin
Copy link
Contributor

cor3ntin commented Dec 3, 2023

@AaronBallman @erichkeane @shafik WDYT?

@aaronpuchert
Copy link
Member

There is one clang-tidy check (bugprone-switch-missing-default-case) also for this feature.

This excludes enumeration types though, while GCC -Wswitch-default warns on them as well. (Even if all enumeration values are covered.)

whether switch statements should have a default is not something the industry seem to agree on, so it would be a very opinionated warning.

Yes. Though I have to say, even though I'm squarely in the -Wcovered-switch-default camp (i.e. no default label in switches that cover all enumeration values), I think it's still valuable to have this warning because some style guides/policies demand a default, and it is very little effort on our side to maintain this.

I understand the policy against off-by-default warnings to avoid crowding the core compiler, but this is essentially just two lines of code, and if GCC has it we're not going out on a limb here. And since we already seem to support the flag for GCC compatibility, we might as well make it work if it's that easy.

LLVM development heavily relies on -Wswitch-enum, for example.

I think we're using -Wswitch, as -Wswitch-enum requires to cover all enumeration values even if there is a default label. Our policy is: cover all enumeration values or have a default. This is "enforced" by -Wswitch in combination with -Wcovered-switch-default.

@aaronpuchert
Copy link
Member

In the end it probably boils down to how you view an enumeration type. Is it a type with the declared enumerators as inhabitants, or is it an integer type of some length with associated symbolic constants of that type? In other words, is an enumeration a "strong" type or a "weak" type?

The standard itself seems more in the "weak" camp. Yes, it does not allow integer values larger than what the largest enumeration value requires (if the underlying integer type isn't declared), but otherwise integer values that don't correspond to enumeration values can be cast to the enumeration type just fine. In [dcl.enum]p8:

[For an enumeration whose underlying type is not fixed,] the values of the enumeration are the values representable by a hypothetical integer type with minimal width M such that all enumerators can be represented.

However, nothing prevents a developer from assuming a stricter model, where only the declared enumeration values are allowed and everything else is undefined behavior. (If not in terms of the language, then in terms of the program.) That is what we have done in the LLVM code base, and it's also the model that I personally prefer.

But it is probably legitimate to follow the weaker model implied by the standard and assume that enumerations can have non-enumerator values, which requires a default label, enforced by this warning.

@erichkeane
Copy link
Collaborator

I think there is value to adding this if only for the GCC compat story here. I realize we don't like non-on-by-default warnings, but it IS something that folks use reasonably often, and we've even acquiesced to making it an ignored flag IIRC. Aaron is the real decision maker here, but I think I'd have no problem holding my nose and allowing the non-on-by-default here, if only to match GCC.

BUT, this should make sure to match behavior of GCC WRT enums/etc. But if folks want a warning for a fully-covered enum, I think there is value in giving it to them.

Patch itself need some additional testing (particularly around templates/dependence/etc), a release note, additional documentation in our commandline reference/etc.

@aaronpuchert
Copy link
Member

Aaron is the real decision maker here

Specifically @AaronBallman, not me.

@@ -10044,6 +10044,8 @@ def warn_missing_case : Warning<"%plural{"
"3:enumeration values %1, %2, and %3 not handled in switch|"
":%0 enumeration values not handled in switch: %1, %2, %3...}0">,
InGroup<Switch>;
def warn_switch_default : Warning<"switch missing default case">,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def warn_switch_default : Warning<"switch missing default case">,
def warn_switch_default : Warning<"'switch' missing 'default' label">,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the correction!

@AaronBallman
Copy link
Collaborator

I think there is value to adding this if only for the GCC compat story here. I realize we don't like non-on-by-default warnings, but it IS something that folks use reasonably often, and we've even acquiesced to making it an ignored flag IIRC. Aaron is the real decision maker here, but I think I'd have no problem holding my nose and allowing the non-on-by-default here, if only to match GCC.

I think GCC compatibility is sufficient reason to support this despite being off-by-default and basically no hope of ever enabling it by default. There's evidence that people enable this in the wild, so I think it meets the bar: https://sourcegraph.com/search?q=context%3Aglobal+-Wswitch-default+-file%3A.*clang.*+-file%3A.*gcc.*&patternType=standard&sm=1&groupBy=repo

Adds a warning, issued by the clang semantic analysis. The patch warns on switch
which don't have the default branch.

This is a counterpart of gcc's Wswitch-default.
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@hstk30-hw hstk30-hw merged commit c281782 into llvm:main Dec 7, 2023
5 checks passed
@dongjianqiang2 dongjianqiang2 deleted the add_Wswitch_default branch December 7, 2023 05:38
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Dec 12, 2023
Revert: breaks MIOpen https://ontrack-internal.amd.com/browse/SWDEV-436625
[clang][Sema] Add -Wswitch-default warning option (llvm#73077)

Change-Id: I182ea84b40257240ed42db5cd28213ab192ac3f7
@gendalph
Copy link

gendalph commented Dec 18, 2023

enum class test
{
  err1,
  err2,
  ok
};

int check_err (test v)
{
  switch (v)
    {
      case test::err1:
        return 1;
      case test::err2:
        return 2;
      case test::ok:
        break;
    }
  return 0;
}

report:
main.cxx:40:3: error: 'switch' missing 'default' label [-Werror,-Wswitch-default]

how to solve for enum class?
try to build libfmt project with
fmt/include/fmt/format.h:3878:3: error: 'switch' missing 'default' label [-Werror,-Wswitch-default]
and other errors

@dongjianqiang2
Copy link
Contributor Author

enum class test
{
  err1,
  err2,
  ok
};

int check_err (test v)
{
  switch (v)
    {
      case test::err1:
        return 1;
      case test::err2:
        return 2;
      case test::ok:
        break;
    }
  return 0;
}

report: main.cxx:40:3: error: 'switch' missing 'default' label [-Werror,-Wswitch-default]

how to solve for enum class? try to build libfmt project with fmt/include/fmt/format.h:3878:3: error: 'switch' missing 'default' label [-Werror,-Wswitch-default] and other errors

Hi, -Wswitch-default is a warning option on switch which don't have the default branch, the option is disabled by default, You can check whether -Wswitch-default is added in the build script.

case 2: a += 2; break;
}
return a;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding a test case that shows that this warns even for completely covered switches e.g. https://godbolt.org/z/zbqr6P5xc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See PR(#75900 )

@gendalph
Copy link

In real projects mix enum class with old int switch. Try to update defaults and have noise from enum class case.
My be mix options -Wswitch -Wswitch-default can disable warning in enum class case without default if full covered?

https://godbolt.org/z/xrdafn6WK

@gendalph
Copy link

Using an enum class without default is a good opportunity to get a warning in all places of use in the switch construct when adding new elements to the enum

@aaronpuchert
Copy link
Member

aaronpuchert commented Dec 20, 2023

@gendalph, this warning is meant to always warn if a default label is missing, even if all enumeration values are covered, like GCC does it. If you don't want a warning on enumerations, use the previously mentioned clang-tidy check bugprone-switch-missing-default-case. We can probably discuss adding this as warning, though I doubt we'll get consensus on that. This warning was added despite never going to be always-on because GCC has it (under the same name) and some code styles ask for it.

hstk30-hw added a commit to hstk30-hw/llvm-project that referenced this pull request Dec 21, 2023
[llvm#73077] added Wswitch-default diagnostic but it produced false
positives in templates. This PR address it.
hstk30-hw added a commit that referenced this pull request Dec 22, 2023
#73077 added -Wswitch-default
diagnostic but it produced false positives in templates. This PR will
address that. #75943
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Jan 12, 2024
Adds a warning, issued by the clang semantic analysis. The patch warns
on switch which don't have the default branch.

This is a counterpart of gcc's Wswitch-default.

Change-Id: Iba221a303560b88fd7e408a517cfba2d728b0931
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Jan 13, 2024
This test case exists upstream currently. It was deleted by
mistake when reapplying the patch:
 [clang][Sema] Add -Wswitch-default warning option (llvm#73077)

The test case had been added upstream for a subsequent patch
 [Clang][Sema] Fix Wswitch-default bad warning in template (llvm#76007)

Change-Id: I94fdbe2daa4703166e0d7fc00a3a4d08f98ae410
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Jan 19, 2024
This reverts commit 2cf433b.

Change-Id: I630da1ecf57cb003cc3062252b39e9e2b9ddf7d1
qihangkong pushed a commit to rvgpu/rvgpu-llvm that referenced this pull request Apr 23, 2024
llvm/llvm-project#73077 added -Wswitch-default
diagnostic but it produced false positives in templates. This PR will
address that. llvm/llvm-project#75943
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants