20 const nlohmann::json &ast,
21 const std::string &name);
26std::string python_annotation<Json>::get_current_func_name()
28 return current_func_name_context_;
32std::string python_annotation<Json>::get_type_from_constant(
const Json &element)
34 if (element.contains(
"esbmc_type_annotation"))
35 return element[
"esbmc_type_annotation"].template get<std::string>();
37 auto rhs = element[
"value"];
39 if (rhs.is_number_integer() || rhs.is_number_unsigned())
41 if (rhs.is_number_float())
54std::string python_annotation<Json>::get_type_from_lhs(
55 const std::string &
id,
59 Json node = find_annotated_assign(
id, body[
"body"]);
62 if (node.empty() && current_func !=
nullptr)
63 node = find_annotated_assign(
id, (*current_func)[
"body"]);
67 if (node.empty() && parent_func !=
nullptr)
68 node = find_annotated_assign(
id, (*parent_func)[
"body"]);
72 node = find_annotated_assign(
id, ast_[
"body"]);
79 if (!node.contains(
"annotation") || node[
"annotation"].is_null())
83 if (!node[
"annotation"].contains(
"id") || node[
"annotation"][
"id"].is_null())
87 return node[
"annotation"][
"id"];
91std::string python_annotation<Json>::get_list_subtype(
const Json &list)
93 std::string list_subtype;
96 list[
"_type"] ==
"Call" && list.contains(
"func") &&
97 list[
"func"].contains(
"attr") && list[
"func"][
"attr"] ==
"array")
98 return get_list_subtype(list[
"args"][0]);
100 if (!list.contains(
"elts"))
104 auto element_type = [
this](
const Json &elem) -> std::string {
105 if (elem.value(
"esbmc_type_annotation", std::string()) ==
"complex")
110 for (
const auto &elem : list[
"elts"])
112 const std::string
type = element_type(elem);
113 if (list_subtype.empty())
115 else if (!
type.empty() && type != list_subtype)
116 throw std::runtime_error(
"Multiple typed lists are not supported\n");
123bool python_annotation<Json>::extract_type_info(
124 const Json &annotation,
126 std::string &element_type)
129 if (annotation.contains(
"id"))
131 std::string full_type = annotation[
"id"];
134 size_t bracket_pos = full_type.find(
'[');
135 if (bracket_pos != std::string::npos)
137 base_type = full_type.substr(0, bracket_pos);
139 full_type.substr(bracket_pos + 1, full_type.length() - bracket_pos - 2);
152 annotation.contains(
"_type") && annotation[
"_type"] ==
"Subscript" &&
153 annotation.contains(
"value") && annotation[
"value"].contains(
"id"))
160 if (annotation.contains(
"slice") && annotation[
"slice"].contains(
"id"))
161 element_type = annotation[
"slice"][
"id"];
170std::string python_annotation<Json>::infer_dict_value_type(
const Json &var_node)
172 if (!var_node.contains(
"value") || var_node[
"value"].is_null())
175 const Json &dict_init = var_node[
"value"];
178 if (dict_init[
"_type"] ==
"Call" && dict_init[
"func"][
"_type"] ==
"Name")
181 dict_init[
"func"][
"id"].
template get<std::string>(), ast_[
"body"]);
183 !func_def.empty() && func_def.contains(
"returns") &&
184 func_def[
"returns"][
"_type"] ==
"Subscript" &&
185 func_def[
"returns"][
"value"][
"id"] ==
"dict" &&
186 func_def[
"returns"][
"slice"][
"elts"].size() >= 2)
188 const Json &val_type = func_def[
"returns"][
"slice"][
"elts"][1];
189 if (val_type[
"_type"] ==
"Subscript" && val_type[
"value"].contains(
"id"))
190 return val_type[
"value"][
"id"].template get<std::string>();
196 dict_init[
"_type"] ==
"Dict" && dict_init.contains(
"values") &&
197 !dict_init[
"values"].empty())
199 const Json &first_value = dict_init[
"values"][0];
200 std::string value_type = get_argument_type(first_value);
202 if (!value_type.empty())
211python_annotation<Json>::subscript_type_from_call(
const Json &subscript_node)
213 const Json &call = subscript_node[
"value"];
215 call[
"_type"] !=
"Call" || call[
"func"][
"_type"] !=
"Name" ||
216 config.options.get_bool_option(
"override-return-annotation"))
220 call[
"func"][
"id"].
template get<std::string>(), ast_[
"body"]);
221 if (func_def.empty() || !func_def.contains(
"returns"))
224 const Json &ret = func_def[
"returns"];
226 !ret.is_object() || ret.value(
"_type",
"") !=
"Subscript" ||
227 ret[
"value"].value(
"id",
"") !=
"list" || !ret.contains(
"slice"))
233 return subscript_node[
"slice"].value(
"_type",
"") ==
"Slice"
234 ?
"list[" + elem +
"]"
239std::string python_annotation<Json>::subscript_type_from_list_literal(
240 const Json &subscript_node)
242 const Json &elts = subscript_node[
"value"][
"elts"];
243 if (elts.empty() || subscript_node[
"slice"].value(
"_type",
"") ==
"Slice")
246 const std::string elem = get_argument_type(elts[0]);
247 for (
const Json &e : elts)
248 if (get_argument_type(e) != elem)
250 return elem.empty() ?
"Any" : elem;
258 if (type.empty() || type.back() !=
']')
260 if (type.rfind(
"list[", 0) == 0)
261 return type.substr(5, type.size() - 6);
262 if (type.rfind(
"tuple[", 0) != 0)
266 return index < elems.size() ? elems[index] :
"Any";
270std::string python_annotation<Json>::resolve_subscript_type(
271 const Json &subscript_node,
275 const Json &
base = subscript_node[
"value"];
278 if (
base[
"_type"] ==
"Subscript")
279 return resolve_subscript_type(
base, body,
true);
281 if (
base[
"_type"] ==
"List")
282 return subscript_type_from_list_literal(subscript_node);
284 if (
base[
"_type"] !=
"Name")
285 return subscript_type_from_call(subscript_node);
287 std::string var_name =
base[
"id"];
292 var_node.empty() || !var_node.contains(
"annotation") ||
293 var_node[
"annotation"].is_null())
298 if (!extract_type_info(var_node[
"annotation"],
base_type, element_type))
303 return infer_dict_value_type(var_node);
312 bool slice_is_list_var =
false;
314 subscript_node.contains(
"slice") &&
315 subscript_node[
"slice"].contains(
"_type") &&
316 subscript_node[
"slice"][
"_type"] ==
"Name" &&
317 subscript_node[
"slice"].contains(
"id"))
319 const std::string idx_name = subscript_node[
"slice"][
"id"];
322 std::string idx_base_type, idx_element_type;
324 !idx_node.empty() && idx_node.contains(
"annotation") &&
325 !idx_node[
"annotation"].is_null() &&
327 idx_node[
"annotation"], idx_base_type, idx_element_type) &&
328 idx_base_type ==
"list")
329 slice_is_list_var =
true;
335 !idx_node.empty() && idx_node.contains(
"value") &&
336 idx_node[
"value"].value(
"_type", std::string()) ==
"List")
337 slice_is_list_var =
true;
343 const bool is_multi_result =
344 (subscript_node.contains(
"slice") &&
345 subscript_node[
"slice"].contains(
"_type") &&
346 (subscript_node[
"slice"][
"_type"] ==
"Slice" ||
347 subscript_node[
"slice"][
"_type"] ==
"List")) ||
351 if (!element_type.empty())
352 return is_multi_result ?
"list[" + element_type +
"]" : element_type;
355 if (var_node.contains(
"value") && !var_node[
"value"].is_null())
357 std::string inferred = get_list_subtype(var_node[
"value"]);
358 if (!inferred.empty())
359 return is_multi_result ?
"list[" + inferred +
"]" : inferred;
363 return is_multi_result ?
"list" :
"Any";
375 const Json &slice = subscript_node.value(
"slice", Json());
377 base_type ==
"tuple" && slice.value(
"_type",
"") ==
"Constant" &&
378 slice[
"value"].is_number_unsigned())
382 slice[
"value"].
template get<size_t>());
392bool python_annotation<Json>::is_imported_name_in_body(
393 const std::string &name,
394 const Json &stmts)
const
398 for (
const auto &stmt : stmts)
400 if (!stmt.contains(
"_type"))
403 const std::string &stype = stmt[
"_type"];
404 if ((stype ==
"ImportFrom" || stype ==
"Import") && stmt.contains(
"names"))
406 for (
const auto &alias : stmt[
"names"])
409 alias.contains(
"name") &&
410 alias[
"name"].template get<std::string>() == name)
413 alias.contains(
"asname") && !alias[
"asname"].is_null() &&
414 alias[
"asname"].template get<std::string>() == name)
424bool python_annotation<Json>::is_imported_name(
const std::string &name)
const
426 return is_imported_name_in_body(name, ast_[
"body"]) ||
427 (current_func !=
nullptr && (*current_func).contains(
"body") &&
428 is_imported_name_in_body(name, (*current_func)[
"body"]));
432std::string python_annotation<Json>::get_object_name(
434 const std::string &prefix)
436 if (call[
"value"][
"_type"] ==
"Attribute")
438 std::string append = call[
"value"][
"attr"].template get<std::string>();
440 append = prefix + std::string(
".") + append;
442 return get_object_name(call[
"value"], append);
444 std::string obj_name(
"");
447 obj_name = prefix + std::string(
".");
454 if (call[
"value"][
"_type"] ==
"Constant")
455 return get_type_from_constant(call[
"value"]);
461 call[
"value"][
"_type"] ==
"Call" && call[
"value"].contains(
"func") &&
462 call[
"value"][
"func"][
"_type"] ==
"Name" &&
463 call[
"value"][
"func"].contains(
"id"))
464 return call[
"value"][
"func"][
"id"].template get<std::string>();
470 call[
"value"][
"_type"] ==
"Call" && call[
"value"].contains(
"func") &&
471 call[
"value"][
"func"][
"_type"] ==
"Attribute")
472 return get_type_from_method(call[
"value"]);
475 if (!call[
"value"].contains(
"id"))
478 obj_name += call[
"value"][
"id"].template get<std::string>();
479 if (obj_name.find(
'.') != std::string::npos)
486std::string python_annotation<Json>::get_string_method_return_type(
487 const std::string &method)
const
490 method ==
"join" || method ==
"lower" || method ==
"upper" ||
491 method ==
"strip" || method ==
"lstrip" || method ==
"rstrip" ||
492 method ==
"format" || method ==
"replace")
496 method ==
"startswith" || method ==
"endswith" || method ==
"isdigit" ||
497 method ==
"isalpha" || method ==
"isspace" || method ==
"islower" ||
501 if (method ==
"find" || method ==
"rfind")
504 if (method ==
"split" || method ==
"rsplit")
508 if (method ==
"encode")
517 if (method ==
"partition" || method ==
"rpartition")
525std::string python_annotation<Json>::match_literal_argument(
526 const Json &call_node,
527 std::vector<Json> overloads)
const
529 if (overloads.empty())
533 if (!call_node.contains(
"args") || call_node[
"args"].empty())
536 for (
const auto &overload : overloads)
538 if (!overload.contains(
"args") || !overload[
"args"].contains(
"args"))
541 const auto ¶ms = overload[
"args"][
"args"];
542 const auto &call_args = call_node[
"args"];
545 if (params.size() > 0 && call_args.size() > 0)
547 const auto ¶m_annotation = params[0][
"annotation"];
548 const auto &call_arg = call_args[0];
552 param_annotation[
"_type"] ==
"Subscript" &&
553 param_annotation[
"value"][
"id"] ==
"Literal" &&
554 param_annotation[
"slice"][
"_type"] ==
"Constant")
556 std::string literal_value =
557 param_annotation[
"slice"][
"value"].template get<std::string>();
561 call_arg[
"_type"] ==
"Constant" && call_arg[
"value"].
is_string() &&
562 call_arg[
"value"].
template get<std::string>() == literal_value)
566 overload.contains(
"returns") && !overload[
"returns"].is_null() &&
567 overload[
"returns"].contains(
"id"))
569 return overload[
"returns"][
"id"];
581std::string python_annotation<Json>::resolve_overload_return_type(
582 const std::string &func_name,
583 const Json &call_node)
const
585 std::vector<Json> overloads;
588 for (
const Json &elem : ast_[
"body"])
591 elem[
"_type"] ==
"FunctionDef" && elem[
"name"] == func_name &&
593 overloads.push_back(elem);
596 return match_literal_argument(call_node, overloads);
601python_annotation<Json>::resolve_object_class_name(
const std::string &obj)
603 auto read_name_id = [](
const Json &annotation) -> std::string {
605 annotation.is_object() && !annotation.is_null() &&
606 annotation.contains(
"_type") && annotation[
"_type"] ==
"Name" &&
607 annotation.contains(
"id") && annotation[
"id"].is_string())
608 return annotation[
"id"].template get<std::string>();
613 current_func !=
nullptr && (*current_func).contains(
"args") &&
614 (*current_func)[
"args"].contains(
"args"))
616 Json param = find_annotated_assign(obj, (*current_func)[
"args"][
"args"]);
617 if (param.contains(
"annotation"))
619 std::string
id = read_name_id(param[
"annotation"]);
629 if (var.contains(
"annotation"))
631 std::string
id = read_name_id(var[
"annotation"]);
637 var.contains(
"value") && var[
"value"].is_object() &&
638 var[
"value"].contains(
"_type") && var[
"value"][
"_type"] ==
"Call" &&
639 var[
"value"].contains(
"func") && var[
"value"][
"func"].is_object() &&
640 var[
"value"][
"func"].contains(
"_type") &&
641 var[
"value"][
"func"][
"_type"] ==
"Name" &&
642 var[
"value"][
"func"].contains(
"id") &&
643 var[
"value"][
"func"][
"id"].is_string())
644 return var[
"value"][
"func"][
"id"].template get<std::string>();
650std::string python_annotation<Json>::infer_unpacked_element_type(
654 if (!rhs.is_object() || !rhs.contains(
"_type"))
657 const std::string &kind = rhs[
"_type"];
660 (kind ==
"Tuple" || kind ==
"List") && rhs.contains(
"elts") &&
661 index < rhs[
"elts"].size())
663 std::string t = get_argument_type(rhs[
"elts"][index]);
664 return t.empty() ?
"Any" : t;
673 find_var_node_for_inference(rhs[
"id"].
template get<std::string>());
674 const std::string
type =
677 : get_argument_type(rhs);
682 kind ==
"Attribute" && rhs.contains(
"value") && rhs[
"value"].is_object() &&
683 rhs[
"value"][
"_type"] ==
"Name" && rhs.contains(
"attr"))
686 resolve_object_class_name(rhs[
"value"][
"id"].
template get<std::string>());
690 cls, rhs[
"attr"].
template get<std::string>(), ast_[
"body"]);
691 if (attr_rhs.empty())
693 return infer_unpacked_element_type(attr_rhs, index);
705 if (container.size() < 6 || container.back() !=
']')
707 const std::string params = container.substr(5, container.size() - 6);
708 if (container.rfind(
"list[", 0) == 0)
709 return is_slice ? container : params;
712 if (container.rfind(
"dict[", 0) == 0 && !is_slice && kv.size() == 2)
722 if (node.empty() || !node.contains(
"value") || node[
"value"].is_null())
724 const Json &
value = node[
"value"];
725 return value.value(
"_type",
"") ==
"List" &&
726 (!
value.contains(
"elts") ||
value[
"elts"].empty());
733 for (
size_t pos = type.find(
"tuple");
pos != std::string::npos;
734 pos = type.find(
"tuple",
pos + 1))
736 const size_t end =
pos + 5;
737 const bool starts =
pos == 0 || type[
pos - 1] ==
'[' ||
738 type[
pos - 1] ==
' ' || type[
pos - 1] ==
',';
739 if (starts && (end == type.size() || type[end] ==
']' || type[end] ==
','))
749 return type.empty() || type ==
"NoneType" || type ==
"Any" ||
758std::string python_annotation<Json>::list_copy_type(
759 const std::string &func_name,
763 (func_name !=
"list" && func_name !=
"tuple") || !call.contains(
"args") ||
764 call[
"args"].size() != 1)
766 const Json &iterable = call[
"args"][0];
767 const std::string source = get_argument_type(iterable);
768 if (source ==
"range")
770 if (source.rfind(
"list[", 0) == 0)
772 return dict_view_list_type(iterable);
778std::string python_annotation<Json>::dict_view_list_type(
const Json &call)
781 call.value(
"_type",
"") !=
"Call" || !call[
"args"].empty() ||
782 call[
"func"].value(
"_type",
"") !=
"Attribute")
784 const std::string dict_type = get_argument_type(call[
"func"][
"value"]);
785 if (dict_type.rfind(
"dict[", 0) != 0 || dict_type.back() !=
']')
788 dict_type.substr(5, dict_type.size() - 6));
791 const std::string method = call[
"func"].value(
"attr",
"");
792 if (method ==
"keys")
793 return "list[" + kv[0] +
"]";
794 if (method ==
"values")
795 return "list[" + kv[1] +
"]";
802python_annotation<Json>::get_dict_literal_type(
const Json &dict_value)
805 !dict_value.contains(
"keys") || !dict_value.contains(
"values") ||
806 dict_value[
"keys"].empty() || dict_value[
"values"].empty())
811 const Json &keys = dict_value[
"keys"];
812 if (std::any_of(keys.begin(), keys.end(), [](
const Json &
key) {
813 return key.is_null();
818 std::string key_type = get_argument_type(dict_value[
"keys"][0]);
819 std::string value_type = get_argument_type(dict_value[
"values"][0]);
824 bool key_is_simple = key_type.find(
'[') == std::string::npos;
825 bool value_is_simple = value_type.find(
'[') == std::string::npos;
827 !key_type.empty() && !value_type.empty() && key_is_simple &&
829 return "dict[" + key_type +
", " + value_type +
"]";
838std::string python_annotation<Json>::get_call_type(
const Json &arg)
842 const std::string attr = arg[
"func"].value(
"attr",
"");
843 if (arg[
"func"][
"_type"] ==
"Attribute" && attr.rfind(
"nondet_", 0) == 0)
844 return attr.substr(attr.find_last_of(
'_') + 1);
846 if (arg[
"func"][
"_type"] ==
"Name")
848 const std::string &func_name = arg[
"func"][
"id"];
852 const std::string class_name =
865 func_name ==
"hash" && arg.contains(
"args") && !arg[
"args"].empty() &&
866 get_argument_type(arg[
"args"][0]) !=
"bytes")
869 const std::string copied = list_copy_type(func_name, arg);
874 return get_builtin_call_type(arg);
879 return get_function_return_type(func_name, ast_);
881 catch (std::runtime_error &)
896python_annotation<Json>::subscript_annotation_type(
const Json &annotation)
899 if (annotation[
"value"][
"id"] ==
"dict" && !
full.empty())
907std::string python_annotation<Json>::container_literal_type(
912 return get_list_type_from_literal(
value);
914 return get_argument_type(
value);
919std::string python_annotation<Json>::get_argument_type(
const Json &arg)
926 static constexpr unsigned max_depth = 32;
927 if (arg_type_depth_ >= max_depth)
932 explicit depth_guardt(
unsigned &
depth) : d(
depth)
940 } guard(arg_type_depth_);
945 if (!arg.is_object())
948 if (arg[
"_type"] ==
"Constant")
949 return get_type_from_constant(arg);
950 else if (arg[
"_type"] ==
"Subscript")
953 const auto &val = arg[
"value"];
954 if (val[
"_type"] ==
"Name")
956 std::string var_name = val[
"id"].template get<std::string>();
961 if (is_rebound_in_scope(var_name))
964 Json var_node = find_var_node_for_inference(var_name);
965 const bool is_slice =
966 arg.contains(
"slice") && arg[
"slice"].value(
"_type",
"") ==
"Slice";
970 const auto &annot = var_node[
"annotation"];
973 annot.contains(
"_type") && annot[
"_type"] ==
"Subscript" &&
974 annot.contains(
"value") && annot[
"value"].contains(
"id") &&
975 annot[
"value"][
"id"] ==
"list")
981 const std::string elem =
982 annot.contains(
"slice")
987 arg.contains(
"slice") && arg[
"slice"].contains(
"_type") &&
988 arg[
"slice"][
"_type"] ==
"Slice")
989 return elem.empty() ?
"list" :
"list[" + elem +
"]";
991 return elem.empty() ?
"Any" : elem;
994 if (annot.contains(
"id") && annot[
"id"] ==
"list")
995 return bare_list_subscript_type(var_node, var_name, is_slice);
1003 !var_node.empty() && var_node.contains(
"value") &&
1004 !var_node[
"value"].is_null())
1011 else if (arg[
"_type"] ==
"Slice")
1013 else if (arg[
"_type"] ==
"UnaryOp")
1016 if (arg.contains(
"operand"))
1017 return get_argument_type(arg[
"operand"]);
1019 else if (arg[
"_type"] ==
"Name")
1021 const std::string &var_name = arg[
"id"];
1024 Json var_node = find_var_node_for_inference(var_name);
1030 if (var_node[
"_type"] ==
"arg")
1036 var_node[
"annotation"].contains(
"_type") &&
1037 var_node[
"annotation"][
"_type"] ==
"Subscript" &&
1038 var_node[
"annotation"].contains(
"value") &&
1039 var_node[
"annotation"][
"value"].contains(
"id"))
1041 return subscript_annotation_type(var_node[
"annotation"]);
1044 else if (var_node[
"annotation"].contains(
"id"))
1047 var_node[
"annotation"][
"id"].template get<std::string>();
1052 var_node.contains(
"value") && !var_node[
"value"].is_null())
1054 const std::string full_type =
1055 container_literal_type(
base_type, var_node[
"value"]);
1056 if (!full_type.empty())
1059 return refine_any_from_value(
base_type, var_node);
1064 !var_node.empty() && var_node.contains(
"value") &&
1065 !var_node[
"value"].is_null())
1073 std::string recovered = recover_list_type_from_appends(var_name);
1074 if (!recovered.empty())
1078 std::string inferred_type = get_argument_type(var_node[
"value"]);
1079 if (!inferred_type.empty())
1080 return inferred_type;
1083 else if (arg[
"_type"] ==
"BinOp")
1088 Json dummy_stmt = {{
"value", arg}};
1091 return get_type_from_binary_expr(dummy_stmt, func.empty() ? ast_ : func);
1093 else if (arg[
"_type"] ==
"List")
1096 return get_list_type_from_literal(arg);
1098 else if (arg[
"_type"] ==
"Dict")
1108 auto all_objects = [](
const Json &arr) {
1109 for (
const auto &e : arr)
1116 arg.contains(
"keys") && arg[
"keys"].is_array() && !arg[
"keys"].empty() &&
1117 arg.contains(
"values") && arg[
"values"].is_array() &&
1118 !arg[
"values"].empty() && all_objects(arg[
"keys"]) &&
1119 all_objects(arg[
"values"]))
1121 std::string key_type = get_argument_type(arg[
"keys"][0]);
1122 std::string value_type = get_argument_type(arg[
"values"][0]);
1123 if (!key_type.empty() && !value_type.empty())
1125 bool homogeneous =
true;
1126 for (
size_t i = 1; i < arg[
"keys"].size(); ++i)
1127 if (get_argument_type(arg[
"keys"][i]) != key_type)
1129 homogeneous =
false;
1132 for (
size_t i = 1; homogeneous && i < arg[
"values"].size(); ++i)
1133 if (get_argument_type(arg[
"values"][i]) != value_type)
1135 homogeneous =
false;
1139 return "dict[" + key_type +
", " + value_type +
"]";
1144 else if (arg[
"_type"] ==
"Set")
1146 else if (arg[
"_type"] ==
"Tuple")
1151 if (arg.contains(
"elts") && arg[
"elts"].is_array() && !arg[
"elts"].empty())
1154 for (
const auto &elt : arg[
"elts"])
1156 const std::string elt_type = get_argument_type(elt);
1157 if (elt_type.empty() || elt_type ==
"Any")
1162 if (!params.empty())
1166 if (!params.empty())
1167 return "tuple[" + params +
"]";
1171 else if (arg[
"_type"] ==
"BoolOp")
1173 if (arg.contains(
"values") && arg[
"values"].is_array())
1175 for (
const auto &val : arg[
"values"])
1178 val.contains(
"_type") && val[
"_type"] ==
"Call" &&
1179 val.contains(
"func") && val[
"func"].contains(
"_type") &&
1180 val[
"func"][
"_type"] ==
"Name" && val[
"func"].contains(
"id"))
1190 else if (arg[
"_type"] ==
"IfExp")
1193 const std::function<
bool(
const Json &)> is_literal = [&](
const Json &
e) {
1194 const std::string kind =
e.value(
"_type",
"");
1195 return kind ==
"Constant" ||
1196 (kind ==
"IfExp" && is_literal(e[
"body"]) &&
1197 is_literal(e[
"orelse"]));
1199 const std::string body_type = get_argument_type(arg[
"body"]);
1200 if (is_literal(arg) && body_type == get_argument_type(arg[
"orelse"]))
1203 else if (arg[
"_type"] ==
"Call")
1204 return get_call_type(arg);
1205 else if (arg[
"_type"] ==
"Attribute")
1206 return get_attribute_type(arg);
1214template <
class Json>
1215std::string python_annotation<Json>::get_attribute_type(
const Json &arg)
1217 const Json &obj = arg[
"value"];
1218 const std::string obj_kind = obj.value(
"_type",
"");
1220 obj_kind !=
"Call" &&
1221 (obj_kind !=
"Name" ||
1222 is_rebound_in_scope(obj[
"id"].
template get<std::string>())))
1225 const bool is_self =
1226 obj_kind ==
"Name" && obj[
"id"] ==
"self" && !current_class_name_.empty();
1227 return class_attribute_type(
1228 is_self ? current_class_name_ : get_argument_type(obj),
1229 arg[
"attr"].
template get<std::string>(),
1235template <
class Json>
1236std::string python_annotation<Json>::class_attribute_type(
1237 const std::string &cls,
1238 const std::string &attr,
1239 bool constructed_only)
1241 for (
const std::string &ancestor : get_class_ancestors(cls))
1244 if (class_node.empty())
1247 if (!annotation.empty())
1248 return constructed_only ?
""
1252 const std::string constructed =
1255 constructed.empty() ||
1259 const auto cls_ancestors = get_class_ancestors(cls);
1260 std::size_t self_stores = 0;
1261 for (
const Json &node : ast_[
"body"])
1263 if (node.value(
"_type",
"") !=
"ClassDef")
1265 const std::string
name = node[
"name"].template get<std::string>();
1266 const auto ancestors = get_class_ancestors(name);
1267 const bool is_base =
1268 std::find(cls_ancestors.begin(), cls_ancestors.end(), name) !=
1269 cls_ancestors.end();
1270 const bool is_derived =
1271 std::find(ancestors.begin(), ancestors.end(), cls) != ancestors.end();
1272 if (is_base || is_derived)
1275 if (self_stores == 1)
1284template <
class Json>
1287 const std::string &attr,
1288 const std::string &name,
1292 if (node.is_array())
1294 for (
const Json &child : node)
1298 if (!node.is_object())
1300 const std::string type = node.value(
"_type",
"");
1301 const std::string ctx =
1302 node.contains(
"ctx") ? node[
"ctx"].value(
"_type",
"") :
"";
1304 (type ==
"Attribute" && node.value(
"attr",
"") == attr && ctx !=
"Load") ||
1305 (type ==
"Attribute" && node.value(
"attr",
"") ==
"__class__") ||
1306 (type ==
"Name" && node.value(
"id",
"") ==
"setattr"))
1308 if (type ==
"Name" && node.value(
"id",
"") == name)
1310 for (
auto it = node.begin(); it != node.end(); ++it)
1311 if (it.key() !=
"annotation" && it.key() !=
"returns")
1320template <
class Json>
1321std::string python_annotation<Json>::self_attribute_type(
const Json &node)
1324 node.value(
"_type",
"") !=
"Attribute" ||
1325 node[
"value"].value(
"_type",
"") !=
"Name" ||
1326 node[
"value"].value(
"id",
"") !=
"self" || current_class_name_.empty() ||
1327 is_rebound_in_scope(
"self"))
1329 const std::string attr = node[
"attr"].template get<std::string>();
1330 for (
const std::string &cls : get_class_ancestors(current_class_name_))
1333 if (class_node.empty())
1336 cls, attr, ast_[
"body"]);
1340 size_t name_reads = 0;
1343 const std::vector<Json> calls = find_function_calls(cls, ast_[
"body"]);
1344 if (stores != 1 || annotation.empty() || calls.size() != name_reads)
1347 if (
value.value(
"_type",
"") ==
"Constant")
1349 if (
value.value(
"_type",
"") !=
"Name")
1354 const Json ¶ms = init[
"args"][
"args"];
1356 while (index < params.size() && params[index][
"arg"] !=
value[
"id"])
1358 if (index == 0 || index == params.size())
1360 for (
const Json &call : calls)
1363 !call[
"keywords"].
empty() || call[
"args"].size() < index ||
1364 call[
"args"][index - 1].
value(
"_type",
"") !=
"Constant" ||
1365 get_argument_type(call[
"args"][index - 1]) != type)
1367 for (
const Json &arg : call[
"args"])
1368 if (arg.value(
"_type",
"") ==
"Starred")
1377template <
class Json>
1378std::string python_annotation<Json>::get_operand_type(
const Json &node)
1380 const std::string
type = get_argument_type(node);
1381 return type.empty() ? self_attribute_type(node) :
type;
1388template <
class Json>
1391 if (node.is_object())
1394 node.value(
"_type", std::string()) ==
"Call" && node.contains(
"func") &&
1395 node[
"func"].is_object() &&
1396 node[
"func"].value(
"_type", std::string()) ==
"Attribute" &&
1397 node[
"func"].value(
"attr", std::string()) ==
"append" &&
1398 node[
"func"].contains(
"value") && node[
"func"][
"value"].is_object() &&
1399 node[
"func"][
"value"].value(
"_type", std::string()) ==
"Name" &&
1400 node[
"func"][
"value"].value(
"id", std::string()) == name &&
1401 node.contains(
"args") && node[
"args"].is_array() &&
1402 node[
"args"].size() == 1)
1404 return node[
"args"][0];
1406 for (
auto it = node.begin(); it != node.end(); ++it)
1413 else if (node.is_array())
1415 for (
const auto &child : node)
1426template <
class Json>
1428python_annotation<Json>::get_list_type_from_literal(
const Json &list_arg)
1430 if (!list_arg.contains(
"elts") || list_arg[
"elts"].empty())
1433 "Empty or malformed list literal detected. Using 'list[int]' as "
1441 std::string element_type = get_argument_type(list_arg[
"elts"][0]);
1444 if (element_type.empty() && list_arg[
"elts"][0].value(
"_type",
"") ==
"List")
1447 if (element_type.empty())
1450 "Could not determine list element type. Using 'list[int]' as fallback "
1460 auto is_numeric = [](
const std::string &t) {
1461 return t ==
"int" || t ==
"float" || t ==
"bool";
1463 bool all_numeric = is_numeric(element_type);
1464 bool saw_float = element_type ==
"float";
1467 for (
size_t i = 1; i < list_arg[
"elts"].size(); ++i)
1469 std::string current_type = get_argument_type(list_arg[
"elts"][i]);
1470 if (current_type.empty())
1472 if (current_type ==
"float")
1474 if (!is_numeric(current_type))
1475 all_numeric =
false;
1477 if (current_type != element_type)
1485 element_type.rfind(
"tuple", 0) == 0 ||
1486 current_type.rfind(
"tuple", 0) == 0)
1493 (element_type.rfind(
"list", 0) == 0) !=
1494 (current_type.rfind(
"list", 0) == 0) ||
1495 (element_type ==
"str") != (current_type ==
"str"))
1502 "Mixed types detected in list literal: {} vs {}. Using 'list[int]' "
1503 "as fallback ({}:{})",
1515 if (mixed && all_numeric)
1516 return saw_float ?
"list[float]" :
"list[int]";
1519 return "list[" + element_type +
"]";
1525template <
class Json>
1526std::string python_annotation<Json>::bare_list_subscript_type(
1527 const Json &var_node,
1528 const std::string &var_name,
1532 return homogeneous_literal_subscript_type(var_node, var_name, is_slice);
1533 const std::string elem =
1535 return elem.empty() ?
"Any" : elem;
1540template <
class Json>
1543 auto is_load = [](
const Json &n) {
1544 return n.contains(
"ctx") && n[
"ctx"].value(
"_type",
"") ==
"Load";
1546 if (node.is_object())
1548 const std::string kind = node.value(
"_type",
"");
1549 if (kind ==
"Name" && node.value(
"id",
"") == name)
1550 return !is_load(node);
1552 kind ==
"Subscript" && node[
"value"].
value(
"_type",
"") ==
"Name" &&
1553 node[
"value"].
value(
"id",
"") == name)
1556 for (
const Json &child : node)
1566template <
class Json>
1567std::string python_annotation<Json>::homogeneous_literal_subscript_type(
1568 const Json &var_node,
1569 const std::string &var_name,
1572 static const std::set<std::string> scalars = {
"bool",
"int",
"float",
"str"};
1573 const Json
value = var_node.value(
"value", Json());
1575 !
value.is_object() ||
value.value(
"_type",
"") !=
"List" ||
1580 for (
const Json &e :
value[
"elts"])
1582 const std::string t = get_argument_type(e);
1583 if (scalars.count(t) == 0 || (!elem.empty() && t != elem))
1587 return is_slice ?
"list[" + elem +
"]" : elem;
1592template <
class Json>
1593std::string python_annotation<Json>::refine_any_from_value(
1594 const std::string &annotated,
1595 const Json &var_node)
1598 annotated !=
"Any" || !var_node.contains(
"value") ||
1599 var_node[
"value"].is_null())
1601 const std::string value_type = get_argument_type(var_node[
"value"]);
1602 return value_type.empty() ? annotated : value_type;
1605template <
class Json>
1606std::string python_annotation<Json>::recover_list_type_from_appends(
1607 const std::string &var_name)
1613 const std::string &scope = get_current_func_name();
1614 const Json &module_body = ast_[
"body"];
1618 (!func.empty() && func.contains(
"body")) ? func[
"body"] : module_body;
1621 if (append_arg.empty())
1628 std::string elem_type = get_argument_type(append_arg);
1629 if (elem_type.empty() || elem_type ==
"Any")
1632 return "list[" + elem_type +
"]";
1635template <
class Json>
1636std::string python_annotation<Json>::arg_dependent_call_type(
const Json &call)
1638 const std::string &func_name = call[
"func"][
"id"];
1642 func_name ==
"hash" && call.contains(
"args") && !call[
"args"].empty() &&
1643 get_argument_type(call[
"args"][0]) !=
"bytes")
1645 return func_name ==
"abs" ? get_builtin_call_type(call) :
"";
1648template <
class Json>
1649std::string python_annotation<Json>::get_builtin_call_type(
const Json &call)
1651 const std::string &func_name = call[
"func"][
"id"];
1653 if (func_name ==
"abs" && call.contains(
"args") && call[
"args"].size() == 1)
1655 const std::string arg_type = get_argument_type(call[
"args"][0]);
1656 if (arg_type ==
"float" || arg_type ==
"complex")
1667template <
class Json>
1668std::string python_annotation<Json>::list_concat_type(
const Json &binop)
1670 const Json &rhs = binop[
"right"];
1671 if (binop[
"op"][
"_type"] !=
"Add")
1673 const std::string callee =
1674 rhs[
"_type"] ==
"Call" ? rhs[
"func"].value(
"id",
"") :
"";
1675 const std::string builder_elem =
1676 callee.rfind(
"_nondet_list_", 0) == 0 ? callee.substr(13) :
"";
1677 std::string right =
"list";
1678 if (rhs[
"_type"] ==
"List")
1679 right = get_list_type_from_literal(rhs);
1681 builder_elem ==
"int" || builder_elem ==
"float" ||
1682 builder_elem ==
"bool" || builder_elem ==
"str")
1683 right =
"list[" + builder_elem +
"]";
1684 const std::string left = get_list_type_from_literal(binop[
"left"]);
1685 return left == right ? left :
"list";
1688template <
class Json>
1689std::string python_annotation<Json>::get_type_from_binary_expr(
1693 std::string
type(
"");
1696 stmt.contains(
"value") ? stmt[
"value"][
"left"] : stmt[
"left"];
1705 stmt.contains(
"value") && stmt[
"value"].contains(
"op") &&
1706 stmt[
"value"][
"op"].contains(
"_type") &&
1707 stmt[
"value"][
"op"][
"_type"] ==
"Div")
1709 const auto &rhs = stmt[
"value"][
"right"];
1710 bool rhs_is_float_literal =
1711 rhs.contains(
"_type") && rhs[
"_type"] ==
"Constant" &&
1712 rhs.contains(
"value") && rhs[
"value"].is_number_float();
1713 bool lhs_is_numeric_shape =
1714 lhs.contains(
"_type") &&
1715 (lhs[
"_type"] ==
"Name" || lhs[
"_type"] ==
"BinOp" ||
1716 lhs[
"_type"] ==
"UnaryOp" ||
1717 (lhs[
"_type"] ==
"Constant" && lhs.contains(
"value") &&
1718 (lhs[
"value"].is_number_integer() || lhs[
"value"].is_number_float())));
1720 rhs_is_float_literal && lhs_is_numeric_shape &&
1721 !is_array_operand(lhs, body))
1732 stmt.contains(
"value") ? stmt[
"value"][
"right"] : stmt[
"right"];
1733 auto operand_is_complex = [&](
const Json &n) ->
bool {
1734 if (!n.is_object() || !n.contains(
"_type"))
1736 if (n[
"_type"] ==
"Constant")
1737 return get_type_from_constant(n) ==
"complex";
1739 return n[
"_type"] ==
"Call" && n.contains(
"func") &&
1740 n[
"func"].is_object() && n[
"func"].contains(
"_type") &&
1741 n[
"func"][
"_type"] ==
"Name" && n[
"func"].contains(
"id") &&
1742 n[
"func"][
"id"] ==
"complex";
1744 if (operand_is_complex(lhs) || operand_is_complex(rhs))
1747 if (!is_array_operand(lhs, body) && is_array_operand(rhs, body))
1751 const Json &
value = stmt.contains(
"value") ? stmt[
"value"] : stmt;
1752 const std::string op =
value[
"op"].value(
"_type",
"");
1754 (op ==
"Add" || op ==
"Mult") && lhs[
"_type"] !=
"List" &&
1755 (rhs[
"_type"] ==
"List" || rhs[
"_type"] ==
"ListComp"))
1759 if (lhs[
"_type"] ==
"BinOp")
1760 type = get_type_from_binary_expr(lhs, body);
1761 else if (lhs[
"_type"] ==
"List")
1762 type = list_concat_type(stmt.contains(
"value") ? stmt[
"value"] : stmt);
1763 else if (lhs[
"_type"] ==
"Tuple")
1767 stmt.contains(
"value") && stmt[
"value"][
"op"][
"_type"] ==
"FloorDiv" &&
1768 !is_array_operand(lhs, body))
1773 if (lhs[
"_type"] ==
"Name")
1777 Json left_op = find_annotated_assign(lhs[
"id"], body[
"body"]);
1781 if (left_op.empty() &&
body.contains(
"args"))
1782 left_op = find_annotated_assign(lhs[
"id"], body[
"args"][
"args"]);
1786 left_op.empty() && current_func !=
nullptr &&
1787 (*current_func).contains(
"args"))
1790 find_annotated_assign(lhs[
"id"], (*current_func)[
"args"][
"args"]);
1793 type = operand_annotation_type(left_op);
1796 type = operand_annotation_type(
1797 find_annotated_assign(lhs[
"id"], ast_[
"body"]));
1799 else if (lhs[
"_type"] ==
"UnaryOp")
1801 const auto &operand = lhs[
"operand"];
1802 if (operand[
"_type"] ==
"Constant")
1803 type = get_type_from_constant(operand);
1805 else if (lhs[
"_type"] ==
"Subscript")
1811 type = resolve_subscript_type(lhs, body);
1813 else if (lhs[
"_type"] ==
"Call" && lhs[
"func"][
"_type"] ==
"Name")
1817 const std::string &func_name = lhs[
"func"][
"id"];
1820 type = get_builtin_call_type(lhs);
1822 type = get_function_return_type(
1825 else if (lhs[
"_type"] ==
"Constant")
1826 type = get_type_from_constant(lhs);
1827 else if (lhs[
"_type"] ==
"Call" && lhs[
"func"][
"_type"] ==
"Attribute")
1828 type = get_type_from_method(lhs);
1829 else if (lhs[
"_type"] ==
"Attribute")
1832 if (lhs[
"value"][
"_type"] ==
"Name" && lhs[
"value"].contains(
"id"))
1834 std::string full_name = lhs[
"value"][
"id"].template get<std::string>() +
1835 "." + lhs[
"attr"].template get<std::string>();
1845 const Json &
value = stmt.contains(
"value") ? stmt[
"value"] : stmt;
1846 const std::string attr_type =
1848 const Json &rhs =
value[
"right"];
1849 const std::string rhs_type = rhs.value(
"_type",
"") ==
"Constant"
1850 ? get_argument_type(rhs)
1851 : self_attribute_type(rhs);
1852 const bool numeric = attr_type ==
"int" || attr_type ==
"float";
1853 const bool rhs_fits =
1854 numeric ? rhs_type ==
"int" || rhs_type ==
"float" || rhs_type ==
"bool"
1855 : rhs_type ==
"int" || rhs_type == attr_type;
1857 !attr_type.empty() && attr_type !=
"bool" && rhs_fits &&
1858 value[
"op"].value(
"_type",
"") !=
"Pow")
1867 stmt.contains(
"value") ? stmt[
"value"][
"right"] : stmt[
"right"];
1869 if (rhs[
"_type"] ==
"Constant")
1870 type = get_type_from_constant(rhs);
1871 else if (rhs[
"_type"] ==
"Name")
1872 type = operand_annotation_type(find_annotated_assign(
1873 rhs[
"id"],
body.contains(
"body") ? body[
"body"] : ast_[
"body"]));
1876 type.empty() && stmt.contains(
"value") && stmt[
"value"].contains(
"op") &&
1877 stmt[
"value"][
"op"].contains(
"_type"))
1883 return promote_to_float_operand(stmt, type);
1889template <
class Json>
1890std::string python_annotation<Json>::operand_annotation_type(
const Json &decl)
1892 if (decl.empty() || !decl.contains(
"annotation"))
1894 const Json &annotation = decl[
"annotation"];
1895 if (annotation.contains(
"id") && annotation[
"id"].is_string())
1896 return annotation[
"id"];
1898 const std::string base_name =
base.substr(0,
base.find(
'['));
1899 return base_name ==
"list" || base_name ==
"tuple" ? base_name :
"";
1904template <
class Json>
1905bool python_annotation<Json>::is_array_operand(
1906 const Json &operand,
1909 const std::string
type = operand.value(
"_type", std::string());
1911 return is_numpy_array_ctor_call(operand) ||
1913 static_cast<const nlohmann::json &
>(operand),
1914 static_cast<const nlohmann::json &
>(ast_))
1918 const std::string
name = operand[
"id"].template get<std::string>();
1919 Json decl = find_annotated_assign(
1920 name,
body.contains(
"body") ? body[
"body"] : ast_[
"body"]);
1922 decl = find_annotated_assign(name, ast_[
"body"]);
1923 return !decl.empty() && decl.contains(
"annotation") &&
1924 decl[
"annotation"].value(
"id", std::string()) ==
"list";
1930template <
class Json>
1933 const std::string &type)
1935 static const std::set<std::string> arithmetic{
1936 "Add",
"Sub",
"Mult",
"FloorDiv",
"Mod",
"Pow"};
1937 const Json &expr = stmt.contains(
"value") ? stmt[
"value"] : stmt;
1939 (type !=
"int" && type !=
"bool") || !expr.is_object() ||
1940 !expr.contains(
"op") || !expr[
"op"].is_object())
1942 const std::string op = expr[
"op"].value(
"_type",
"");
1945 return arithmetic.count(op) != 0 && get_operand_type(expr[
"right"]) ==
"float"
1950template <
class Json>
1952python_annotation<Json>::infer_lambda_return_type(
const Json &lambda_elem)
const
1954 const Json &lambda_body = lambda_elem[
"value"][
"body"];
1955 if (lambda_body[
"_type"] ==
"BinOp")
1957 else if (lambda_body[
"_type"] ==
"Compare")
1964template <
class Json>
1967 if (!slice.is_object() || !slice.contains(
"elts"))
1969 for (
const auto &elt : slice[
"elts"])
1970 if (elt.value(
"_type",
"") ==
"Constant" && elt[
"value"].is_null())
1975template <
class Json>
1976std::string python_annotation<Json>::get_function_return_type(
1977 const std::string &func_name,
1981 if (functions_in_analysis_.count(func_name) > 0)
1988 if (functions_reentered_.count(func_name) > 0)
1990 struct reentry_scope
1992 std::set<std::string> &set;
1993 const std::string &
name;
1998 } scope{functions_reentered_, func_name};
1999 functions_reentered_.insert(func_name);
2003 for (
const Json &elem : ast[
"body"])
2005 if (elem[
"_type"] ==
"FunctionDef" && elem[
"name"] == func_name)
2008 bool should_override =
2009 config.options.get_bool_option(
"override-return-annotation");
2013 !should_override && elem.contains(
"returns") &&
2014 !elem[
"returns"].is_null() && elem[
"returns"].contains(
"id"))
2015 return elem[
"returns"][
"id"];
2020 std::string saved_rec_ctx = current_func_name_context_;
2021 current_func_name_context_ = func_name;
2022 std::string inferred =
2023 infer_from_return_statements(elem[
"body"], func_name);
2024 current_func_name_context_ = saved_rec_ctx;
2032 functions_in_analysis_.insert(func_name);
2035 if (func_name.find(
"nondet_") == 0)
2037 size_t last_underscore_pos = func_name.find_last_of(
'_');
2038 if (last_underscore_pos != std::string::npos)
2040 functions_in_analysis_.erase(func_name);
2041 return func_name.substr(last_underscore_pos + 1);
2046 bool should_override =
2047 config.options.get_bool_option(
"override-return-annotation");
2053 if (func_elem.empty() && current_func !=
nullptr)
2059 if (!func_elem.empty())
2062 if (!should_override)
2065 if (func_elem.contains(
"returns") && !func_elem[
"returns"].is_null())
2067 const auto &returns = func_elem[
"returns"];
2070 if (returns.contains(
"_type"))
2072 const std::string &return_type = returns[
"_type"];
2075 if (return_type ==
"Subscript")
2077 functions_in_analysis_.erase(func_name);
2082 returns.contains(
"value") && returns[
"value"].contains(
"id") &&
2083 (returns[
"value"][
"id"] ==
"Optional" ||
2084 (returns[
"value"][
"id"] ==
"Union" &&
2087 if (returns.contains(
"value") && returns[
"value"].contains(
"id"))
2088 return returns[
"value"][
"id"];
2093 else if (return_type ==
"Constant")
2095 functions_in_analysis_.erase(func_name);
2096 if (returns.contains(
"value") && returns[
"value"].is_null())
2098 else if (returns.contains(
"value") && returns[
"value"].is_string())
2104 std::string type_name =
2105 returns[
"value"].template get<std::string>();
2106 bool valid = !type_name.empty() &&
2107 (std::isalpha((
unsigned char)type_name[0]) ||
2108 type_name[0] ==
'_');
2109 for (
size_t i = 1; valid && i < type_name.size(); ++i)
2110 valid = std::isalnum((
unsigned char)type_name[i]) ||
2111 type_name[i] ==
'_' || type_name[i] ==
'.';
2117 else if (returns.contains(
"value"))
2124 if (returns.contains(
"id"))
2131 functions_in_analysis_.erase(func_name);
2134 functions_in_analysis_.erase(func_name);
2135 return returns[
"id"];
2143 "Unrecognized return type annotation for function "
2146 functions_in_analysis_.erase(func_name);
2160 std::string saved_return_ctx = current_func_name_context_;
2161 current_func_name_context_ = func_name;
2162 bool divergent =
false;
2163 std::string inferred_type =
2164 infer_from_return_statements(func_elem[
"body"], func_name, &divergent);
2165 const std::string optional_return =
2166 returned_optional_scalar(func_elem, func_elem[
"body"]);
2167 current_func_name_context_ = saved_return_ctx;
2169 if (optional_return ==
"Optional[" + inferred_type +
"]")
2171 functions_in_analysis_.erase(func_name);
2172 return optional_return;
2175 if (!inferred_type.empty() && inferred_type !=
"NoneType")
2181 functions_in_analysis_.erase(func_name);
2184 functions_in_analysis_.erase(func_name);
2185 return inferred_type;
2190 if (!return_node.empty())
2197 functions_in_analysis_.erase(func_name);
2201 std::string fallback_type;
2202 std::string saved_fallback_ctx = current_func_name_context_;
2203 current_func_name_context_ = func_name;
2206 infer_type(return_node, func_elem, fallback_type);
2208 catch (std::runtime_error &)
2213 current_func_name_context_ = saved_fallback_ctx;
2214 functions_in_analysis_.erase(func_name);
2217 return fallback_type ==
"Any" ?
"" : fallback_type;
2221 functions_in_analysis_.erase(func_name);
2229 if (lambda_elem.empty() && current_func !=
nullptr)
2232 if (!lambda_elem.empty())
2234 functions_in_analysis_.erase(func_name);
2235 return infer_lambda_return_type(lambda_elem);
2241 if (module_manager_)
2243 const auto &import_node =
2248 if (!import_node.contains(
"module") || import_node[
"module"].is_null())
2249 throw std::runtime_error(
"import has no resolvable module name");
2250 auto module = module_manager_->get_module(import_node["module"]);
2253 throw std::runtime_error(
"module not found");
2258 auto func_info =
module->get_function(func_name);
2262 func_info.return_type_.empty() ||
2263 func_info.return_type_ ==
"NoneType")
2267 functions_in_analysis_.erase(func_name);
2268 return module->get_class(func_name).name_.empty()
2269 ? func_info.return_type_
2273 functions_in_analysis_.erase(func_name);
2274 return func_info.return_type_;
2276 catch (std::runtime_error &)
2279 functions_in_analysis_.erase(func_name);
2284 catch (std::runtime_error &)
2292 if (std::string t = resolve_wildcard_import_func(func_name); !t.empty())
2294 functions_in_analysis_.erase(func_name);
2302 functions_in_analysis_.erase(func_name);
2312 functions_in_analysis_.erase(func_name);
2323 !decl.empty() && decl.contains(
"value") && decl[
"value"].is_object() &&
2324 decl[
"value"].value(
"_type",
"") ==
"Name" &&
2325 decl[
"value"].contains(
"id"))
2327 const std::string target =
2328 decl[
"value"][
"id"].template get<std::string>();
2331 functions_in_analysis_.erase(func_name);
2337 functions_in_analysis_.erase(func_name);
2339 std::ostringstream oss;
2340 oss <<
"Function \"" << func_name <<
"\" not found (" << python_filename_
2341 <<
" line " << current_line_ <<
")";
2343 throw std::runtime_error(oss.str());
2346template <
class Json>
2347std::string python_annotation<Json>::get_type_from_rhs_variable(
2348 const Json &element,
2351 const auto &value_type = element[
"value"][
"_type"];
2355 value_type ==
"Subscript" &&
2356 element[
"value"][
"value"][
"_type"] ==
"Constant" &&
2357 element[
"value"][
"value"][
"value"].
is_string())
2363 if (value_type ==
"Subscript")
2364 return resolve_subscript_type(element[
"value"], body);
2366 std::string rhs_var_name;
2368 if (value_type ==
"Name")
2369 rhs_var_name = element[
"value"][
"id"];
2370 else if (value_type ==
"UnaryOp")
2371 rhs_var_name = element[
"value"][
"operand"][
"id"];
2375 assert(!rhs_var_name.empty());
2377 auto extract_lhs_name = [](
const Json &stmt) -> std::string {
2379 stmt.contains(
"_type") && stmt[
"_type"] ==
"Assign" &&
2380 stmt.contains(
"targets") && !stmt[
"targets"].empty() &&
2381 stmt[
"targets"][0].contains(
"_type") &&
2382 stmt[
"targets"][0][
"_type"] ==
"Name" &&
2383 stmt[
"targets"][0].contains(
"id"))
2384 return stmt[
"targets"][0][
"id"].template get<std::string>();
2387 stmt.contains(
"_type") && stmt[
"_type"] ==
"AnnAssign" &&
2388 stmt.contains(
"target") && stmt[
"target"].contains(
"_type") &&
2389 stmt[
"target"][
"_type"] ==
"Name" && stmt[
"target"].contains(
"id"))
2390 return stmt[
"target"][
"id"].template get<std::string>();
2395 const std::string lhs_name = extract_lhs_name(element);
2396 if (!lhs_name.empty() && lhs_name == rhs_var_name)
2398 std::string lhs_type = get_type_from_lhs(lhs_name, body);
2399 if (!lhs_type.empty())
2404 if (resolving_rhs_vars_.contains(rhs_var_name))
2406 std::string lhs_type = get_type_from_lhs(rhs_var_name, body);
2407 if (!lhs_type.empty())
2412 resolving_rhs_vars_.insert(rhs_var_name);
2415 std::set<std::string> &vars;
2421 } erase_guard{resolving_rhs_vars_, rhs_var_name};
2424 Json rhs_node = find_annotated_assign(rhs_var_name, body[
"body"]);
2427 if (rhs_node.empty())
2431 if (infer_type(var, body, type) == InferResult::OK && !
type.empty())
2436 if (rhs_node.empty() && current_func)
2437 rhs_node = find_annotated_assign(rhs_var_name, (*current_func)[
"body"]);
2441 rhs_node.empty() && current_func !=
nullptr &&
2442 (*current_func).contains(
"args"))
2445 find_annotated_assign(rhs_var_name, (*current_func)[
"args"][
"args"]);
2447 if (rhs_node.empty() && (*current_func)[
"args"].contains(
"kwonlyargs"))
2448 rhs_node = find_annotated_assign(
2449 rhs_var_name, (*current_func)[
"args"][
"kwonlyargs"]);
2453 if (rhs_node.empty() &&
body.contains(
"args"))
2455 rhs_node = find_annotated_assign(rhs_var_name, body[
"args"][
"args"]);
2457 if (rhs_node.empty() && body[
"args"].contains(
"kwonlyargs"))
2459 find_annotated_assign(rhs_var_name, body[
"args"][
"kwonlyargs"]);
2465 if (rhs_node.empty() && parent_func !=
nullptr)
2468 (*parent_func).contains(
"args") &&
2469 (*parent_func)[
"args"].contains(
"args"))
2472 find_annotated_assign(rhs_var_name, (*parent_func)[
"args"][
"args"]);
2473 if (rhs_node.empty() && (*parent_func)[
"args"].contains(
"kwonlyargs"))
2474 rhs_node = find_annotated_assign(
2475 rhs_var_name, (*parent_func)[
"args"][
"kwonlyargs"]);
2478 if (rhs_node.empty())
2479 rhs_node = find_annotated_assign(rhs_var_name, (*parent_func)[
"body"]);
2483 if (rhs_node.empty())
2484 rhs_node = find_annotated_assign(rhs_var_name, ast_[
"body"]);
2486 if (rhs_node.empty())
2499 static const std::unordered_set<std::string> iterable_builtins = {
2500 "range",
"enumerate",
"zip"};
2501 if (iterable_builtins.count(rhs_var_name))
2516 if (reads_import(rhs_var_name, element[
"value"]))
2519 const auto &
lineno = element[
"lineno"].template get<int>();
2521 std::ostringstream oss;
2522 oss <<
"Type inference failed at line " <<
lineno;
2523 oss <<
". Variable " << rhs_var_name <<
" not found";
2525 throw std::runtime_error(oss.str());
2528 if (value_type ==
"Subscript")
2532 rhs_node.contains(
"annotation") && !rhs_node[
"annotation"].is_null() &&
2533 rhs_node[
"annotation"].contains(
"id"))
2536 const std::string &var_type = rhs_node[
"annotation"][
"id"];
2539 if (var_type ==
"str")
2542 else if (var_type ==
"list")
2544 if (!rhs_node[
"value"].is_null())
2545 return get_list_subtype(rhs_node[
"value"]);
2559 rhs_node.contains(
"annotation") && !rhs_node[
"annotation"].is_null() &&
2560 rhs_node[
"annotation"].contains(
"id") &&
2561 !rhs_node[
"annotation"][
"id"].is_null())
2563 return rhs_node[
"annotation"][
"id"];
2571template <
class Json>
2572bool python_annotation<Json>::holds_callable_value(
2573 const std::string &name)
const
2575 const nlohmann::json &
body =
2576 static_cast<const nlohmann::json &
>(ast_[
"body"]);
2577 for (
const auto &stmt : body)
2579 if (!stmt.contains(
"_type"))
2581 const std::string &stype = stmt[
"_type"];
2583 if (stype ==
"Assign" && stmt.contains(
"targets"))
2585 for (
const auto &t : stmt[
"targets"])
2586 if (t.contains(
"id") && t[
"id"] == name)
2591 stype ==
"AnnAssign" && stmt[
"target"].
value(
"id",
"") == name &&
2593 body, stmt[
"annotation"].
value(
"id",
""),
"__call__"))
2597 return current_func !=
nullptr &&
2601template <
class Json>
2602std::string python_annotation<Json>::get_type_from_call(
const Json &element)
2604 const Json &func = element[
"value"][
"func"];
2607 if (func[
"_type"] ==
"Name")
2609 const std::string &func_id = func[
"id"];
2612 const std::string copied =
2613 is_user_class ?
"" : list_copy_type(func_id, element[
"value"]);
2614 if (!copied.empty())
2629 std::string overload_type =
2630 resolve_overload_return_type(func_id, element[
"value"]);
2631 if (!overload_type.empty())
2632 return overload_type;
2640 return get_function_return_type(func_id, ast_);
2642 catch (std::runtime_error &)
2645 if (is_imported_name(func_id))
2648 const std::string type =
2649 function_value_return_type(func, &element[
"value"]);
2652 if (holds_callable_value(func_id))
2660 else if (func[
"_type"] ==
"Attribute" && func[
"value"][
"_type"] ==
"Name")
2662 const std::string &class_name = func[
"value"][
"id"];
2663 const std::string &method_name = func[
"attr"];
2669 if (class_name ==
"int" && method_name ==
"from_bytes")
2671 else if (class_name ==
"int" && method_name ==
"to_bytes")
2674 class_name ==
"str" &&
2675 (method_name ==
"join" || method_name ==
"format"))
2678 class_name ==
"bytes" &&
2679 (method_name ==
"decode" || method_name ==
"hex"))
2681 else if (class_name ==
"bytes" && method_name ==
"fromhex")
2683 else if (class_name ==
"list" && method_name ==
"copy")
2686 class_name ==
"dict" &&
2687 (method_name ==
"copy" || method_name ==
"fromkeys"))
2693 if (module_manager_)
2695 auto module = module_manager_->get_module(class_name);
2698 auto overloads_funcs =
module->overloads();
2699 std::vector<Json> overloads;
2701 for (
const auto &elem : overloads_funcs)
2703 if (elem[
"_type"] ==
"FunctionDef" && elem[
"name"] == method_name)
2704 overloads.push_back(elem);
2707 return match_literal_argument(element[
"value"], overloads);
2717template <
class Json>
2718std::optional<std::pair<Json, std::string>>
2719python_annotation<Json>::function_signature(
2723 const Json &
args = def[
"args"];
2725 !def[
"decorator_list"].
empty() || !args[
"posonlyargs"].
empty() ||
2726 !args[
"kwonlyargs"].
empty() || !args[
"vararg"].is_null() ||
2727 !args[
"kwarg"].is_null())
2728 return std::nullopt;
2731 Json params = Json::array();
2732 const std::size_t first_default =
2733 args[
"args"].size() -
args[
"defaults"].size();
2734 for (std::size_t i = 0; i <
args[
"args"].size(); ++i)
2736 {{
"annotation", args[
"args"][i][
"annotation"]},
2737 {
"default", i >= first_default}});
2739 const std::string
name = def[
"name"];
2740 const Json &returns = def[
"returns"];
2741 std::string returned;
2743 returned = returns.is_object() && returns.value(
"_type",
"") ==
"Name"
2744 ? returns[
"id"].template get<std::string>()
2750 returned = get_function_return_type(name, ast_);
2752 catch (std::runtime_error &)
2756 if (returned.empty() || returned ==
"Any")
2757 return std::nullopt;
2758 return std::make_pair(params, returned);
2764template <
class Json>
2765std::string python_annotation<Json>::function_value_return_type(
2770 const auto functions = values.of(
value);
2773 std::optional<std::pair<Json, std::string>> agreed;
2780 functions_in_analysis_.count((*
function.def)[
"name"]) > 0)
2782 const auto signature = function_signature(
function);
2784 !signature || (agreed && (signature->second != agreed->second ||
2786 signature->first, agreed->first))))
2792 const auto is_starred = [](
const Json &arg) {
2793 return arg.value(
"_type",
"") ==
"Starred";
2795 const auto required = std::count_if(
2796 agreed->first.begin(), agreed->first.end(), [](
const Json ¶m) {
2797 return !param[
"default"].template get<bool>();
2799 const std::size_t passed = call ? (*call)[
"args"].size() : 0;
2802 (passed <
static_cast<std::size_t
>(required) ||
2803 passed > agreed->first.size() || !(*call)[
"keywords"].empty() ||
2804 std::any_of((*call)[
"args"].begin(), (*call)[
"args"].end(), is_starred)))
2806 return agreed->second;
2809template <
class Json>
2810std::string python_annotation<Json>::method_return_type(
2812 const std::string &class_name)
2814 if (member.contains(
"returns") && !member[
"returns"].is_null())
2816 const Json &ret = member[
"returns"];
2817 if (ret.contains(
"id"))
2818 return ret[
"id"].template get<std::string>();
2820 ret.contains(
"_type") && ret[
"_type"] ==
"Subscript" &&
2821 ret.contains(
"value") && ret[
"value"].contains(
"id"))
2822 return ret[
"value"][
"id"].template get<std::string>();
2824 std::string inferred = infer_method_return_type(member, class_name);
2825 return inferred.empty() ?
"Any" : inferred;
2828template <
class Json>
2829std::string python_annotation<Json>::infer_method_return_type(
2831 const std::string &class_name)
2833 const std::string method_name = member[
"name"].template get<std::string>();
2834 const std::string saved_ctx = current_func_name_context_;
2835 current_func_name_context_ = class_name +
"@C@" + method_name;
2836 std::string inferred =
2837 infer_from_return_statements(member[
"body"], method_name);
2838 current_func_name_context_ = saved_ctx;
2842template <
class Json>
2843std::optional<std::string>
2844python_annotation<Json>::get_type_from_literal_method(
const Json &call)
2846 if (!call[
"func"].contains(
"value"))
2847 return std::nullopt;
2848 const Json &receiver = call[
"func"][
"value"];
2850 if (receiver[
"_type"] ==
"JoinedStr" && call[
"func"].contains(
"attr"))
2851 return get_string_method_return_type(call[
"func"][
"attr"]);
2852 if (receiver[
"_type"] !=
"Constant")
2853 return std::nullopt;
2855 std::string obj_type = get_type_from_constant(receiver);
2858 if (obj_type ==
"str" && call[
"func"].contains(
"attr"))
2860 const std::string &method = call[
"func"][
"attr"];
2861 return get_string_method_return_type(method);
2866 obj_type ==
"bytes" && call[
"func"].contains(
"attr") &&
2867 call[
"func"][
"attr"] ==
"hex")
2872 obj_type ==
"int" && call[
"func"].contains(
"attr") &&
2873 call[
"func"][
"attr"] ==
"to_bytes")
2878 obj_type ==
"bytes" && call[
"func"].contains(
"attr") &&
2879 call[
"func"][
"attr"] ==
"decode")
2885template <
class Json>
2886std::string python_annotation<Json>::get_type_from_method(
const Json &call)
2888 std::string
type(
"");
2895 call[
"func"].contains(
"value") && call[
"func"][
"value"].is_object() &&
2896 call[
"func"][
"value"].
value(
"_type", std::string()) ==
"Name" &&
2897 call[
"func"][
"value"].
value(
"id", std::string()) ==
"random" &&
2898 call[
"func"].contains(
"attr"))
2900 const std::string &method = call[
"func"][
"attr"];
2901 if (method ==
"choice")
2903 if (method ==
"sample")
2909 if (
const auto literal_type = get_type_from_literal_method(call))
2910 return *literal_type;
2914 call[
"func"].contains(
"value") &&
2915 call[
"func"][
"value"][
"_type"] ==
"JoinedStr" &&
2916 call[
"func"].contains(
"attr"))
2917 return get_string_method_return_type(call[
"func"][
"attr"]);
2921 call[
"func"].contains(
"value") && call[
"func"][
"value"][
"_type"] ==
"BinOp")
2923 std::string obj_type =
2924 get_type_from_binary_expr(call[
"func"][
"value"], ast_);
2925 if (obj_type ==
"str" && call[
"func"].contains(
"attr"))
2927 const std::string &method = call[
"func"][
"attr"];
2928 return get_string_method_return_type(method);
2930 if (!obj_type.empty())
2938 if (call[
"func"].contains(
"value"))
2940 const std::string &recv_kind =
2941 call[
"func"][
"value"].value(
"_type", std::string());
2942 if (recv_kind ==
"Dict")
2944 const std::string method =
2945 call[
"func"].contains(
"attr")
2946 ? call[
"func"][
"attr"].template get<std::string>()
2950 if (method ==
"setdefault" || method ==
"get" || method ==
"pop")
2958 if (method ==
"keys" || method ==
"values" || method ==
"items")
2967 call[
"func"].contains(
"value") &&
2968 call[
"func"][
"value"][
"_type"] ==
"Subscript")
2970 std::string method = call[
"func"].contains(
"attr")
2971 ? call[
"func"][
"attr"].template get<std::string>()
2973 if (method ==
"pop")
2976 const auto &subscript = call[
"func"][
"value"];
2978 subscript.contains(
"value") && subscript[
"value"][
"_type"] ==
"Name" &&
2979 subscript[
"value"].contains(
"id"))
2981 const std::string &base_name =
2982 subscript[
"value"][
"id"].template get<std::string>();
2985 if (!base_decl.empty() && base_decl.contains(
"value"))
2987 const Json &rhs = base_decl[
"value"];
2989 if (rhs[
"_type"] ==
"ListComp" && rhs.contains(
"elt"))
2991 std::string inner_type = get_argument_type(rhs[
"elt"]);
2994 inner_type.size() > 5 && inner_type.substr(0, 5) ==
"list[" &&
2995 inner_type.back() ==
']')
2997 return inner_type.substr(5, inner_type.size() - 6);
3007 const std::string &obj = get_object_name(call[
"func"], std::string());
3008 std::string attr_name = call[
"func"].contains(
"attr")
3009 ? call[
"func"][
"attr"].template get<std::string>()
3016 if (obj ==
"super" && !attr_name.empty() && !current_class_name_.empty())
3020 !class_node.empty() && class_node.contains(
"bases") &&
3021 !class_node[
"bases"].empty() && class_node[
"bases"][0].contains(
"id"))
3023 const std::string base_name =
3024 class_node[
"bases"][0][
"id"].template get<std::string>();
3026 if (!base_class.empty())
3028 for (
const Json &member : base_class[
"body"])
3030 if (member[
"_type"] !=
"FunctionDef" || member[
"name"] != attr_name)
3032 return method_return_type(member, base_name);
3045 if (obj ==
"self" && !attr_name.empty() && !current_class_name_.empty())
3050 std::set<std::string> visited;
3051 for (std::string cls = current_class_name_;
3052 !cls.empty() && visited.insert(cls).second;)
3055 if (class_node.empty() || !class_node.contains(
"body"))
3057 for (
const Json &member : class_node[
"body"])
3059 if (member[
"_type"] !=
"FunctionDef" || member[
"name"] != attr_name)
3061 return method_return_type(member, cls);
3067 class_node.contains(
"bases") && !class_node[
"bases"].empty() &&
3068 class_node[
"bases"][0].contains(
"id"))
3069 cls = class_node[
"bases"][0][
"id"].template get<std::string>();
3074 if (attr_name ==
"keys" || attr_name ==
"values")
3081 obj_node.empty() && current_func !=
nullptr &&
3082 (*current_func).contains(
"args") &&
3083 (*current_func)[
"args"].contains(
"args"))
3085 obj_node = find_annotated_assign(obj, (*current_func)[
"args"][
"args"]);
3089 !obj_node.empty() && obj_node.contains(
"annotation") &&
3090 !obj_node[
"annotation"].is_null())
3092 const Json &annotation = obj_node[
"annotation"];
3096 if (annotation.contains(
"id"))
3097 base_type = annotation[
"id"].template get<std::string>();
3100 annotation.contains(
"_type") && annotation[
"_type"] ==
"Subscript" &&
3101 annotation.contains(
"value") && annotation[
"value"].contains(
"id"))
3102 base_type = annotation[
"value"][
"id"].template get<std::string>();
3109 annotation.contains(
"_type") && annotation[
"_type"] ==
"Subscript" &&
3110 annotation.contains(
"slice"))
3112 const Json &slice = annotation[
"slice"];
3116 slice.contains(
"_type") && slice[
"_type"] ==
"Tuple" &&
3117 slice.contains(
"elts") && slice[
"elts"].size() >= 2)
3119 std::string key_type, value_type;
3122 if (slice[
"elts"][0].contains(
"id"))
3123 key_type = slice[
"elts"][0][
"id"].template get<std::string>();
3126 if (slice[
"elts"][1].contains(
"id"))
3127 value_type = slice[
"elts"][1][
"id"].template get<std::string>();
3130 if (attr_name ==
"keys" && !key_type.empty())
3131 return "list[" + key_type +
"]";
3132 else if (attr_name ==
"values" && !value_type.empty())
3133 return "list[" + value_type +
"]";
3141 if (module_manager_)
3144 auto module = module_manager_->get_module(obj);
3147 if (!
module && !obj.empty())
3150 if (resolved_obj != obj)
3152 module = module_manager_->get_module(resolved_obj);
3159 function func =
module->get_function(attr_name);
3160 if (!func.
name_.empty())
3167 if (!cls.
name_.empty())
3179 Json obj_node = find_receiver_decl(obj);
3183 obj_node.empty() && current_func !=
nullptr &&
3184 (*current_func).contains(
"args") &&
3185 (*current_func)[
"args"].contains(
"args"))
3187 obj_node = find_annotated_assign(obj, (*current_func)[
"args"][
"args"]);
3191 if (obj_node.empty() && call[
"func"][
"value"][
"_type"] ==
"Attribute")
3195 std::function<std::string(
const Json &, std::vector<std::string> &)>
3196 extract_attr_chain =
3197 [&](
const Json &node, std::vector<std::string> &chain) -> std::string {
3198 if (node[
"_type"] ==
"Attribute")
3200 std::string attr = node[
"attr"].template get<std::string>();
3201 chain.push_back(attr);
3202 return extract_attr_chain(node[
"value"], chain);
3204 else if (node[
"_type"] ==
"Name" && node.contains(
"id"))
3206 return node[
"id"].template get<std::string>();
3211 std::vector<std::string> attr_chain;
3212 std::string base_obj =
3213 extract_attr_chain(call[
"func"][
"value"], attr_chain);
3217 std::reverse(attr_chain.begin(), attr_chain.end());
3222 std::string initial_type;
3223 if (base_obj ==
"self" && current_func !=
nullptr)
3225 for (
const Json &elem : ast_[
"body"])
3227 if (elem[
"_type"] ==
"ClassDef" && elem.contains(
"body"))
3229 for (
const Json &member : elem[
"body"])
3232 member[
"_type"] ==
"FunctionDef" &&
3233 member[
"name"] == (*current_func)[
"name"])
3235 initial_type = elem[
"name"].template get<std::string>();
3239 if (!initial_type.empty())
3244 else if (!base_obj.empty())
3248 if (!var_node.empty())
3252 var_node.contains(
"annotation") &&
3253 !var_node[
"annotation"].is_null() &&
3254 var_node[
"annotation"].contains(
"id"))
3256 var_node[
"annotation"][
"id"].template get<std::string>();
3259 var_node.contains(
"value") && var_node[
"value"].contains(
"_type") &&
3260 var_node[
"value"][
"_type"] ==
"Call" &&
3261 var_node[
"value"].contains(
"func") &&
3262 var_node[
"value"][
"func"].contains(
"_type") &&
3263 var_node[
"value"][
"func"][
"_type"] ==
"Name" &&
3264 var_node[
"value"][
"func"].contains(
"id"))
3266 var_node[
"value"][
"func"][
"id"].template get<std::string>();
3270 initial_type.empty() && current_func !=
nullptr &&
3271 (*current_func).contains(
"args") &&
3272 (*current_func)[
"args"].contains(
"args"))
3275 find_annotated_assign(base_obj, (*current_func)[
"args"][
"args"]);
3277 !param.empty() && param.contains(
"annotation") &&
3278 !param[
"annotation"].is_null() && param[
"annotation"].contains(
"id"))
3279 initial_type = param[
"annotation"][
"id"].template get<std::string>();
3283 if (!initial_type.empty() && !attr_chain.empty())
3285 std::string current_type = initial_type;
3286 for (
size_t i = 0; i < attr_chain.size(); ++i)
3288 const std::string &chain_attr = attr_chain[i];
3290 if (current_class.empty())
3294 for (
const Json &member : current_class[
"body"])
3296 if (member[
"_type"] !=
"FunctionDef" || member[
"name"] !=
"__init__")
3298 for (
const Json &stmt : member[
"body"])
3302 stmt.contains(
"_type") && stmt[
"_type"] ==
"AnnAssign" &&
3303 stmt.contains(
"target") && stmt[
"target"].is_object() &&
3304 stmt[
"target"].contains(
"_type") &&
3305 stmt[
"target"][
"_type"] ==
"Attribute" &&
3306 stmt[
"target"].contains(
"value") &&
3307 stmt[
"target"][
"value"].is_object() &&
3308 stmt[
"target"][
"value"].contains(
"id") &&
3309 stmt[
"target"][
"value"][
"id"] ==
"self" &&
3310 stmt[
"target"][
"attr"] == chain_attr &&
3311 stmt.contains(
"annotation") && !stmt[
"annotation"].is_null() &&
3312 stmt[
"annotation"].contains(
"id") &&
3313 !stmt[
"annotation"][
"id"].is_null())
3316 stmt[
"annotation"][
"id"].template get<std::string>();
3322 stmt.contains(
"_type") && stmt[
"_type"] ==
"Assign" &&
3323 stmt.contains(
"targets") && stmt[
"targets"].is_array() &&
3324 !stmt[
"targets"].empty() && stmt[
"targets"][0].is_object() &&
3325 stmt[
"targets"][0].contains(
"_type") &&
3326 stmt[
"targets"][0][
"_type"] ==
"Attribute" &&
3327 stmt[
"targets"][0].contains(
"value") &&
3328 stmt[
"targets"][0][
"value"].is_object() &&
3329 stmt[
"targets"][0][
"value"].contains(
"id") &&
3330 stmt[
"targets"][0][
"value"][
"id"] ==
"self" &&
3331 stmt[
"targets"][0][
"attr"] == chain_attr &&
3332 stmt.contains(
"value") && stmt[
"value"].contains(
"_type") &&
3333 stmt[
"value"][
"_type"] ==
"Call" &&
3334 stmt[
"value"].contains(
"func") &&
3335 stmt[
"value"][
"func"].contains(
"_type") &&
3336 stmt[
"value"][
"func"][
"_type"] ==
"Name" &&
3337 stmt[
"value"][
"func"].contains(
"id"))
3340 stmt[
"value"][
"func"][
"id"].template get<std::string>();
3352 if (i == attr_chain.size() - 1)
3355 if (!final_class.empty())
3357 const std::string method_name =
3358 call[
"func"][
"attr"].template get<std::string>();
3359 for (
const Json &method : final_class[
"body"])
3362 method[
"_type"] ==
"FunctionDef" &&
3363 method[
"name"] == method_name)
3364 return method_return_type(method, current_type);
3376 if (obj_node.empty())
3381 obj.empty() && call[
"func"].contains(
"value") &&
3382 call[
"func"][
"value"].contains(
"_type") &&
3383 call[
"func"][
"value"][
"_type"] ==
"BinOp")
3385 std::string inferred_expr_type =
3386 get_type_from_binary_expr(call[
"func"][
"value"], ast_);
3387 return inferred_expr_type.empty() ?
"Any" : inferred_expr_type;
3392 if (!class_node.empty())
3394 const std::string &method_name = call[
"func"][
"attr"];
3396 for (
const Json &member : class_node[
"body"])
3398 if (member[
"_type"] ==
"FunctionDef" && member[
"name"] == method_name)
3401 if (member.contains(
"returns") && !member[
"returns"].is_null())
3403 const Json &ret = member[
"returns"];
3405 if (ret.contains(
"id"))
3406 return ret[
"id"].template get<std::string>();
3409 ret.contains(
"_type") && ret[
"_type"] ==
"Subscript" &&
3410 ret.contains(
"value") && ret[
"value"].contains(
"id"))
3411 return ret[
"value"][
"id"].template get<std::string>();
3413 std::string inferred_type = infer_method_return_type(member, obj);
3414 if (!inferred_type.empty())
3415 return inferred_type;
3425 call[
"func"].contains(
"value") &&
3426 call[
"func"][
"value"].contains(
"_type") &&
3427 call[
"func"][
"value"][
"_type"] ==
"Call")
3432 if (is_imported_name(obj))
3435 throw std::runtime_error(
"Object \"" + obj +
"\" not found.");
3438 std::string obj_type;
3440 obj_node.contains(
"annotation") && !obj_node[
"annotation"].is_null() &&
3441 obj_node[
"annotation"].contains(
"id") &&
3442 !obj_node[
"annotation"][
"id"].is_null())
3444 obj_type = obj_node[
"annotation"][
"id"].template get<std::string>();
3453 obj_type ==
"str" && call[
"func"].contains(
"attr") &&
3454 (call[
"func"][
"attr"] ==
"split" || call[
"func"][
"attr"] ==
"rsplit"))
3458 obj_type ==
"bytes" && call[
"func"].contains(
"attr") &&
3459 call[
"func"][
"attr"] ==
"hex")
3467 obj_type ==
"int" && call[
"func"].contains(
"attr") &&
3468 call[
"func"][
"attr"] ==
"to_bytes")
3472 obj_type ==
"str" && call[
"func"].contains(
"attr") &&
3473 call[
"func"][
"attr"] ==
"encode")
3476 obj_type ==
"bytes" && call[
"func"].contains(
"attr") &&
3477 call[
"func"][
"attr"] ==
"decode")
3481 (obj_type ==
"str" || obj_type ==
"list" || obj_type ==
"bytes") &&
3482 call[
"func"].contains(
"attr") &&
3483 (call[
"func"][
"attr"] ==
"index" || call[
"func"][
"attr"] ==
"rindex" ||
3484 call[
"func"][
"attr"] ==
"count" || call[
"func"][
"attr"] ==
"find" ||
3485 call[
"func"][
"attr"] ==
"rfind"))
3489 obj_type ==
"list" && call[
"func"].contains(
"attr") &&
3490 call[
"func"][
"attr"] ==
"pop")
3502 obj_type ==
"dict" && call[
"func"].contains(
"attr") &&
3503 (call[
"func"][
"attr"] ==
"setdefault" || call[
"func"][
"attr"] ==
"get" ||
3504 call[
"func"][
"attr"] ==
"pop"))
3517 if (!class_node.empty())
3519 const std::string &method_name = call[
"func"][
"attr"];
3522 for (
const Json &member : class_node[
"body"])
3524 if (member[
"_type"] ==
"FunctionDef" && member[
"name"] == method_name)
3528 member.contains(
"returns") && !member[
"returns"].is_null() &&
3529 member[
"returns"].contains(
"id"))
3531 return member[
"returns"][
"id"];
3542template <
class Json>
3543InferResult python_annotation<Json>::infer_type_from_ifexp(
3544 const Json &ifexp_node,
3546 std::string &inferred_type)
3553 {
"_type",
"Assign"},
3554 {
"value", ifexp_node[
"body"]},
3555 {
"lineno", current_line_}};
3557 Json orelse_stmt = {
3558 {
"_type",
"Assign"},
3559 {
"value", ifexp_node[
"orelse"]},
3560 {
"lineno", current_line_}};
3562 std::string body_type, orelse_type;
3565 InferResult body_result = infer_type(body_stmt, body, body_type);
3568 InferResult orelse_result = infer_type(orelse_stmt, body, orelse_type);
3571 if (body_result == InferResult::OK && orelse_result == InferResult::OK)
3573 if (body_type == orelse_type)
3575 inferred_type = body_type;
3576 return InferResult::OK;
3582 (body_type ==
"int" && orelse_type ==
"float") ||
3583 (body_type ==
"float" && orelse_type ==
"int"))
3585 inferred_type =
"float";
3586 return InferResult::OK;
3591 "Conditional expression has different types in branches: {} vs {}. "
3592 "Using 'Any' as fallback ({}:{})",
3597 inferred_type =
"Any";
3598 return InferResult::OK;
3602 if (body_result == InferResult::OK)
3604 inferred_type = body_type;
3605 return InferResult::OK;
3607 if (orelse_result == InferResult::OK)
3609 inferred_type = orelse_type;
3610 return InferResult::OK;
3615 return InferResult::UNKNOWN;
3620template <
class Json>
3622python_annotation<Json>::get_type_from_call_or_random(
const Json &element)
3625 static_cast<const nlohmann::json &
>(element[
"value"]),
3626 static_cast<const nlohmann::json &
>(ast_));
3627 return random.empty() ? get_type_from_call(element) :
random;
3630template <
class Json>
3631bool python_annotation<Json>::is_numpy_array_ctor_call(
3632 const Json &call_value)
const
3635 !call_value.is_object() ||
3636 call_value.value(
"_type", std::string()) !=
"Call" ||
3637 !call_value.contains(
"func") || !call_value[
"func"].is_object() ||
3638 call_value[
"func"].value(
"_type", std::string()) !=
"Attribute" ||
3639 !call_value[
"func"].contains(
"value") ||
3640 !call_value[
"func"][
"value"].is_object() ||
3641 call_value[
"func"][
"value"].value(
"_type", std::string()) !=
"Name")
3644 static const std::set<std::string> numpy_array_ctors = {
3654 const std::string method_name = call_value[
"func"].value(
"attr",
"");
3655 const std::string module_alias = call_value[
"func"][
"value"].value(
"id",
"");
3656 return numpy_array_ctors.count(method_name) != 0 &&
3658 static_cast<const nlohmann::json &
>(ast_), module_alias);
3661template <
class Json>
3662bool python_annotation<Json>::current_func_returns_name_directly(
3663 const std::string &name)
const
3665 if (current_func ==
nullptr || !current_func->contains(
"body"))
3668 const Json *block = &(*current_func)[
"body"];
3669 while (block->is_array() && !block->empty())
3671 const Json &last = block->back();
3672 if (!last.is_object())
3675 const std::string
type = last.value(
"_type", std::string());
3676 if (type ==
"Return")
3677 return last.contains(
"value") && last[
"value"].is_object() &&
3678 last[
"value"].value(
"_type", std::string()) ==
"Name" &&
3679 last[
"value"].value(
"id", std::string()) ==
name;
3682 type !=
"If" || !last.contains(
"body") || !last[
"body"].is_array() ||
3683 !last.contains(
"orelse") || !last[
"orelse"].is_array() ||
3684 last[
"orelse"].empty())
3690 bool else_returns =
false;
3692 const Json *else_block = &last[
"orelse"];
3693 if (!else_block->is_array() || else_block->empty())
3695 const Json &else_last = else_block->back();
3697 else_last.is_object() &&
3698 else_last.value(
"_type", std::string()) ==
"Return" &&
3699 else_last.contains(
"value") && else_last[
"value"].is_object() &&
3700 else_last[
"value"].value(
"_type", std::string()) ==
"Name" &&
3701 else_last[
"value"].value(
"id", std::string()) ==
name;
3705 block = &last[
"body"];
3710template <
class Json>
3714 std::string &inferred_type)
3717 return InferResult::UNKNOWN;
3719 if (stmt[
"_type"] ==
"arg")
3721 if (!stmt.contains(
"annotation") || stmt[
"annotation"].is_null())
3722 return InferResult::UNKNOWN;
3734 stmt[
"annotation"].contains(
"_type") &&
3735 stmt[
"annotation"][
"_type"] ==
"Constant" &&
3736 stmt[
"annotation"].contains(
"value") &&
3737 stmt[
"annotation"][
"value"].
is_string())
3738 inferred_type = stmt[
"annotation"][
"value"].template get<std::string>();
3740 stmt[
"annotation"].contains(
"_type") &&
3741 stmt[
"annotation"][
"_type"] ==
"Attribute" &&
3742 stmt[
"annotation"].contains(
"attr"))
3743 inferred_type = stmt[
"annotation"][
"attr"].template get<std::string>();
3745 stmt[
"annotation"].contains(
"value") &&
3746 stmt[
"annotation"][
"value"].is_object() &&
3747 stmt[
"annotation"][
"value"].contains(
"id"))
3749 stmt[
"annotation"][
"value"][
"id"].template get<std::string>();
3750 else if (stmt[
"annotation"].contains(
"id"))
3751 inferred_type = stmt[
"annotation"][
"id"].template get<std::string>();
3753 stmt[
"annotation"].contains(
"_type") &&
3754 stmt[
"annotation"][
"_type"] ==
"BinOp")
3757 const auto &left = stmt[
"annotation"][
"left"];
3758 const auto &right = stmt[
"annotation"][
"right"];
3762 (left.contains(
"_type") && left[
"_type"] ==
"Constant" &&
3763 left.contains(
"value") && left[
"value"].is_null());
3764 bool right_is_none =
3765 (right.contains(
"_type") && right[
"_type"] ==
"Constant" &&
3766 right.contains(
"value") && right[
"value"].is_null());
3768 if (right_is_none && left.contains(
"id"))
3769 inferred_type = left[
"id"].template get<std::string>();
3770 else if (left_is_none && right.contains(
"id"))
3771 inferred_type = right[
"id"].template get<std::string>();
3773 return InferResult::UNKNOWN;
3776 return InferResult::UNKNOWN;
3778 return InferResult::OK;
3782 if (!stmt.contains(
"value") || stmt[
"value"].is_null())
3783 return InferResult::UNKNOWN;
3785 const auto &value_type = stmt[
"value"][
"_type"];
3788 if (value_type ==
"Constant")
3789 inferred_type = get_type_from_constant(stmt[
"value"]);
3790 else if (value_type ==
"List")
3794 inferred_type =
"list";
3806 const auto &elts = stmt[
"value"];
3808 (!elts.contains(
"elts") || elts[
"elts"].empty()) &&
3809 stmt.contains(
"targets") && stmt[
"targets"].is_array() &&
3810 !stmt[
"targets"].empty() && stmt[
"targets"][0].is_object() &&
3811 stmt[
"targets"][0].value(
"_type", std::string()) ==
"Name")
3813 const std::string target_name =
3814 stmt[
"targets"][0].value(
"id", std::string());
3815 if (!target_name.empty())
3825 if (!append_arg.empty() && append_arg_is_plain_int(append_arg))
3826 inferred_type =
"list[int]";
3830 else if (value_type ==
"Set")
3831 inferred_type =
"set";
3832 else if (value_type ==
"Tuple")
3833 inferred_type =
"tuple";
3834 else if (value_type ==
"Dict")
3835 inferred_type = get_dict_literal_type(stmt[
"value"]);
3836 else if (value_type ==
"Compare")
3837 inferred_type =
"bool";
3838 else if (value_type ==
"UnaryOp")
3840 const auto &operand = stmt[
"value"][
"operand"];
3841 const auto &operand_type = operand[
"_type"];
3842 if (operand_type ==
"Constant")
3843 inferred_type = get_type_from_constant(operand);
3844 else if (operand_type ==
"Name")
3845 inferred_type = get_type_from_rhs_variable(stmt, body);
3846 else if (operand_type ==
"BinOp")
3849 Json temp_stmt = {{
"value", operand}};
3850 inferred_type = get_type_from_binary_expr(temp_stmt, body);
3858 const Json operand_stmt = {
3859 {
"_type",
"Assign"}, {
"value", operand}, {
"lineno", current_line_}};
3860 std::string operand_inferred;
3862 infer_type(operand_stmt, body, operand_inferred);
3863 if (operand_result != InferResult::OK)
3864 return operand_result;
3865 if (stmt[
"value"][
"op"][
"_type"] ==
"Not")
3866 inferred_type =
"bool";
3868 inferred_type = operand_inferred ==
"bool" ?
"int" : operand_inferred;
3873 else if (value_type ==
"Name" || value_type ==
"Subscript")
3878 value_type ==
"Name" && stmt[
"value"].contains(
"id") &&
3880 ast_[
"body"], stmt[
"value"][
"id"].
template get<std::string>())
3882 return InferResult::UNKNOWN;
3884 inferred_type = get_type_from_rhs_variable(stmt, body);
3885 if (inferred_type.empty() && value_type ==
"Name")
3886 return InferResult::UNKNOWN;
3890 else if (value_type ==
"BinOp")
3892 std::string got_type = get_type_from_binary_expr(stmt, body);
3893 if (!got_type.empty())
3894 inferred_type = got_type;
3898 else if (value_type ==
"IfExp")
3901 if (!function_value_return_type(stmt[
"value"]).
empty())
3902 return InferResult::UNKNOWN;
3903 return infer_type_from_ifexp(stmt[
"value"], body, inferred_type);
3908 value_type ==
"Call" && stmt[
"value"][
"func"][
"_type"] ==
"Name" &&
3911 inferred_type = get_type_from_call(stmt);
3912 if (inferred_type.empty())
3913 return InferResult::UNKNOWN;
3918 value_type ==
"Call" && stmt[
"value"][
"func"][
"_type"] ==
"Attribute")
3937 is_numpy_array_ctor_call(stmt[
"value"]) && stmt.contains(
"targets") &&
3938 stmt[
"targets"].is_array() && stmt[
"targets"].size() == 1 &&
3939 stmt[
"targets"][0].contains(
"id") &&
3940 current_func_returns_name_directly(
3941 stmt[
"targets"][0][
"id"].
template get<std::string>()))
3942 return InferResult::UNKNOWN;
3945 inferred_type = get_type_from_call_or_random(stmt);
3948 if (inferred_type.empty())
3949 inferred_type = get_type_from_method(stmt[
"value"]);
3953 if (inferred_type.empty())
3955 const auto &func = stmt[
"value"][
"func"];
3957 func.contains(
"attr") && func.contains(
"value") &&
3958 func[
"value"][
"_type"] ==
"Name" && func[
"value"].contains(
"id"))
3960 std::string attr_name = func[
"attr"].template get<std::string>();
3964 if (!attr_name.empty() && std::isupper(attr_name[0]))
3965 inferred_type = attr_name;
3968 return InferResult::UNKNOWN;
3971 else if (func.contains(
"value") && func[
"value"][
"_type"] ==
"Subscript")
3972 return InferResult::UNKNOWN;
3975 else if (func.contains(
"value") && func[
"value"][
"_type"] ==
"Call")
3976 return InferResult::UNKNOWN;
3979 else if (func.contains(
"value") && func[
"value"][
"_type"] ==
"Attribute")
3980 return InferResult::UNKNOWN;
3984 return InferResult::UNKNOWN;
3986 return InferResult::OK;
3994template <
class Json>
3995bool python_annotation<Json>::is_rebound_in_scope(
const std::string &var_name)
4009template <
class Json>
4010Json python_annotation<Json>::find_var_node_for_inference(
4011 const std::string &var_name)
4018 (var_node.empty() ||
4019 (var_node.contains(
"_type") && var_node[
"_type"] ==
"arg")) &&
4020 parent_func !=
nullptr)
4023 (*parent_func).contains(
"args") &&
4024 (*parent_func)[
"args"].contains(
"args"))
4027 find_annotated_assign(var_name, (*parent_func)[
"args"][
"args"]);
4029 if (var_node.empty() && (*parent_func)[
"args"].contains(
"kwonlyargs"))
4031 find_annotated_assign(var_name, (*parent_func)[
"args"][
"kwonlyargs"]);
4034 if (!
has_annotation(var_node) && (*parent_func).contains(
"body"))
4035 var_node = find_annotated_assign(var_name, (*parent_func)[
"body"]);
4040 (var_node.empty() ||
4041 (var_node.contains(
"_type") && var_node[
"_type"] ==
"arg")))
4044 if (!global_var_node.empty())
4045 var_node = global_var_node;
4049 var_node = find_annotated_assign(var_name, ast_[
"body"]);
4054template <
class Json>
4055bool python_annotation<Json>::append_arg_is_plain_int(
const Json &arg)
4057 const std::string
type = arg.value(
"_type", std::string());
4059 if (type ==
"Constant")
4060 return get_type_from_constant(arg) ==
"int";
4062 if (type ==
"BinOp" || type ==
"UnaryOp")
4063 return get_argument_type(arg) ==
"int";
4067 Json var_node = find_var_node_for_inference(arg.value(
"id", std::string()));
4075 if (var_node.contains(
"value") && var_node[
"value"].is_object())
4076 return append_arg_is_plain_int(var_node[
"value"]);
4084template <
class Json>
4085bool python_annotation<Json>::expr_calls_function(
4087 const std::string &func_name)
4089 if (node.is_object())
4091 auto type_it = node.find(
"_type");
4092 if (type_it != node.end() && *type_it ==
"Call")
4094 auto func_it = node.find(
"func");
4095 if (func_it != node.end() && func_it->is_object())
4097 auto ft = func_it->find(
"_type");
4098 auto fid = func_it->find(
"id");
4100 ft != func_it->end() && *ft ==
"Name" && fid != func_it->end() &&
4105 for (
auto it = node.begin(); it != node.end(); ++it)
4106 if (expr_calls_function(*it, func_name))
4109 else if (node.is_array())
4111 for (
const Json &child : node)
4112 if (expr_calls_function(child, func_name))
4118template <
class Json>
4119bool python_annotation<Json>::arithmetic_reads_attribute(
const Json &node)
4121 const std::string
type = node.value(
"_type", std::string());
4122 if (type ==
"Attribute")
4124 if (type ==
"BinOp")
4125 return arithmetic_reads_attribute(node[
"left"]) ||
4126 arithmetic_reads_attribute(node[
"right"]);
4127 return type ==
"UnaryOp" && arithmetic_reads_attribute(node[
"operand"]);
4137 static const std::set<std::string> scalars = {
"bool",
"int",
"str"};
4138 return !types.empty() &&
4139 std::all_of(types.begin(), types.end(), [](
const std::string &t) {
4140 return scalars.count(t) != 0;
4144template <
class Json>
4145void python_annotation<Json>::collect_return_types(
4147 const std::string &func_name,
4148 std::set<std::string> &types)
4150 for (
const Json &stmt : body)
4159 const std::string &stmt_type = stmt[
"_type"];
4161 stmt_type ==
"FunctionDef" || stmt_type ==
"AsyncFunctionDef" ||
4162 stmt_type ==
"ClassDef")
4166 if (stmt[
"_type"] ==
"Return" && !stmt[
"value"].is_null())
4168 const Json &return_val = stmt[
"value"];
4175 if (expr_calls_function(return_val, func_name))
4180 return_val[
"_type"] ==
"Call" && return_val.contains(
"func") &&
4181 return_val[
"func"][
"_type"] ==
"Name")
4183 const std::string &called_func = return_val[
"func"][
"id"];
4186 const std::string arg_dependent = arg_dependent_call_type(return_val);
4187 if (!arg_dependent.empty())
4189 types.insert(arg_dependent);
4196 std::string called_func_type =
4197 get_function_return_type(called_func, ast_);
4198 if (!called_func_type.empty() && called_func_type !=
"NoneType")
4200 types.insert(called_func_type);
4204 catch (std::runtime_error &)
4207 const std::string held =
4208 function_value_return_type(return_val[
"func"], &return_val);
4228 return_val[
"_type"] ==
"Call" && return_val.contains(
"func") &&
4229 return_val[
"func"][
"_type"] ==
"Attribute" &&
4230 return_val[
"func"].contains(
"attr") &&
4231 get_string_method_return_type(return_val[
"func"][
"attr"]) ==
"list")
4233 types.insert(
"list");
4241 std::string inferred_type = get_argument_type(return_val);
4242 if (inferred_type.empty() && return_val[
"_type"] ==
"Name")
4245 !inferred_type.empty() &&
4246 (inferred_type !=
"Any" || !arithmetic_reads_attribute(return_val)))
4247 types.insert(inferred_type);
4251 if (stmt.contains(
"body") && stmt[
"body"].is_array())
4252 collect_return_types(stmt[
"body"], func_name, types);
4254 if (stmt.contains(
"orelse") && stmt[
"orelse"].is_array())
4255 collect_return_types(stmt[
"orelse"], func_name, types);
4259template <
class Json>
4260std::string python_annotation<Json>::infer_from_return_statements(
4262 const std::string &func_name,
4265 std::set<std::string> types;
4266 collect_return_types(body, func_name, types);
4280 if (types.size() > 1)
4282 types.erase(
"NoneType");
4296 if (types.size() == 1)
4297 return *types.begin();
4308 static const std::map<std::string, int> numeric_rank = {
4309 {
"bool", 0}, {
"int", 1}, {
"float", 2}, {
"complex", 3}};
4312 for (
const std::string &t : types)
4314 auto it = numeric_rank.find(t);
4315 if (it == numeric_rank.end())
4320 if (it->second > best)
4326 if (!widest.empty())
4341 auto base_of = [](
const std::string &t) {
4342 auto p = t.find(
'[');
4343 return p == std::string::npos ? t : t.substr(0, p);
4345 const std::string
base = base_of(*types.begin());
4346 std::string bracketed;
4347 bool same_base =
true, conflicting_elements =
false;
4348 for (
const std::string &t : types)
4350 if (base_of(t) !=
base)
4357 if (!bracketed.empty() && bracketed != t)
4358 conflicting_elements =
true;
4363 return (bracketed.empty() || conflicting_elements) ?
base : bracketed;
4378template <
class Json>
4379const Json python_annotation<Json>::find_annotated_assign(
4383 for (
const Json &elem : body)
4387 elem.contains(
"_type") &&
4388 ((elem[
"_type"] ==
"AnnAssign" && elem.contains(
"target") &&
4389 elem[
"target"].contains(
"id") &&
4390 elem[
"target"][
"id"].template get<std::string>() ==
node_name) ||
4391 (elem[
"_type"] ==
"Assign" && elem.contains(
"targets") &&
4392 elem[
"targets"].is_array() && !elem[
"targets"].empty() &&
4393 elem[
"targets"][0].contains(
"_type") &&
4394 elem[
"targets"][0][
"_type"] ==
"Name" &&
4395 elem[
"targets"][0].contains(
"id") &&
4396 elem[
"targets"][0][
"id"].template get<std::string>() ==
node_name) ||
4397 (elem[
"_type"] ==
"arg" && elem[
"arg"] ==
node_name)))
4406 Json synth = match_unpacking_assignment(elem,
node_name); !synth.empty())
4410 if (elem.contains(
"_type"))
4412 const std::string &elem_type = elem[
"_type"];
4415 if (elem_type ==
"If")
4417 if (elem.contains(
"body") && !elem[
"body"].empty())
4419 Json result = find_annotated_assign(
node_name, elem[
"body"]);
4420 if (!result.empty())
4423 if (elem.contains(
"orelse") && !elem[
"orelse"].empty())
4425 Json result = find_annotated_assign(
node_name, elem[
"orelse"]);
4426 if (!result.empty())
4431 else if (elem_type ==
"While")
4433 if (elem.contains(
"body") && !elem[
"body"].empty())
4435 Json result = find_annotated_assign(
node_name, elem[
"body"]);
4436 if (!result.empty())
4441 else if (elem_type ==
"For")
4443 if (elem.contains(
"body") && !elem[
"body"].empty())
4445 Json result = find_annotated_assign(
node_name, elem[
"body"]);
4446 if (!result.empty())
4451 else if (elem_type ==
"Try")
4453 if (elem.contains(
"body") && !elem[
"body"].empty())
4455 Json result = find_annotated_assign(
node_name, elem[
"body"]);
4456 if (!result.empty())
4459 if (elem.contains(
"handlers") && !elem[
"handlers"].empty())
4461 for (
const Json &handler : elem[
"handlers"])
4463 if (handler.contains(
"body") && !handler[
"body"].empty())
4465 Json result = find_annotated_assign(
node_name, handler[
"body"]);
4466 if (!result.empty())
4477template <
class Json>
4478Json python_annotation<Json>::find_receiver_decl(
const std::string &obj)
4485 current_func !=
nullptr ? (*current_func)[
"body"] : ast_[
"body"];
4486 return find_annotated_assign(obj, body);
4489template <
class Json>
4490Json python_annotation<Json>::match_unpacking_assignment(
4495 !elem.contains(
"_type") || elem[
"_type"] !=
"Assign" ||
4496 !elem.contains(
"targets") || !elem[
"targets"].is_array() ||
4497 elem[
"targets"].empty())
4499 const Json &target = elem[
"targets"][0];
4501 !target.is_object() || !target.contains(
"_type") ||
4502 (target[
"_type"] !=
"Tuple" && target[
"_type"] !=
"List") ||
4503 !target.contains(
"elts") || !target[
"elts"].is_array())
4506 const Json &elts = target[
"elts"];
4507 for (
size_t i = 0; i < elts.size(); ++i)
4510 !elts[i].is_object() || !elts[i].contains(
"_type") ||
4511 elts[i][
"_type"] !=
"Name" || !elts[i].contains(
"id") ||
4514 std::string elem_type = elem.contains(
"value")
4515 ? infer_unpacked_element_type(elem[
"value"], i)
4518 {
"_type",
"AnnAssign"},
4519 {
"target", Json{{
"_type",
"Name"}, {
"id",
node_name}}},
4520 {
"annotation", Json{{
"_type",
"Name"}, {
"id", elem_type}}},
4521 {
"lineno", elem.value(
"lineno", 0)}};
4528template <
class Json>
4529std::vector<std::string>
4530python_annotation<Json>::infer_subscript_key_tuple_types(
4531 const std::string &class_name)
4537 bool any_reject =
false;
4538 std::vector<std::string> expected;
4539 auto reducer = [&](
const Json &elts,
const Json *enclosing_func) {
4542 std::vector<std::string> types =
4543 infer_tuple_element_types(elts, enclosing_func);
4549 if (expected.empty())
4550 expected = std::move(types);
4551 else if (expected != types)
4555 auto scan = [&](
const Json &ast) {
4556 std::unordered_map<std::string, std::string> var_to_class;
4557 collect_class_instance_map(ast, var_to_class);
4558 visit_class_tuple_subscripts(ast, class_name, var_to_class, reducer);
4562 for (
const Json *extra : extra_subscript_inference_asts_)
4570template <
class Json>
4571void python_annotation<Json>::collect_class_instance_map(
4573 std::unordered_map<std::string, std::string> &out)
const
4575 auto record = [&](
const Json &target_id,
const Json &annotation) {
4577 !target_id.is_string() || !annotation.is_object() ||
4578 !annotation.contains(
"_type") || annotation[
"_type"] !=
"Name" ||
4579 !annotation.contains(
"id") || !annotation[
"id"].is_string())
4581 out[target_id.template get<std::string>()] =
4582 annotation[
"id"].template get<std::string>();
4585 std::function<void(
const Json &)>
walk = [&](
const Json &node) {
4586 if (node.is_object())
4589 node.contains(
"_type") && node[
"_type"] ==
"AnnAssign" &&
4590 node.contains(
"target") && node[
"target"].is_object() &&
4591 node[
"target"].contains(
"_type") && node[
"target"][
"_type"] ==
"Name" &&
4592 node[
"target"].contains(
"id") && node.contains(
"annotation"))
4593 record(node[
"target"][
"id"], node[
"annotation"]);
4595 node.contains(
"_type") && node[
"_type"] ==
"arg" &&
4596 node.contains(
"arg") && node.contains(
"annotation"))
4597 record(node[
"arg"], node[
"annotation"]);
4598 for (
auto it = node.begin(); it != node.end(); ++it)
4601 else if (node.is_array())
4603 for (
const auto &elem : node)
4608 if (ast.is_object() && ast.contains(
"body"))
4612template <
class Json>
4613template <
typename Visitor>
4614void python_annotation<Json>::visit_class_tuple_subscripts(
4616 const std::string &class_name,
4617 const std::unordered_map<std::string, std::string> &var_to_class,
4619 const Json *enclosing_func)
4621 if (node.is_object())
4623 const Json *next_enclosing = enclosing_func;
4625 node.contains(
"_type") &&
4626 (node[
"_type"] ==
"FunctionDef" || node[
"_type"] ==
"AsyncFunctionDef"))
4627 next_enclosing = &node;
4630 node.contains(
"_type") && node[
"_type"] ==
"Subscript" &&
4631 node.contains(
"value") && node[
"value"].is_object() &&
4632 node[
"value"].contains(
"_type") && node[
"value"][
"_type"] ==
"Name" &&
4633 node[
"value"].contains(
"id") && node.contains(
"slice") &&
4634 node[
"slice"].is_object() && node[
"slice"].contains(
"_type") &&
4635 node[
"slice"][
"_type"] ==
"Tuple" && node[
"slice"].contains(
"elts") &&
4636 node[
"slice"][
"elts"].is_array() && !node[
"slice"][
"elts"].empty())
4639 var_to_class.find(node[
"value"][
"id"].
template get<std::string>());
4640 if (it != var_to_class.end() && it->second == class_name)
4641 visit(node[
"slice"][
"elts"], next_enclosing);
4643 for (
auto it = node.begin(); it != node.end(); ++it)
4644 visit_class_tuple_subscripts(
4645 it.value(), class_name, var_to_class, visit, next_enclosing);
4647 else if (node.is_array())
4649 for (
const auto &elem : node)
4650 visit_class_tuple_subscripts(
4651 elem, class_name, var_to_class, visit, enclosing_func);
4655template <
class Json>
4656std::string python_annotation<Json>::resolve_name_in_enclosing_func(
4657 const std::string &name,
4658 const Json &enclosing_func)
4660 auto lookup = [&](
const Json &scope) -> std::string {
4661 Json node = find_annotated_assign(name, scope);
4667 if (enclosing_func.contains(
"args"))
4669 const Json &arg_spec = enclosing_func[
"args"];
4670 for (
const char *
key : {
"args",
"kwonlyargs"})
4671 if (arg_spec.contains(
key))
4672 if (std::string t = lookup(arg_spec[
key]); !t.empty())
4675 if (enclosing_func.contains(
"body"))
4676 return lookup(enclosing_func[
"body"]);
4680template <
class Json>
4681std::string python_annotation<Json>::resolve_wildcard_import_func(
4682 const std::string &func_name)
4684 if (!module_manager_ || !ast_.contains(
"body"))
4686 for (
const auto &node : ast_[
"body"])
4689 !node.contains(
"_type") || node[
"_type"] !=
"ImportFrom" ||
4690 !node.contains(
"names") || !node.contains(
"module") ||
4691 node[
"module"].is_null())
4693 bool is_star =
false;
4694 for (
const auto &name : node[
"names"])
4695 if (
name.contains(
"name") && name[
"name"] ==
"*")
4702 auto module = module_manager_->get_module(node["module"]);
4705 auto func_info =
module->get_function(func_name);
4706 if (func_info.name_.empty())
4708 if (func_info.return_type_.empty() || func_info.return_type_ ==
"NoneType")
4710 return func_info.return_type_;
4715template <
class Json>
4716bool python_annotation<Json>::reads_import(
4717 const std::string &name,
4718 const Json &use)
const
4722 for (
const auto &node : ast_[
"body"])
4724 const Json names = node.value(
"names", Json::array());
4726 node.value(
"_type",
"") !=
"ImportFrom" ||
4727 std::none_of(names.begin(), names.end(), [](
const Json &alias) {
4728 return alias.value(
"name",
"") ==
"*";
4731 const Json from = node.value(
"module", Json());
4733 module_manager_ && from.is_string()
4734 ? module_manager_->get_module(from.template get<std::string>())
4747 !current_class_name_.empty(),
4748 current_func ==
nullptr,
4753 catch (
const std::runtime_error &)
4759template <
class Json>
4760std::string python_annotation<Json>::resolve_expr_in_enclosing_func(
4762 const Json &enclosing_func)
4764 if (!elt.contains(
"_type"))
4766 const std::string &t = elt[
"_type"];
4767 if (t ==
"Constant")
4768 return get_type_from_constant(elt);
4769 if (t ==
"Name" && elt.contains(
"id"))
4770 return resolve_name_in_enclosing_func(elt[
"id"], enclosing_func);
4771 if (t ==
"UnaryOp" && elt.contains(
"operand"))
4772 return resolve_expr_in_enclosing_func(elt[
"operand"], enclosing_func);
4773 if (t ==
"BinOp" && elt.contains(
"left") && elt.contains(
"right"))
4775 std::string l = resolve_expr_in_enclosing_func(elt[
"left"], enclosing_func);
4777 resolve_expr_in_enclosing_func(elt[
"right"], enclosing_func);
4778 if (l.empty() || r.empty())
4780 if (l ==
"float" || r ==
"float")
4782 if (l ==
"int" && r ==
"int")
4789template <
class Json>
4790std::vector<std::string> python_annotation<Json>::infer_tuple_element_types(
4792 const Json *enclosing_func)
4794 std::vector<std::string> types;
4795 types.reserve(elts.size());
4796 for (
const Json &elt : elts)
4798 if (!elt.contains(
"_type"))
4800 const std::string &t = elt[
"_type"];
4803 types.emplace_back(
"slice");
4807 t ==
"Call" && elt.contains(
"func") && elt[
"func"].is_object() &&
4808 elt[
"func"].contains(
"_type") && elt[
"func"][
"_type"] ==
"Name" &&
4809 elt[
"func"].contains(
"id") && elt[
"func"][
"id"] ==
"slice")
4811 types.emplace_back(
"slice");
4814 std::string resolved;
4815 if (enclosing_func !=
nullptr)
4816 resolved = resolve_expr_in_enclosing_func(elt, *enclosing_func);
4817 if (resolved.empty())
4818 resolved = get_argument_type(elt);
4819 if (resolved.empty() || resolved ==
"Any")
4821 types.push_back(std::move(resolved));
4828template <
class Json>
4829std::vector<Json> python_annotation<Json>::find_function_calls(
4830 const std::string &func_name,
4833 std::vector<Json> calls;
4834 find_function_calls_recursive(func_name, body, calls);
4838template <
class Json>
4839void python_annotation<Json>::find_function_calls_recursive(
4840 const std::string &func_name,
4842 std::vector<Json> &calls)
4844 if (node.is_object())
4848 node.contains(
"_type") && node[
"_type"] ==
"Call" &&
4849 node.contains(
"func") && node[
"func"][
"_type"] ==
"Name" &&
4850 node[
"func"][
"id"] == func_name)
4852 calls.push_back(node);
4856 for (
auto it = node.begin(); it != node.end(); ++it)
4857 find_function_calls_recursive(func_name, it.value(), calls);
4859 else if (node.is_array())
4861 for (
const auto &element : node)
4862 find_function_calls_recursive(func_name, element, calls);
4866template <
class Json>
4867std::vector<std::string>
4868python_annotation<Json>::collect_parameter_types_from_calls(
4870 const std::vector<Json> &function_calls)
4872 std::vector<std::string> types;
4873 std::unordered_set<std::string> seen;
4875 for (
const Json &call : function_calls)
4877 if (!call.contains(
"args") || param_index >= call[
"args"].size())
4880 std::string arg_type = get_argument_type(call[
"args"][param_index]);
4881 if (arg_type.empty())
4884 if (seen.insert(arg_type).second)
4885 types.push_back(arg_type);
4891template <
class Json>
4892std::vector<std::string>
4893python_annotation<Json>::infer_tuple_param_types_from_calls(
4895 const std::vector<Json> &function_calls)
4897 std::vector<std::string> result;
4898 bool initialized =
false;
4900 for (
const Json &call : function_calls)
4902 if (!call.contains(
"args") || param_index >= call[
"args"].size())
4905 const Json &arg = call[
"args"][param_index];
4907 !arg.contains(
"_type") || arg[
"_type"] !=
"Tuple" ||
4908 !arg.contains(
"elts"))
4911 std::vector<std::string> shape;
4912 shape.reserve(arg[
"elts"].size());
4913 for (
const Json &elt : arg[
"elts"])
4915 std::string t = get_argument_type(elt);
4919 if (t.empty() || t ==
"tuple" || t ==
"list" || t ==
"dict")
4921 shape.push_back(std::move(t));
4926 result = std::move(shape);
4931 if (shape.size() != result.size())
4933 for (
size_t k = 0; k < result.size(); ++k)
4934 if (result[k] != shape[k])
4945template <
class Json>
4947 const std::string &name,
4948 const std::string &ctx,
4955 ast, std::vector<std::string>(
path.begin(),
path.begin() +
depth));
4959 if (decl.value(
"_type",
"") ==
"arg")
4968template <
class Json>
4969std::string python_annotation<Json>::caller_arg_type(
4971 const caller_scopet &scope)
4973 if (scope.in_lambda)
4976 arg.value(
"_type",
"") ==
"Name" &&
4979 const std::string saved_ctx = current_func_name_context_;
4980 current_func_name_context_ = scope.ctx;
4981 const std::string
type = get_argument_type(arg);
4982 current_func_name_context_ = saved_ctx;
4992template <
class Json>
4993void python_annotation<Json>::collect_caller_arg_types(
4994 const std::string &callee,
4997 const caller_scopet &scope,
4998 std::vector<std::string> &types)
5000 if (node.is_array())
5002 for (
const auto &elem : node)
5003 collect_caller_arg_types(callee, index, elem, scope, types);
5006 if (!node.is_object())
5009 caller_scopet inner = scope;
5010 const std::string kind = node.value(
"_type",
"");
5011 if (kind ==
"ClassDef" && scope.ctx.empty())
5012 inner.class_prefix = node.value(
"name",
"") +
"@C@";
5013 else if (kind ==
"FunctionDef" || kind ==
"AsyncFunctionDef")
5015 const std::string
name = node.value(
"name",
"");
5017 scope.ctx.empty() ? scope.class_prefix +
name : scope.ctx +
"@F@" +
name;
5018 inner.class_prefix.clear();
5020 else if (kind ==
"Lambda")
5021 inner.in_lambda =
true;
5023 kind ==
"Call" && scope.ctx != scope.root && node.contains(
"args") &&
5024 index < node[
"args"].size() && node[
"func"].value(
"_type",
"") ==
"Name" &&
5025 node[
"func"].value(
"id",
"") == callee)
5027 const std::string
type = caller_arg_type(node[
"args"][index], scope);
5028 const std::string callee_path =
5029 scope.root.empty() ? callee : scope.root +
"@F@" + callee;
5030 if (!
type.empty() || scope.ctx != callee_path)
5031 types.push_back(type);
5034 for (
auto it = node.begin(); it != node.end(); ++it)
5035 collect_caller_arg_types(callee, index, it.value(), inner, types);
5040template <
class Json>
5041std::vector<std::string> python_annotation<Json>::caller_arg_types(
5042 const std::string &func_name,
5044 const Json &search_context)
5046 caller_scopet scope;
5047 scope.root = current_func_name_context_;
5048 scope.ctx = scope.root;
5049 std::vector<std::string> types;
5050 collect_caller_arg_types(func_name, index, search_context, scope, types);
5061 const size_t a_bracket = a.find(
'[');
5062 const size_t b_bracket = b.find(
'[');
5063 return (a_bracket == std::string::npos || b_bracket == std::string::npos) &&
5064 a.substr(0, a_bracket) == b.substr(0, b_bracket);
5070template <
class Json>
5071std::string python_annotation<Json>::infer_parameter_type_at_call_sites(
5072 const std::string &func_name,
5074 const std::vector<Json> &function_calls,
5075 const Json &search_context)
5077 std::string inferred =
5078 infer_parameter_type_from_calls(param_index, function_calls);
5079 const std::vector<std::string> callers =
5080 caller_arg_types(func_name, param_index, search_context);
5084 inferred.empty() && !callers.empty() &&
5085 callers.size() == function_calls.size())
5086 inferred = callers.front();
5087 for (
const std::string &type : callers)
5096template <
class Json>
5097bool python_annotation<Json>::specializable_at_call_sites(
5098 const std::string &func_name,
5100 std::vector<std::string> &call_types,
5101 const Json &search_context)
5103 for (
const std::string &type :
5104 caller_arg_types(func_name, param_index, search_context))
5109 std::find(call_types.begin(), call_types.end(), type) == call_types.end())
5110 call_types.push_back(type);
5112 return are_specializable_types(call_types);
5115template <
class Json>
5116std::string python_annotation<Json>::infer_parameter_type_from_calls(
5118 const std::vector<Json> &function_calls)
5120 std::string inferred_type;
5122 for (
const Json &call : function_calls)
5124 if (call.contains(
"args") && param_index < call[
"args"].size())
5126 const Json &arg = call[
"args"][param_index];
5127 std::string arg_type = get_argument_type(arg);
5129 if (!arg_type.empty())
5131 if (inferred_type.empty())
5132 inferred_type = arg_type;
5133 else if (inferred_type != arg_type)
5135 std::string common = find_common_ancestor(inferred_type, arg_type);
5141 const auto bare_of = [](
const std::string &t) {
5142 const auto bracket = t.find(
'[');
5143 return bracket == std::string::npos ? t : t.substr(0, bracket);
5145 const bool same_container =
5146 (inferred_type ==
"list" || inferred_type ==
"dict" ||
5147 inferred_type ==
"set" || arg_type ==
"list" ||
5148 arg_type ==
"dict" || arg_type ==
"set") &&
5149 bare_of(inferred_type) == bare_of(arg_type);
5152 if (inferred_type.find(
'[') == std::string::npos)
5153 inferred_type = arg_type;
5155 else if (!common.empty())
5156 inferred_type = common;
5162 inferred_type.rfind(
"tuple", 0) == 0 &&
5163 arg_type.rfind(
"tuple", 0) == 0)
5164 inferred_type =
"tuple";
5172 return inferred_type;
5176template <
class Json>
5180 if (stmt.value(
"_type",
"") !=
"Assign" || !stmt.contains(
"targets"))
5182 const auto &targets = stmt[
"targets"];
5183 if (targets.empty() || targets[0].value(
"id",
"") != name)
5185 if (!stmt.contains(
"value") || stmt[
"value"].value(
"_type",
"") !=
"Name")
5196template <
class Json>
5197std::string python_annotation<Json>::infer_type_from_parameter_default(
5198 const Json &function_element,
5202 const auto &args_node = function_element[
"args"];
5203 if (!args_node.contains(
"defaults"))
5206 const auto &defaults = args_node[
"defaults"];
5207 const size_t defaults_start = param_count - defaults.size();
5208 if (param_index < defaults_start)
5211 const Json &def_node = defaults[param_index - defaults_start];
5212 const std::string def_type = def_node.value(
"_type",
"");
5217 if (def_type ==
"List")
5220 if (def_type !=
"Name" || !def_node.contains(
"id"))
5223 const std::string def_name = def_node.value(
"id",
"");
5224 const nlohmann::json &const_body = ast_[
"body"];
5228 for (
const auto &stmt : ast_[
"body"])
5234template <
class Json>
5235void python_annotation<Json>::infer_parameter_types(Json &function_element)
5237 const std::string &func_name = function_element[
"name"];
5240 bool has_meaningful_body =
false;
5241 if (function_element.contains(
"body") && !function_element[
"body"].empty())
5243 for (
const auto &stmt : function_element[
"body"])
5245 if (stmt.contains(
"_type") && stmt[
"_type"] !=
"Pass")
5247 has_meaningful_body =
true;
5256 const Json &search_context =
5257 (parent_func !=
nullptr)
5258 ? (*parent_func)[
"body"]
5262 std::vector<Json> function_calls =
5263 find_function_calls(func_name, search_context);
5268 std::string call_site_context;
5269 if (parent_func !=
nullptr)
5271 std::string cur = current_func_name_context_;
5272 size_t pos = cur.rfind(
"@F@");
5273 if (
pos != std::string::npos)
5274 call_site_context = cur.substr(0,
pos);
5275 else if (parent_func->contains(
"name"))
5276 call_site_context = (*parent_func)[
"name"].template get<std::string>();
5281 function_element.contains(
"args") &&
5282 function_element[
"args"].contains(
"args"))
5284 Json ¶ms = function_element[
"args"][
"args"];
5286 for (
size_t i = 0; i < params.size(); ++i)
5288 Json ¶m = params[i];
5291 bool needs_inference =
false;
5292 bool has_partial_annotation =
false;
5293 std::string current_type;
5295 if (param.contains(
"annotation") && !param[
"annotation"].is_null())
5298 if (param[
"annotation"].contains(
"id"))
5299 current_type = param[
"annotation"][
"id"];
5302 current_type ==
"list" || current_type ==
"dict" ||
5303 current_type ==
"set")
5305 has_partial_annotation =
true;
5306 needs_inference =
true;
5308 else if (current_type ==
"tuple")
5313 if (!function_calls.empty())
5315 std::vector<std::string> elem_types =
5316 infer_tuple_param_types_from_calls(i, function_calls);
5317 if (!elem_types.empty())
5319 int lineno = param.value(
"lineno", 0);
5320 int col_offset = param.value(
"col_offset", 0) +
5321 param[
"arg"].template get<std::string>().size() +
5323 param[
"annotation"] = create_tuple_subscript_annotation(
5324 elem_types, lineno, col_offset, lineno);
5333 needs_inference =
true;
5335 std::string inferred_type;
5341 bool arg_is_dynamically_typed =
false;
5342 if (needs_inference)
5344 for (
const Json &call : function_calls)
5346 if (!call.contains(
"args") || i >= call[
"args"].size())
5348 const Json &arg_node = call[
"args"][i];
5350 arg_node.contains(
"_type") && arg_node[
"_type"] ==
"Name" &&
5351 arg_node.contains(
"id") &&
5353 arg_node[
"id"].template get<std::string>(), search_context))
5355 arg_is_dynamically_typed =
true;
5363 needs_inference && !arg_is_dynamically_typed && !function_calls.empty())
5365 std::vector<std::string> call_types =
5366 collect_parameter_types_from_calls(i, function_calls);
5369 parent_func ==
nullptr && call_types.size() > 1 &&
5370 specializable_at_call_sites(func_name, i, call_types, search_context))
5372 if (!is_specialization_pending(func_name, i, call_types))
5374 pending_specializations_.push_back(
5375 {func_name, i, std::move(call_types)});
5381 std::string saved_ctx = current_func_name_context_;
5382 current_func_name_context_ = call_site_context;
5383 inferred_type = infer_parameter_type_at_call_sites(
5384 func_name, i, function_calls, search_context);
5385 current_func_name_context_ = saved_ctx;
5389 if (inferred_type.empty() && !has_partial_annotation)
5391 infer_type_from_parameter_default(function_element, i, params.size());
5401 inferred_type.empty() && !current_class_name_.empty() &&
5402 (func_name ==
"__getitem__" || func_name ==
"__setitem__") && i == 1)
5404 std::vector<std::string> elem_types =
5405 infer_subscript_key_tuple_types(current_class_name_);
5406 if (!elem_types.empty())
5408 int lineno = param.value(
"lineno", 0);
5409 int col_offset = param.value(
"col_offset", 0) +
5410 param[
"arg"].template get<std::string>().size() + 1;
5411 param[
"annotation"] = create_tuple_subscript_annotation(
5412 elem_types, lineno, col_offset, lineno);
5418 if (has_partial_annotation)
5421 if (!inferred_type.empty() && inferred_type != current_type)
5422 add_parameter_annotation(param, inferred_type);
5427 if (inferred_type.empty() && !has_meaningful_body)
5428 inferred_type =
"Any";
5430 if (!inferred_type.empty())
5433 add_parameter_annotation(param, inferred_type);
5444template <
class Json>
5445Json *python_annotation<Json>::class_init(
5446 const std::string &class_name,
5452 for (Json &class_node : ast_[
"body"])
5454 if (class_node[
"_type"] !=
"ClassDef" || class_node[
"name"] != class_name)
5456 for (Json &member : class_node[
"body"])
5457 if (member[
"_type"] ==
"FunctionDef" && member[
"name"] ==
"__init__")
5459 inherited =
depth > 0;
5462 if (class_node.contains(
"bases"))
5463 for (
const Json &
base : class_node[
"bases"])
5464 if (
base.value(
"_type",
"") ==
"Name")
5465 if (Json *init = class_init(
base[
"id"],
depth + 1, inherited))
5474template <
class Json>
5477 return arg.value(
"_type",
"") ==
"Name" &&
5479 arg[
"id"].
template get<std::string>(), scope);
5482template <
class Json>
5485 bool annotated =
false;
5486 if (node.is_object())
5490 node.contains(
"_type") && node[
"_type"] ==
"Call" &&
5491 node.contains(
"func") && node[
"func"][
"_type"] ==
"Name")
5493 bool inherited =
false;
5494 Json *init = class_init(node[
"func"][
"id"], 0, inherited);
5495 if (init && init->contains(
"args") && (*init)[
"args"].contains(
"args"))
5497 Json ¶ms = (*init)[
"args"][
"args"];
5498 const Json &call_args = node[
"args"];
5500 for (
size_t i = 1; i < params.size() && (i - 1) < call_args.size(); ++i)
5502 Json ¶m = params[i];
5507 if (param.contains(
"annotation") && !param[
"annotation"].is_null())
5509 const Json &arg = call_args[i - 1];
5511 current_func !=
nullptr ? (*current_func)[
"body"] : ast_[
"body"];
5514 std::string arg_type = get_argument_type(arg);
5518 inherited_init_args_[¶m].insert(arg_type);
5519 else if (!arg_type.empty())
5521 add_parameter_annotation(param, arg_type);
5528 const bool is_function_def =
5529 node.contains(
"_type") && node[
"_type"] ==
"FunctionDef" &&
5530 node.contains(
"name") && node[
"name"].is_string();
5531 const bool is_class_def =
5532 node.contains(
"_type") && node[
"_type"] ==
"ClassDef" &&
5533 node.contains(
"name") && node[
"name"].is_string();
5534 Json *saved_current = current_func;
5535 Json *saved_parent = parent_func;
5536 const std::string saved_ctx = current_func_name_context_;
5537 const std::string saved_class = current_class_name_;
5539 current_class_name_ = node[
"name"].template get<std::string>();
5540 if (is_function_def)
5542 const std::string name = node[
"name"].template get<std::string>();
5543 current_func_name_context_ =
5544 !saved_ctx.empty() ? saved_ctx +
"@F@" + name
5545 : !current_class_name_.empty() ? current_class_name_ +
"@C@" + name
5547 parent_func = saved_current;
5548 current_func = &node;
5551 for (
auto &kv : node.items())
5553 current_class_name_ = saved_class;
5554 if (is_function_def)
5556 current_func_name_context_ = saved_ctx;
5557 current_func = saved_current;
5558 parent_func = saved_parent;
5561 else if (node.is_array())
5564 for (
auto &element : node)
5570template <
class Json>
5573 if (node.is_object())
5579 node.contains(
"_type") && node[
"_type"] ==
"Call" &&
5580 node.contains(
"func") && node[
"func"][
"_type"] ==
"Attribute" &&
5581 node[
"func"].contains(
"value") &&
5582 node[
"func"][
"value"][
"_type"] ==
"Call" &&
5583 node[
"func"][
"value"].contains(
"func") &&
5584 node[
"func"][
"value"][
"func"][
"_type"] ==
"Name" &&
5585 node[
"func"][
"value"][
"func"].contains(
"id") &&
5586 node[
"func"].contains(
"attr"))
5588 const std::string &class_name =
5589 node[
"func"][
"value"][
"func"][
"id"].template get<std::string>();
5590 const std::string &method_name =
5591 node[
"func"][
"attr"].template get<std::string>();
5592 const Json &call_args =
5593 node.contains(
"args") ? node[
"args"] : Json::array();
5595 for (Json &class_node : ast_[
"body"])
5598 class_node[
"_type"] ==
"ClassDef" && class_node[
"name"] == class_name)
5600 for (Json &member : class_node[
"body"])
5603 member[
"_type"] ==
"FunctionDef" &&
5604 member[
"name"] == method_name && member.contains(
"args") &&
5605 member[
"args"].contains(
"args"))
5607 Json ¶ms = member[
"args"][
"args"];
5610 i < params.size() && (i - 1) < call_args.size();
5613 Json ¶m = params[i];
5616 param.contains(
"annotation") &&
5617 !param[
"annotation"].is_null())
5619 const Json &arg = call_args[i - 1];
5620 std::string arg_type = get_argument_type(arg);
5621 if (!arg_type.empty())
5622 add_parameter_annotation(param, arg_type);
5631 for (
auto &kv : node.items())
5634 else if (node.is_array())
5636 for (
const auto &element : node)
5642template <
class Json>
5646 for (Json &class_node : ast_[
"body"])
5647 if (class_node[
"_type"] ==
"ClassDef" && class_node[
"name"] == cls)
5648 for (Json &member : class_node[
"body"])
5649 if (member[
"_type"] ==
"FunctionDef" && member[
"name"] == method)
5656template <
class Json>
5659 if (func.value(
"_type",
"") !=
"Name")
5661 const std::string &name = func[
"id"].template get<std::string>();
5670template <
class Json>
5673 std::map<std::pair<Json *, size_t>, std::set<std::string>> seen;
5674 std::function<void(
const Json &)> walk = [&](
const Json &node) {
5675 if (node.is_array())
5677 for (
const Json &item : node)
5681 if (!node.is_object())
5683 const std::string kind = node.value(
"_type",
"");
5684 if (kind ==
"FunctionDef" || kind ==
"ClassDef" || kind ==
"Lambda")
5686 const Json &func = node.value(
"func", Json());
5689 const bool method_call =
5690 kind ==
"Call" && func.value(
"_type",
"") ==
"Attribute";
5691 const Json receiver =
5692 instance_call ? func : method_call ? func[
"value"] : Json();
5694 (instance_call || method_call) &&
5695 receiver.value(
"_type",
"") ==
"Name" &&
5696 !is_rebound_in_scope(receiver[
"id"].
template get<std::string>()))
5698 const std::string attr =
5699 instance_call ?
"__call__" : func[
"attr"].template get<std::string>();
5700 for (
const std::string &cls :
5701 get_class_ancestors(get_argument_type(receiver)))
5705 if (!(*method)[
"decorator_list"].empty())
5707 const Json ¶ms = (*method)[
"args"][
"args"];
5708 for (
size_t i = 1; i < params.size(); ++i)
5709 seen[{method, i}].insert(
5710 i - 1 < node[
"args"].size() &&
5712 ? get_argument_type(node[
"args"][i - 1])
5717 for (
const auto &kv : node.items())
5718 if (kv.value().is_structured())
5723 for (
const auto &[
key, types] : seen)
5725 Json ¶m = (*
key.first)[
"args"][
"args"][
key.second];
5727 types.size() == 1 && !types.begin()->empty() &&
5728 param.value(
"annotation", Json()).is_null())
5729 add_parameter_annotation(param, *types.begin());
5752template <
class Json>
5760 std::set<std::string> top_level_funcs;
5761 for (
const Json &top_node : ast_[
"body"])
5764 top_node[
"_type"] ==
"FunctionDef" && top_node.contains(
"name") &&
5765 top_node[
"name"].is_string())
5766 top_level_funcs.insert(top_node[
"name"].
template get<std::string>());
5769 std::map<std::pair<std::string, size_t>, std::set<std::string>> param_types;
5770 std::set<std::pair<std::string, size_t>> dynamically_typed_params;
5772 root, param_types, top_level_funcs, dynamically_typed_params);
5776 bool annotated =
false;
5777 for (Json &top_node : ast_[
"body"])
5780 top_node[
"_type"] !=
"FunctionDef" || !top_node.contains(
"args") ||
5781 !top_node[
"args"].contains(
"args"))
5784 const std::string fname = top_node[
"name"].template get<std::string>();
5785 Json ¶ms = top_node[
"args"][
"args"];
5786 for (
size_t i = 0; i < params.size(); ++i)
5788 Json ¶m = params[i];
5789 if (param.contains(
"annotation") && !param[
"annotation"].is_null())
5793 if (dynamically_typed_params.count({fname, i}) != 0)
5795 auto it = param_types.find({fname, i});
5796 if (it != param_types.end())
5798 root, param, {fname, i}, it->second, top_level_funcs);
5809template <
class Json>
5813 const std::pair<std::string, size_t> &
key,
5814 std::set<std::string> types,
5815 const std::set<std::string> &top_level_funcs)
5818 if (types.size() != 1)
5820 const Json unannotated = param;
5821 add_parameter_annotation(param, *types.begin());
5822 if (!has_unresolved)
5825 std::map<std::pair<std::string, size_t>, std::set<std::string>> rechecked;
5826 std::set<std::pair<std::string, size_t>> dynamically_typed;
5828 root, rechecked, top_level_funcs, dynamically_typed);
5829 if (rechecked[
key] == types)
5831 param = unannotated;
5835template <
class Json>
5838 std::map<std::pair<std::string, size_t>, std::set<std::string>> ¶m_types,
5839 const std::set<std::string> &top_level_funcs,
5840 std::set<std::pair<std::string, size_t>> &dynamically_typed_params)
5842 if (node.is_object())
5845 node.contains(
"_type") && node[
"_type"] ==
"Call" &&
5846 node.contains(
"func") && node[
"func"].is_object() &&
5847 node[
"func"].contains(
"_type") && node[
"func"][
"_type"] ==
"Name" &&
5848 node[
"func"].contains(
"id") && node[
"func"][
"id"].is_string())
5850 const std::string func_name =
5851 node[
"func"][
"id"].template get<std::string>();
5858 top_level_funcs.count(func_name) != 0 &&
5859 func_name != current_func_name_context_)
5861 const Json &call_args =
5862 node.contains(
"args") ? node[
"args"] : Json::array();
5863 const Json &enclosing_scope_body =
5864 current_func !=
nullptr ? (*current_func)[
"body"] : ast_[
"body"];
5865 for (
size_t i = 0; i < call_args.size(); ++i)
5867 const Json &arg_node = call_args[i];
5871 arg_node.contains(
"_type") && arg_node[
"_type"] ==
"Name" &&
5872 arg_node.contains(
"id") &&
5874 arg_node[
"id"].template get<std::string>(), enclosing_scope_body))
5876 dynamically_typed_params.insert({func_name, i});
5880 std::string arg_type = get_argument_type(arg_node);
5890 param_types[{func_name, i}].insert(arg_type);
5895 const bool is_function_def =
5896 node.contains(
"_type") && node[
"_type"] ==
"FunctionDef" &&
5897 node.contains(
"name") && node[
"name"].is_string();
5898 std::string saved_ctx = current_func_name_context_;
5899 Json *saved_current = current_func;
5900 Json *saved_parent = parent_func;
5901 if (is_function_def)
5906 current_func_name_context_ = node[
"name"].template get<std::string>();
5907 parent_func = saved_current;
5908 current_func = &node;
5911 for (
auto &kv : node.items())
5913 kv.value(), param_types, top_level_funcs, dynamically_typed_params);
5915 if (is_function_def)
5917 current_func_name_context_ = saved_ctx;
5918 current_func = saved_current;
5919 parent_func = saved_parent;
5922 else if (node.is_array())
5924 for (
auto &element : node)
5926 element, param_types, top_level_funcs, dynamically_typed_params);
5930template <
class Json>
5937 for (
const auto &[param, types] : inherited_init_args_)
5939 types.size() == 1 && !types.begin()->empty() &&
5940 param->value(
"annotation", Json()).is_null())
5941 add_parameter_annotation(*param, *types.begin());
5942 inherited_init_args_.clear();
5961 annotate_global_scope();
5965 for (Json &element : ast_[
"body"])
5968 if (element[
"_type"] ==
"FunctionDef")
5969 annotate_function(element);
5971 else if (element[
"_type"] ==
"ClassDef")
5972 annotate_class(element);
5975 apply_pending_specializations();
5978template <
class Json>
5982 extra_subscript_inference_asts_.push_back(&ast);
5985template <
class Json>
5989 annotating_function_entry_point_ =
true;
5991 for (Json &elem : ast_[
"body"])
5993 if (elem[
"_type"] ==
"FunctionDef" && elem[
"name"] == func_name)
5995 get_global_elements(elem[
"body"]);
5997 if (!referenced_global_elements.empty())
5998 annotate_global_scope();
5999 filter_global_elements_ =
false;
6002 annotate_function(elem);
6008template <
class Json>
6009void python_annotation<Json>::get_global_elements(
const Json &node)
6012 if (node.contains(
"_type") && node[
"_type"] ==
"Name" && node.contains(
"id"))
6014 const std::string &var_name = node[
"id"];
6016 if (!var_node.empty())
6018 gs_.add_variable(var_name);
6019 referenced_global_elements.push_back(var_node);
6024 node.contains(
"_type") && node[
"_type"] ==
"Call" &&
6025 node.contains(
"func") && node[
"func"][
"_type"] ==
"Name")
6027 const std::string &class_name = node[
"func"][
"id"];
6030 if (!class_node.empty())
6032 gs_.add_class(class_name);
6033 referenced_global_elements.push_back(class_node);
6037 const auto &func_name = node[
"func"][
"id"];
6044 get_global_elements(func_node);
6046 catch (std::runtime_error &)
6054 if (node.is_object())
6056 for (
auto it = node.begin(); it != node.end(); ++it)
6057 get_global_elements(it.value());
6061 else if (node.is_array())
6063 for (
const auto &element : node)
6064 get_global_elements(element);
6066 filter_global_elements_ =
true;
6069template <
class Json>
6070void python_annotation<Json>::annotate_global_scope()
6072 add_annotation(ast_);
6075template <
class Json>
6076void python_annotation<Json>::annotate_function(Json &function_element)
6078 std::string saved_func_name_context = current_func_name_context_;
6079 Json *saved_parent_func = parent_func;
6081 const std::string &func_name =
6082 function_element[
"name"].template get<std::string>();
6084 if (!saved_func_name_context.empty())
6087 current_func_name_context_ = saved_func_name_context +
"@F@" + func_name;
6089 else if (!current_class_name_.empty())
6092 current_func_name_context_ = current_class_name_ +
"@C@" + func_name;
6097 current_func_name_context_ = func_name;
6100 parent_func = current_func;
6101 current_func = &function_element;
6104 infer_parameter_types(function_element);
6107 add_annotation(function_element);
6110 if (func_name ==
"__init__")
6113 if (function_element[
"returns"].is_null())
6115 function_element[
"returns"] = {
6116 {
"_type",
"Constant"},
6118 {
"lineno", function_element[
"lineno"]},
6119 {
"col_offset", function_element[
"col_offset"]},
6120 {
"end_lineno", function_element[
"lineno"]},
6122 function_element[
"col_offset"].template get<int>() + 4}};
6126 update_end_col_offset(function_element);
6127 current_func =
nullptr;
6128 parent_func = saved_parent_func;
6129 current_func_name_context_ = saved_func_name_context;
6134 bool should_override =
6135 config.options.get_bool_option(
"override-return-annotation");
6136 bool has_no_annotation = function_element[
"returns"].is_null();
6138 if (has_no_annotation || should_override)
6140 std::string inferred_type =
6141 infer_from_return_statements(function_element[
"body"], func_name);
6145 if (!inferred_type.empty() && inferred_type !=
"NoneType")
6152 if (!has_none_return)
6160 function_element[
"returns"] = {
6162 {
"id", inferred_type},
6163 {
"ctx", {{
"_type",
"Load"}}},
6164 {
"lineno", function_element[
"lineno"]},
6165 {
"col_offset", function_element[
"col_offset"]},
6166 {
"end_lineno", function_element[
"lineno"]},
6168 function_element[
"col_offset"].template get<int>() +
6169 inferred_type.size()},
6170 {
"_inferred_annotation",
true}};
6173 else if (inferred_type ==
"NoneType")
6176 function_element[
"returns"] = {
6177 {
"_type",
"Constant"},
6179 {
"lineno", function_element[
"lineno"]},
6180 {
"col_offset", function_element[
"col_offset"]},
6181 {
"end_lineno", function_element[
"lineno"]},
6183 function_element[
"col_offset"].template get<int>() + 4},
6184 {
"_inferred_annotation",
true}};
6191 update_end_col_offset(function_element);
6193 current_func =
nullptr;
6194 parent_func = saved_parent_func;
6195 current_func_name_context_ = saved_func_name_context;
6198template <
class Json>
6199void python_annotation<Json>::annotate_class(Json &class_element)
6201 std::string saved_class_name = current_class_name_;
6202 std::string saved_context = current_func_name_context_;
6204 current_class_name_ = class_element[
"name"].template get<std::string>();
6205 current_func_name_context_.clear();
6207 for (Json &class_member : class_element[
"body"])
6210 if (class_member[
"_type"] ==
"FunctionDef")
6213 annotate_function(class_member);
6216 else if (class_member[
"_type"] ==
"Assign")
6218 std::string inferred_type;
6222 infer_type(class_member, class_element, inferred_type) ==
6226 update_assignment_node(class_member, inferred_type);
6231 std::string attr_name =
6232 class_member[
"targets"][0][
"id"].template get<std::string>();
6233 throw std::runtime_error(
6234 "Cannot infer type for class attribute '" + attr_name +
6235 "' in class '" + class_element[
"name"].
template get<std::string>() +
6236 "'. Please add explicit type annotation.");
6241 current_class_name_ = saved_class_name;
6242 current_func_name_context_ = saved_context;
6245template <
class Json>
6246void python_annotation<Json>::infer_loop_variable_types(
6248 bool synthesized_only)
6250 if (!while_stmt.contains(
"body"))
6256 std::string loop_var, loop_var_type;
6257 for (
auto &stmt : while_stmt[
"body"])
6261 !loop_var.empty() && stmt[
"_type"] ==
"Assign" &&
6262 stmt[
"targets"].size() == 1 && stmt[
"targets"][0][
"_type"] ==
"Name" &&
6263 stmt[
"value"][
"_type"] ==
"Subscript" &&
6264 stmt[
"value"][
"value"].value(
"id",
"") == loop_var &&
6265 stmt[
"value"][
"slice"].value(
"_type",
"") ==
"Constant" &&
6266 stmt[
"value"][
"slice"][
"value"].is_number_unsigned())
6269 loop_var_type, stmt[
"value"][
"slice"][
"value"].
template get<size_t>());
6271 update_assignment_node(stmt, type);
6281 stmt[
"_type"] ==
"AnnAssign" && stmt.contains(
"value") &&
6282 !stmt[
"value"].is_null() && stmt[
"value"][
"_type"] ==
"Subscript")
6285 !stmt[
"value"][
"value"].contains(
"id") ||
6286 (synthesized_only && !stmt.value(
"esbmc_synthesized",
false)))
6289 std::string iter_var =
6290 stmt[
"value"][
"value"][
"id"].template get<std::string>();
6294 if (current_func !=
nullptr && (*current_func).contains(
"body"))
6295 iter_node = find_annotated_assign(iter_var, (*current_func)[
"body"]);
6297 iter_node.empty() && current_func !=
nullptr &&
6298 (*current_func).contains(
"args"))
6300 find_annotated_assign(iter_var, (*current_func)[
"args"][
"args"]);
6301 if (iter_node.empty())
6302 iter_node = find_annotated_assign(iter_var, ast_[
"body"]);
6304 if (iter_node.empty() || !iter_node.contains(
"annotation"))
6307 auto &iter_annotation = iter_node[
"annotation"];
6311 iter_annotation[
"_type"] ==
"Subscript" &&
6312 iter_annotation.contains(
"value") &&
6313 iter_annotation[
"value"].contains(
"id"))
6315 std::string container_type =
6316 iter_annotation[
"value"][
"id"].template get<std::string>();
6318 if (container_type ==
"list" || container_type ==
"List")
6321 if (iter_annotation.contains(
"slice"))
6322 stmt[
"annotation"] = list_element_annotation(iter_node);
6324 else if (container_type ==
"dict" || container_type ==
"Dict")
6328 iter_annotation.contains(
"slice") &&
6329 iter_annotation[
"slice"][
"_type"] ==
"Tuple" &&
6330 iter_annotation[
"slice"].contains(
"elts") &&
6331 !iter_annotation[
"slice"][
"elts"].empty())
6333 stmt[
"annotation"] = iter_annotation[
"slice"][
"elts"][0];
6337 else if (iter_annotation[
"_type"] ==
"Name")
6340 std::string type_name =
6341 iter_annotation[
"id"].template get<std::string>();
6342 if (type_name ==
"str")
6343 stmt[
"annotation"] = iter_annotation;
6346 if (stmt.value(
"esbmc_synthesized",
false))
6348 loop_var = stmt[
"target"].value(
"id",
"");
6357template <
class Json>
6358Json python_annotation<Json>::list_element_annotation(
const Json &iter_node)
6360 const Json &elem = iter_node[
"annotation"][
"slice"];
6361 const Json &
value = iter_node.value(
"value", Json());
6363 elem.value(
"id",
"") !=
"Any" || !
value.is_object() ||
6364 value.value(
"_type",
"") !=
"Attribute" ||
6365 value[
"value"].value(
"id",
"") !=
"self")
6368 const Json attr_elem =
6369 self_attr_list_element(
value[
"attr"].
template get<std::string>());
6370 return attr_elem.is_null() ? elem : attr_elem;
6376template <
class Json>
6377Json python_annotation<Json>::self_attr_list_element(
const std::string &attr)
6383 auto is_self_attr = [&attr](
const Json &node) {
6384 return node.is_object() && node.value(
"_type",
"") ==
"Attribute" &&
6385 node.value(
"attr",
"") == attr && node.contains(
"value") &&
6386 node[
"value"].value(
"id",
"") ==
"self";
6389 std::function<void(
const Json &)>
count = [&](
const Json &node) {
6391 is_self_attr(node) && node.contains(
"ctx") &&
6392 node[
"ctx"].value(
"_type",
"") ==
"Store")
6394 if (node.is_structured())
6395 for (
const Json &child : node)
6406 auto list_slice = [](
const Json &ann) {
6407 return ann.is_object() && ann.value(
"_type",
"") ==
"Subscript" &&
6408 ann[
"value"].value(
"id",
"") ==
"list"
6412 for (
const Json &stmt : init[
"body"])
6415 (stmt[
"_type"] ==
"AnnAssign" && is_self_attr(stmt[
"target"])) ||
6416 (stmt[
"_type"] ==
"Assign" && is_self_attr(stmt[
"targets"][0]));
6421 const Json elem = list_slice(stmt.value(
"annotation", Json()));
6422 const Json &
value = stmt[
"value"];
6424 !elem.is_null() || !
value.is_object() ||
6425 value.value(
"_type",
"") !=
"Name")
6427 const Json param = find_annotated_assign(
6428 value[
"id"].
template get<std::string>(), init[
"args"][
"args"]);
6429 return param.empty() ? Json() : list_slice(param[
"annotation"]);
6438template <
class Json>
6439void python_annotation<Json>::annotate_branch_blocks(Json &element)
6441 for (
const char *field : {
"orelse",
"finalbody"})
6443 if (!element.contains(field) || element[field].empty())
6445 Json block = {{
"body", element[
field]}};
6446 add_annotation(block);
6447 element[
field] = block[
"body"];
6449 if (current_func_name_context_.empty() && element.contains(
"handlers"))
6450 for (Json &handler : element[
"handlers"])
6451 add_annotation(handler);
6454template <
class Json>
6455void python_annotation<Json>::add_annotation(Json &body)
6457 for (
auto &element : body[
"body"])
6459 auto itr = std::find(
6460 referenced_global_elements.begin(),
6461 referenced_global_elements.end(),
6464 if (filter_global_elements_ && itr == referenced_global_elements.end())
6467 if (element.contains(
"lineno"))
6468 current_line_ = element[
"lineno"].template get<int>();
6470 auto &stmt_type = element[
"_type"];
6472 if (stmt_type ==
"If" || stmt_type ==
"While" || stmt_type ==
"Try")
6476 if (stmt_type ==
"While")
6477 infer_loop_variable_types(element,
true);
6479 add_annotation(element);
6482 if (stmt_type ==
"While")
6483 infer_loop_variable_types(element,
false);
6485 if (stmt_type !=
"While")
6486 annotate_branch_blocks(element);
6491 if (stmt_type ==
"FunctionDef")
6494 if (current_func !=
nullptr && &element != current_func)
6495 annotate_function(element);
6500 if (annotating_function_entry_point_)
6503 stmt_type ==
"Expr" && element.contains(
"value") &&
6504 element[
"value"][
"_type"] ==
"Call" &&
6505 element[
"value"][
"func"][
"_type"] ==
"Name")
6510 const Json &top_level_body = ast_[
"body"];
6512 top_level_body, element[
"value"][
"func"][
"id"]);
6513 if (!func_node.empty())
6514 add_annotation(func_node);
6518 if (stmt_type !=
"Assign" || !element[
"type_comment"].is_null())
6524 element.contains(
"targets") && !element[
"targets"].empty() &&
6525 element[
"targets"][0].contains(
"_type") &&
6526 (element[
"targets"][0][
"_type"] ==
"Tuple" ||
6527 element[
"targets"][0][
"_type"] ==
"List"))
6532 std::string inferred_type(
"");
6535 if (element[
"value"][
"_type"] ==
"Name")
6537 const std::string &rhs_name = element[
"value"][
"id"];
6541 inferred_type =
"type";
6542 update_assignment_node(element, inferred_type);
6543 if (itr != referenced_global_elements.end())
6550 if (element.contains(
"targets") && element[
"targets"][0][
"_type"] ==
"Name")
6552 inferred_type = get_type_from_lhs(element[
"targets"][0][
"id"], body);
6555 if (infer_type(element, body, inferred_type) == InferResult::UNKNOWN)
6558 const auto &rhs = element[
"value"];
6561 (rhs[
"_type"] ==
"Constant" && rhs[
"value"].is_null()) ||
6562 (rhs[
"_type"] ==
"NameConstant" && rhs[
"value"].is_null()) ||
6563 (rhs[
"_type"] ==
"Name" && rhs.contains(
"id") && rhs[
"id"] ==
"None"))
6564 inferred_type =
"NoneType";
6566 if (inferred_type.empty())
6568 std::ostringstream oss;
6569 oss <<
"Type inference failed for "
6570 << stmt_type.template get<std::string>() <<
" at line "
6573 throw std::runtime_error(oss.str());
6576 update_assignment_node(
6577 element, widen_to_optional_scalar(element, inferred_type));
6578 if (itr != referenced_global_elements.end())
6586template <
class Json>
6587std::string python_annotation<Json>::widen_to_optional_scalar(
6588 const Json &element,
6589 const std::string &inferred_type)
6591 const Json &target = element[
"targets"][0];
6592 if (target[
"_type"] !=
"Name")
6593 return inferred_type;
6594 const Json &scope = current_func !=
nullptr ? *current_func : ast_;
6596 std::make_pair(&scope, target[
"id"].
template get<std::string>());
6597 auto it = optional_scalar_names_.find(
key);
6598 if (it == optional_scalar_names_.end())
6600 optional_scalar_names_.emplace(
key, optional_scalar_of(
key.second, scope))
6602 return it->second.empty() ? inferred_type : it->second;
6608template <
class Json>
6609std::string python_annotation<Json>::optional_scalar_of(
6610 const std::string &name,
6613 auto stores_none = [](
const Json *store) {
6614 const Json &
value = (*store)[
"value"];
6615 return value.value(
"_type",
"") ==
"Constant" &&
value[
"value"].is_null();
6617 std::vector<const Json *> stores;
6618 collect_name_stores(name, scope[
"body"], stores);
6620 std::none_of(stores.begin(), stores.end(), stores_none) ||
6624 std::set<std::string> types;
6625 for (
const Json *store : stores)
6628 if (stores_none(store))
6631 infer_type(*store, scope, type);
6634 if (types.erase(
"NoneType") == 0 || types.size() != 1)
6636 const std::string &scalar = *types.begin();
6637 return scalar ==
"int" || scalar ==
"float" || scalar ==
"bool"
6638 ?
"Optional[" + scalar +
"]"
6644template <
class Json>
6645std::string python_annotation<Json>::returned_optional_scalar(
6649 for (
const Json &stmt : body)
6651 const std::string kind = stmt.value(
"_type",
"");
6652 if (kind ==
"FunctionDef" || kind ==
"ClassDef")
6655 kind ==
"Return" && stmt[
"value"].is_object() &&
6656 stmt[
"value"].
value(
"_type",
"") ==
"Name")
6658 std::string optional = optional_scalar_of(
6659 stmt[
"value"][
"id"].
template get<std::string>(),
function);
6660 if (!optional.empty())
6663 for (
const char *
key : {
"body",
"orelse",
"finalbody",
"handlers",
"cases"})
6664 if (stmt.contains(
key) && stmt[
key].is_array())
6666 std::string optional = returned_optional_scalar(
function, stmt[
key]);
6675template <
class Json>
6676void python_annotation<Json>::collect_name_stores(
6677 const std::string &name,
6679 std::vector<const Json *> &stores)
6681 for (
const Json &stmt : body)
6683 const std::string kind = stmt.value(
"_type",
"");
6684 if (kind ==
"FunctionDef" || kind ==
"ClassDef")
6687 kind ==
"Assign" && stmt[
"targets"].size() == 1 &&
6688 stmt[
"targets"][0].
value(
"_type",
"") ==
"Name" &&
6689 stmt[
"targets"][0].
value(
"id",
"") == name)
6690 stores.push_back(&stmt);
6691 for (
const char *
key : {
"body",
"orelse",
"finalbody",
"handlers",
"cases"})
6692 if (stmt.contains(
key) && stmt[
key].is_array())
6693 collect_name_stores(name, stmt[
key], stores);
static void count_attr_writers(const Json &node, const std::string &attr, const std::string &name, size_t &stores, size_t &name_reads)
Counts the nodes under node that may store to an attribute named attr, a setattr call or a __class__ ...
Definition annotation_conversion.inl:1285
static Json find_first_append_arg(const Json &node, const std::string &name)
Definition annotation_conversion.inl:1389
static bool call_site_types_agree(const std::string &a, const std::string &b)
Whether two call-site types agree: equal, or the same container where one spells only the bare kind (...
Definition annotation_conversion.inl:5057
std::string unpacked_name_element_type(const std::string &type, size_t index)
The type of element index of a value typed type: the matching parameter of tuple[....
Definition annotation_conversion.inl:256
static bool rebound_in_function(const std::string &name, const std::string &ctx, const Json &ast)
Whether the function at ctx, or the enclosing one that binds name, assigns name after binding it,...
Definition annotation_conversion.inl:4946
static bool all_pointer_sized_scalars(const std::set< std::string > &types)
Whether types is non-empty and holds only types whose values an untyped (pointer-sized) return slot c...
Definition annotation_conversion.inl:4135
bool is_unresolved_arg_type(const std::string &type)
True when an argument type says too little to annotate a parameter with (see collect_function_call_ar...
Definition annotation_conversion.inl:747
bool has_bare_tuple(const std::string &type)
True when type spells a tuple without its element types anywhere.
Definition annotation_conversion.inl:731
static bool union_has_none(const Json &slice)
Whether a Union[...] slice lists None among its members.
Definition annotation_conversion.inl:1965
bool is_empty_list_literal(const Json &node)
True when node binds the empty list literal [], which is how a comprehension's result list starts bef...
Definition annotation_conversion.inl:720
static const char *const untyped_name_return
Marks a return <name> whose type is unknown. Not a valid type name.
Definition annotation_conversion.inl:4131
static bool is_dynamically_typed_name(const Json &arg, const Json &scope)
A bare name that scope assigns incompatible literal kinds, which must reach a method as a tagged valu...
Definition annotation_conversion.inl:5475
std::string subscript_result_type(const std::string &container, bool is_slice)
The type a subscript of a container-typed value yields, or empty.
Definition annotation_conversion.inl:701
static bool aliases_function(const Json &stmt, const std::string &name, const Json &body)
True when stmt aliases name to a function defined in body.
Definition annotation_conversion.inl:5178
static bool name_only_indexed(const Json &node, const std::string &name)
Whether every read of name under node indexes it (name[i]), so no store, method call or alias can cha...
Definition annotation_conversion.inl:1541
bool is_imported_numpy_module_alias(const nlohmann::json &ast, const std::string &name)
Definition converter_stmt.cpp:756
void base_type(type2tc &type, const namespacet &ns)
Definition base_type.cpp:6
static const twodig_t base
Definition bigint.cpp:28
The functions a Python expression may evaluate to, gathered from every binding of the names it reads:...
Definition function_values.h:18
function get_function(const std::string &func_name) const
Retrieve a function definition by name.
Definition module.h:151
bool star_import_binds(const std::string &name) const
Whether from <this module> import * binds name: a public name, and no mention of __all__,...
Definition module.h:190
static std::string list_annotation(const nlohmann::json &call, const nlohmann::json &ast)
"list" for a random call that returns an array, so the annotator types it and arithmetic on it as it ...
Definition numpy_random.cpp:247
void collect_function_call_arg_types(Json &node, std::map< std::pair< std::string, size_t >, std::set< std::string > > ¶m_types, const std::set< std::string > &top_level_funcs, std::set< std::pair< std::string, size_t > > &dynamically_typed_params)
Definition annotation_conversion.inl:5836
bool is_instance_call(const Json &func) const
Whether func names neither a module class nor a module function, so a call through it can only reach ...
Definition annotation_conversion.inl:5657
void add_extra_subscript_inference_source(const Json &ast)
Definition annotation_conversion.inl:5979
void preprocess_method_calls(const Json &node)
Definition annotation_conversion.inl:5571
void add_type_annotation()
Definition annotation_conversion.inl:5931
Json * class_method(const std::string &cls, const Json &method)
The FunctionDef of method in module-level class cls, or null.
Definition annotation_conversion.inl:5644
std::string promote_to_float_operand(const Json &stmt, const std::string &type)
True division of an int or bool yields a float, and so does other arithmetic with a float operand,...
Definition annotation_conversion.inl:1931
bool annotate_from_call_sites(Json &root, Json ¶m, const std::pair< std::string, size_t > &key, std::set< std::string > types, const std::set< std::string > &top_level_funcs)
Annotates param when its call sites agree on one type.
Definition annotation_conversion.inl:5810
bool preprocess_constructor_calls(Json &node)
Definition annotation_conversion.inl:5483
static const std::string & unresolved_arg_type()
Sentinel recorded for a call-site argument this pass cannot type; never a valid Python type name,...
Definition python_annotation.h:77
bool preprocess_function_calls(Json &root)
Infer unannotated parameter types of top-level functions from the argument types at every plain f(arg...
Definition annotation_conversion.inl:5753
void preprocess_receiver_method_calls()
Types the unannotated parameters of a method from its obj.m(args) calls at module level,...
Definition annotation_conversion.inl:5671
static std::optional< bool > binding_is_import(const nlohmann::json &module, const scope_bindings &global, const nlohmann::json *function, bool in_class, bool in_order, const std::string &name, const nlohmann::json &use)
Whether name, read at use inside function (null at module or class level), reads a binding made by an...
Definition converter_symbols.cpp:535
static scope_bindings module_scope_bindings(const nlohmann::json &module, const std::string &name)
The bindings of name in module's own scope, with declared_outer meaning that a function rebinds it th...
Definition converter_symbols.cpp:558
static bool is_type_identifier(const std::string &name)
Definition type_utils.h:451
static bool is_python_exceptions(const std::string &name)
Definition type_utils.h:170
static bool is_python_model_func(const std::string &name)
Definition type_utils.h:161
static std::string get_type_from_consensus_func(const std::string &name)
Definition type_utils.h:145
static bool is_consensus_type(const std::string &name)
Definition type_utils.h:116
static bool is_consensus_func(const std::string &name)
Definition type_utils.h:139
static bool is_builtin_type(const std::string &name)
Definition type_utils.h:86
configt config
Definition config.cpp:8
int count
Definition cuda.h:51
@ full
Alpha-rename every symbol: the digest sees structure and types only.
Definition fingerprint.h:23
literalt pos(literalt a)
Definition literal.h:130
#define log_warning(fmt,...)
Definition message.h:89
float e
Definition cmath.py:18
field(*args, **kwargs)
Definition dataclasses.py:28
bool scope_assigns_divergent_literal_types(const std::string &name, const nlohmann::json &scope_body, const nlohmann::json &module_body=nlohmann::json())
Definition literal_divergence.h:347
Definition json_utils.h:18
const JsonType get_var_node(const std::string &var_name, const JsonType &block)
Definition json_utils.h:704
JsonType find_class(const JsonType &ast_json, const std::string &class_name)
Definition json_utils.h:159
const JsonType find_var_decl(const std::string &var_name, const std::string &function, const JsonType &ast)
Definition json_utils.h:918
std::vector< std::string > split_top_level_params(const std::string &s)
Split a rendered parameter list on commas that are not inside brackets, trimming each part.
Definition json_utils.h:982
JsonType find_function_by_path(const JsonType &ast, const std::vector< std::string > &path)
Definition json_utils.h:834
bool is_class(const std::string &name, const JsonType &ast_json)
Definition json_utils.h:441
JsonType find_var_in_function(const std::string &var_name, const JsonType &func_node)
Definition json_utils.h:888
std::size_t count_name_bindings(const nlohmann::json &node, const std::string &name)
Definition json_utils.h:1092
bool ast_equal_ignoring_location(const nlohmann::json &a, const nlohmann::json &b)
Definition json_utils.h:74
std::string get_annotation_type_name(const JsonType &annotation)
Definition json_utils.h:957
std::vector< std::string > split_function_path(const std::string &function)
Definition json_utils.h:792
std::string render_annotation_type(const JsonType &annotation)
The full generic spelling of an annotation node, e.g.
Definition json_utils.h:1012
std::string constructed_attr_class(const Json &class_node, const std::string &attr)
C when the __init__ of class_node holds self.
Definition json_utils.h:229
JsonType & find_function_or_throw(JsonType &json, const std::string &func_name)
Throws std::runtime_error on a miss.
Definition json_utils.h:562
const std::string get_object_alias(const JsonType &ast, const std::string &obj_name)
Definition json_utils.h:675
bool has_overload_decorator(const JsonType &func_node)
Definition json_utils.h:1367
std::size_t count_attr_stores(const Json &node, const std::string &attr, bool non_self_only)
Counts the stores and deletions of <obj>.
Definition json_utils.h:211
bool class_defines_method(const JsonType &ast_body, const std::string &class_name, const std::string &method_name)
True when class_name or one of its (transitive) base classes defines a method named method_name.
Definition json_utils.h:338
bool has_multiple_assignments_in_block(const std::string &var_name, const JsonType &block)
Definition json_utils.h:1134
const JsonType find_return_node(const JsonType &block)
Definition json_utils.h:1312
const JsonType & find_imported_function(const JsonType &ast, const std::string &func_name)
Definition json_utils.h:596
JsonType try_find_function(const JsonType &json, const std::string &func_name)
Returns an empty JsonType on a miss. Never throws.
Definition json_utils.h:550
Json find_attr_annotation(const Json &class_node, const std::string &attr)
The annotation of self.
Definition json_utils.h:173
list[float] empty(int shape)
Definition numpy.py:29
list[tuple[str, list[str], list[str]]] walk(str top)
Definition os.py:48
lineno
Definition threading_lowering.py:600
body
Definition threading_lowering.py:616
nlohmann::json find_self_attr_init_rhs(const std::string &cls, const std::string &attr, const nlohmann::json &ast_body)
Definition annotation_parser.cpp:66
Definition exception.cpp:51
static std::string node_name(const nlohmann::json &node)
Definition expr.cpp:520
std::string get_base_var_name(const nlohmann::json &node)
Definition annotation_utils.cpp:63
nlohmann::json find_function_recursive(const std::string &func_name, const nlohmann::json &body)
Definition annotation_parser.cpp:26
const std::map< std::string, std::string > & builtin_functions()
Definition annotation_intrinsics.cpp:5
bool has_return_none(const nlohmann::json &body)
Definition annotation_utils.cpp:36
std::string get_type_from_json(const nlohmann::json &value)
Definition annotation_utils.cpp:9
std::string invert_substrings(const std::string &input)
Definition annotation_utils.cpp:78
bool has_annotation(const nlohmann::json &node)
Definition annotation_utils.cpp:30
std::string infer_type_from_default_arg_shape(const nlohmann::json &args_node)
Definition annotation_utils.cpp:103
nlohmann::json find_lambda_in_body(const std::string &func_name, const nlohmann::json &body)
Definition annotation_parser.cpp:10
InferResult
Definition annotation_utils.h:19
void * value
Definition solidity_mapping.c:2
uint256_t key
Definition solidity_mapping.c:1
unsigned stack_budget_guardt< Tag >::depth
Definition stack_budget.h:72
#define bool
Definition stdbool.h:9
std::string name_
Definition module.h:39
A function definition and the module AST it belongs to.
Definition function_values.h:22
std::string name_
Definition module.h:13
std::string return_type_
Definition module.h:14
constexpr bool is_string
Definition toml.hpp:2379