Skip to content

Commit d89678f

Browse files
committed
Rust: Data flow through tuple and struct fields
1 parent 9ec9d79 commit d89678f

File tree

4 files changed

+220
-29
lines changed

4 files changed

+220
-29
lines changed

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

Lines changed: 131 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,10 @@ module Node {
252252
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
253253
* to the value before the update.
254254
*/
255-
final class PostUpdateNode extends Node, TArgumentPostUpdateNode {
255+
final class PostUpdateNode extends Node, TExprPostUpdateNode {
256256
private ExprCfgNode n;
257257

258-
PostUpdateNode() { this = TArgumentPostUpdateNode(n) }
258+
PostUpdateNode() { this = TExprPostUpdateNode(n) }
259259

260260
/** Gets the node before the state update. */
261261
Node getPreUpdateNode() { result = TExprNode(n) }
@@ -449,6 +449,49 @@ private class VariantFieldContent extends VariantContent, TVariantFieldContent {
449449
}
450450
}
451451

452+
/** A canonical path pointing to a struct. */
453+
private class StructCanonicalPath extends MkStructCanonicalPath {
454+
CrateOriginOption crate;
455+
string path;
456+
457+
StructCanonicalPath() { this = MkStructCanonicalPath(crate, path) }
458+
459+
/** Gets the underlying struct. */
460+
Struct getStruct() { hasExtendedCanonicalPath(result, crate, path) }
461+
462+
string toString() { result = this.getStruct().getName().getText() }
463+
464+
Location getLocation() { result = this.getStruct().getLocation() }
465+
}
466+
467+
/** Content stored in a field on a struct. */
468+
private class StructFieldContent extends VariantContent, TStructFieldContent {
469+
private StructCanonicalPath s;
470+
private string field_;
471+
472+
StructFieldContent() { this = TStructFieldContent(s, field_) }
473+
474+
StructCanonicalPath getStructCanonicalPath(string field) { result = s and field = field_ }
475+
476+
override string toString() { result = s.toString() + "." + field_.toString() }
477+
}
478+
479+
/**
480+
* Content stored at a position in a tuple.
481+
*
482+
* NOTE: Unlike `struct`s and `enum`s tuples are structural and not nominal,
483+
* hence we don't store a canonical path for them.
484+
*/
485+
private class TuplePositionContent extends VariantContent, TTuplePositionContent {
486+
private int pos;
487+
488+
TuplePositionContent() { this = TTuplePositionContent(pos) }
489+
490+
int getPosition() { result = pos }
491+
492+
override string toString() { result = "tuple." + pos.toString() }
493+
}
494+
452495
/** A value that represents a set of `Content`s. */
453496
abstract class ContentSet extends TContentSet {
454497
/** Gets a textual representation of this element. */
@@ -608,6 +651,14 @@ module RustDataFlow implements InputSig<Location> {
608651
*/
609652
predicate jumpStep(Node node1, Node node2) { none() }
610653

654+
/** Holds if path `p` resolves to struct `s`. */
655+
private predicate pathResolveToStructCanonicalPath(Path p, StructCanonicalPath s) {
656+
exists(CrateOriginOption crate, string path |
657+
resolveExtendedCanonicalPath(p, crate, path) and
658+
s = MkStructCanonicalPath(crate, path)
659+
)
660+
}
661+
611662
/** Holds if path `p` resolves to variant `v`. */
612663
private predicate pathResolveToVariantCanonicalPath(Path p, VariantCanonicalPath v) {
613664
exists(CrateOriginOption crate, string path |
@@ -636,6 +687,12 @@ module RustDataFlow implements InputSig<Location> {
636687
pathResolveToVariantCanonicalPath(p.getPath(), v)
637688
}
638689

690+
/** Holds if `p` destructs an struct `s`. */
691+
pragma[nomagic]
692+
private predicate structDestruction(RecordPat p, StructCanonicalPath s) {
693+
pathResolveToStructCanonicalPath(p.getPath(), s)
694+
}
695+
639696
/**
640697
* Holds if data can flow from `node1` to `node2` via a read of `c`. Thus,
641698
* `node1` references an object with a content `c.getAReadContent()` whose
@@ -652,10 +709,24 @@ module RustDataFlow implements InputSig<Location> {
652709
or
653710
exists(RecordPatCfgNode pat, string field |
654711
pat = node1.asPat() and
655-
recordVariantDestruction(pat.getPat(),
656-
c.(VariantFieldContent).getVariantCanonicalPath(field)) and
712+
(
713+
// Pattern destructs a struct-like variant.
714+
recordVariantDestruction(pat.getPat(),
715+
c.(VariantFieldContent).getVariantCanonicalPath(field))
716+
or
717+
// Pattern destructs a struct.
718+
structDestruction(pat.getPat(), c.(StructFieldContent).getStructCanonicalPath(field))
719+
) and
657720
node2.asPat() = pat.getFieldPat(field)
658721
)
722+
or
723+
exists(FieldExprCfgNode access |
724+
// Read of a tuple entry
725+
access.getNameRef().getText().toInt() = c.(TuplePositionContent).getPosition() and
726+
// TODO: Handle read of a struct field.
727+
node1.asExpr() = access.getExpr() and
728+
node2.asExpr() = access
729+
)
659730
)
660731
}
661732

@@ -671,30 +742,44 @@ module RustDataFlow implements InputSig<Location> {
671742
pathResolveToVariantCanonicalPath(re.getPath(), v)
672743
}
673744

745+
/** Holds if `re` constructs a struct value of type `v`. */
746+
pragma[nomagic]
747+
private predicate structConstruction(RecordExpr re, StructCanonicalPath s) {
748+
pathResolveToStructCanonicalPath(re.getPath(), s)
749+
}
750+
674751
/**
675752
* Holds if data can flow from `node1` to `node2` via a store into `c`. Thus,
676753
* `node2` references an object with a content `c.getAStoreContent()` that
677754
* contains the value of `node1`.
678755
*/
679756
predicate storeStep(Node node1, ContentSet cs, Node node2) {
680757
exists(Content c | c = cs.(SingletonContentSet).getContent() |
681-
node2.asExpr() =
682-
any(CallExprCfgNode call, int pos |
683-
tupleVariantConstruction(call.getCallExpr(),
684-
c.(VariantPositionContent).getVariantCanonicalPath(pos)) and
685-
node1.asExpr() = call.getArgument(pos)
686-
|
687-
call
688-
)
758+
exists(CallExprCfgNode call, int pos |
759+
tupleVariantConstruction(call.getCallExpr(),
760+
c.(VariantPositionContent).getVariantCanonicalPath(pos)) and
761+
node1.asExpr() = call.getArgument(pos) and
762+
node2.asExpr() = call
763+
)
689764
or
690-
node2.asExpr() =
691-
any(RecordExprCfgNode re, string field |
765+
exists(RecordExprCfgNode re, string field |
766+
(
767+
// Expression is for a struct-like enum variant.
692768
recordVariantConstruction(re.getRecordExpr(),
693-
c.(VariantFieldContent).getVariantCanonicalPath(field)) and
694-
node1.asExpr() = re.getFieldExpr(field)
695-
|
696-
re
697-
)
769+
c.(VariantFieldContent).getVariantCanonicalPath(field))
770+
or
771+
// Expression is for a struct.
772+
structConstruction(re.getRecordExpr(),
773+
c.(StructFieldContent).getStructCanonicalPath(field))
774+
) and
775+
node1.asExpr() = re.getFieldExpr(field) and
776+
node2.asExpr() = re
777+
)
778+
or
779+
exists(TupleExprCfgNode tuple |
780+
node1.asExpr() = tuple.getField(c.(TuplePositionContent).getPosition()) and
781+
node2.asExpr() = tuple
782+
)
698783
)
699784
}
700785

@@ -703,7 +788,14 @@ module RustDataFlow implements InputSig<Location> {
703788
* any value stored inside `f` is cleared at the pre-update node associated with `x`
704789
* in `x.f = newValue`.
705790
*/
706-
predicate clearsContent(Node n, ContentSet c) { none() }
791+
predicate clearsContent(Node n, ContentSet c) {
792+
exists(AssignmentExprCfgNode assignment, FieldExprCfgNode access |
793+
assignment.getLhs() = access and
794+
n.asExpr() = access.getExpr() and
795+
access.getNameRef().getText().toInt() =
796+
c.(SingletonContentSet).getContent().(TuplePositionContent).getPosition()
797+
)
798+
}
707799

708800
/**
709801
* Holds if the value that is being tracked is expected to be stored inside content `c`
@@ -773,7 +865,9 @@ private module Cached {
773865
TExprNode(ExprCfgNode n) or
774866
TParameterNode(ParamBaseCfgNode p) or
775867
TPatNode(PatCfgNode p) or
776-
TArgumentPostUpdateNode(ExprCfgNode e) { isArgumentForCall(e, _, _) } or
868+
TExprPostUpdateNode(ExprCfgNode e) {
869+
isArgumentForCall(e, _, _) or e = any(FieldExprCfgNode access).getExpr()
870+
} or
777871
TSsaNode(SsaImpl::DataFlowIntegration::SsaNode node)
778872

779873
cached
@@ -811,6 +905,12 @@ private module Cached {
811905
name = ["Ok", "Err"]
812906
}
813907

908+
cached
909+
newtype TStructCanonicalPath =
910+
MkStructCanonicalPath(CrateOriginOption crate, string path) {
911+
exists(Struct s | hasExtendedCanonicalPath(s, crate, path))
912+
}
913+
814914
cached
815915
newtype TContent =
816916
TVariantPositionContent(VariantCanonicalPath v, int pos) {
@@ -826,6 +926,16 @@ private module Cached {
826926
} or
827927
TVariantFieldContent(VariantCanonicalPath v, string field) {
828928
field = v.getVariant().getFieldList().(RecordFieldList).getAField().getName().getText()
929+
} or
930+
TTuplePositionContent(int pos) {
931+
pos in [0 .. max([
932+
any(TuplePat pat).getNumberOfFields(),
933+
any(FieldExpr access).getNameRef().getText().toInt()
934+
]
935+
)]
936+
} or
937+
TStructFieldContent(StructCanonicalPath s, string field) {
938+
field = s.getStruct().getFieldList().(RecordFieldList).getAField().getName().getText()
829939
}
830940

831941
cached

rust/ql/test/library-tests/dataflow/local/DataFlowStep.expected

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,9 @@ localStep
103103
| main.rs:109:10:109:10 | a | main.rs:110:10:110:10 | a |
104104
| main.rs:110:10:110:10 | a | main.rs:111:5:111:5 | a |
105105
| main.rs:111:5:111:5 | a | main.rs:112:5:112:5 | a |
106-
| main.rs:111:11:111:11 | 2 | main.rs:111:5:111:7 | a.1 |
106+
| main.rs:111:11:111:20 | source(...) | main.rs:111:5:111:7 | a.0 |
107107
| main.rs:112:5:112:5 | a | main.rs:113:10:113:10 | a |
108-
| main.rs:112:11:112:20 | source(...) | main.rs:112:5:112:7 | a.0 |
108+
| main.rs:112:11:112:11 | 2 | main.rs:112:5:112:7 | a.1 |
109109
| main.rs:113:10:113:10 | a | main.rs:114:10:114:10 | a |
110110
| main.rs:118:9:118:9 | [SSA] a | main.rs:119:14:119:14 | a |
111111
| main.rs:118:9:118:9 | a | main.rs:118:9:118:9 | [SSA] a |
@@ -329,6 +329,31 @@ localStep
329329
| main.rs:304:22:304:22 | n | main.rs:304:22:304:22 | [SSA] n |
330330
| main.rs:304:29:304:35 | sink(...) | main.rs:302:5:305:5 | match s2 { ... } |
331331
storeStep
332+
| main.rs:94:14:94:22 | source(...) | tuple.0 | main.rs:94:13:94:26 | TupleExpr |
333+
| main.rs:94:25:94:25 | 2 | tuple.1 | main.rs:94:13:94:26 | TupleExpr |
334+
| main.rs:100:14:100:14 | 2 | tuple.0 | main.rs:100:13:100:30 | TupleExpr |
335+
| main.rs:100:17:100:26 | source(...) | tuple.1 | main.rs:100:13:100:30 | TupleExpr |
336+
| main.rs:100:29:100:29 | 2 | tuple.2 | main.rs:100:13:100:30 | TupleExpr |
337+
| main.rs:108:18:108:18 | 2 | tuple.0 | main.rs:108:17:108:31 | TupleExpr |
338+
| main.rs:108:21:108:30 | source(...) | tuple.1 | main.rs:108:17:108:31 | TupleExpr |
339+
| main.rs:118:14:118:14 | 3 | tuple.0 | main.rs:118:13:118:27 | TupleExpr |
340+
| main.rs:118:17:118:26 | source(...) | tuple.1 | main.rs:118:13:118:27 | TupleExpr |
341+
| main.rs:119:14:119:14 | a | tuple.0 | main.rs:119:13:119:18 | TupleExpr |
342+
| main.rs:119:17:119:17 | 3 | tuple.1 | main.rs:119:13:119:18 | TupleExpr |
343+
| main.rs:135:12:135:20 | source(...) | Point.x | main.rs:134:13:137:5 | Point {...} |
344+
| main.rs:136:12:136:12 | 2 | Point.y | main.rs:134:13:137:5 | Point {...} |
345+
| main.rs:144:12:144:20 | source(...) | Point.x | main.rs:143:17:146:5 | Point {...} |
346+
| main.rs:145:12:145:12 | 2 | Point.y | main.rs:143:17:146:5 | Point {...} |
347+
| main.rs:154:12:154:21 | source(...) | Point.x | main.rs:153:13:156:5 | Point {...} |
348+
| main.rs:155:12:155:12 | 2 | Point.y | main.rs:153:13:156:5 | Point {...} |
349+
| main.rs:169:16:172:9 | Point {...} | Point3D.plane | main.rs:168:13:174:5 | Point3D {...} |
350+
| main.rs:170:16:170:16 | 2 | Point.x | main.rs:169:16:172:9 | Point {...} |
351+
| main.rs:171:16:171:25 | source(...) | Point.y | main.rs:169:16:172:9 | Point {...} |
352+
| main.rs:173:12:173:12 | 4 | Point3D.z | main.rs:168:13:174:5 | Point3D {...} |
353+
| main.rs:182:16:185:9 | Point {...} | Point3D.plane | main.rs:181:13:187:5 | Point3D {...} |
354+
| main.rs:183:16:183:16 | 2 | Point.x | main.rs:182:16:185:9 | Point {...} |
355+
| main.rs:184:16:184:25 | source(...) | Point.y | main.rs:182:16:185:9 | Point {...} |
356+
| main.rs:186:12:186:12 | 4 | Point3D.z | main.rs:181:13:187:5 | Point3D {...} |
332357
| main.rs:214:19:214:28 | source(...) | Some | main.rs:214:14:214:29 | Some(...) |
333358
| main.rs:215:19:215:19 | 2 | Some | main.rs:215:14:215:20 | Some(...) |
334359
| main.rs:232:29:232:38 | source(...) | A | main.rs:232:14:232:39 | ...::A(...) |
@@ -338,6 +363,22 @@ storeStep
338363
| main.rs:312:27:312:27 | 0 | Some | main.rs:312:22:312:28 | Some(...) |
339364
readStep
340365
| main.rs:33:9:33:15 | TupleStructPat | Some | main.rs:33:14:33:14 | _ |
366+
| main.rs:95:10:95:10 | a | tuple.0 | main.rs:95:10:95:12 | a.0 |
367+
| main.rs:96:10:96:10 | a | tuple.1 | main.rs:96:10:96:12 | a.1 |
368+
| main.rs:109:10:109:10 | a | tuple.0 | main.rs:109:10:109:12 | a.0 |
369+
| main.rs:110:10:110:10 | a | tuple.1 | main.rs:110:10:110:12 | a.1 |
370+
| main.rs:111:5:111:5 | a | tuple.0 | main.rs:111:5:111:7 | a.0 |
371+
| main.rs:112:5:112:5 | a | tuple.1 | main.rs:112:5:112:7 | a.1 |
372+
| main.rs:113:10:113:10 | a | tuple.0 | main.rs:113:10:113:12 | a.0 |
373+
| main.rs:114:10:114:10 | a | tuple.1 | main.rs:114:10:114:12 | a.1 |
374+
| main.rs:120:10:120:10 | b | tuple.0 | main.rs:120:10:120:12 | b.0 |
375+
| main.rs:120:10:120:12 | b.0 | tuple.0 | main.rs:120:10:120:14 | ... .0 |
376+
| main.rs:121:10:121:10 | b | tuple.0 | main.rs:121:10:121:12 | b.0 |
377+
| main.rs:121:10:121:12 | b.0 | tuple.1 | main.rs:121:10:121:14 | ... .1 |
378+
| main.rs:122:10:122:10 | b | tuple.1 | main.rs:122:10:122:12 | b.1 |
379+
| main.rs:157:9:157:28 | Point {...} | Point.x | main.rs:157:20:157:20 | a |
380+
| main.rs:157:9:157:28 | Point {...} | Point.y | main.rs:157:26:157:26 | b |
381+
| main.rs:189:9:189:45 | Point3D {...} | Point3D.plane | main.rs:189:26:189:39 | Point {...} |
341382
| main.rs:217:9:217:15 | TupleStructPat | Some | main.rs:217:14:217:14 | n |
342383
| main.rs:221:9:221:15 | TupleStructPat | Some | main.rs:221:14:221:14 | n |
343384
| main.rs:235:9:235:25 | TupleStructPat | A | main.rs:235:24:235:24 | n |

rust/ql/test/library-tests/dataflow/local/inline-flow.expected

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ edges
55
| main.rs:31:13:31:21 | source(...) | main.rs:36:10:36:10 | b | provenance | |
66
| main.rs:45:15:45:23 | source(...) | main.rs:47:10:47:10 | b | provenance | |
77
| main.rs:53:9:53:17 | source(...) | main.rs:54:10:54:10 | i | provenance | |
8+
| main.rs:94:13:94:26 | TupleExpr [tuple.0] | main.rs:95:10:95:10 | a [tuple.0] | provenance | |
9+
| main.rs:94:14:94:22 | source(...) | main.rs:94:13:94:26 | TupleExpr [tuple.0] | provenance | |
10+
| main.rs:95:10:95:10 | a [tuple.0] | main.rs:95:10:95:12 | a.0 | provenance | |
11+
| main.rs:108:17:108:31 | TupleExpr [tuple.1] | main.rs:110:10:110:10 | a [tuple.1] | provenance | |
12+
| main.rs:108:21:108:30 | source(...) | main.rs:108:17:108:31 | TupleExpr [tuple.1] | provenance | |
13+
| main.rs:110:10:110:10 | a [tuple.1] | main.rs:110:10:110:12 | a.1 | provenance | |
14+
| main.rs:118:13:118:27 | TupleExpr [tuple.1] | main.rs:119:14:119:14 | a [tuple.1] | provenance | |
15+
| main.rs:118:17:118:26 | source(...) | main.rs:118:13:118:27 | TupleExpr [tuple.1] | provenance | |
16+
| main.rs:119:13:119:18 | TupleExpr [tuple.0, tuple.1] | main.rs:121:10:121:10 | b [tuple.0, tuple.1] | provenance | |
17+
| main.rs:119:14:119:14 | a [tuple.1] | main.rs:119:13:119:18 | TupleExpr [tuple.0, tuple.1] | provenance | |
18+
| main.rs:121:10:121:10 | b [tuple.0, tuple.1] | main.rs:121:10:121:12 | b.0 [tuple.1] | provenance | |
19+
| main.rs:121:10:121:12 | b.0 [tuple.1] | main.rs:121:10:121:14 | ... .1 | provenance | |
20+
| main.rs:153:13:156:5 | Point {...} [Point.x] | main.rs:157:9:157:28 | Point {...} [Point.x] | provenance | |
21+
| main.rs:154:12:154:21 | source(...) | main.rs:153:13:156:5 | Point {...} [Point.x] | provenance | |
22+
| main.rs:157:9:157:28 | Point {...} [Point.x] | main.rs:157:20:157:20 | a | provenance | |
23+
| main.rs:157:20:157:20 | a | main.rs:158:10:158:10 | a | provenance | |
824
| main.rs:214:14:214:29 | Some(...) [Some] | main.rs:217:9:217:15 | TupleStructPat [Some] | provenance | |
925
| main.rs:214:19:214:28 | source(...) | main.rs:214:14:214:29 | Some(...) [Some] | provenance | |
1026
| main.rs:217:9:217:15 | TupleStructPat [Some] | main.rs:217:14:217:14 | n | provenance | |
@@ -35,6 +51,26 @@ nodes
3551
| main.rs:47:10:47:10 | b | semmle.label | b |
3652
| main.rs:53:9:53:17 | source(...) | semmle.label | source(...) |
3753
| main.rs:54:10:54:10 | i | semmle.label | i |
54+
| main.rs:94:13:94:26 | TupleExpr [tuple.0] | semmle.label | TupleExpr [tuple.0] |
55+
| main.rs:94:14:94:22 | source(...) | semmle.label | source(...) |
56+
| main.rs:95:10:95:10 | a [tuple.0] | semmle.label | a [tuple.0] |
57+
| main.rs:95:10:95:12 | a.0 | semmle.label | a.0 |
58+
| main.rs:108:17:108:31 | TupleExpr [tuple.1] | semmle.label | TupleExpr [tuple.1] |
59+
| main.rs:108:21:108:30 | source(...) | semmle.label | source(...) |
60+
| main.rs:110:10:110:10 | a [tuple.1] | semmle.label | a [tuple.1] |
61+
| main.rs:110:10:110:12 | a.1 | semmle.label | a.1 |
62+
| main.rs:118:13:118:27 | TupleExpr [tuple.1] | semmle.label | TupleExpr [tuple.1] |
63+
| main.rs:118:17:118:26 | source(...) | semmle.label | source(...) |
64+
| main.rs:119:13:119:18 | TupleExpr [tuple.0, tuple.1] | semmle.label | TupleExpr [tuple.0, tuple.1] |
65+
| main.rs:119:14:119:14 | a [tuple.1] | semmle.label | a [tuple.1] |
66+
| main.rs:121:10:121:10 | b [tuple.0, tuple.1] | semmle.label | b [tuple.0, tuple.1] |
67+
| main.rs:121:10:121:12 | b.0 [tuple.1] | semmle.label | b.0 [tuple.1] |
68+
| main.rs:121:10:121:14 | ... .1 | semmle.label | ... .1 |
69+
| main.rs:153:13:156:5 | Point {...} [Point.x] | semmle.label | Point {...} [Point.x] |
70+
| main.rs:154:12:154:21 | source(...) | semmle.label | source(...) |
71+
| main.rs:157:9:157:28 | Point {...} [Point.x] | semmle.label | Point {...} [Point.x] |
72+
| main.rs:157:20:157:20 | a | semmle.label | a |
73+
| main.rs:158:10:158:10 | a | semmle.label | a |
3874
| main.rs:214:14:214:29 | Some(...) [Some] | semmle.label | Some(...) [Some] |
3975
| main.rs:214:19:214:28 | source(...) | semmle.label | source(...) |
4076
| main.rs:217:9:217:15 | TupleStructPat [Some] | semmle.label | TupleStructPat [Some] |
@@ -65,6 +101,10 @@ testFailures
65101
| main.rs:36:10:36:10 | b | main.rs:31:13:31:21 | source(...) | main.rs:36:10:36:10 | b | $@ | main.rs:31:13:31:21 | source(...) | source(...) |
66102
| main.rs:47:10:47:10 | b | main.rs:45:15:45:23 | source(...) | main.rs:47:10:47:10 | b | $@ | main.rs:45:15:45:23 | source(...) | source(...) |
67103
| main.rs:54:10:54:10 | i | main.rs:53:9:53:17 | source(...) | main.rs:54:10:54:10 | i | $@ | main.rs:53:9:53:17 | source(...) | source(...) |
104+
| main.rs:95:10:95:12 | a.0 | main.rs:94:14:94:22 | source(...) | main.rs:95:10:95:12 | a.0 | $@ | main.rs:94:14:94:22 | source(...) | source(...) |
105+
| main.rs:110:10:110:12 | a.1 | main.rs:108:21:108:30 | source(...) | main.rs:110:10:110:12 | a.1 | $@ | main.rs:108:21:108:30 | source(...) | source(...) |
106+
| main.rs:121:10:121:14 | ... .1 | main.rs:118:17:118:26 | source(...) | main.rs:121:10:121:14 | ... .1 | $@ | main.rs:118:17:118:26 | source(...) | source(...) |
107+
| main.rs:158:10:158:10 | a | main.rs:154:12:154:21 | source(...) | main.rs:158:10:158:10 | a | $@ | main.rs:154:12:154:21 | source(...) | source(...) |
68108
| main.rs:217:25:217:25 | n | main.rs:214:19:214:28 | source(...) | main.rs:217:25:217:25 | n | $@ | main.rs:214:19:214:28 | source(...) | source(...) |
69109
| main.rs:235:35:235:35 | n | main.rs:232:29:232:38 | source(...) | main.rs:235:35:235:35 | n | $@ | main.rs:232:29:232:38 | source(...) | source(...) |
70110
| main.rs:239:55:239:55 | n | main.rs:232:29:232:38 | source(...) | main.rs:239:55:239:55 | n | $@ | main.rs:232:29:232:38 | source(...) | source(...) |

0 commit comments

Comments
 (0)