Skip to content

Commit d996956

Browse files
committed
Rust: Improve handling of implicit derefs/borrows in data flow
1 parent a4c2246 commit d996956

34 files changed

+810
-938
lines changed

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowConsistency.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ private module Input implements InputSig<Location, RustDataFlow> {
2121
or
2222
// We allow flow into post-update node for receiver expressions (from the
2323
// synthetic post receiever node).
24-
n.(Node::PostUpdateNode).getPreUpdateNode().asExpr() = any(Node::ReceiverNode r).getReceiver()
24+
n.(Node::PostUpdateNode).getPreUpdateNode().asExpr() = any(Node::DerefBorrowNode r).getNode()
2525
or
2626
n.(Node::PostUpdateNode).getPreUpdateNode().asExpr() = getPostUpdateReverseStep(_, _)
2727
or

rust/ql/lib/codeql/rust/dataflow/internal/DataFlowImpl.qll

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ private import codeql.rust.elements.Call
1212
private import SsaImpl as SsaImpl
1313
private import codeql.rust.controlflow.internal.Scope as Scope
1414
private import codeql.rust.internal.PathResolution
15-
private import codeql.rust.internal.TypeInference as TypeInference
1615
private import codeql.rust.controlflow.ControlFlowGraph
1716
private import codeql.rust.dataflow.Ssa
1817
private import codeql.rust.dataflow.FlowSummary
@@ -135,24 +134,17 @@ final class ArgumentPosition extends ParameterPosition {
135134
Expr getArgument(Call call) {
136135
result = call.getPositionalArgument(this.getPosition())
137136
or
138-
result = call.getReceiver() and this.isSelf()
137+
this.isSelf() and result = call.getReceiver()
139138
}
140139
}
141140

142141
/**
143142
* Holds if `arg` is an argument of `call` at the position `pos`.
144-
*
145-
* Note that this does not hold for the receiever expression of a method call
146-
* as the synthetic `ReceiverNode` is the argument for the `self` parameter.
147143
*/
148-
predicate isArgumentForCall(Expr arg, Call call, ParameterPosition pos) {
144+
predicate isArgumentForCall(Expr arg, Call call, ArgumentPosition pos) {
149145
// TODO: Handle index expressions as calls in data flow.
150146
not call instanceof IndexExpr and
151-
(
152-
call.getPositionalArgument(pos.getPosition()) = arg
153-
or
154-
call.getReceiver() = arg and pos.isSelf() and not call.receiverImplicitlyBorrowed()
155-
)
147+
arg = pos.getArgument(call)
156148
}
157149

158150
/** Provides logic related to SSA. */
@@ -283,14 +275,6 @@ module LocalFlow {
283275
or
284276
nodeFrom.asPat().(OrPat).getAPat() = nodeTo.asPat()
285277
or
286-
// Simple value step from receiver expression to receiver node, in case
287-
// there is no implicit deref or borrow operation.
288-
nodeFrom.asExpr() = nodeTo.(ReceiverNode).getReceiver()
289-
or
290-
// The dual step of the above, for the post-update nodes.
291-
nodeFrom.(PostUpdateNode).getPreUpdateNode().(ReceiverNode).getReceiver() =
292-
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr()
293-
or
294278
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
295279
getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr(), true)
296280
}
@@ -380,7 +364,7 @@ module RustDataFlow implements InputSig<Location> {
380364
node.(FlowSummaryNode).getSummaryNode().isHidden() or
381365
node instanceof CaptureNode or
382366
node instanceof ClosureParameterNode or
383-
node instanceof ReceiverNode or
367+
node instanceof DerefBorrowNode or
384368
node.asExpr() instanceof ParenExpr or
385369
nodeIsHidden(node.(PostUpdateNode).getPreUpdateNode())
386370
}
@@ -520,16 +504,16 @@ module RustDataFlow implements InputSig<Location> {
520504
}
521505

522506
pragma[nomagic]
523-
private predicate implicitDerefToReceiver(Node node1, ReceiverNode node2, ReferenceContent c) {
524-
TypeInference::receiverHasImplicitDeref(node1.asExpr()) and
525-
node1.asExpr() = node2.getReceiver() and
507+
private predicate implicitDeref(Node node1, DerefBorrowNode node2, ReferenceContent c) {
508+
not node2.isBorrow() and
509+
node1.asExpr() = node2.getNode() and
526510
exists(c)
527511
}
528512

529513
pragma[nomagic]
530-
private predicate implicitBorrowToReceiver(Node node1, ReceiverNode node2, ReferenceContent c) {
531-
TypeInference::receiverHasImplicitBorrow(node1.asExpr()) and
532-
node1.asExpr() = node2.getReceiver() and
514+
private predicate implicitBorrow(Node node1, DerefBorrowNode node2, ReferenceContent c) {
515+
node2.isBorrow() and
516+
node1.asExpr() = node2.getNode() and
533517
exists(c)
534518
}
535519

@@ -539,6 +523,15 @@ module RustDataFlow implements InputSig<Location> {
539523
exists(c)
540524
}
541525

526+
private Node getFieldExprContainerNode(FieldExpr fe) {
527+
exists(Expr container | container = fe.getContainer() |
528+
not any(DerefBorrowNode n).getNode() = container and
529+
result.asExpr() = container
530+
or
531+
result.(DerefBorrowNode).getNode() = container
532+
)
533+
}
534+
542535
pragma[nomagic]
543536
additional predicate readContentStep(Node node1, Content c, Node node2) {
544537
exists(TupleStructPat pat, int pos |
@@ -563,9 +556,9 @@ module RustDataFlow implements InputSig<Location> {
563556
node1.asPat().(RefPat).getPat() = node2.asPat()
564557
or
565558
exists(FieldExpr access |
566-
node1.asExpr() = access.getContainer() and
567559
node2.asExpr() = access and
568-
access = c.(FieldContent).getAnAccess()
560+
access = c.(FieldContent).getAnAccess() and
561+
node1 = getFieldExprContainerNode(access)
569562
)
570563
or
571564
exists(IndexExpr arr |
@@ -616,12 +609,10 @@ module RustDataFlow implements InputSig<Location> {
616609
referenceExprToExpr(node2.(PostUpdateNode).getPreUpdateNode(),
617610
node1.(PostUpdateNode).getPreUpdateNode(), c)
618611
or
619-
// Step from receiver expression to receiver node, in case of an implicit
620-
// dereference.
621-
implicitDerefToReceiver(node1, node2, c)
612+
implicitDeref(node1, node2, c)
622613
or
623614
// A read step dual to the store step for implicit borrows.
624-
implicitBorrowToReceiver(node2.(PostUpdateNode).getPreUpdateNode(),
615+
implicitBorrow(node2.(PostUpdateNode).getPreUpdateNode(),
625616
node1.(PostUpdateNode).getPreUpdateNode(), c)
626617
or
627618
VariableCapture::readStep(node1, c, node2)
@@ -657,7 +648,7 @@ module RustDataFlow implements InputSig<Location> {
657648
exists(AssignmentExpr assignment, FieldExpr access |
658649
assignment.getLhs() = access and
659650
node1.asExpr() = assignment.getRhs() and
660-
node2.asExpr() = access.getContainer() and
651+
node2 = getFieldExprContainerNode(access) and
661652
access = c.getAnAccess()
662653
)
663654
}
@@ -729,9 +720,11 @@ module RustDataFlow implements InputSig<Location> {
729720
or
730721
VariableCapture::storeStep(node1, c, node2)
731722
or
732-
// Step from receiver expression to receiver node, in case of an implicit
733-
// borrow.
734-
implicitBorrowToReceiver(node1, node2, c)
723+
implicitBorrow(node1, node2, c)
724+
or
725+
// A store step dual to the read step for implicit dereferences.
726+
implicitDeref(node2.(PostUpdateNode).getPreUpdateNode(),
727+
node1.(PostUpdateNode).getPreUpdateNode(), c)
735728
}
736729

737730
/**

rust/ql/lib/codeql/rust/dataflow/internal/Node.qll

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private import codeql.rust.controlflow.ControlFlowGraph
1414
private import codeql.rust.controlflow.CfgNodes
1515
private import codeql.rust.dataflow.Ssa
1616
private import codeql.rust.dataflow.FlowSummary
17+
private import codeql.rust.internal.TypeInference as TypeInference
1718
private import Node as Node
1819
private import DataFlowImpl
1920
private import FlowSummaryImpl as FlowSummaryImpl
@@ -226,35 +227,53 @@ final class ExprArgumentNode extends ArgumentNode, ExprNode {
226227
private Call call_;
227228
private RustDataFlow::ArgumentPosition pos_;
228229

229-
ExprArgumentNode() { isArgumentForCall(n, call_, pos_) }
230+
ExprArgumentNode() {
231+
isArgumentForCall(n, call_, pos_) and
232+
not TypeInference::implicitDeref(n) and
233+
not TypeInference::implicitBorrow(n)
234+
}
230235

231236
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
232237
call.asCall() = call_ and pos = pos_
233238
}
234239
}
235240

236241
/**
237-
* The receiver of a method call _after_ any implicit borrow or dereferencing
238-
* has taken place.
242+
* A node that represents the value of an expression after implicit dereference
243+
* or borrow.
239244
*/
240-
final class ReceiverNode extends ArgumentNode, TReceiverNode {
241-
private Call n;
245+
class DerefBorrowNode extends Node, TDerefBorrowNode {
246+
AstNode n;
247+
boolean isBorrow;
242248

243-
ReceiverNode() { this = TReceiverNode(n, false) }
249+
DerefBorrowNode() { this = TDerefBorrowNode(n, isBorrow, false) }
244250

245-
Expr getReceiver() { result = n.getReceiver() }
251+
AstNode getNode() { result = n }
246252

247-
MethodCallExpr getMethodCall() { result = n }
253+
predicate isBorrow() { isBorrow = true }
248254

249-
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
250-
call.asCall() = n and pos = TSelfParameterPosition()
255+
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
256+
257+
override Location getLocation() { result = n.getLocation() }
258+
259+
override string toString() {
260+
if isBorrow = true then result = n + " [borrowed]" else result = n + " [dereferenced]"
251261
}
262+
}
252263

253-
override CfgScope getCfgScope() { result = n.getEnclosingCfgScope() }
264+
/**
265+
* The argument of a call _after_ any implicit borrow or dereferencing
266+
* has taken place.
267+
*/
268+
final class DerefBorrowArgNode extends DerefBorrowNode, ArgumentNode {
269+
private DataFlowCall call_;
270+
private RustDataFlow::ArgumentPosition pos_;
254271

255-
override Location getLocation() { result = this.getReceiver().getLocation() }
272+
DerefBorrowArgNode() { isArgumentForCall(n, call_.asCall(), pos_) }
256273

257-
override string toString() { result = "receiver for " + this.getReceiver() }
274+
override predicate isArgumentOf(DataFlowCall call, RustDataFlow::ArgumentPosition pos) {
275+
call = call_ and pos = pos_
276+
}
258277
}
259278

260279
final class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
@@ -402,16 +421,17 @@ final class ExprPostUpdateNode extends PostUpdateNode, TExprPostUpdateNode {
402421
override Location getLocation() { result = e.getLocation() }
403422
}
404423

405-
final class ReceiverPostUpdateNode extends PostUpdateNode, TReceiverNode {
406-
private Call call;
424+
final class DerefBorrowPostUpdateNode extends PostUpdateNode, TDerefBorrowNode {
425+
private Expr arg;
426+
private boolean isBorrow;
407427

408-
ReceiverPostUpdateNode() { this = TReceiverNode(call, true) }
428+
DerefBorrowPostUpdateNode() { this = TDerefBorrowNode(arg, isBorrow, true) }
409429

410-
override Node getPreUpdateNode() { result = TReceiverNode(call, false) }
430+
override DerefBorrowNode getPreUpdateNode() { result = TDerefBorrowNode(arg, isBorrow, false) }
411431

412-
override CfgScope getCfgScope() { result = call.getEnclosingCfgScope() }
432+
override CfgScope getCfgScope() { result = arg.getEnclosingCfgScope() }
413433

414-
override Location getLocation() { result = call.getReceiver().getLocation() }
434+
override Location getLocation() { result = arg.getLocation() }
415435
}
416436

417437
final class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
@@ -467,18 +487,17 @@ newtype TNode =
467487
any(IndexExpr i).getBase(), //
468488
any(FieldExpr access).getContainer(), //
469489
any(TryExpr try).getExpr(), //
470-
any(PrefixExpr pe | pe.getOperatorName() = "*").getExpr(), //
471490
any(AwaitExpr a).getExpr(), //
472-
any(MethodCallExpr mc).getReceiver(), //
473491
getPostUpdateReverseStep(any(PostUpdateNode n).getPreUpdateNode().asExpr(), _)
474492
]
475493
)
476494
} or
477-
TReceiverNode(Call call, Boolean isPost) {
478-
call.hasEnclosingCfgScope() and
479-
call.receiverImplicitlyBorrowed() and
480-
// TODO: Handle index expressions as calls in data flow.
481-
not call instanceof IndexExpr
495+
TDerefBorrowNode(AstNode n, boolean borrow, Boolean isPost) {
496+
TypeInference::implicitDeref(n) and
497+
borrow = false
498+
or
499+
TypeInference::implicitBorrow(n) and
500+
borrow = true
482501
} or
483502
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node) or
484503
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {

rust/ql/lib/codeql/rust/elements/internal/FieldExprImpl.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ module Impl {
2323
*/
2424
class FieldExpr extends Generated::FieldExpr {
2525
/** Gets the record field that this access references, if any. */
26-
StructField getStructField() { result = TypeInference::resolveStructFieldExpr(this) }
26+
StructField getStructField() { result = TypeInference::resolveStructFieldExpr(this, _) }
2727

2828
/** Gets the tuple field that this access references, if any. */
29-
TupleField getTupleField() { result = TypeInference::resolveTupleFieldExpr(this) }
29+
TupleField getTupleField() { result = TypeInference::resolveTupleFieldExpr(this, _) }
3030

3131
override string toStringImpl() {
3232
exists(string abbr, string name |

rust/ql/lib/codeql/rust/frameworks/futures.model.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ extensions:
77
- ["<futures_util::io::buf_reader::BufReader>::new", "Argument[0]", "ReturnValue", "taint", "manual"]
88
- ["<_ as futures_util::io::AsyncReadExt>::read", "Argument[self]", "Argument[0].Reference", "taint", "manual"]
99
- ["<_ as futures_util::io::AsyncReadExt>::read", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"]
10-
- ["<_ as futures_util::io::AsyncReadExt>::read_to_end", "Argument[self]", "Argument[0].Reference", "taint", "manual"]
1110
- ["<_ as futures_util::io::AsyncReadExt>::read_to_end", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"]
12-
- ["<_ as futures_util::io::AsyncBufReadExt>::read_line", "Argument[self]", "Argument[0].Reference", "taint", "manual"]
1311
- ["<_ as futures_util::io::AsyncBufReadExt>::read_line", "Argument[self].Reference", "Argument[0].Reference", "taint", "manual"]
14-
- ["<_ as futures_util::io::AsyncBufReadExt>::read_until", "Argument[self]", "Argument[1].Reference", "taint", "manual"]
1512
- ["<_ as futures_util::io::AsyncBufReadExt>::read_until", "Argument[self].Reference", "Argument[1].Reference", "taint", "manual"]
16-
- ["<_ as futures_util::io::AsyncBufReadExt>::fill_buf", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
13+
- ["<_ as futures_util::io::AsyncBufReadExt>::fill_buf", "Argument[self].Reference", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
1714
- ["<_ as futures_util::io::AsyncBufReadExt>::lines", "Argument[self]", "ReturnValue", "taint", "manual"]
1815
- ["<_ as futures_io::if_std::AsyncBufRead>::poll_fill_buf", "Argument[self].Reference", "ReturnValue.Field[core::task::poll::Poll::Ready(0)].Field[core::result::Result::Ok(0)]", "taint", "manual"]
1916
- ["<_ as futures_io::if_std::AsyncRead>::poll_read", "Argument[self].Reference", "Argument[1].Reference", "taint", "manual"]

rust/ql/lib/codeql/rust/frameworks/reqwest.model.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ extensions:
1818
- ["<reqwest::response::Response>::text", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
1919
- ["<reqwest::response::Response>::text_with_charset", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
2020
- ["<reqwest::response::Response>::bytes", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
21-
- ["<reqwest::response::Response>::chunk", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)].Field[core::option::Option::Some(0)]", "taint", "manual"]
21+
- ["<reqwest::response::Response>::chunk", "Argument[self].Reference", "ReturnValue.Future.Field[core::result::Result::Ok(0)].Field[core::option::Option::Some(0)]", "taint", "manual"]
2222
- ["<reqwest::blocking::response::Response>::text", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
2323
- ["<reqwest::blocking::response::Response>::text_with_charset", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
2424
- ["<reqwest::blocking::response::Response>::bytes", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
2525
- ["<reqwest::async_impl::response::Response>::text", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
2626
- ["<reqwest::async_impl::response::Response>::bytes", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)]", "taint", "manual"]
27-
- ["<reqwest::async_impl::response::Response>::chunk", "Argument[self]", "ReturnValue.Future.Field[core::result::Result::Ok(0)].Field[core::option::Option::Some(0)]", "taint", "manual"]
27+
- ["<reqwest::async_impl::response::Response>::chunk", "Argument[self].Reference", "ReturnValue.Future.Field[core::result::Result::Ok(0)].Field[core::option::Option::Some(0)]", "taint", "manual"]

rust/ql/lib/codeql/rust/frameworks/stdlib/alloc.model.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,21 @@ extensions:
3333
- ["<core::alloc::layout::Layout>::from_size_align", "Argument[0]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
3434
- ["<core::alloc::layout::Layout>::from_size_align_unchecked", "Argument[0]", "ReturnValue", "taint", "manual"]
3535
- ["<core::alloc::layout::Layout>::array", "Argument[0]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
36-
- ["<core::alloc::layout::Layout>::repeat", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)].Field[0]", "taint", "manual"]
36+
- ["<core::alloc::layout::Layout>::repeat", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)].Field[0]", "taint", "manual"]
3737
- ["<core::alloc::layout::Layout>::repeat", "Argument[0]", "ReturnValue.Field[core::result::Result::Ok(0)].Field[0]", "taint", "manual"]
38-
- ["<core::alloc::layout::Layout>::repeat_packed", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
38+
- ["<core::alloc::layout::Layout>::repeat_packed", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
3939
- ["<core::alloc::layout::Layout>::repeat_packed", "Argument[0]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
40-
- ["<core::alloc::layout::Layout>::extend", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)].Field[0]", "taint", "manual"]
40+
- ["<core::alloc::layout::Layout>::extend", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)].Field[0]", "taint", "manual"]
4141
- ["<core::alloc::layout::Layout>::extend", "Argument[0]", "ReturnValue.Field[core::result::Result::Ok(0)].Field[0]", "taint", "manual"]
42-
- ["<core::alloc::layout::Layout>::extend_packed", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
42+
- ["<core::alloc::layout::Layout>::extend_packed", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
4343
- ["<core::alloc::layout::Layout>::extend_packed", "Argument[0]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
44-
- ["<core::alloc::layout::Layout>::align_to", "Argument[self]", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
45-
- ["<core::alloc::layout::Layout>::align_to", "Argument[self].Element", "ReturnValue.Field[0,1,2].Reference.Element", "taint", "manual"]
46-
- ["<core::alloc::layout::Layout>::pad_to_align", "Argument[self]", "ReturnValue", "taint", "manual"]
47-
- ["<core::alloc::layout::Layout>::size", "Argument[self]", "ReturnValue", "taint", "manual"]
44+
- ["<core::alloc::layout::Layout>::align_to", "Argument[self].Reference", "ReturnValue.Field[core::result::Result::Ok(0)]", "taint", "manual"]
45+
- ["<core::alloc::layout::Layout>::align_to", "Argument[self].Reference.Element", "ReturnValue.Field[0,1,2].Reference.Element", "taint", "manual"]
46+
- ["<core::alloc::layout::Layout>::pad_to_align", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
47+
- ["<core::alloc::layout::Layout>::size", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
4848
# String
4949
- ["<alloc::string::String>::as_str", "Argument[self]", "ReturnValue", "value", "manual"]
5050
- ["<alloc::string::String>::as_bytes", "Argument[self]", "ReturnValue", "value", "manual"]
51-
- ["<_ as alloc::string::ToString>::to_string", "Argument[self]", "ReturnValue", "taint", "manual"]
51+
- ["<_ as alloc::string::ToString>::to_string", "Argument[self].Reference", "ReturnValue", "taint", "manual"]
5252
# Vec
5353
- ["alloc::vec::from_elem", "Argument[0]", "ReturnValue.Element", "value", "manual"]

0 commit comments

Comments
 (0)