Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Sema] Warn about omitting deprecated enumerator in switch #138562

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 9 commits into from
May 23, 2025

Conversation

zmodem
Copy link
Collaborator

@zmodem zmodem commented May 5, 2025

This undoes part of 3e4e3b1 which added the "Omitting a deprecated constant is ok; it should never materialize." logic.

That seems wrong: deprecated means the enumerator is likely to be removed in future versions, not that it cannot materialize.

This undoes part of 3e4e3b1 which
added the "Omitting a deprecated constant is ok; it should never
materialize." logic.

That seems wrong: deprecated means the enumerator is likely to be
removed in future versions, not that it canot materialize.
@zmodem zmodem requested review from rnk, hyp, AaronBallman and dwblaikie May 5, 2025 18:30
@zmodem zmodem added the clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer label May 5, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 5, 2025
@llvmbot
Copy link
Member

llvmbot commented May 5, 2025

@llvm/pr-subscribers-clang

Author: Hans Wennborg (zmodem)

Changes

This undoes part of 3e4e3b1 which added the "Omitting a deprecated constant is ok; it should never materialize." logic.

That seems wrong: deprecated means the enumerator is likely to be removed in future versions, not that it cannot materialize.


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

2 Files Affected:

  • (modified) clang/lib/Sema/SemaStmt.cpp (+5-1)
  • (modified) clang/test/Sema/switch-availability.c (+1-1)
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index e8c1f8490342a..990d2fadaf5aa 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -1667,8 +1667,12 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
         // Don't warn about omitted unavailable EnumConstantDecls.
         switch (EI->second->getAvailability()) {
         case AR_Deprecated:
-          // Omitting a deprecated constant is ok; it should never materialize.
+          // Deprecated enumerators still need to be handled: they may be
+          // deprecated, but can still occur.
+          break;
+
         case AR_Unavailable:
+          // Omitting an unavailable enumerator is ok; it should never occur.
           continue;
 
         case AR_NotYetIntroduced:
diff --git a/clang/test/Sema/switch-availability.c b/clang/test/Sema/switch-availability.c
index 888edddac463d..b4f8726addc0b 100644
--- a/clang/test/Sema/switch-availability.c
+++ b/clang/test/Sema/switch-availability.c
@@ -15,7 +15,7 @@ enum SwitchTwo {
 };
 
 void testSwitchTwo(enum SwitchTwo st) {
-  switch (st) {} // expected-warning{{enumeration values 'Vim' and 'Emacs' not handled in switch}}
+  switch (st) {} // expected-warning{{enumeration values 'Ed', 'Vim' and 'Emacs' not handled in switch}}
 }
 
 enum SwitchThree {

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

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

Thanks! I think this is sufficiently niche that we don't need a flag flip to manage the diagnostic change fallout.

clang/test/Sema/switch-availability.c Show resolved Hide resolved
@ReticulateSplines
Copy link

Fly-by comment: I drafted the following alternative comment in another thread. Please feel free to disregard as I don't know the tone and style of comments in these files. (But I appreciate your consideration for the contents in the message I'd like to see!)

      // We currently treat deprecated constants as if they don't exist;
      // however, note that this current behavior leads to compile-time
      // false negatives for coverage checking of switch statements. If
      // a switch is missing a case for a deprecated constant, we will not
      // emit a diagnostic, even though the deprecated constant might still
      // be present in legacy use.

@zmodem
Copy link
Collaborator Author

zmodem commented May 6, 2025

Fly-by comment: I drafted the following alternative comment in another thread. Please feel free to disregard as I don't know the tone and style of comments in these files. (But I appreciate your consideration for the contents in the message I'd like to see!)

      // We currently treat deprecated constants as if they don't exist;
      // however, note that this current behavior leads to compile-time
      // false negatives for coverage checking of switch statements. If
      // a switch is missing a case for a deprecated constant, we will not
      // emit a diagnostic, even though the deprecated constant might still
      // be present in legacy use.

My patch changes the behavior so that deprecated constants are not ignored by the switch coverage warning.

@zmodem
Copy link
Collaborator Author

zmodem commented May 6, 2025

I wonder if we should also do something special about the -Wdeprecated-declarations diagnostic in switches.

If the user fixes the -Wswitch / -Wreturn-type warnings from testSwitchFour the natural way:

int testSwitchFour(enum SwitchFour e) {
  switch (e) {
  case Red:   return 1;
  case Green: return 2;
  case Blue:  return 3;
  }
} 

They will get warning: 'Blue' is deprecated [-Wdeprecated-declarations] instead.

Maybe we should suppress that warning for switch cases? Or suggest adding a default label instead?

@cor3ntin
Copy link
Contributor

Maybe we should suppress that warning for switch cases? Or suggest adding a default label instead?

