Merge pull request #20906 from paldepind/rust/enum-fieldless

Rust: Add predicates for fieldless and unit-only enums
This commit is contained in:
Simon Friis Vindum
2025-11-26 12:52:28 +01:00
committed by GitHub
9 changed files with 87 additions and 16 deletions

View File

@@ -31,5 +31,23 @@ module Impl {
result = this.getVariantList().getAVariant() and result = this.getVariantList().getAVariant() and
result.getName().getText() = name result.getName().getText() = name
} }
/**
* Holds if this is a field-less enum, that is, an enum where no constructors contain fields.
*
* See: https://doc.rust-lang.org/reference/items/enumerations.html#r-items.enum.fieldless
*/
predicate isFieldless() {
forall(Variant v | v = this.getVariantList().getAVariant() | v.getNumberOfFields() = 0)
}
/**
* Holds if this is a unit-only enum, that is, an enum where all constructors are unit variants.
*
* See: https://doc.rust-lang.org/reference/items/enumerations.html#r-items.enum.unit-only
*/
predicate isUnitOnly() {
forall(Variant v | v = this.getVariantList().getAVariant() | v.isUnit())
}
} }
} }

View File

@@ -46,12 +46,11 @@ module Impl {
pragma[nomagic] pragma[nomagic]
predicate isTuple() { this.getFieldList() instanceof TupleFieldList } predicate isTuple() { this.getFieldList() instanceof TupleFieldList }
/** /** Holds if this struct uses struct fields. */
* Holds if this struct uses record fields.
*
* Empty structs are considered to use record fields.
*/
pragma[nomagic] pragma[nomagic]
predicate isStruct() { not this.isTuple() } predicate isStruct() { this.getFieldList() instanceof StructFieldList }
/** Holds if this struct does not have a field list. */
predicate isUnit() { not this.hasFieldList() }
} }
} }

View File

@@ -36,17 +36,27 @@ module Impl {
pragma[nomagic] pragma[nomagic]
TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) }
/** Gets the number of fields of this variant. */
int getNumberOfFields() {
not this.hasFieldList() and
result = 0
or
result = this.getFieldList().(StructFieldList).getNumberOfFields()
or
result = this.getFieldList().(TupleFieldList).getNumberOfFields()
}
/** Holds if this variant uses tuple fields. */ /** Holds if this variant uses tuple fields. */
pragma[nomagic] pragma[nomagic]
predicate isTuple() { this.getFieldList() instanceof TupleFieldList } predicate isTuple() { this.getFieldList() instanceof TupleFieldList }
/** /** Holds if this variant uses struct fields. */
* Holds if this variant uses struct fields.
*
* Empty variants are considered to use struct fields.
*/
pragma[nomagic] pragma[nomagic]
predicate isStruct() { not this.isTuple() } predicate isStruct() { this.getFieldList() instanceof StructFieldList }
/** Holds if this variant does not have a field list. */
pragma[nomagic]
predicate isUnit() { not this.hasFieldList() }
/** Gets the enum that this variant belongs to. */ /** Gets the enum that this variant belongs to. */
Enum getEnum() { this = result.getVariantList().getAVariant() } Enum getEnum() { this = result.getVariantList().getAVariant() }

View File

@@ -659,7 +659,7 @@ private class VariantItemNode extends ParameterizableItemNode instanceof Variant
override string getName() { result = Variant.super.getName().getText() } override string getName() { result = Variant.super.getName().getText() }
override Namespace getNamespace() { override Namespace getNamespace() {
if super.getFieldList() instanceof StructFieldList then result.isType() else result.isValue() if super.isStruct() then result.isType() else result.isValue()
} }
override TypeParam getTypeParam(int i) { override TypeParam getTypeParam(int i) {
@@ -969,7 +969,7 @@ private class StructItemNode extends TypeItemNode, ParameterizableItemNode insta
override Namespace getNamespace() { override Namespace getNamespace() {
result.isType() // the struct itself result.isType() // the struct itself
or or
not super.getFieldList() instanceof StructFieldList and not super.isStruct() and
result.isValue() // the constructor result.isValue() // the constructor
} }

View File

@@ -787,7 +787,7 @@ private module StructExprMatchingInput implements MatchingInputSig {
} }
private class StructDecl extends Declaration, Struct { private class StructDecl extends Declaration, Struct {
StructDecl() { this.isStruct() } StructDecl() { this.isStruct() or this.isUnit() }
override TypeParam getATypeParam() { result = this.getGenericParamList().getATypeParam() } override TypeParam getATypeParam() { result = this.getGenericParamList().getATypeParam() }
@@ -804,7 +804,7 @@ private module StructExprMatchingInput implements MatchingInputSig {
} }
private class StructVariantDecl extends Declaration, Variant { private class StructVariantDecl extends Declaration, Variant {
StructVariantDecl() { this.isStruct() } StructVariantDecl() { this.isStruct() or this.isUnit() }
Enum getEnum() { result.getVariantList().getAVariant() = this } Enum getEnum() { result.getVariantList().getAVariant() = this }

View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "test"
version = "0.0.1"

View File

@@ -0,0 +1,7 @@
fieldless
| enums.rs:1:1:5:1 | enum Foo |
| enums.rs:7:1:11:1 | enum Fieldless |
| enums.rs:13:1:18:1 | enum Direction |
unitOnly
| enums.rs:1:1:5:1 | enum Foo |
| enums.rs:13:1:18:1 | enum Direction |

View File

@@ -0,0 +1,6 @@
import rust
import TestUtils
query predicate fieldless(Enum e) { toBeTested(e) and e.isFieldless() }
query predicate unitOnly(Enum e) { toBeTested(e) and e.isUnitOnly() }

View File

@@ -0,0 +1,24 @@
enum Foo {
Bar,
Baz,
Qux,
}
enum Fieldless {
Tuple(),
Struct{},
Unit,
}
enum Direction {
North = 0,
East = 90,
South = 180,
West = 270,
}
enum Color {
Red(u8),
Green(u8),
Blue(u8),
}