From b4ae58871aeaccea9ff8aa100dc750a802319ee6 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 25 Nov 2025 10:07:51 +0100 Subject: [PATCH 1/4] Rust: Tweak existing `isStruct` predicates --- .../lib/codeql/rust/elements/internal/StructImpl.qll | 11 +++++------ .../codeql/rust/elements/internal/VariantImpl.qll | 12 ++++++------ rust/ql/lib/codeql/rust/internal/PathResolution.qll | 4 ++-- rust/ql/lib/codeql/rust/internal/TypeInference.qll | 4 ++-- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll index e4414305ae84..cb4121b7224d 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll @@ -46,12 +46,11 @@ module Impl { pragma[nomagic] predicate isTuple() { this.getFieldList() instanceof TupleFieldList } - /** - * Holds if this struct uses record fields. - * - * Empty structs are considered to use record fields. - */ + /** Holds if this struct uses struct fields. */ 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() } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll index d6b25b21e289..940ab5554b81 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll @@ -40,13 +40,13 @@ module Impl { pragma[nomagic] predicate isTuple() { this.getFieldList() instanceof TupleFieldList } - /** - * Holds if this variant uses struct fields. - * - * Empty variants are considered to use struct fields. - */ + /** Holds if this variant uses struct fields. */ 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. */ Enum getEnum() { this = result.getVariantList().getAVariant() } diff --git a/rust/ql/lib/codeql/rust/internal/PathResolution.qll b/rust/ql/lib/codeql/rust/internal/PathResolution.qll index d754258f16d0..767d8c3674ec 100644 --- a/rust/ql/lib/codeql/rust/internal/PathResolution.qll +++ b/rust/ql/lib/codeql/rust/internal/PathResolution.qll @@ -659,7 +659,7 @@ private class VariantItemNode extends ParameterizableItemNode instanceof Variant override string getName() { result = Variant.super.getName().getText() } 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) { @@ -969,7 +969,7 @@ private class StructItemNode extends TypeItemNode, ParameterizableItemNode insta override Namespace getNamespace() { result.isType() // the struct itself or - not super.getFieldList() instanceof StructFieldList and + not super.isStruct() and result.isValue() // the constructor } diff --git a/rust/ql/lib/codeql/rust/internal/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/TypeInference.qll index 8f9840e0566d..75147c29e04a 100644 --- a/rust/ql/lib/codeql/rust/internal/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/TypeInference.qll @@ -787,7 +787,7 @@ private module StructExprMatchingInput implements MatchingInputSig { } private class StructDecl extends Declaration, Struct { - StructDecl() { this.isStruct() } + StructDecl() { this.isStruct() or this.isUnit() } override TypeParam getATypeParam() { result = this.getGenericParamList().getATypeParam() } @@ -804,7 +804,7 @@ private module StructExprMatchingInput implements MatchingInputSig { } private class StructVariantDecl extends Declaration, Variant { - StructVariantDecl() { this.isStruct() } + StructVariantDecl() { this.isStruct() or this.isUnit() } Enum getEnum() { result.getVariantList().getAVariant() = this } From 50e8d0ca20e6d7a6e3b08c0014a7ef70127318e0 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 25 Nov 2025 10:08:26 +0100 Subject: [PATCH 2/4] Rust: Add `isFieldless` and `isUnitOnly` to `Enum` --- .../codeql/rust/elements/internal/EnumImpl.qll | 18 ++++++++++++++++++ .../rust/elements/internal/VariantImpl.qll | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/rust/ql/lib/codeql/rust/elements/internal/EnumImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/EnumImpl.qll index e57a416fa721..3862cc42137e 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/EnumImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/EnumImpl.qll @@ -31,5 +31,23 @@ module Impl { result = this.getVariantList().getAVariant() and 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()) + } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll index 940ab5554b81..c28553492a02 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll @@ -36,6 +36,15 @@ module Impl { pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } + 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. */ pragma[nomagic] predicate isTuple() { this.getFieldList() instanceof TupleFieldList } From 393da4567e1fcaf5a9c14660db1d41727d996f92 Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 25 Nov 2025 10:09:59 +0100 Subject: [PATCH 3/4] Rust: Add tests for `Enum` --- .../library-tests/elements/enum/Cargo.lock | 7 ++++++ .../library-tests/elements/enum/Enum.expected | 7 ++++++ .../test/library-tests/elements/enum/Enum.ql | 6 +++++ .../test/library-tests/elements/enum/enums.rs | 24 +++++++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 rust/ql/test/library-tests/elements/enum/Cargo.lock create mode 100644 rust/ql/test/library-tests/elements/enum/Enum.expected create mode 100644 rust/ql/test/library-tests/elements/enum/Enum.ql create mode 100644 rust/ql/test/library-tests/elements/enum/enums.rs diff --git a/rust/ql/test/library-tests/elements/enum/Cargo.lock b/rust/ql/test/library-tests/elements/enum/Cargo.lock new file mode 100644 index 000000000000..b9856cfaf77d --- /dev/null +++ b/rust/ql/test/library-tests/elements/enum/Cargo.lock @@ -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" diff --git a/rust/ql/test/library-tests/elements/enum/Enum.expected b/rust/ql/test/library-tests/elements/enum/Enum.expected new file mode 100644 index 000000000000..f910b240ca74 --- /dev/null +++ b/rust/ql/test/library-tests/elements/enum/Enum.expected @@ -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 | diff --git a/rust/ql/test/library-tests/elements/enum/Enum.ql b/rust/ql/test/library-tests/elements/enum/Enum.ql new file mode 100644 index 000000000000..82baa8dffe66 --- /dev/null +++ b/rust/ql/test/library-tests/elements/enum/Enum.ql @@ -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() } diff --git a/rust/ql/test/library-tests/elements/enum/enums.rs b/rust/ql/test/library-tests/elements/enum/enums.rs new file mode 100644 index 000000000000..0f5a964c284f --- /dev/null +++ b/rust/ql/test/library-tests/elements/enum/enums.rs @@ -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), +} From 4f13ae3fc9dabc24eaf53ade159169c5bc4d2dad Mon Sep 17 00:00:00 2001 From: Simon Friis Vindum Date: Tue, 25 Nov 2025 10:19:49 +0100 Subject: [PATCH 4/4] Rust: Add qldoc --- rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll index c28553492a02..ed8b93f6c1d2 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll @@ -36,6 +36,7 @@ module Impl { pragma[nomagic] 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