I think silencing the warning is better than suggesting a default case, which may not be considered good practice.
Maybe we can add a -Wdeprecated-switch-case warning that people can control independently of -Wdeprecated-declarations

@zmodem
Copy link
Collaborator Author

zmodem commented May 15, 2025

I've added suppression of deprecated values in case expressions. Please take another look!

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

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

I'm OK with this, but I feel like this is creating scope creep. Now we have a special Wdeprecated-declarations carveouts for switches, but if you unpack the switch into if / else chain comparisons, you get deprecation warnings. Should we disable deprecation warnings that directly compare enumerators? I wouldn't want to expand scope that much.

It is possible to write code that avoids mentioning the deprecated enum (add a default, and/or fallthrough code) and silences -Wreturn-type, so I have a soft preference for going back to the old version.

@@ -6767,6 +6767,9 @@ class Sema final : public SemaBase {
};
std::optional<InitializationContext> DelayedDefaultInitializationContext;

/// Whether evaluating an expression for a switch case label.
Copy link
Collaborator

Choose a reason for hiding this comment

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

supernit: pack it with the contextual bools above, for both layout reasons and because it shows there's prior art

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done.

@zmodem
Copy link
Collaborator Author

zmodem commented May 20, 2025

Now we have a special Wdeprecated-declarations carveouts for switches, but if you unpack the switch into if / else chain comparisons, you get deprecation warnings.

Agreed, that it is a bit inconsistent. There is prior art though: ba87c62 makes exactly the same carve-out for -Wunguarded-availability (though the mechanism is different).

@AaronBallman
Copy link
Collaborator

I wonder if we should also do something special about the -Wdeprecated-declarations diagnostic in switches.

If the user fixes the -Wswitch / -Wreturn-type warnings from testSwitchFour the natural way:

int testSwitchFour(enum SwitchFour e) {
  switch (e) {
  case Red:   return 1;
  case Green: return 2;
  case Blue:  return 3;
  }
} 

They will get warning: 'Blue' is deprecated [-Wdeprecated-declarations] instead.

Maybe we should suppress that warning for switch cases? Or suggest adding a default label instead?

I don't think we should suppress the warning; the enumerator is deprecated and the user should be told that when they enable deprecation warnings. Isn't the better solution in this case for the user to use __builtin_unreachable outside of the switch so they don't have to mention the deprecated case, and locally suppress -Wswitch if they intentionally do not want the switch to be fully covered but they want other switches to continue to be checked?

Copy link
Collaborator

@shafik shafik left a comment

Choose a reason for hiding this comment

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

I think I expected a deprecated warning but I agree with the rest of the change. I would be ok w/ @cor3ntin suggestion.

@zmodem
Copy link
Collaborator Author

zmodem commented May 20, 2025

I don't think we should suppress the warning; the enumerator is deprecated and the user should be told that when they enable deprecation warnings.

But the user also needs to handle it in the switch: deprecated doesn't mean the enumerator can't occur (a recent experience of this motivated the patch ;)

Isn't the better solution in this case for the user to use __builtin_unreachable outside of the switch so they don't have to mention the deprecated case, and locally suppress -Wswitch if they intentionally do not want the switch to be fully covered but they want other switches to continue to be checked?

I'm not sure I'm following. __builtin_unreachable wouldn't suppress the -Wswitch warning, and anyway we don't want to trigger UB when the deprecated enumerator comes up, we want to handle it.

I think this is exactly ba87c62 but for deprecated constants, and that we should handle it the same.

Maybe we can add a -Wdeprecated-switch-case warning that people can control independently of -Wdeprecated-declarations

I'd be okay with this too. We'd turn it off though, and would it actually catch real issues?

@AaronBallman
Copy link
Collaborator

I don't think we should suppress the warning; the enumerator is deprecated and the user should be told that when they enable deprecation warnings.

But the user also needs to handle it in the switch: deprecated doesn't mean the enumerator can't occur (a recent experience of this motivated the patch ;)

Sure! But both sides of that are "the problem"; once you mark the enumerator as deprecated, you'll get diagnostics when you assign that value or when you test against that value.

Isn't the better solution in this case for the user to use __builtin_unreachable outside of the switch so they don't have to mention the deprecated case, and locally suppress -Wswitch if they intentionally do not want the switch to be fully covered but they want other switches to continue to be checked?

I'm not sure I'm following. __builtin_unreachable wouldn't suppress the -Wswitch warning, and anyway we don't want to trigger UB when the deprecated enumerator comes up, we want to handle it.

Oh good point, that case still needs to exist to handle the enumerator. I was thinking about it handling the -Wreturn-type diagnostic.

I think this is exactly ba87c62 but for deprecated constants, and that we should handle it the same.

I can see the rationale for handling both the same way. But the flip side is, then you get false negatives which can also come up legitimately. Consider:

