diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll index d7704894c49a..897196b78cbe 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructExprImpl.qll @@ -50,5 +50,10 @@ module Impl { or result = this.getVariant().getStructField(name) } + + /** Gets the `i`th struct field of the instantiated struct or variant. */ + StructField getNthStructField(int i) { + result = [this.getStruct().getNthStructField(i), this.getVariant().getNthStructField(i)] + } } } diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll index cb4121b7224d..23fa1e76d9a8 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructImpl.qll @@ -35,6 +35,12 @@ module Impl { /** Gets a record field, if any. */ StructField getAStructField() { result = this.getStructField(_) } + /** Gets the `i`th struct field, if any. */ + pragma[nomagic] + StructField getNthStructField(int i) { + result = this.getFieldList().(StructFieldList).getField(i) + } + /** Gets the `i`th tuple field, if any. */ pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } diff --git a/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll index 28afc2a5b0d7..e649d2a57787 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/StructPatImpl.qll @@ -42,6 +42,13 @@ module Impl { ) } + /** Gets the `i`th struct field of the instantiated struct or variant. */ + StructField getNthStructField(int i) { + exists(PathResolution::ItemNode item | item = this.getResolvedPath(_) | + result = [item.(Struct).getNthStructField(i), item.(Variant).getNthStructField(i)] + ) + } + /** Gets the struct pattern for the field `name`. */ pragma[nomagic] StructPatField getPatField(string name) { diff --git a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll index ed8b93f6c1d2..58b061049bdd 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/VariantImpl.qll @@ -32,6 +32,12 @@ module Impl { result.getName().getText() = name } + /** Gets the `i`th struct field, if any. */ + pragma[nomagic] + StructField getNthStructField(int i) { + result = this.getFieldList().(StructFieldList).getField(i) + } + /** Gets the `i`th tuple field, if any. */ pragma[nomagic] TupleField getTupleField(int i) { result = this.getFieldList().(TupleFieldList).getField(i) } diff --git a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll index c194531a0781..41af0ec31f7f 100644 --- a/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll +++ b/rust/ql/lib/codeql/rust/internal/typeinference/TypeInference.qll @@ -872,188 +872,6 @@ private Type inferTypeEquality(AstNode n, TypePath path) { ) } -/** - * A matching configuration for resolving types of struct expressions - * like `Foo { bar = baz }`. - * - * This also includes nullary struct expressions like `None`. - */ -private module StructExprMatchingInput implements MatchingInputSig { - private newtype TPos = - TFieldPos(string name) { exists(any(Declaration decl).getField(name)) } or - TStructPos() - - class DeclarationPosition extends TPos { - string asFieldPos() { this = TFieldPos(result) } - - predicate isStructPos() { this = TStructPos() } - - string toString() { - result = this.asFieldPos() - or - this.isStructPos() and - result = "(struct)" - } - } - - abstract class Declaration extends AstNode { - final TypeParameter getTypeParameter(TypeParameterPosition ppos) { - typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) - } - - abstract StructField getField(string name); - - abstract TypeItem getTypeItem(); - - Type getDeclaredType(DeclarationPosition dpos, TypePath path) { - // type of a field - exists(TypeMention tp | - tp = this.getField(dpos.asFieldPos()).getTypeRepr() and - result = tp.getTypeAt(path) - ) - or - // type parameter of the struct itself - dpos.isStructPos() and - result = this.getTypeParameter(_) and - path = TypePath::singleton(result) - or - // type of the struct or enum itself - dpos.isStructPos() and - path.isEmpty() and - result = TDataType(this.getTypeItem()) - } - } - - private class StructDecl extends Declaration, Struct { - StructDecl() { this.isStruct() or this.isUnit() } - - override StructField getField(string name) { result = this.getStructField(name) } - - override TypeItem getTypeItem() { result = this } - } - - private class StructVariantDecl extends Declaration, Variant { - StructVariantDecl() { this.isStruct() or this.isUnit() } - - override StructField getField(string name) { result = this.getStructField(name) } - - override TypeItem getTypeItem() { result = this.getEnum() } - } - - class AccessPosition = DeclarationPosition; - - abstract class Access extends AstNode { - pragma[nomagic] - abstract AstNode getNodeAt(AccessPosition apos); - - pragma[nomagic] - Type getInferredType(AccessPosition apos, TypePath path) { - result = inferType(this.getNodeAt(apos), path) - } - - pragma[nomagic] - abstract Path getStructPath(); - - pragma[nomagic] - Declaration getTarget() { result = resolvePath(this.getStructPath()) } - - pragma[nomagic] - Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - // Handle constructions that use `Self {...}` syntax - exists(TypeMention tm, TypePath path0 | - tm = this.getStructPath() and - result = tm.getTypeAt(path0) and - path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) - ) - } - - /** - * Holds if the return type of this struct expression at `path` may have to - * be inferred from the context. - */ - pragma[nomagic] - predicate hasUnknownTypeAt(DeclarationPosition pos, TypePath path) { - exists(Declaration d, TypeParameter tp | - d = this.getTarget() and - pos.isStructPos() and - tp = d.getDeclaredType(pos, path) and - not exists(DeclarationPosition fieldPos | - not fieldPos.isStructPos() and - tp = d.getDeclaredType(fieldPos, _) - ) and - // check that no explicit type arguments have been supplied for `tp` - not exists(TypeArgumentPosition tapos | - exists(this.getTypeArgument(tapos, _)) and - TTypeParamTypeParameter(tapos.asTypeParam()) = tp - ) - ) - } - } - - private class StructExprAccess extends Access, StructExpr { - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - result = super.getTypeArgument(apos, path) - or - exists(TypePath suffix | - suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and - result = CertainTypeInference::inferCertainType(this, suffix) - ) - } - - override AstNode getNodeAt(AccessPosition apos) { - result = this.getFieldExpr(apos.asFieldPos()).getExpr() - or - result = this and - apos.isStructPos() - } - - override Path getStructPath() { result = this.getPath() } - } - - /** - * A potential nullary struct/variant construction such as `None`. - */ - private class PathExprAccess extends Access, PathExpr { - PathExprAccess() { not exists(CallExpr ce | this = ce.getFunction()) } - - override AstNode getNodeAt(AccessPosition apos) { - result = this and - apos.isStructPos() - } - - override Path getStructPath() { result = this.getPath() } - } - - predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { - apos = dpos - } -} - -private module StructExprMatching = Matching; - -pragma[nomagic] -private Type inferStructExprType0( - AstNode n, FunctionPosition pos, boolean hasReceiver, TypePath path -) { - exists(StructExprMatchingInput::Access a, StructExprMatchingInput::AccessPosition apos | - n = a.getNodeAt(apos) and - hasReceiver = false and - if apos.isStructPos() then pos.isReturn() else pos.asPosition() = 0 // the actual position doesn't matter, as long as it is positional - | - result = StructExprMatching::inferAccessType(a, apos, path) - or - a.hasUnknownTypeAt(apos, path) and - result = TUnknownType() - ) -} - -/** - * Gets the type of `n` at `path`, where `n` is either a struct expression or - * a field expression of a struct expression. - */ -private predicate inferStructExprType = - ContextTyping::CheckContextTyping::check/2; - pragma[nomagic] private TupleType inferTupleRootType(AstNode n) { // `typeEquality` handles the non-root cases @@ -3083,14 +2901,14 @@ private Type inferFunctionCallTypePreCheck( private predicate inferFunctionCallType = ContextTyping::CheckContextTyping::check/2; -abstract private class TupleLikeConstructor extends Addressable { +abstract private class Constructor extends Addressable { final TypeParameter getTypeParameter(TypeParameterPosition ppos) { typeParamMatchPosition(this.getTypeItem().getGenericParamList().getATypeParam(), result, ppos) } abstract TypeItem getTypeItem(); - abstract TupleField getTupleField(int i); + abstract TypeRepr getParameterTypeRepr(int pos); Type getReturnType(TypePath path) { result = TDataType(this.getTypeItem()) and @@ -3105,65 +2923,59 @@ abstract private class TupleLikeConstructor extends Addressable { or pos.isReturn() and result = this.getReturnType(path) - or - pos.isTypeQualifier() and - result = this.getReturnType(path) } Type getParameterType(int pos, TypePath path) { - result = this.getTupleField(pos).getTypeRepr().(TypeMention).getTypeAt(path) + result = this.getParameterTypeRepr(pos).(TypeMention).getTypeAt(path) } } -private class TupleLikeStruct extends TupleLikeConstructor instanceof Struct { - TupleLikeStruct() { this.isTuple() } - +private class StructConstructor extends Constructor instanceof Struct { override TypeItem getTypeItem() { result = this } - override TupleField getTupleField(int i) { result = Struct.super.getTupleField(i) } + override TypeRepr getParameterTypeRepr(int i) { + result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] + } } -private class TupleLikeVariant extends TupleLikeConstructor instanceof Variant { - TupleLikeVariant() { this.isTuple() } - +private class VariantConstructor extends Constructor instanceof Variant { override TypeItem getTypeItem() { result = super.getEnum() } - override TupleField getTupleField(int i) { result = Variant.super.getTupleField(i) } + override TypeRepr getParameterTypeRepr(int i) { + result = [super.getTupleField(i).getTypeRepr(), super.getNthStructField(i).getTypeRepr()] + } } /** - * A matching configuration for resolving types of tuple-like variants and tuple - * structs such as `Result::Ok(42)`. + * A matching configuration for resolving types of constructors of enums and + * structs, such as `Result::Ok(42)`, `Foo { bar: 1 }` and `None`. */ -private module TupleLikeConstructionMatchingInput implements MatchingInputSig { +private module ConstructorMatchingInput implements MatchingInputSig { import FunctionPositionMatchingInput - class Declaration = TupleLikeConstructor; + class Declaration = Constructor; - class Access extends NonAssocCallExpr, ContextTyping::ContextTypedCallCand { - Access() { - this instanceof CallExprImpl::TupleStructExpr or - this instanceof CallExprImpl::TupleVariantExpr - } + abstract class Access extends AstNode { + abstract Type getInferredType(FunctionPosition pos, TypePath path); - override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { - result = NonAssocCallExpr.super.getTypeArgument(apos, path) - } + abstract Declaration getTarget(); - Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } + abstract AstNode getNodeAt(AccessPosition apos); + + abstract Type getTypeArgument(TypeArgumentPosition apos, TypePath path); /** - * Holds if the return type of this tuple-like construction at `path` may have to be inferred - * from the context, for example in `Result::Ok(42)` the error type has to be inferred from the - * context. + * Holds if the return type of this constructor expression at `path` may + * have to be inferred from the context. For example in `Result::Ok(42)` the + * error type has to be inferred from the context. */ pragma[nomagic] predicate hasUnknownTypeAt(FunctionPosition pos, TypePath path) { - exists(TupleLikeConstructor tc, TypeParameter tp | - tc = this.getTarget() and + exists(Declaration d, TypeParameter tp | + d = this.getTarget() and pos.isReturn() and - tp = tc.getReturnType(path) and - not tp = tc.getParameterType(_, _) and + tp = d.getDeclaredType(pos, path) and + not exists(FunctionPosition pos2 | not pos2.isReturn() and tp = d.getDeclaredType(pos2, _)) and // check that no explicit type arguments have been supplied for `tp` not exists(TypeArgumentPosition tapos | exists(this.getTypeArgument(tapos, _)) and @@ -3172,25 +2984,93 @@ private module TupleLikeConstructionMatchingInput implements MatchingInputSig { ) } } + + private class NonAssocCallAccess extends Access, NonAssocCallExpr, + ContextTyping::ContextTypedCallCand + { + NonAssocCallAccess() { + this instanceof CallExprImpl::TupleStructExpr or + this instanceof CallExprImpl::TupleVariantExpr + } + + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = NonAssocCallExpr.super.getTypeArgument(apos, path) + } + + override AstNode getNodeAt(AccessPosition apos) { + result = NonAssocCallExpr.super.getNodeAt(apos) + } + + override Type getInferredType(FunctionPosition pos, TypePath path) { + result = NonAssocCallExpr.super.getInferredType(pos, path) + } + + override Declaration getTarget() { result = this.resolveCallTargetViaPathResolution() } + } + + abstract private class StructAccess extends Access instanceof PathAstNode { + pragma[nomagic] + override Type getInferredType(AccessPosition apos, TypePath path) { + result = inferType(this.getNodeAt(apos), path) + } + + pragma[nomagic] + override Declaration getTarget() { result = resolvePath(super.getPath()) } + + pragma[nomagic] + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + // Handle constructions that use `Self {...}` syntax + exists(TypeMention tm, TypePath path0 | + tm = super.getPath() and + result = tm.getTypeAt(path0) and + path0.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) + ) + } + } + + private class StructExprAccess extends StructAccess, StructExpr { + override Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { + result = super.getTypeArgument(apos, path) + or + exists(TypePath suffix | + suffix.isCons(TTypeParamTypeParameter(apos.asTypeParam()), path) and + result = CertainTypeInference::inferCertainType(this, suffix) + ) + } + + override AstNode getNodeAt(AccessPosition apos) { + result = + this.getFieldExpr(this.getNthStructField(apos.asPosition()).getName().getText()).getExpr() + or + result = this and apos.isReturn() + } + } + + /** A potential nullary struct/variant construction such as `None`. */ + private class PathExprAccess extends StructAccess, PathExpr { + PathExprAccess() { not exists(CallExpr ce | this = ce.getFunction()) } + + override AstNode getNodeAt(AccessPosition apos) { result = this and apos.isReturn() } + } } -private module TupleLikeConstructionMatching = Matching; +private module ConstructorMatching = Matching; pragma[nomagic] -private Type inferTupleLikeConstructionTypePreCheck( +private Type inferConstructorTypePreCheck( AstNode n, FunctionPosition pos, boolean hasReceiver, TypePath path ) { hasReceiver = false and - exists(TupleLikeConstructionMatchingInput::Access a | n = a.getNodeAt(pos) | - result = TupleLikeConstructionMatching::inferAccessType(a, pos, path) + exists(ConstructorMatchingInput::Access a | n = a.getNodeAt(pos) | + result = ConstructorMatching::inferAccessType(a, pos, path) or a.hasUnknownTypeAt(pos, path) and result = TUnknownType() ) } -private predicate inferTupleLikeConstructionType = - ContextTyping::CheckContextTyping::check/2; +private predicate inferConstructorType = + ContextTyping::CheckContextTyping::check/2; /** * A matching configuration for resolving types of operations like `a + b`. @@ -3676,71 +3556,27 @@ private Type inferDereferencedExprPtrType(AstNode n, TypePath path) { } /** - * A matching configuration for resolving types of struct patterns - * like `let Foo { bar } = ...`. + * A matching configuration for resolving types of constructor patterns like + * `let Foo { bar } = ...` or `let Some(x) = ...`. */ -private module StructPatMatchingInput implements MatchingInputSig { - class DeclarationPosition = StructExprMatchingInput::DeclarationPosition; +private module ConstructorPatMatchingInput implements MatchingInputSig { + import FunctionPositionMatchingInput - class Declaration = StructExprMatchingInput::Declaration; + class Declaration = ConstructorMatchingInput::Declaration; - class AccessPosition = DeclarationPosition; + class Access extends Pat instanceof PathAstNode { + Access() { this instanceof TupleStructPat or this instanceof StructPat } - class Access extends StructPat { Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } AstNode getNodeAt(AccessPosition apos) { - result = this.getPatField(apos.asFieldPos()).getPat() + this = + any(StructPat sp | + result = + sp.getPatField(sp.getNthStructField(apos.asPosition()).getName().getText()).getPat() + ) or - result = this and - apos.isStructPos() - } - - Type getInferredType(AccessPosition apos, TypePath path) { - result = inferType(this.getNodeAt(apos), path) - or - // The struct/enum type is supplied explicitly as a type qualifier, e.g. - // `let Foo::Variant { ... } = ...`. - apos.isStructPos() and - result = this.getPath().(TypeMention).getTypeAt(path) - } - - Declaration getTarget() { result = resolvePath(this.getPath()) } - } - - predicate accessDeclarationPositionMatch(AccessPosition apos, DeclarationPosition dpos) { - apos = dpos - } -} - -private module StructPatMatching = Matching; - -/** - * Gets the type of `n` at `path`, where `n` is either a struct pattern or - * a field pattern of a struct pattern. - */ -pragma[nomagic] -private Type inferStructPatType(AstNode n, TypePath path) { - exists(StructPatMatchingInput::Access a, StructPatMatchingInput::AccessPosition apos | - n = a.getNodeAt(apos) and - result = StructPatMatching::inferAccessType(a, apos, path) - ) -} - -/** - * A matching configuration for resolving types of tuple struct patterns - * like `let Some(x) = ...`. - */ -private module TupleStructPatMatchingInput implements MatchingInputSig { - import FunctionPositionMatchingInput - - class Declaration = TupleLikeConstructor; - - class Access extends TupleStructPat { - Type getTypeArgument(TypeArgumentPosition apos, TypePath path) { none() } - - AstNode getNodeAt(AccessPosition apos) { - result = this.getField(apos.asPosition()) + result = this.(TupleStructPat).getField(apos.asPosition()) or result = this and apos.isReturn() @@ -3750,26 +3586,27 @@ private module TupleStructPatMatchingInput implements MatchingInputSig { result = inferType(this.getNodeAt(apos), path) or // The struct/enum type is supplied explicitly as a type qualifier, e.g. + // `let Foo::::Variant { ... } = ...` or // `let Option::::Some(x) = ...`. - apos.isTypeQualifier() and - result = this.getPath().(TypeMention).getTypeAt(path) + apos.isReturn() and + result = super.getPath().(TypeMention).getTypeAt(path) } - Declaration getTarget() { result = resolvePath(this.getPath()) } + Declaration getTarget() { result = resolvePath(super.getPath()) } } } -private module TupleStructPatMatching = Matching; +private module ConstructorPatMatching = Matching; /** - * Gets the type of `n` at `path`, where `n` is either a tuple struct pattern or - * a positional pattern of a tuple struct pattern. + * Gets the type of `n` at `path`, where `n` is a pattern for a constructor, + * either a struct pattern or a tuple-struct pattern. */ pragma[nomagic] -private Type inferTupleStructPatType(AstNode n, TypePath path) { - exists(TupleStructPatMatchingInput::Access a, TupleStructPatMatchingInput::AccessPosition apos | +private Type inferConstructorPatType(AstNode n, TypePath path) { + exists(ConstructorPatMatchingInput::Access a, FunctionPosition apos | n = a.getNodeAt(apos) and - result = TupleStructPatMatching::inferAccessType(a, apos, path) + result = ConstructorPatMatching::inferAccessType(a, apos, path) ) } @@ -4080,11 +3917,9 @@ private module Cached { or result = inferTypeEquality(n, path) or - result = inferStructExprType(n, path) - or result = inferFunctionCallType(n, path) or - result = inferTupleLikeConstructionType(n, path) + result = inferConstructorType(n, path) or result = inferOperationType(n, path) or @@ -4106,9 +3941,7 @@ private module Cached { or result = inferClosureExprType(n, path) or - result = inferStructPatType(n, path) - or - result = inferTupleStructPatType(n, path) + result = inferConstructorPatType(n, path) ) } } @@ -4157,9 +3990,9 @@ private module Debug { t = inferFunctionCallType(n, path) } - predicate debugInferTupleLikeConstructionType(AstNode n, TypePath path, Type t) { + predicate debugInferConstructorType(AstNode n, TypePath path, Type t) { n = getRelevantLocatable() and - t = inferTupleLikeConstructionType(n, path) + t = inferConstructorType(n, path) } predicate debugTypeMention(TypeMention tm, TypePath path, Type type) {