@@ -106,11 +106,6 @@ predicate compoundAssignmentUsesUnscopedUnfixedEnum(
106106 isUnscopedEnumWithoutFixedUnderlyingType ( compoundAssignment .getAnOperand ( ) .getUnderlyingType ( ) )
107107}
108108
109- predicate assignmentSourceIsUnscopedUnfixedEnum ( AssignExpr assign ) {
110- isUnscopedEnumWithoutFixedUnderlyingType ( assign .getRValue ( ) .getUnderlyingType ( ) ) and
111- not enumFitsInType ( assign .getRValue ( ) .getUnderlyingType ( ) , assign .getLValue ( ) .getUnderlyingType ( ) )
112- }
113-
114109/**
115110 * Gets the minimum number of bits required to hold all values of enum `e`.
116111 */
@@ -156,20 +151,60 @@ int enumMinBits(Enum e, boolean signed) {
156151 */
157152predicate enumFitsInType ( Enum e , IntegralType type ) {
158153 exists ( int minBits , boolean signed | minBits = enumMinBits ( e , signed ) |
154+ /* If it has exactly the minimum number of bits, then check its signedness. */
159155 type .getSize ( ) * 8 = minBits and
160156 (
161157 signed = true and type .isSigned ( )
162158 or
163159 signed = false and type .isUnsigned ( )
164160 )
165161 or
162+ /* If it exceeds the minimum number of bits, signedness doesn't matter. */
166163 type .getSize ( ) * 8 > minBits
167164 )
168165}
169166
170- predicate staticCastSourceIsUnscopedUnfixedEnumVariant ( StaticCast cast ) { none ( ) }
167+ predicate assignmentSourceIsUnscopedUnfixedEnum ( AssignExpr assign ) {
168+ isUnscopedEnumWithoutFixedUnderlyingType ( assign .getRValue ( ) .getUnderlyingType ( ) ) and
169+ (
170+ not enumFitsInType ( assign .getRValue ( ) .getUnderlyingType ( ) ,
171+ assign .getLValue ( ) .getUnderlyingType ( ) ) and
172+ /* Exclude cases where the assignment's target type is the same enum. */
173+ not assign .getRValue ( ) .getUnderlyingType ( ) = assign .getLValue ( ) .getUnderlyingType ( )
174+ )
175+ }
171176
172- predicate switchCaseIsAnUnfixedEnumVariant ( SwitchCase switchCase ) { none ( ) }
177+ predicate staticCastSourceIsUnscopedUnfixedEnumVariant ( StaticCast cast ) {
178+ isUnscopedEnumWithoutFixedUnderlyingType ( cast .getExpr ( ) .getUnderlyingType ( ) ) and
179+ (
180+ not enumFitsInType ( cast .getExpr ( ) .getUnderlyingType ( ) , cast .getUnderlyingType ( ) ) and
181+ /* Exclude cases where the assignment's target type is the same enum. */
182+ not cast .getExpr ( ) .getUnderlyingType ( ) = cast .getUnderlyingType ( )
183+ )
184+ }
185+
186+ predicate switchConditionIsAnUnfixedEnumVariant ( SwitchStmt switch ) {
187+ exists ( Enum e |
188+ isUnscopedEnumWithoutFixedUnderlyingType ( e ) and
189+ e = switch .getExpr ( ) .getType ( ) and
190+ exists ( SwitchCase case | case = switch .getASwitchCase ( ) |
191+ not case .getExpr ( ) .getUnderlyingType ( ) = e
192+ )
193+ )
194+ }
195+
196+ /**
197+ * Holds if a `static_cast` expression has an enum with fixed underlying type but
198+ * the target type is not an unscoped enum.
199+ */
200+ predicate staticCastTargetIsUnscopedUnfixedEnumVariant ( StaticCast cast ) {
201+ exists ( Enum e |
202+ e = cast .getType ( ) and
203+ isUnscopedEnumWithoutFixedUnderlyingType ( e ) and
204+ // Exclude same-type casts (allowed by the "from" rule)
205+ not cast .getExpr ( ) .getType ( ) = e
206+ )
207+ }
173208
174209from Element x
175210where
@@ -180,6 +215,7 @@ where
180215 compoundAssignmentUsesUnscopedUnfixedEnum ( x ) or
181216 assignmentSourceIsUnscopedUnfixedEnum ( x ) or
182217 staticCastSourceIsUnscopedUnfixedEnumVariant ( x ) or
183- switchCaseIsAnUnfixedEnumVariant ( x )
218+ switchConditionIsAnUnfixedEnumVariant ( x ) or
219+ staticCastTargetIsUnscopedUnfixedEnumVariant ( x )
184220 )
185221select x , "TODO"
0 commit comments