// Enumeration which has existed since the dawn of time.
enum Stuff {
  GoodStuff,
  BetterStuff,
  BsetStuff [[deprecated("use BestStuff instead")]], // Ugh, typos are the worst.
  BestStuff = BesterStuff
};
...
// Brand-new code written just now
void do_interesting_things(Stuff S) {
  switch (S) {
  case GoodStuff: whatever(); break;
  case BetterStuff: whatever_else(); break;
  case BsetStuff: wahoo(); break; // Oops, meant to use BestStuff instead
  }
}

That code will continue to work great right up until the enumerator is removed.

@rnk
Copy link
Collaborator

rnk commented May 20, 2025

I think silencing the warning is better than suggesting a default case, which may not be considered good practice.

I'm not sure, I think our perspectives as compiler people might be a bit off base. We're always forming closed algebraic sum types, like variants, AST nodes, that kind of thing. I think normal programs dealing with untrusted input coming from a file or over the wire typically need to be more conservative, and pushing users towards adding a default case or code to the fallthrough block without explicitly mentioning the deprecated enumerator might be the best outcome. Consider that -Wcovered-switch-default is not part of -Wall/-Wextra: https://godbolt.org/z/PrjqqxKrP

That code will continue to work great right up until the enumerator is removed.

To Aaron's point about deprecation being a tool for removal, if the goal of deprecation is to remove the enumerator, then we do want folks to use default/fallthrough code patterns that don't explicitly mention the enum.

@zmodem
Copy link
Collaborator Author

zmodem commented May 21, 2025

Thanks Aaron, that's a good example.

This is a pickle; it doesn't seem like there's an obviously Right Solution(tm) here.

I think we're agreeing on the first part, that unhandled deprecated enums should trigger the "not covered" warning.

Some users will prefer to handle that with a default: to avoid using the deprecated enum, while others will want to handle the enum explicitly and suppress the warning.

Corentin's flag suggestion seems like the best way to satisfy both camps. I've updated the PR.

@@ -6009,6 +6009,8 @@ def note_not_found_by_two_phase_lookup : Note<"%0 should be declared prior to th
def err_undeclared_use : Error<"use of undeclared %0">;
def warn_deprecated : Warning<"%0 is deprecated">,
InGroup<DeprecatedDeclarations>;
def warn_deprecated_switch_case : Warning<"%0 is deprecated">,
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can reuse diagnostic text with warn_deprecated.Summary. See other examples like:

def ext_vla_cxx_in_gnu_mode : Extension<ext_vla_cxx.Summary>,

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks! Done.

@AaronBallman
Copy link
Collaborator

Thanks Aaron, that's a good example.

This is a pickle; it doesn't seem like there's an obviously Right Solution(tm) here.

That's the same conclusion I'm coming to. These situations are kind of mutually exclusive.

I think we're agreeing on the first part, that unhandled deprecated enums should trigger the "not covered" warning.

Yup!

Some users will prefer to handle that with a default: to avoid using the deprecated enum, while others will want to handle the enum explicitly and suppress the warning.

Corentin's flag suggestion seems like the best way to satisfy both camps. I've updated the PR.

I agree, thank you for heading that direction (and good suggestion @cor3ntin)!

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.

Please add a release note to clang/docs/ReleaseNotes.rst so users know about the new functionality and flag.

Otherwise, LGTM!

Copy link
Collaborator

@rnk rnk left a comment

Choose a reason for hiding this comment

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

Thanks!

@zmodem zmodem merged commit f4e14bf into llvm:main May 23, 2025
11 of 12 checks passed
zmodem added a commit to zmodem/llvm-project that referenced this pull request May 28, 2025
…switch-case

To make it more clear that it's a subset of -Wdeprecated-declarations.

Follow-up to llvm#138562
zmodem added a commit that referenced this pull request May 28, 2025
…switch-case (#141779)

To make it more clear that it's a subset of -Wdeprecated-declarations.

Follow-up to #138562
google-yfyang pushed a commit to google-yfyang/llvm-project that referenced this pull request May 29, 2025
…switch-case (llvm#141779)

To make it more clear that it's a subset of -Wdeprecated-declarations.

Follow-up to llvm#138562
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Jun 3, 2025
This undoes part of 3e4e3b1 which added
the "Omitting a deprecated constant is ok; it should never materialize."
logic.

That seems wrong: deprecated means the enumerator is likely to be
removed in future versions, not that it cannot materialize.

Also move warnings about the use of deprecated enumerators in switch cases
behind a separate flag, -Wdeprecated-switch-case, for users who wish to
handle such enums explicitly and suppress the warning.
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Jun 3, 2025
…switch-case (llvm#141779)

To make it more clear that it's a subset of -Wdeprecated-declarations.

Follow-up to llvm#138562
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:diagnostics New/improved warning or error message in Clang, but not in clang-tidy or static analyzer 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.

7 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.