Skip to content

[analyzer] Ignore [[clang::flag_enum]] enums in the EnumCastOutOfRange checker #141232

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 1 commit into from
May 25, 2025

Conversation

steakhal
Copy link
Contributor

Resolves #76208 (comment)

Quoting the docs of [[clang::flag_enum]]:
https://clang.llvm.org/docs/AttributeReference.html#flag-enum

This attribute can be added to an enumerator to signal to the compiler that it
is intended to be used as a flag type. This will cause the compiler to assume
that the range of the type includes all of the values that you can get by
manipulating bits of the enumerator when issuing warnings.

…e checker

Resolves llvm#76208 (comment)

Quoting the docs of [[clang::flag_enum]]:
https://clang.llvm.org/docs/AttributeReference.html#flag-enum

> This attribute can be added to an enumerator to signal to the compiler that it
> is intended to be used as a flag type. This will cause the compiler to assume
> that the range of the type includes all of the values that you can get by
> manipulating bits of the enumerator when issuing warnings.
@steakhal steakhal requested review from gamesh411 and NagyDonat May 23, 2025 13:20
@llvmbot llvmbot added the clang Clang issues not falling into any other category label May 23, 2025
@llvmbot
Copy link
Member

llvmbot commented May 23, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Balazs Benics (steakhal)

Changes

Resolves #76208 (comment)

Quoting the docs of [[clang::flag_enum]]:
https://clang.llvm.org/docs/AttributeReference.html#flag-enum

> This attribute can be added to an enumerator to signal to the compiler that it
> is intended to be used as a flag type. This will cause the compiler to assume
> that the range of the type includes all of the values that you can get by
> manipulating bits of the enumerator when issuing warnings.


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

4 Files Affected:

  • (modified) clang/docs/analyzer/checkers.rst (+5-1)
  • (modified) clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp (+5)
  • (modified) clang/test/Analysis/enum-cast-out-of-range.c (+11)
  • (modified) clang/test/Analysis/enum-cast-out-of-range.cpp (+11)
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c2ae80c47eca1..26c5028e04955 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -773,7 +773,11 @@ enumerators at all.
 **Limitations**
 
 This checker does not accept the coding pattern where an enum type is used to
-store combinations of flag values:
+store combinations of flag values.
+Such enums should be annotated with the `__attribute__((flag_enum))` or by the
+`[[clang::flag_enum]]` attribute to signal this intent. Refer to the
+`documentation <https://clang.llvm.org/docs/AttributeReference.html#flag-enum>`_
+of this Clang attribute.
 
 .. code-block:: cpp
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 0fa20428c1b56..355e82e465e82 100644
--- a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -19,6 +19,7 @@
 //   enumeration value
 //===----------------------------------------------------------------------===//
 
+#include "clang/AST/Attr.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -149,6 +150,10 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
   // function to handle this.
   const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
 
+  // [[clang::flag_enum]] annotated enums are by definition should be ignored.
+  if (ED->hasAttr<FlagEnumAttr>())
+    return;
+
   EnumValueVector DeclValues = getDeclValuesForEnum(ED);
 
   // If the declarator list is empty, bail out.
diff --git a/clang/test/Analysis/enum-cast-out-of-range.c b/clang/test/Analysis/enum-cast-out-of-range.c
index a6eef92f418d1..f9f7aac5ae1b7 100644
--- a/clang/test/Analysis/enum-cast-out-of-range.c
+++ b/clang/test/Analysis/enum-cast-out-of-range.c
@@ -64,3 +64,14 @@ void testTrackExpression(int i) {
   (void)(enum En_t)(i); // expected-warning {{not in the valid range of values for 'En_t'}}
                         // expected-note@-1 {{not in the valid range of values for 'En_t'}}
 }
+
+enum __attribute__((flag_enum)) FlagEnum {
+  FE_BIT_1 = 1 << 0,
+  FE_BIT_2 = 1 << 1,
+  FE_BIT_3 = 1 << 2,
+};
+
+void testFlagEnum_gh_76208(void) {
+  enum FlagEnum First2BitsSet = (enum FlagEnum)(FE_BIT_1 | FE_BIT_2); // no-warning: Enums with the attribute 'flag_enum' are not checked
+  (void)First2BitsSet;
+}
diff --git a/clang/test/Analysis/enum-cast-out-of-range.cpp b/clang/test/Analysis/enum-cast-out-of-range.cpp
index a5ac4f3fd0567..81763ae893135 100644
--- a/clang/test/Analysis/enum-cast-out-of-range.cpp
+++ b/clang/test/Analysis/enum-cast-out-of-range.cpp
@@ -230,3 +230,14 @@ void foo() {
 
   ignore_unused(c, x, d);
 }
+
+enum [[clang::flag_enum]] FlagEnum {
+  FE_BIT_1 = 1 << 0,
+  FE_BIT_2 = 1 << 1,
+  FE_BIT_3 = 1 << 2,
+};
+
+void testFlagEnum_gh_76208(void) {
+  FlagEnum First2BitsSet = (FlagEnum)(FE_BIT_1 | FE_BIT_2); // no-warning: Enums with the attribute 'flag_enum' are not checked
+  (void)First2BitsSet;
+}

Copy link
Collaborator

@Xazax-hun Xazax-hun left a comment

Choose a reason for hiding this comment

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

Should we still warn if we store a negative value or a value >= 2*last member?

@steakhal
Copy link
Contributor Author

Should we still warn if we store a negative value or a value >= 2*last member?

I was thinking about this but I chose the simplest way to suppress these.

@steakhal steakhal merged commit 9a440f8 into llvm:main May 25, 2025
15 checks passed
@steakhal steakhal deleted the bb/flag-enum branch May 25, 2025 10:00
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Jun 3, 2025
…e checker (llvm#141232)

Resolves
llvm#76208 (comment)

Quoting the docs of `[[clang::flag_enum]]`:
https://clang.llvm.org/docs/AttributeReference.html#flag-enum

> This attribute can be added to an enumerator to signal to the compiler that it
> is intended to be used as a flag type. This will cause the compiler to assume
> that the range of the type includes all of the values that you can get by
> manipulating bits of the enumerator when issuing warnings.

Ideally, we should still check the upper bounds but for simplicity let's not bother for now.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang][analyzer] suppress optin.core.EnumCastOutOfRange for bit-wise operator of scoped enum
3 participants