diff --git a/source/interprocedural_analyses/taint/classModels.ml b/source/interprocedural_analyses/taint/classModels.ml index 1cc0d431c76..21022e8fbfc 100644 --- a/source/interprocedural_analyses/taint/classModels.ml +++ b/source/interprocedural_analyses/taint/classModels.ml @@ -21,41 +21,126 @@ open Analysis open Interprocedural open Domains +module FeatureSet = struct + type t = { + breadcrumbs: Features.BreadcrumbSet.t; + via_features: Features.ViaFeatureSet.t; + } + + let empty = + { breadcrumbs = Features.BreadcrumbSet.bottom; via_features = Features.ViaFeatureSet.bottom } + + + let from_taint taint = + { + breadcrumbs = BackwardState.Tree.joined_breadcrumbs taint; + via_features = + BackwardState.Tree.fold + Features.ViaFeatureSet.Self + ~f:Features.ViaFeatureSet.join + ~init:Features.ViaFeatureSet.bottom + taint; + } +end + let infer ~environment ~user_models = Log.info "Computing inferred models..."; let timer = Timer.start () in let global_resolution = TypeEnvironment.ReadOnly.global_resolution environment in - let add_tito ~input_root ~input_path ~output_path ~collapse_depth existing_state = + (* Translate ViaXXX features on attributes to ViaXX features on callables. *) + let translate_via_features_on_attribute attribute root tree = + let expand_via_feature via_feature taint = + match via_feature with + | Features.ViaFeature.ViaAttributeName { tag } -> + BackwardTaint.add_local_breadcrumb + (Features.Breadcrumb.ViaAttributeName { tag; value = attribute } + |> Features.BreadcrumbInterned.intern) + taint + | Features.ViaFeature.ViaValueOf { tag; _ } -> + BackwardTaint.transform + Features.ViaFeatureSet.Element + Add + ~f:(Features.ViaFeature.ViaValueOf { parameter = root; tag }) + taint + | Features.ViaFeature.ViaTypeOf { tag; _ } -> + BackwardTaint.transform + Features.ViaFeatureSet.Element + Add + ~f:(Features.ViaFeature.ViaTypeOf { parameter = root; tag }) + taint + in + let transform taint = + let via_features = BackwardTaint.via_features taint in + let taint = + BackwardTaint.transform + Features.ViaFeatureSet.Self + Map + ~f:(fun _ -> Features.ViaFeatureSet.bottom) + taint + in + Features.ViaFeatureSet.fold + Features.ViaFeatureSet.Element + ~f:expand_via_feature + ~init:taint + via_features + in + BackwardState.Tree.transform BackwardTaint.Self Map ~f:transform tree + in + let get_attribute_model class_name attribute = + Reference.create ~prefix:(Reference.create class_name) attribute + |> Target.create_object + |> Registry.get user_models + in + let get_attribute_tito_features class_name attribute root = + match get_attribute_model class_name attribute with + | Some { Model.backward = { taint_in_taint_out; _ }; _ } -> + BackwardState.read ~root:GlobalModel.global_root ~path:[] taint_in_taint_out + |> translate_via_features_on_attribute attribute root + |> FeatureSet.from_taint + | None -> FeatureSet.empty + in + let add_tito + ~input_root + ~input_path + ~output_path + ~collapse_depth + ~breadcrumbs + ~via_features + existing_state + = let leaf = BackwardTaint.singleton CallInfo.Tito Sinks.LocalReturn Frame.initial |> BackwardState.Tree.create_leaf |> BackwardState.Tree.transform Features.ReturnAccessPathTree.Self Map ~f:(fun _ -> Features.ReturnAccessPathTree.create [Part (Features.ReturnAccessPathTree.Path, (output_path, collapse_depth))]) + |> BackwardState.Tree.add_local_breadcrumbs breadcrumbs + |> BackwardState.Tree.add_via_features via_features in BackwardState.assign ~root:input_root ~path:input_path leaf existing_state in - let add_parameter_tito ~positional position existing_state attribute = + let add_parameter_to_attribute_tito ~class_name ~positional position existing_state attribute = let input_root = if positional then AccessPath.Root.PositionalParameter { position; name = attribute; positional_only = false } else AccessPath.Root.NamedParameter { name = attribute } in + let { FeatureSet.breadcrumbs; via_features } = + get_attribute_tito_features class_name attribute input_root + in add_tito ~input_root ~input_path:[] ~output_path:[Abstract.TreeDomain.Label.create_name_index attribute] ~collapse_depth:Features.CollapseDepth.no_collapse + ~breadcrumbs + ~via_features existing_state in - let add_sink_from_attribute_model ~positional class_name position existing_state attribute = - let qualified_attribute = - Target.create_object (Reference.create ~prefix:class_name attribute) - in - match Registry.get user_models qualified_attribute with + let add_sink_from_attribute_model ~class_name ~positional position existing_state attribute = + match get_attribute_model class_name attribute with | Some { Model.backward = { sink_taint; _ }; _ } -> - let taint = BackwardState.read ~root:GlobalModel.global_root ~path:[] sink_taint in let root = if positional then AccessPath.Root.PositionalParameter @@ -63,6 +148,15 @@ let infer ~environment ~user_models = else AccessPath.Root.NamedParameter { name = attribute } in + let { FeatureSet.breadcrumbs; via_features } = + get_attribute_tito_features class_name attribute root + in + let taint = + BackwardState.read ~root:GlobalModel.global_root ~path:[] sink_taint + |> translate_via_features_on_attribute attribute root + |> BackwardState.Tree.add_local_breadcrumbs breadcrumbs + |> BackwardState.Tree.add_via_features via_features + in BackwardState.assign ~weak:true ~root ~path:[] taint existing_state | None -> existing_state in @@ -94,13 +188,14 @@ let infer ~environment ~user_models = in let taint_in_taint_out = List.foldi - ~f:(fun position -> add_parameter_tito ~positional:true (position + 1)) + ~f:(fun position -> + add_parameter_to_attribute_tito ~class_name ~positional:true (position + 1)) ~init:BackwardState.empty attributes in let sink_taint = List.foldi attributes ~init:BackwardState.empty ~f:(fun position -> - add_sink_from_attribute_model ~positional:true (Reference.create class_name) (position + 1)) + add_sink_from_attribute_model ~class_name ~positional:true (position + 1)) in [ ( Target.Method { Target.class_name; method_name = "__init__"; kind = Normal }, @@ -141,7 +236,10 @@ let infer ~environment ~user_models = |> Option.value ~default:[] in let taint_in_taint_out = - List.foldi ~f:(add_parameter_tito ~positional:false) ~init:BackwardState.empty fields + List.foldi + ~f:(add_parameter_to_attribute_tito ~class_name ~positional:false) + ~init:BackwardState.empty + fields (* `TypedDict.__init__ also accepts iterables and **kwargs. *) |> add_tito ~input_root: @@ -150,6 +248,8 @@ let infer ~environment ~user_models = ~input_path:[Abstract.TreeDomain.Label.AnyIndex] ~output_path:[Abstract.TreeDomain.Label.AnyIndex] ~collapse_depth:0 + ~breadcrumbs:Features.BreadcrumbSet.bottom + ~via_features:Features.ViaFeatureSet.bottom |> add_tito ~input_root: (AccessPath.Root.PositionalParameter @@ -157,17 +257,21 @@ let infer ~environment ~user_models = ~input_path:[AccessPath.dictionary_keys] ~output_path:[AccessPath.dictionary_keys] ~collapse_depth:Features.CollapseDepth.no_collapse + ~breadcrumbs:Features.BreadcrumbSet.bottom + ~via_features:Features.ViaFeatureSet.bottom |> add_tito ~input_root:(AccessPath.Root.StarStarParameter { excluded = fields }) ~input_path:[] ~output_path:[Abstract.TreeDomain.Label.AnyIndex] ~collapse_depth:Features.CollapseDepth.no_collapse + ~breadcrumbs:Features.BreadcrumbSet.bottom + ~via_features:Features.ViaFeatureSet.bottom in let sink_taint = List.foldi fields ~init:BackwardState.empty - ~f:(add_sink_from_attribute_model ~positional:false (Reference.create class_name)) + ~f:(add_sink_from_attribute_model ~class_name ~positional:false) in [ ( Target.Method { Target.class_name; method_name = "__init__"; kind = Normal }, diff --git a/source/interprocedural_analyses/taint/test/integration/via_attribute_name.py.models b/source/interprocedural_analyses/taint/test/integration/via_attribute_name.py.models index 1683f67a8bb..d7e72ee2c93 100644 --- a/source/interprocedural_analyses/taint/test/integration/via_attribute_name.py.models +++ b/source/interprocedural_analyses/taint/test/integration/via_attribute_name.py.models @@ -311,6 +311,7 @@ } ], "local_features": [ + { "always-via-model-query-attribute": "x" }, { "always-via": "tito" }, { "always-via": "broadening" }, { "always-via": "issue-broadening" } @@ -346,6 +347,7 @@ } ], "features": [ + { "always-via-model-query-attribute": "x" }, { "always-via": "tito" }, { "always-via": "broadening" }, { "always-via": "issue-broadening" } @@ -384,6 +386,7 @@ } ], "local_features": [ + { "always-via-model-query-attribute": "y" }, { "always-via": "tito" }, { "always-via": "broadening" }, { "always-via": "issue-broadening" } @@ -419,6 +422,7 @@ } ], "features": [ + { "always-via-model-query-attribute": "y" }, { "always-via": "tito" }, { "always-via": "broadening" }, { "always-via": "issue-broadening" } @@ -457,6 +461,7 @@ } ], "local_features": [ + { "always-via-model-query-attribute": "z" }, { "always-via": "tito" }, { "always-via": "broadening" }, { "always-via": "issue-broadening" } @@ -492,6 +497,7 @@ } ], "features": [ + { "always-via-model-query-attribute": "z" }, { "always-via": "tito" }, { "always-via": "broadening" }, { "always-via": "issue-broadening" } @@ -1047,14 +1053,8 @@ "port": "formal(z)", "taint": [ { - "kinds": [ - { - "via_features": [ - { "kind": "ViaAttributeName", "tag": "TheZ" } - ], - "kind": "Test" - } - ], + "kinds": [ { "kind": "Test" } ], + "local_features": [ { "always-via-TheZ-attribute": "z" } ], "declaration": null } ] @@ -1063,12 +1063,8 @@ "port": "formal(y)", "taint": [ { - "kinds": [ - { - "via_features": [ { "kind": "ViaAttributeName" } ], - "kind": "Test" - } - ], + "kinds": [ { "kind": "Test" } ], + "local_features": [ { "always-via-attribute": "y" } ], "declaration": null } ] @@ -1077,12 +1073,8 @@ "port": "formal(x)", "taint": [ { - "kinds": [ - { - "via_features": [ { "kind": "ViaAttributeName" } ], - "kind": "Test" - } - ], + "kinds": [ { "kind": "Test" } ], + "local_features": [ { "always-via-attribute": "x" } ], "declaration": null } ] @@ -1178,6 +1170,7 @@ "kinds": [ { "return_paths": { "[z]": 999999 }, "kind": "LocalReturn" } ], + "local_features": [ { "always-via-model-query-attribute": "z" } ], "tito": null } ] @@ -1189,6 +1182,7 @@ "kinds": [ { "return_paths": { "[y]": 999999 }, "kind": "LocalReturn" } ], + "local_features": [ { "always-via-model-query-attribute": "y" } ], "tito": null } ] @@ -1200,6 +1194,7 @@ "kinds": [ { "return_paths": { "[x]": 999999 }, "kind": "LocalReturn" } ], + "local_features": [ { "always-via-model-query-attribute": "x" } ], "tito": null } ] diff --git a/source/interprocedural_analyses/taint/test/integration/via_type_of.py b/source/interprocedural_analyses/taint/test/integration/via_type_of.py index dbc77a6f8f6..51a3ef301ca 100644 --- a/source/interprocedural_analyses/taint/test/integration/via_type_of.py +++ b/source/interprocedural_analyses/taint/test/integration/via_type_of.py @@ -81,6 +81,11 @@ def test2_alarm4(foo): _test_sink(foo) +def test2_alarm5_via_constructor(): + taint: str = _test_source() + _test_sink(Test2_C(x={}, y=[], z=taint)) + + class Test3_Foo: ... diff --git a/source/interprocedural_analyses/taint/test/integration/via_type_of.py.cg b/source/interprocedural_analyses/taint/test/integration/via_type_of.py.cg index 6f71ad8bd8c..ceae2b143a5 100644 --- a/source/interprocedural_analyses/taint/test/integration/via_type_of.py.cg +++ b/source/interprocedural_analyses/taint/test/integration/via_type_of.py.cg @@ -1,5 +1,6 @@ @generated Call dependencies +via_type_of.test2_alarm5_via_constructor (fun) -> [_test_sink (fun) _test_source (fun) object::__new__ (method) via_type_of.Test2_C::__init__ (method)] via_type_of.test_via_type_of_does_not_propagate (fun) -> [via_type_of.meta (fun)] via_type_of.$toplevel (fun) -> [] via_type_of.meta (fun) -> [via_type_of.return_via_parameter_type (fun)] diff --git a/source/interprocedural_analyses/taint/test/integration/via_type_of.py.models b/source/interprocedural_analyses/taint/test/integration/via_type_of.py.models index 07d9717d439..09f0b006316 100644 --- a/source/interprocedural_analyses/taint/test/integration/via_type_of.py.models +++ b/source/interprocedural_analyses/taint/test/integration/via_type_of.py.models @@ -342,6 +342,7 @@ "local_features": [ { "has": "first-field" }, { "first-field": "x" }, + { "always-via-type": "unknown" }, { "always-via-type": "typing.Dict[str, int]" }, { "always-via": "tito" } ], @@ -378,6 +379,7 @@ "features": [ { "has": "first-field" }, { "first-field": "x" }, + { "always-via-type": "unknown" }, { "always-via-type": "typing.Dict[str, int]" }, { "always-via": "tito" } ], @@ -417,6 +419,7 @@ "local_features": [ { "has": "first-field" }, { "first-field": "y" }, + { "always-via-type": "unknown" }, { "always-via-type": "typing.List[str]" }, { "always-via": "tito" } ], @@ -453,6 +456,7 @@ "features": [ { "has": "first-field" }, { "first-field": "y" }, + { "always-via-type": "unknown" }, { "always-via-type": "typing.List[str]" }, { "always-via": "tito" } ], @@ -493,6 +497,7 @@ { "has": "first-field" }, { "first-field": "z" }, { "always-via-TheZ-type": "float" }, + { "always-via-TheZ-type": "unknown" }, { "always-via": "tito" } ], "tito_positions": [ { "line": 69, "start": 16, "end": 32 } ], @@ -529,6 +534,7 @@ { "has": "first-field" }, { "first-field": "z" }, { "always-via-TheZ-type": "float" }, + { "always-via-TheZ-type": "unknown" }, { "always-via": "tito" } ], "sink_handle": { @@ -569,9 +575,11 @@ { "first-field": "x" }, { "first-field": "z" }, { "first-field": "y" }, + { "via-type": "unknown" }, { "via-TheZ-type": "float" }, { "via-type": "typing.List[str]" }, { "via-type": "typing.Dict[str, int]" }, + { "via-TheZ-type": "unknown" }, { "always-via": "tito" } ], "tito_positions": [ { "line": 75, "start": 16, "end": 32 } ], @@ -609,9 +617,11 @@ { "first-field": "x" }, { "first-field": "z" }, { "first-field": "y" }, + { "via-type": "unknown" }, { "via-TheZ-type": "float" }, { "via-type": "typing.List[str]" }, { "via-type": "typing.Dict[str, int]" }, + { "via-TheZ-type": "unknown" }, { "always-via": "tito" } ], "sink_handle": { @@ -623,13 +633,88 @@ "master_handle": "via_type_of.test2_alarm4:5002:0:Call|_test_sink|0|formal(arg):adc30b327f877b06271e6820c612af79" } } +{ + "kind": "issue", + "data": { + "callable": "via_type_of.test2_alarm5_via_constructor", + "callable_line": 84, + "code": 5002, + "line": 86, + "start": 15, + "end": 43, + "filename": "via_type_of.py", + "message": "Data from [Test] source(s) may reach [Test] sink(s)", + "traces": [ + { + "name": "forward", + "roots": [ + { + "kinds": [ + { + "leaves": [ + { "port": "leaf:return", "name": "_test_source" } + ], + "kind": "Test" + } + ], + "local_features": [ + { "always-via-TheZ-type": "str" }, + { "always-via": "tito" }, + { "always-via": "broadening" }, + { "always-via": "issue-broadening" } + ], + "tito_positions": [ { "line": 86, "start": 37, "end": 42 } ], + "origin": { + "filename": "via_type_of.py", + "line": 85, + "start": 17, + "end": 31 + } + } + ] + }, + { + "name": "backward", + "roots": [ + { + "kinds": [ + { + "leaves": [ { "port": "leaf:arg", "name": "_test_sink" } ], + "kind": "Test" + } + ], + "origin": { + "filename": "via_type_of.py", + "line": 86, + "start": 15, + "end": 43 + } + } + ] + } + ], + "features": [ + { "always-via-TheZ-type": "str" }, + { "always-via": "tito" }, + { "always-via": "broadening" }, + { "always-via": "issue-broadening" } + ], + "sink_handle": { + "kind": "Call", + "callee": "_test_sink", + "index": 0, + "parameter": "formal(arg)" + }, + "master_handle": "via_type_of.test2_alarm5_via_constructor:5002:0:Call|_test_sink|0|formal(arg):4e298d4412811c8854de6bb64f3cd3ee" + } +} { "kind": "issue", "data": { "callable": "via_type_of.test4_alarm1", - "callable_line": 129, + "callable_line": 134, "code": 5002, - "line": 131, + "line": 136, "start": 4, "end": 7, "filename": "via_type_of.py", @@ -649,7 +734,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 131, + "line": 136, "start": 10, "end": 24 } @@ -674,7 +759,7 @@ "local_features": [ { "always-via-type": "unknown" } ], "origin": { "filename": "via_type_of.py", - "line": 131, + "line": 136, "start": 4, "end": 7 } @@ -695,9 +780,9 @@ "kind": "issue", "data": { "callable": "via_type_of.test4_alarm2", - "callable_line": 134, + "callable_line": 139, "code": 5002, - "line": 136, + "line": 141, "start": 4, "end": 7, "filename": "via_type_of.py", @@ -717,7 +802,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 136, + "line": 141, "start": 10, "end": 24 } @@ -742,7 +827,7 @@ "local_features": [ { "always-via-type": "typing.Any" } ], "origin": { "filename": "via_type_of.py", - "line": 136, + "line": 141, "start": 4, "end": 7 } @@ -763,9 +848,9 @@ "kind": "issue", "data": { "callable": "via_type_of.test4_alarm3", - "callable_line": 139, + "callable_line": 144, "code": 5002, - "line": 141, + "line": 146, "start": 4, "end": 7, "filename": "via_type_of.py", @@ -785,7 +870,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 141, + "line": 146, "start": 10, "end": 24 } @@ -810,7 +895,7 @@ "local_features": [ { "always-via-TheZ-type": "object" } ], "origin": { "filename": "via_type_of.py", - "line": 141, + "line": 146, "start": 4, "end": 7 } @@ -1226,7 +1311,17 @@ "taint": [ { "kinds": [ - { "return_paths": { "[z]": 999999 }, "kind": "LocalReturn" } + { + "via_features": [ + { + "kind": "ViaTypeOf", + "parameter": "formal(z)", + "tag": "TheZ" + } + ], + "return_paths": { "[z]": 999999 }, + "kind": "LocalReturn" + } ], "tito": null } @@ -1237,7 +1332,13 @@ "taint": [ { "kinds": [ - { "return_paths": { "[y]": 999999 }, "kind": "LocalReturn" } + { + "via_features": [ + { "kind": "ViaTypeOf", "parameter": "formal(y)" } + ], + "return_paths": { "[y]": 999999 }, + "kind": "LocalReturn" + } ], "tito": null } @@ -1248,7 +1349,13 @@ "taint": [ { "kinds": [ - { "return_paths": { "[x]": 999999 }, "kind": "LocalReturn" } + { + "via_features": [ + { "kind": "ViaTypeOf", "parameter": "formal(x)" } + ], + "return_paths": { "[x]": 999999 }, + "kind": "LocalReturn" + } ], "tito": null } @@ -1312,7 +1419,7 @@ "via_features": [ { "kind": "ViaTypeOf", - "parameter": "formal($global)", + "parameter": "formal(z)", "tag": "TheZ" } ], @@ -1330,7 +1437,7 @@ "kinds": [ { "via_features": [ - { "kind": "ViaTypeOf", "parameter": "formal($global)" } + { "kind": "ViaTypeOf", "parameter": "formal(y)" } ], "kind": "Test" } @@ -1346,7 +1453,7 @@ "kinds": [ { "via_features": [ - { "kind": "ViaTypeOf", "parameter": "formal($global)" } + { "kind": "ViaTypeOf", "parameter": "formal(x)" } ], "kind": "Test" } @@ -1416,7 +1523,7 @@ "local_features": [ { "always-via-type": "typing.Any" } ], "origin": { "filename": "via_type_of.py", - "line": 161, + "line": 166, "start": 11, "end": 47 } @@ -1495,7 +1602,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 97, + "line": 102, "start": 15, "end": 18 } @@ -1509,9 +1616,9 @@ "kind": "issue", "data": { "callable": "via_type_of.test3_alarm1", - "callable_line": 95, + "callable_line": 100, "code": 5002, - "line": 97, + "line": 102, "start": 15, "end": 18, "filename": "via_type_of.py", @@ -1537,7 +1644,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 97, + "line": 102, "start": 15, "end": 18 } @@ -1556,7 +1663,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 97, + "line": 102, "start": 15, "end": 18 } @@ -1593,7 +1700,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 102, + "line": 107, "start": 15, "end": 18 } @@ -1607,9 +1714,9 @@ "kind": "issue", "data": { "callable": "via_type_of.test3_alarm2", - "callable_line": 100, + "callable_line": 105, "code": 5002, - "line": 102, + "line": 107, "start": 15, "end": 18, "filename": "via_type_of.py", @@ -1635,7 +1742,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 102, + "line": 107, "start": 15, "end": 18 } @@ -1654,7 +1761,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 102, + "line": 107, "start": 15, "end": 18 } @@ -1689,7 +1796,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 107, + "line": 112, "start": 15, "end": 18 } @@ -1703,9 +1810,9 @@ "kind": "issue", "data": { "callable": "via_type_of.test3_alarm3", - "callable_line": 105, + "callable_line": 110, "code": 5002, - "line": 107, + "line": 112, "start": 15, "end": 18, "filename": "via_type_of.py", @@ -1731,7 +1838,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 107, + "line": 112, "start": 15, "end": 18 } @@ -1750,7 +1857,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 107, + "line": 112, "start": 15, "end": 18 } @@ -1787,7 +1894,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 119, + "line": 124, "start": 15, "end": 18 } @@ -1806,7 +1913,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 119, + "line": 124, "start": 15, "end": 18 } @@ -1825,7 +1932,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 119, + "line": 124, "start": 15, "end": 18 } @@ -1839,9 +1946,9 @@ "kind": "issue", "data": { "callable": "via_type_of.test3_alarm4", - "callable_line": 110, + "callable_line": 115, "code": 5002, - "line": 119, + "line": 124, "start": 15, "end": 18, "filename": "via_type_of.py", @@ -1867,7 +1974,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 114, + "line": 119, "start": 10, "end": 13 } @@ -1889,7 +1996,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 116, + "line": 121, "start": 14, "end": 17 } @@ -1911,7 +2018,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 118, + "line": 123, "start": 14, "end": 17 } @@ -1930,7 +2037,7 @@ ], "origin": { "filename": "via_type_of.py", - "line": 119, + "line": 124, "start": 15, "end": 18 } @@ -1971,7 +2078,7 @@ "local_features": [ { "always-via-type": "str" }, { "always-via": "tito" } ], - "tito_positions": [ { "line": 186, "start": 16, "end": 25 } ], + "tito_positions": [ { "line": 191, "start": 16, "end": 25 } ], "tito": null } ] @@ -2002,7 +2109,7 @@ "local_features": [ { "always-via-type": "typing.List[str]" } ], "origin": { "filename": "via_type_of.py", - "line": 157, + "line": 162, "start": 11, "end": 48 } @@ -2035,7 +2142,7 @@ "local_features": [ { "always-via-type": "int" } ], "origin": { "filename": "via_type_of.py", - "line": 153, + "line": 158, "start": 11, "end": 39 } @@ -2068,7 +2175,7 @@ "local_features": [ { "always-via-TheY-type": "int" } ], "origin": { "filename": "via_type_of.py", - "line": 182, + "line": 187, "start": 28, "end": 35 } @@ -2101,7 +2208,7 @@ "local_features": [ { "always-via-type": "str" } ], "origin": { "filename": "via_type_of.py", - "line": 149, + "line": 154, "start": 11, "end": 41 } @@ -2132,10 +2239,10 @@ { "always-via-type": "typing.List[int]" }, { "always-via": "tito" } ], - "tito_positions": [ { "line": 173, "start": 13, "end": 27 } ], + "tito_positions": [ { "line": 178, "start": 13, "end": 27 } ], "origin": { "filename": "via_type_of.py", - "line": 173, + "line": 178, "start": 13, "end": 27 } @@ -2164,7 +2271,7 @@ "call": { "position": { "filename": "via_type_of.py", - "line": 165, + "line": 170, "start": 11, "end": 23 },