Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,9 @@ Static Analyzer
`#65888 <https://github.com/llvm/llvm-project/pull/65888>`_, and
`#65887 <https://github.com/llvm/llvm-project/pull/65887>`_

- Move checker ``alpha.cplusplus.EnumCastOutOfRange`` out of the ``alpha``
package to ``optin.core.EnumCastOutOfRange``.

.. _release-notes-sanitizers:

Sanitizers
Expand Down
63 changes: 46 additions & 17 deletions clang/docs/analyzer/checkers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,52 @@ optin

Checkers for portability, performance or coding style specific rules.

.. _optin-core-EnumCastOutOfRange:

optin.core.EnumCastOutOfRange (C, C++)
""""""""""""""""""""""""""""""""""""""
Check for integer to enumeration casts that would produce a value with no
corresponding enumerator. This is not necessarily undefined behavior, but can
lead to nasty surprises, so projects may decide to use a coding standard that
disallows these "unusual" conversions.

Note that no warnings are produced when the enum type (e.g. `std::byte`) has no
enumerators at all.

.. code-block:: cpp

enum WidgetKind { A=1, B, C, X=99 };

void foo() {
WidgetKind c = static_cast<WidgetKind>(3); // OK
WidgetKind x = static_cast<WidgetKind>(99); // OK
WidgetKind d = static_cast<WidgetKind>(4); // warn
}

**Limitations**

This checker does not accept the coding pattern where an enum type is used to
store combinations of flag values:

.. code-block:: cpp

enum AnimalFlags
{
HasClaws = 1,
CanFly = 2,
EatsFish = 4,
Endangered = 8
};

AnimalFlags operator|(AnimalFlags a, AnimalFlags b)
{
return static_cast<AnimalFlags>(static_cast<int>(a) | static_cast<int>(b));
}

auto flags = HasClaws | CanFly;

Projects that use this pattern should not enable this optin checker.

.. _optin-cplusplus-UninitializedObject:

optin.cplusplus.UninitializedObject (C++)
Expand Down Expand Up @@ -2113,23 +2159,6 @@ Reports destructions of polymorphic objects with a non-virtual destructor in the
// destructor
}

.. _alpha-cplusplus-EnumCastOutOfRange:

alpha.cplusplus.EnumCastOutOfRange (C++)
""""""""""""""""""""""""""""""""""""""""
Check for integer to enumeration casts that could result in undefined values.

.. code-block:: cpp

enum TestEnum {
A = 0
};

void foo() {
TestEnum t = static_cast(-1);
// warn: the value provided to the cast expression is not in
// the valid range of values for the enum

.. _alpha-cplusplus-InvalidatedIterator:

alpha.cplusplus.InvalidatedIterator (C++)
Expand Down
17 changes: 13 additions & 4 deletions clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def CoreAlpha : Package<"core">, ParentPackage<Alpha>;
// Note: OptIn is *not* intended for checkers that are too noisy to be on by
// default. Such checkers belong in the alpha package.
def OptIn : Package<"optin">;
def CoreOptIn : Package<"core">, ParentPackage<OptIn>;

// In the Portability package reside checkers for finding code that relies on
// implementation-defined behavior. Such checks are wanted for cross-platform
Expand Down Expand Up @@ -439,6 +440,18 @@ def UndefinedNewArraySizeChecker : Checker<"NewArraySize">,

} // end "core.uninitialized"

//===----------------------------------------------------------------------===//
// Optin checkers for core language features
//===----------------------------------------------------------------------===//

let ParentPackage = CoreOptIn in {

def EnumCastOutOfRangeChecker : Checker<"EnumCastOutOfRange">,
HelpText<"Check integer to enumeration casts for out of range values">,
Documentation<HasDocumentation>;

} // end "optin.core"

//===----------------------------------------------------------------------===//
// Unix API checkers.
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -774,10 +787,6 @@ def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">,
"destructor in their base class">,
Documentation<HasDocumentation>;

def EnumCastOutOfRangeChecker : Checker<"EnumCastOutOfRange">,
HelpText<"Check integer to enumeration casts for out of range values">,
Documentation<HasDocumentation>;

def IteratorModeling : Checker<"IteratorModeling">,
HelpText<"Models iterators of C++ containers">,
Dependencies<[ContainerModeling]>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
// Every initialization an enum with a fixed underlying type but without any
// enumerators would produce a warning if we were to continue at this point.
// The most notable example is std::byte in the C++17 standard library.
// TODO: Create heuristics to bail out when the enum type is intended to be
// used to store combinations of flag values (to mitigate the limitation
// described in the docs).
if (DeclValues.size() == 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion clang/test/Analysis/enum-cast-out-of-range.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=core,alpha.cplusplus.EnumCastOutOfRange \
// RUN: -analyzer-checker=core,optin.core.EnumCastOutOfRange \
// RUN: -analyzer-output text \
// RUN: -verify %s

Expand Down
13 changes: 12 additions & 1 deletion clang/test/Analysis/enum-cast-out-of-range.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=core,alpha.cplusplus.EnumCastOutOfRange \
// RUN: -analyzer-checker=core,optin.core.EnumCastOutOfRange \
// RUN: -std=c++11 -verify %s

// expected-note@+1 + {{enum declared here}}
Expand Down Expand Up @@ -219,3 +219,14 @@ void empty_enums_init_with_zero_should_not_warn() {

ignore_unused(eu, ef, efu);
}

//Test the example from checkers.rst:
enum WidgetKind { A=1, B, C, X=99 }; // expected-note {{enum declared here}}

void foo() {
WidgetKind c = static_cast<WidgetKind>(3); // OK
WidgetKind x = static_cast<WidgetKind>(99); // OK
WidgetKind d = static_cast<WidgetKind>(4); // expected-warning {{The value provided to the cast expression is not in the valid range of values for 'WidgetKind'}}

ignore_unused(c, x, d);
}
19 changes: 0 additions & 19 deletions clang/www/analyzer/alpha_checks.html
Original file line number Diff line number Diff line change
Expand Up @@ -370,25 +370,6 @@ <h3 id="cplusplus_alpha_checkers">C++ Alpha Checkers</h3>
}
</pre></div></div></td></tr>

<tr><td><a id="alpha.cplusplus.EnumCastOutOfRange"><div class="namedescr expandable"><span class="name">
alpha.cplusplus.EnumCastOutOfRange</span><span class="lang">
(C++)</span><div class="descr">
Check for integer to enumeration casts that could result in undefined values.
</div></div></a></td>
<td><div class="exampleContainer expandable">
<div class="example"><pre>
enum TestEnum {
A = 0
};

void foo() {
TestEnum t = static_cast<TestEnum>(-1);
// warn: the value provided to the cast expression is not in
the valid range of values for the enum
}
</pre></div></div></td></tr>


<tr><td><a id="alpha.cplusplus.InvalidatedIterator"><div class="namedescr expandable"><span class="name">
alpha.cplusplus.InvalidatedIterator</span><span class="lang">
(C++)</span><div class="descr">
Expand Down