ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
json_utils.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <filesystem>
5#include <fstream>
6#include <sstream>
7#include <stdexcept>
8#include <string>
9#include <functional>
10#include <map>
11#include <unordered_map>
12#include <vector>
13#include <nlohmann/json.hpp>
14
15#define DUMP_OBJECT(obj) printf("%s\n", (obj).dump(2).c_str())
16
17namespace json_utils
18{
19inline bool
20ast_equal_ignoring_location(const nlohmann::json &a, const nlohmann::json &b);
21
22inline bool ast_location_key(const std::string &k)
23{
24 static constexpr const char *loc_keys[] = {
25 "lineno", "col_offset", "end_lineno", "end_col_offset"};
26 for (const char *lk : loc_keys)
27 if (k == lk)
28 return true;
29 return false;
30}
31
32// Object case of ast_equal_ignoring_location. Split out to keep that
33// function's own decision count down.
35 const nlohmann::json &a,
36 const nlohmann::json &b)
37{
38 for (auto it = a.begin(); it != a.end(); ++it)
39 {
40 if (ast_location_key(it.key()))
41 continue;
42 if (
43 !b.contains(it.key()) ||
44 !ast_equal_ignoring_location(it.value(), b[it.key()]))
45 return false;
46 }
47 for (auto it = b.begin(); it != b.end(); ++it)
48 if (!ast_location_key(it.key()) && !a.contains(it.key()))
49 return false;
50 return true;
51}
52
53// Array case of ast_equal_ignoring_location. Split out to keep that
54// function's own decision count down.
56 const nlohmann::json &a,
57 const nlohmann::json &b)
58{
59 if (a.size() != b.size())
60 return false;
61 for (size_t i = 0; i < a.size(); ++i)
62 if (!ast_equal_ignoring_location(a[i], b[i]))
63 return false;
64 return true;
65}
66
67// Structural equality of two AST JSON nodes, ignoring source-location keys
68// (lineno/col_offset/...). Two textually distinct occurrences of the same
69// expression -- e.g. the `l[i+1:]` on each side of
70// `l[i+1:] = reversed(l[i+1:])`, or `np.zeros(3)` spelled on both arms of an
71// if/else -- differ only in their location fields, so a raw `==` would
72// wrongly report them as different.
73inline bool
74ast_equal_ignoring_location(const nlohmann::json &a, const nlohmann::json &b)
75{
76 if (a.type() != b.type())
77 return false;
78 if (a.is_object())
80 if (a.is_array())
82 return a == b;
83}
84
86// True if the AST subtree contains a function-call node. Used to gate
87// constant-folding of assertion tests to expressions that actually invoke a
88// (potentially pure) function — plain symbolic asserts stay on the solver path.
89inline bool ast_contains_call(const nlohmann::json &n)
90{
91 if (n.is_object())
92 {
93 if (n.contains("_type") && n["_type"] == "Call")
94 return true;
95 for (auto it = n.begin(); it != n.end(); ++it)
96 if (ast_contains_call(it.value()))
97 return true;
98 }
99 else if (n.is_array())
100 {
101 for (const auto &e : n)
102 if (ast_contains_call(e))
103 return true;
104 }
105 return false;
106}
107
108inline bool is_name_alias(const nlohmann::json &stmt)
109{
110 return stmt.value("_type", "") == "Assign" && stmt["targets"].size() == 1 &&
111 stmt["targets"][0].value("_type", "") == "Name" &&
112 stmt["value"].value("_type", "") == "Name";
113}
114
117inline std::string dotted_to_path(const std::string &module_name)
118{
119 std::string result = module_name;
120 std::replace(result.begin(), result.end(), '.', '/');
121 return result;
122}
123
124template <typename JsonType>
125bool search_function_in_ast(const JsonType &node, const std::string &func_name)
126{
127 if (!node.is_object())
128 return false;
129
130 // Check if this is a function definition with matching name
131 if (
132 node.contains("_type") && node["_type"] == "FunctionDef" &&
133 node.contains("name") && node["name"] == func_name)
134 {
135 return true;
136 }
137
138 // Recursively search all child nodes
139 for (const auto &[key, value] : node.items())
140 {
141 if (value.is_array())
142 {
143 for (const auto &item : value)
144 {
145 if (search_function_in_ast(item, func_name))
146 return true;
147 }
148 }
149 else if (value.is_object())
150 {
151 if (search_function_in_ast(value, func_name))
152 return true;
153 }
154 }
155 return false;
156}
157
158template <typename JsonType>
159JsonType find_class(const JsonType &ast_json, const std::string &class_name)
160{
161 auto it =
162 std::find_if(ast_json.begin(), ast_json.end(), [&](const JsonType &obj) {
163 return obj.contains("_type") && obj["_type"] == "ClassDef" &&
164 obj["name"] == class_name;
165 });
166
167 return (it != ast_json.end()) ? *it : JsonType();
168}
169
172template <class Json>
173Json find_attr_annotation(const Json &class_node, const std::string &attr)
174{
175 auto annotation_of = [&](const Json &stmt, bool on_self) -> Json {
176 if (stmt.value("_type", "") != "AnnAssign" || !stmt.contains("target"))
177 return Json();
178 const Json &target = stmt["target"];
179 const bool matches =
180 on_self
181 ? target.value("_type", "") == "Attribute" &&
182 target.value("attr", "") == attr && target.contains("value") &&
183 target["value"].value("id", "") == "self"
184 : target.value("_type", "") == "Name" && target.value("id", "") == attr;
185 return matches ? stmt["annotation"] : Json();
186 };
187
188 for (const Json &member : class_node["body"])
189 {
190 Json annotation = annotation_of(member, false);
191 if (!annotation.empty())
192 return annotation;
193 if (
194 member.value("_type", "") != "FunctionDef" ||
195 member["name"] != "__init__")
196 continue;
197 for (const Json &stmt : member["body"])
198 {
199 annotation = annotation_of(stmt, true);
200 if (!annotation.empty())
201 return annotation;
202 }
203 }
204 return Json();
205}
206
209template <class Json>
210std::size_t
211count_attr_stores(const Json &node, const std::string &attr, bool non_self_only)
212{
213 std::size_t n = 0;
214 if (
215 node.is_object() && node.value("_type", "") == "Attribute" &&
216 node.value("attr", "") == attr && node.contains("ctx") &&
217 node["ctx"].value("_type", "") != "Load")
218 n += !non_self_only || node["value"].value("id", "") != "self";
219 for (const auto &child : node)
220 if (child.is_structured())
221 n += count_attr_stores(child, attr, non_self_only);
222 return n;
223}
224
227template <class Json>
228std::string
229constructed_attr_class(const Json &class_node, const std::string &attr)
230{
231 for (const Json &member : class_node["body"])
232 {
233 if (
234 member.value("_type", "") != "FunctionDef" ||
235 member["name"] != "__init__")
236 continue;
237 for (const Json &stmt : member["body"])
238 {
239 if (stmt.value("_type", "") != "Assign" || stmt["targets"].size() != 1)
240 continue;
241 const Json &target = stmt["targets"][0];
242 const Json &value = stmt["value"];
243 if (
244 target.value("_type", "") == "Attribute" &&
245 target.value("attr", "") == attr &&
246 target["value"].value("id", "") == "self" &&
247 value.value("_type", "") == "Call" &&
248 value["func"].value("_type", "") == "Name")
249 return value["func"]["id"].template get<std::string>();
250 }
251 }
252 return "";
253}
254
255template <typename JsonType>
256bool is_property_def(const JsonType &function_def)
257{
258 if (function_def.contains("decorator_list"))
259 for (const auto &dec : function_def["decorator_list"])
260 if (
261 dec.value("_type", std::string()) == "Name" &&
262 dec.value("id", std::string()) == "property")
263 return true;
264 return false;
265}
266
269template <typename JsonType>
270bool any_class_has_property(const JsonType &ast_body, const std::string &name)
271{
272 for (const auto &cls : ast_body)
273 if (cls.value("_type", std::string()) == "ClassDef" && cls.contains("body"))
274 for (const auto &stmt : cls["body"])
275 if (
276 stmt.value("_type", std::string()) == "FunctionDef" &&
277 stmt.value("name", std::string()) == name && is_property_def(stmt))
278 return true;
279 return false;
280}
281
283template <typename JsonType>
284bool any_class_defines_method(const JsonType &node, const std::string &name)
285{
286 if (
287 node.is_object() && node.value("_type", "") == "ClassDef" &&
288 node.contains("body"))
289 for (const auto &stmt : node["body"])
290 if (
291 stmt.value("_type", "") == "FunctionDef" &&
292 stmt.value("name", "") == name)
293 return true;
294 if (node.is_object() || node.is_array())
295 for (const auto &child : node)
296 if (any_class_defines_method(child, name))
297 return true;
298 return false;
299}
300
301// True when `method_name` is a method decorated with @property in `class_name`
302// or one of its (transitive) base classes. Reading `obj.prop` then invokes the
303// getter. A same-named non-property method in a derived class shadows a base
304// property (Python MRO), so a match that is not @property stops the search.
305template <typename JsonType>
307 const JsonType &ast_body,
308 const std::string &class_name,
309 const std::string &method_name)
310{
311 const JsonType cls = find_class(ast_body, class_name);
312 if (cls.empty() || !cls.contains("body"))
313 return false;
314
315 for (const auto &stmt : cls["body"])
316 {
317 if (
318 stmt.value("_type", std::string()) != "FunctionDef" ||
319 stmt.value("name", std::string()) != method_name)
320 continue;
321 return is_property_def(stmt); // a plain method shadows any base property
322 }
323
324 if (cls.contains("bases"))
325 for (const auto &base : cls["bases"])
326 if (
327 base.contains("id") &&
329 ast_body, base["id"].template get<std::string>(), method_name))
330 return true;
331
332 return false;
333}
334
337template <typename JsonType>
339 const JsonType &ast_body,
340 const std::string &class_name,
341 const std::string &method_name)
342{
343 const JsonType cls = find_class(ast_body, class_name);
344 if (cls.empty() || !cls.contains("body"))
345 return false;
346
347 for (const auto &stmt : cls["body"])
348 if (
349 stmt.value("_type", std::string()) == "FunctionDef" &&
350 stmt.value("name", std::string()) == method_name)
351 return true;
352
353 if (cls.contains("bases"))
354 for (const auto &base : cls["bases"])
355 if (
356 base.contains("id") && base["id"] != class_name &&
358 ast_body, base["id"].template get<std::string>(), method_name))
359 return true;
360
361 return false;
362}
363
365template <typename JsonType>
366std::size_t count_class_defs(const JsonType &node)
367{
368 std::size_t count = 0;
369 if (node.is_object() && node.value("_type", "") == "ClassDef")
370 ++count;
371 if (node.is_object() || node.is_array())
372 for (const auto &child : node)
373 count += count_class_defs(child);
374 return count;
375}
376
380template <typename JsonType>
382 const JsonType &body,
383 const std::string &class_name,
384 bool in_function = false)
385{
386 unsigned count = 0;
387 for (const auto &node : body)
388 {
389 if (!node.is_object() || !node.contains("_type"))
390 continue;
391
392 const auto &type = node["_type"];
393 if (in_function && type == "ClassDef" && node["name"] == class_name)
394 ++count;
395
396 if (
397 (type == "FunctionDef" || type == "AsyncFunctionDef") &&
398 node.contains("body"))
399 count += count_function_scope_classes(node["body"], class_name, true);
400 }
401 return count;
402}
403
406template <typename JsonType>
408 const JsonType &node,
409 const std::string &class_name,
410 std::vector<int> &lines)
411{
412 if (node.is_array())
413 {
414 for (const auto &child : node)
415 collect_class_definition_lines(child, class_name, lines);
416 return;
417 }
418
419 if (!node.is_object())
420 return;
421
422 const bool is_class_def = node.value("_type", "") == "ClassDef";
423 if (is_class_def && node.value("name", "") == class_name)
424 lines.push_back(node.value("lineno", 0));
425
426 for (const auto &child : node.items())
427 {
428 // A class nested directly in a class body is never registered.
429 if (is_class_def && child.key() == "body")
430 {
431 for (const auto &stmt : child.value())
432 if (stmt.value("_type", "") != "ClassDef")
433 collect_class_definition_lines(stmt, class_name, lines);
434 continue;
435 }
436 collect_class_definition_lines(child.value(), class_name, lines);
437 }
438}
439
440template <typename JsonType>
441bool is_class(const std::string &name, const JsonType &ast_json)
442{
443 // Find class definition in the current json
444 if (find_class(ast_json["body"], name) != JsonType())
445 return true;
446
447 // A class defined in a function body is an ordinary class (#6743). Accept
448 // it only when the name is unambiguous: a class symbol is keyed by name
449 // alone, so same-named classes in two functions share one symbol and
450 // resolving here would answer for whichever was converted last.
451 if (count_function_scope_classes(ast_json["body"], name) == 1)
452 return true;
453
454 // Cache loaded module JSONs
455 // To avoid repeated filesystem reads across calls
456 static std::unordered_map<std::string, JsonType> module_cache;
457
458 // ast_output_dir may be absent in minimal/test JSON documents
459 // that have no imported modules;
460 // skip the import scan in that case.
461 if (!ast_json.contains("ast_output_dir"))
462 return false;
463
464 const std::string output_dir =
465 ast_json["ast_output_dir"].template get<std::string>();
466
467 // Check whether name is a class defined in an imported module
468 // Use continue after a negative result
469 // so that all Import&ImportFrom nodes are scanned
470 auto load_and_check = [&](const std::string &module_name) -> bool {
471 const std::string path =
472 output_dir + "/" + dotted_to_path(module_name) + ".json";
473 auto it = module_cache.find(path);
474 if (it == module_cache.end())
475 {
476 std::ifstream f(path);
477 if (!f.is_open())
478 return false;
479 JsonType j;
480 f >> j;
481 it = module_cache.emplace(path, std::move(j)).first;
482 }
483 return is_class(name, it->second);
484 };
485
486 for (const auto &obj : ast_json["body"])
487 {
488 if (obj["_type"] == "ImportFrom")
489 {
490 // `from . import X` (relative, no module name) has module == null; skip
491 // it rather than crashing on a null-to-string conversion (#6281).
492 if (
493 !obj["module"].is_null() &&
494 load_and_check(obj["module"].template get<std::string>()))
495 return true;
496 }
497 else if (obj["_type"] == "Import")
498 {
499 for (const auto &imported : obj["names"])
500 {
501 if (load_and_check(imported["name"].template get<std::string>()))
502 return true;
503 }
504 }
505 }
506
507 return false;
508}
509
510template <typename JsonType>
511bool is_module(const std::string &module_name, const JsonType &ast)
512{
513 static std::unordered_map<std::string, bool> is_module_cache;
514
515 if (!ast.contains("ast_output_dir"))
516 return false;
517
518 const std::string rel = dotted_to_path(module_name) + ".json";
519 const std::string dir = ast["ast_output_dir"].template get<std::string>();
520 const std::string path = dir + "/" + rel;
521
522 auto it = is_module_cache.find(path);
523 if (it != is_module_cache.end())
524 return it->second;
525
526 // Opening the path is not enough to decide the name *is* a module: on a
527 // case-insensitive filesystem (macOS, Windows) `Queue.json` opens
528 // `queue.json`, so the class `Queue` imported from `queue` is taken for a
529 // module and every method call on it fails to dispatch. Require a directory
530 // entry that matches byte-for-byte.
531 const std::filesystem::path full(path);
532 std::error_code ec;
533 bool result = false;
534 for (std::filesystem::directory_iterator dit(full.parent_path(), ec), end;
535 !ec && dit != end;
536 dit.increment(ec))
537 {
538 if (dit->path().filename() == full.filename())
539 {
540 result = true;
541 break;
542 }
543 }
544 is_module_cache.emplace(path, result);
545 return result;
546}
547
549template <typename JsonType>
550JsonType try_find_function(const JsonType &json, const std::string &func_name)
551{
552 for (const auto &elem : json)
553 {
554 if (elem["_type"] == "FunctionDef" && elem["name"] == func_name)
555 return elem;
556 }
557 return JsonType();
558}
559
561template <typename JsonType>
562JsonType &find_function_or_throw(JsonType &json, const std::string &func_name)
563{
564 for (auto &elem : json)
565 {
566 if (elem["_type"] == "FunctionDef" && elem["name"] == func_name)
567 return elem;
568 }
569 throw std::runtime_error("Function " + func_name + " not found\n");
570}
571
573template <typename JsonType>
575 const JsonType &ast,
576 const std::string &module,
577 const std::string &entity)
578{
579 for (const auto &stmt : ast["body"])
580 {
581 if (
582 stmt.contains("_type") && stmt["_type"] == "ImportFrom" &&
583 stmt.contains("module") && stmt["module"] == module &&
584 stmt.contains("names"))
585 {
586 for (const auto &name : stmt["names"])
587 if (name.contains("name") && name["name"] == entity)
588 return true;
589 }
590 }
591 return false;
592}
593
594template <typename JsonType>
595const JsonType &
596find_imported_function(const JsonType &ast, const std::string &func_name)
597{
598 for (const auto &node : ast["body"])
599 {
600 if (node["_type"] == "ImportFrom" || node["_type"] == "Import")
601 {
602 for (const auto &name : node["names"])
603 if (name["name"] == func_name)
604 return node;
605 }
606 }
607 throw std::runtime_error("Function " + func_name + " not found\n");
608}
609
611inline std::string dotted_name(const nlohmann::json &node)
612{
613 if (!node.is_object())
614 return {};
615
616 const std::string type = node.value("_type", "");
617 if (type == "Name")
618 return node.value("id", "");
619
620 if (type == "Attribute")
621 {
622 std::string left =
623 dotted_name(node.value("value", nlohmann::json::object()));
624 std::string right = node.value("attr", "");
625 if (!left.empty() && !right.empty())
626 return left + "." + right;
627 return right.empty() ? left : right;
628 }
629
630 return {};
631}
632
636inline std::string
637resolve_import(const nlohmann::json &ast, const std::string &name)
638{
639 for (const auto &node : ast["body"])
640 {
641 const std::string type = node.value("_type", "");
642 if (type != "Import" && type != "ImportFrom")
643 continue;
644 const std::string from = type == "ImportFrom" && node["module"].is_string()
645 ? node["module"].get<std::string>() + "."
646 : "";
647 for (const auto &alias : node["names"])
648 {
649 const std::string bound = alias["asname"].is_string()
650 ? alias["asname"].get<std::string>()
651 : alias["name"].get<std::string>();
652 if (name == bound || name.starts_with(bound + "."))
653 return from + alias["name"].get<std::string>() +
654 name.substr(bound.size());
655 }
656 }
657 return name;
658}
659
661inline std::string
662collections_abc_class(const nlohmann::json &ast, const std::string &name)
663{
664 const std::string prefix = "collections.abc.";
665 const std::string resolved = resolve_import(ast, name);
666 if (
667 !resolved.starts_with(prefix) ||
668 resolved.find('.', prefix.size()) != std::string::npos)
669 return {};
670 return resolved.substr(prefix.size());
671}
672
673template <typename JsonType>
674const std::string
675get_object_alias(const JsonType &ast, const std::string &obj_name)
676{
677 for (auto &node : ast["body"])
678 {
679 if (node["_type"] == "ImportFrom" || node["_type"] == "Import")
680 {
681 for (const auto &name : node["names"])
682 {
683 if (name["_type"] == "alias" && name["asname"] == obj_name)
684 return name["name"];
685 }
686 }
687 }
688
689 std::size_t dot_pos = obj_name.rfind('.');
690 if (dot_pos == std::string::npos)
691 return obj_name;
692
693 std::string prefix = obj_name.substr(0, dot_pos);
694 std::string suffix = obj_name.substr(dot_pos);
695 std::string resolved_prefix = get_object_alias(ast, prefix);
696
697 if (resolved_prefix != prefix)
698 return resolved_prefix + suffix;
699
700 return obj_name;
701}
702
703template <typename JsonType>
704const JsonType get_var_node(const std::string &var_name, const JsonType &block)
705{
706 auto find_in_body = [&](const JsonType &body, const auto &self) -> JsonType {
707 for (auto &element : body)
708 {
709 // Check for annotated assignment (AnnAssign)
710 if (
711 element["_type"] == "AnnAssign" && element.contains("target") &&
712 element["target"].contains("id") && element["target"]["id"] == var_name)
713 return element;
714
715 // Check for regular assignment (Assign)
716 if (
717 element["_type"] == "Assign" && element.contains("targets") &&
718 !element["targets"].empty() &&
719 element["targets"][0].contains("_type") &&
720 element["targets"][0]["_type"] == "Name" &&
721 element["targets"][0].contains("id") &&
722 element["targets"][0]["id"] == var_name)
723 return element;
724
725 // Avoid descending into new scopes
726 if (
727 element.contains("_type") &&
728 (element["_type"] == "FunctionDef" || element["_type"] == "ClassDef" ||
729 element["_type"] == "Lambda"))
730 continue;
731
732 // Recurse into nested blocks
733 for (const auto &key : {"body", "orelse", "finalbody"})
734 {
735 if (element.contains(key) && element[key].is_array())
736 {
737 JsonType nested = self(element[key], self);
738 if (!nested.empty())
739 return nested;
740 }
741 }
742
743 // Try/Except handlers
744 if (element.contains("handlers") && element["handlers"].is_array())
745 {
746 for (auto &handler : element["handlers"])
747 {
748 if (handler.contains("body") && handler["body"].is_array())
749 {
750 JsonType nested = self(handler["body"], self);
751 if (!nested.empty())
752 return nested;
753 }
754 }
755 }
756
757 // Match cases
758 if (element.contains("cases") && element["cases"].is_array())
759 {
760 for (auto &case_node : element["cases"])
761 {
762 if (case_node.contains("body") && case_node["body"].is_array())
763 {
764 JsonType nested = self(case_node["body"], self);
765 if (!nested.empty())
766 return nested;
767 }
768 }
769 }
770 }
771 return JsonType();
772 };
773
774 if (block.contains("body") && block["body"].is_array())
775 {
776 JsonType found = find_in_body(block["body"], find_in_body);
777 if (!found.empty())
778 return found;
779 }
780
781 if (block.contains("args"))
782 {
783 for (auto &arg : block["args"]["args"])
784 if (arg.contains("arg") && arg["arg"] == var_name)
785 return arg;
786 }
787
788 return JsonType();
789}
790
791// Split function path "foo@F@bar@F@baz" -> ["foo", "bar", "baz"]
792inline std::vector<std::string> split_function_path(const std::string &function)
793{
794 std::vector<std::string> path;
795 size_t start = 0;
796 size_t pos = function.find("@F@");
797
798 while (pos != std::string::npos)
799 {
800 path.push_back(function.substr(start, pos - start));
801 start = pos + 3; // Skip "@F@"
802 pos = function.find("@F@", start);
803 }
804
805 if (start < function.length())
806 path.push_back(function.substr(start));
807
808 return path;
809}
810
811// The annotation pass names a method "Cls@C@m". For such a @p name, return the
812// body of class Cls in @p body (null if absent) and strip @p name to "m";
813// otherwise return @p body unchanged.
814template <typename JsonType>
815const JsonType *class_method_scope(const JsonType &body, std::string &name)
816{
817 const size_t sep = name.find("@C@");
818 if (sep == std::string::npos)
819 return &body;
820 const std::string class_name = name.substr(0, sep);
821 name.erase(0, sep + 3);
822 const JsonType *scope = nullptr;
823 for (const auto &elem : body)
824 if (elem["_type"] == "ClassDef" && elem["name"] == class_name)
825 scope = &elem["body"];
826 return scope;
827}
828
829// Find a function in AST by hierarchical path
830// Example: ["foo", "bar"] finds nested function bar() inside foo(), and
831// ["Cls@C@m"] finds method m() of class Cls
832template <typename JsonType>
833JsonType
834find_function_by_path(const JsonType &ast, const std::vector<std::string> &path)
835{
836 if (path.empty())
837 return JsonType();
838
839 std::function<JsonType(const JsonType &, size_t)> search_recursive;
840 search_recursive =
841 [&](const JsonType &parent_body, size_t depth) -> JsonType {
842 if (depth >= path.size())
843 return JsonType();
844
845 std::string target_name = path[depth];
846 const JsonType *scope = class_method_scope(parent_body, target_name);
847 if (scope == nullptr)
848 return JsonType();
849
850 for (const auto &elem : *scope)
851 {
852 if (elem["_type"] == "FunctionDef" && elem["name"] == target_name)
853 {
854 // Found at this level
855 if (depth == path.size() - 1)
856 return elem; // This is the target function
857
858 // Need to go deeper into nested functions
859 if (elem.contains("body") && elem["body"].is_array())
860 return search_recursive(elem["body"], depth + 1);
861 }
862 }
863 return JsonType();
864 };
865
866 // First, try to find in top-level functions
867 JsonType result = search_recursive(ast["body"], 0);
868 if (!result.empty())
869 return result;
870
871 // If not found, search inside class methods
872 for (const auto &elem : ast["body"])
873 {
874 if (elem["_type"] == "ClassDef" && elem.contains("body"))
875 {
876 result = search_recursive(elem["body"], 0);
877 if (!result.empty())
878 return result;
879 }
880 }
881
882 return JsonType(); // Not found
883}
884
885// Find variable in a specific function node (searches params + body)
886template <typename JsonType>
887JsonType
888find_var_in_function(const std::string &var_name, const JsonType &func_node)
889{
890 if (func_node.empty())
891 return JsonType();
892
893 // First, search in function parameters
894 if (func_node.contains("args") && func_node["args"].contains("args"))
895 {
896 for (const auto &arg : func_node["args"]["args"])
897 {
898 if (arg.contains("arg") && arg["arg"] == var_name)
899 return arg;
900 }
901 }
902
903 // Search in keyword-only arguments (after *)
904 if (func_node.contains("args") && func_node["args"].contains("kwonlyargs"))
905 {
906 for (const auto &arg : func_node["args"]["kwonlyargs"])
907 {
908 if (arg.contains("arg") && arg["arg"] == var_name)
909 return arg;
910 }
911 }
912
913 // Then search in function body
914 return get_var_node(var_name, func_node);
915}
916
917template <typename JsonType>
918const JsonType find_var_decl(
919 const std::string &var_name,
920 const std::string &function,
921 const JsonType &ast)
922{
923 // If no function context, search in global scope
924 if (function.empty())
925 return get_var_node(var_name, ast);
926
927 // Parse function path (e.g., "foo@F@bar" -> ["foo", "bar"])
928 std::vector<std::string> function_path = split_function_path(function);
929
930 // Search from innermost to outermost scope (closure semantics)
931 // Example: for "foo@F@bar", search first in bar(), then in foo()
932 for (int scope_level = static_cast<int>(function_path.size()) - 1;
933 scope_level >= 0;
934 --scope_level)
935 {
936 // Build partial path up to current scope level
937 std::vector<std::string> partial_path(
938 function_path.begin(), function_path.begin() + scope_level + 1);
939
940 // Find the function node at this scope level
941 JsonType func_node = find_function_by_path(ast, partial_path);
942
943 if (!func_node.empty())
944 {
945 // Search for variable in this function's scope
946 JsonType var = find_var_in_function(var_name, func_node);
947 if (!var.empty())
948 return var; // Found in this scope
949 }
950 }
951
952 // Fallback: search in global scope
953 return get_var_node(var_name, ast);
954}
955
956template <typename JsonType>
957std::string get_annotation_type_name(const JsonType &annotation)
958{
959 if (annotation.contains("id"))
960 return annotation["id"];
961
962 if (
963 annotation.contains("_type") && annotation["_type"] == "Subscript" &&
964 annotation.contains("value") && annotation["value"].contains("id"))
965 {
966 std::string base_type =
967 annotation["value"]["id"].template get<std::string>();
968 if (annotation.contains("slice") && annotation["slice"].contains("id"))
969 return base_type + "[" +
970 annotation["slice"]["id"].template get<std::string>() + "]";
971 return base_type;
972 }
973
974 if (annotation.contains("value") && annotation["value"].contains("id"))
975 return annotation["value"]["id"];
976
977 return "";
978}
979
982inline std::vector<std::string> split_top_level_params(const std::string &s)
983{
984 std::vector<std::string> parts;
985 int depth = 0;
986 size_t start = 0;
987 for (size_t i = 0; i <= s.size(); ++i)
988 {
989 if (i == s.size() || (s[i] == ',' && depth == 0))
990 {
991 std::string part = s.substr(start, i - start);
992 const size_t b = part.find_first_not_of(" \t");
993 if (b != std::string::npos)
994 parts.push_back(part.substr(b, part.find_last_not_of(" \t") - b + 1));
995 start = i + 1;
996 }
997 else if (s[i] == '[')
998 ++depth;
999 else if (s[i] == ']')
1000 --depth;
1001 }
1002 return parts;
1003}
1004
1011template <typename JsonType>
1012std::string render_annotation_type(const JsonType &annotation)
1013{
1014 if (!annotation.is_object())
1015 return "";
1016
1017 if (annotation.contains("id") && annotation["id"].is_string())
1018 return annotation["id"];
1019
1020 if (annotation.value("_type", "") != "Subscript")
1021 return "";
1022
1023 const std::string base = render_annotation_type(annotation["value"]);
1024 if (base.empty() || !annotation.contains("slice"))
1025 return base;
1026
1027 const auto &slice = annotation["slice"];
1028
1029 // tuple[A, B] and dict[K, V] carry their parameters in a Tuple slice.
1030 if (slice.value("_type", "") == "Tuple" && slice.contains("elts"))
1031 {
1032 std::string params;
1033 for (const auto &elt : slice["elts"])
1034 {
1035 const std::string rendered = render_annotation_type(elt);
1036 if (rendered.empty())
1037 return "";
1038 if (!params.empty())
1039 params += ", ";
1040 params += rendered;
1041 }
1042 return params.empty() ? base : base + "[" + params + "]";
1043 }
1044
1045 const std::string param = render_annotation_type(slice);
1046 return param.empty() ? "" : base + "[" + param + "]";
1047}
1048
1049template <typename JsonType>
1050const JsonType get_var_value(
1051 const std::string &var_name,
1052 const std::string &function,
1053 const JsonType &ast)
1054{
1055 JsonType value = find_var_decl(var_name, function, ast);
1056 while (!value.empty() && value["_type"] != "arg" &&
1057 value["value"]["_type"] == "Name")
1058 {
1059 value = find_var_decl(value["value"]["id"], function, ast);
1060 }
1061 return value;
1062}
1063
1064// Whether `node` itself binds `name`, other than as a Store/Del Name.
1065inline std::size_t binds_by_field(
1066 const nlohmann::json &node,
1067 const std::string &kind,
1068 const std::string &name)
1069{
1070 static const std::map<std::string, std::string> name_field = {
1071 {"arg", "arg"},
1072 {"FunctionDef", "name"},
1073 {"AsyncFunctionDef", "name"},
1074 {"ClassDef", "name"},
1075 {"ExceptHandler", "name"},
1076 {"MatchAs", "name"},
1077 {"MatchStar", "name"},
1078 {"Global", "names"},
1079 {"Nonlocal", "names"}};
1080 const auto it = name_field.find(kind);
1081 if (it == name_field.end() || !node.contains(it->second))
1082 return 0;
1083 const nlohmann::json &field = node[it->second];
1084 return field.is_array() ? std::count(field.begin(), field.end(), name)
1085 : field == name;
1086}
1087
1088// Counts every construct under `node` that binds `name`, in any scope: a
1089// Store/Del Name (assignment, walrus, loop and with targets), a
1090// global/nonlocal declaration, a parameter, a def/class, an import alias.
1091inline std::size_t
1092count_name_bindings(const nlohmann::json &node, const std::string &name)
1093{
1094 std::size_t n = 0;
1095 if (node.is_object())
1096 {
1097 const std::string kind = node.value("_type", "");
1098 if (kind == "Name")
1099 n += node.value("id", "") == name &&
1100 node["ctx"].value("_type", "") != "Load";
1101 else if (kind == "alias")
1102 n += (node["asname"].is_null() ? node["name"] : node["asname"]) == name;
1103 else
1104 n += binds_by_field(node, kind, name);
1105 }
1106 for (const auto &child : node)
1107 if (child.is_structured())
1108 n += count_name_bindings(child, name);
1109 return n;
1110}
1111
1112// The value a top-level `name = ...` or `name: T = ...` statement assigns.
1113inline const nlohmann::json *
1114top_level_value(const nlohmann::json &module, const std::string &name)
1115{
1116 auto binds = [&](const nlohmann::json &target) {
1117 return target.value("_type", "") == "Name" && target["id"] == name;
1118 };
1119 for (const auto &stmt : module["body"])
1120 {
1121 const std::string type = stmt.value("_type", "");
1122 if (
1123 type == "Assign" && stmt["targets"].size() == 1 &&
1124 binds(stmt["targets"][0]))
1125 return &stmt["value"];
1126 if (
1127 type == "AnnAssign" && binds(stmt["target"]) && !stmt["value"].is_null())
1128 return &stmt["value"];
1129 }
1130 return nullptr;
1131}
1132
1133template <typename JsonType>
1135 const std::string &var_name,
1136 const JsonType &block)
1137{
1138 unsigned count = 0;
1139 auto visit = [&](const JsonType &body, const auto &self) -> bool {
1140 for (const auto &element : body)
1141 {
1142 const auto &t = element["_type"];
1143
1144 // Check for annotated assignment (AnnAssign)
1145 const bool is_ann = t == "AnnAssign" && element.contains("target") &&
1146 element["target"].contains("id") &&
1147 element["target"]["id"] == var_name;
1148 // Check for regular assignment (Assign)
1149 const bool is_assign = t == "Assign" && element.contains("targets") &&
1150 !element["targets"].empty() &&
1151 element["targets"][0].contains("_type") &&
1152 element["targets"][0]["_type"] == "Name" &&
1153 element["targets"][0].contains("id") &&
1154 element["targets"][0]["id"] == var_name;
1155 // Check for augmented assignment (AugAssign)
1156 const bool is_aug = t == "AugAssign" && element.contains("target") &&
1157 element["target"].contains("id") &&
1158 element["target"]["id"] == var_name;
1159 if ((is_ann || is_assign || is_aug) && ++count > 1)
1160 return true;
1161
1162 // Avoid descending into new scopes
1163 if (t == "FunctionDef" || t == "ClassDef" || t == "Lambda")
1164 continue;
1165
1166 // Recurse into nested blocks
1167 for (const auto &key : {"body", "orelse", "finalbody"})
1168 if (
1169 element.contains(key) && element[key].is_array() &&
1170 self(element[key], self))
1171 return true;
1172
1173 // Try/Except handlers
1174 if (element.contains("handlers") && element["handlers"].is_array())
1175 for (const auto &h : element["handlers"])
1176 if (
1177 h.contains("body") && h["body"].is_array() && self(h["body"], self))
1178 return true;
1179
1180 // Match cases
1181 if (element.contains("cases") && element["cases"].is_array())
1182 for (const auto &c : element["cases"])
1183 if (
1184 c.contains("body") && c["body"].is_array() && self(c["body"], self))
1185 return true;
1186 }
1187 return false;
1188 };
1189 return block.contains("body") && block["body"].is_array() &&
1190 visit(block["body"], visit);
1191}
1192
1193template <typename JsonType>
1195 const std::string &var_name,
1196 const std::string &function,
1197 const JsonType &ast)
1198{
1199 // If no function context, search in global scope
1200 if (function.empty())
1201 return has_multiple_assignments_in_block(var_name, ast);
1202
1203 // Parse function path (e.g., "foo@F@bar" -> ["foo", "bar"])
1204 std::vector<std::string> function_path = split_function_path(function);
1205
1206 // Search from innermost to outermost scope (closure semantics)
1207 for (int level = static_cast<int>(function_path.size()) - 1; level >= 0;
1208 --level)
1209 {
1210 std::vector<std::string> partial_path(
1211 function_path.begin(), function_path.begin() + level + 1);
1212 JsonType func_node = find_function_by_path(ast, partial_path);
1213 if (!func_node.empty() && !get_var_node(var_name, func_node).empty())
1214 return has_multiple_assignments_in_block(var_name, func_node);
1215 }
1216
1217 // Fallback: global scope
1218 return has_multiple_assignments_in_block(var_name, ast);
1219}
1220
1221template <typename JsonType>
1223 const JsonType &node,
1224 const std::string &function,
1225 const JsonType &ast,
1226 long long &value)
1227{
1228 if (
1229 node.contains("_type") && node["_type"] == "Constant" &&
1230 node.contains("value") && node["value"].is_number_integer())
1231 {
1232 value = node["value"].template get<long long>();
1233 return true;
1234 }
1235
1236 if (
1237 node.contains("_type") && node["_type"] == "UnaryOp" &&
1238 node.contains("operand") && node["operand"].contains("value") &&
1239 node["operand"]["value"].is_number_integer() && node.contains("op"))
1240 {
1241 const auto &op = node["op"];
1242 const bool is_usub =
1243 (op.is_object() && op.contains("_type") && op["_type"] == "USub") ||
1244 (op.is_string() && op == "USub");
1245 const bool is_uadd =
1246 (op.is_object() && op.contains("_type") && op["_type"] == "UAdd") ||
1247 (op.is_string() && op == "UAdd");
1248
1249 if (is_usub || is_uadd)
1250 {
1251 value = node["operand"]["value"].template get<long long>();
1252 if (is_usub)
1253 value = -value;
1254 return true;
1255 }
1256 }
1257
1258 if (node.contains("_type") && node["_type"] == "Name" && node.contains("id"))
1259 {
1260 const std::string var_name = node["id"].template get<std::string>();
1261 JsonType var_value = get_var_value(var_name, function, ast);
1262
1263 if (
1264 !var_value.empty() && var_value.contains("value") &&
1265 var_value["value"].contains("_type") &&
1266 var_value["value"]["_type"] == "Constant" &&
1267 var_value["value"].contains("value") &&
1268 var_value["value"]["value"].is_number_integer())
1269 {
1270 value = var_value["value"]["value"].template get<long long>();
1271 return true;
1272 }
1273 }
1274
1275 return false;
1276}
1277
1278template <typename JsonType>
1279const JsonType get_list_element(const JsonType &list_value, int pos)
1280{
1281 // Handle direct List node
1282 if (
1283 list_value["_type"] == "List" && list_value.contains("elts") &&
1284 !list_value["elts"].empty())
1285 {
1286 return list_value["elts"][pos];
1287 }
1288
1289 // Handle BinOp (e.g., list concatenation or repetition)
1290 if (list_value["_type"] == "BinOp")
1291 {
1292 if (list_value["left"]["_type"] == "List")
1293 return list_value["left"]["elts"][pos];
1294 if (list_value["right"]["_type"] == "List")
1295 return list_value["right"]["elts"][pos];
1296 }
1297
1298 // Handle Subscript (e.g., d['a'] where d is a dict containing lists)
1299 // Return empty JSON: caller should use type annotations instead
1300 if (list_value["_type"] == "Subscript")
1301 return JsonType();
1302
1303 // Handle Name reference (variable that holds a list)
1304 // Return empty JSON: caller should resolve the variable
1305 if (list_value["_type"] == "Name")
1306 return JsonType();
1307
1308 return JsonType();
1309}
1310
1311template <typename JsonType>
1312const JsonType find_return_node(const JsonType &block)
1313{
1314 for (const auto &stmt : block)
1315 {
1316 if (stmt.contains("_type") && stmt["_type"] == "Return")
1317 return stmt;
1318 }
1319 return JsonType();
1320}
1321
1327inline std::string extract_var_name_from_symbol_id(const std::string &symbol_id)
1328{
1329 size_t last_at = symbol_id.find_last_of('@');
1330 return (last_at != std::string::npos) ? symbol_id.substr(last_at + 1)
1331 : symbol_id;
1332}
1333
1334// Build a List/Tuple AST node whose elements are the single-character strings
1335// of @p chars. Used to lower list("abc") / tuple("abc") to the proven
1336// list/tuple-literal path. Location fields are copied from @p loc_src.
1337template <typename JsonType>
1339 const char *kind,
1340 const std::string &chars,
1341 const JsonType &loc_src)
1342{
1343 static constexpr const char *loc_keys[] = {
1344 "lineno", "col_offset", "end_lineno", "end_col_offset"};
1345 auto copy_loc = [&](JsonType &node) {
1346 for (const char *k : loc_keys)
1347 if (loc_src.contains(k))
1348 node[k] = loc_src[k];
1349 };
1350
1351 JsonType node;
1352 node["_type"] = kind;
1353 node["elts"] = JsonType::array();
1354 for (const char ch : chars)
1355 {
1356 JsonType elt;
1357 elt["_type"] = "Constant";
1358 elt["value"] = std::string(1, ch);
1359 copy_loc(elt);
1360 node["elts"].push_back(elt);
1361 }
1362 copy_loc(node);
1363 return node;
1364}
1365
1366template <typename JsonType>
1367bool has_overload_decorator(const JsonType &func_node)
1368{
1369 // Check for @overload decorators
1370 if (!func_node.contains("decorator_list"))
1371 return false;
1372
1373 for (const auto &decorator : func_node["decorator_list"])
1374 {
1375 if (decorator["_type"] == "Name" && decorator["id"] == "overload")
1376 return true;
1377 }
1378 return false;
1379}
1380
1387template <typename JsonType>
1388std::string
1389imported_function_return_type(const std::string &name, const JsonType &ast_json)
1390{
1391 if (!ast_json.contains("ast_output_dir") || !ast_json.contains("body"))
1392 return "";
1393
1394 const std::string output_dir =
1395 ast_json["ast_output_dir"].template get<std::string>();
1396
1397 auto lookup = [&](const std::string &module_name) -> std::string {
1398 std::ifstream f(output_dir + "/" + dotted_to_path(module_name) + ".json");
1399 if (!f.is_open())
1400 return "";
1401 JsonType module_json;
1402 f >> module_json;
1403 if (!module_json.contains("body"))
1404 return "";
1405 JsonType fn = try_find_function(module_json["body"], name);
1406 if (fn.empty() || !fn.contains("returns") || fn["returns"].is_null())
1407 return "";
1408 return get_annotation_type_name(fn["returns"]);
1409 };
1410
1411 for (const auto &obj : ast_json["body"])
1412 {
1413 if (obj["_type"] == "ImportFrom")
1414 {
1415 if (obj["module"].is_null())
1416 continue;
1417 const std::string r = lookup(obj["module"].template get<std::string>());
1418 if (!r.empty())
1419 return r;
1420 }
1421 else if (obj["_type"] == "Import")
1422 {
1423 for (const auto &imported : obj["names"])
1424 {
1425 const std::string r =
1426 lookup(imported["name"].template get<std::string>());
1427 if (!r.empty())
1428 return r;
1429 }
1430 }
1431 }
1432
1433 return "";
1434}
1435
1436// Every `return` value under `node`, through nested blocks but not nested
1437// definitions.
1439 const nlohmann::json &node,
1440 std::vector<nlohmann::json> &values)
1441{
1442 if (!node.is_object() && !node.is_array())
1443 return;
1444 const std::string type = node.is_object() ? node.value("_type", "") : "";
1445 if (type == "FunctionDef" || type == "ClassDef" || type == "Lambda")
1446 return;
1447 if (type == "Return" && node.contains("value") && !node["value"].is_null())
1448 values.push_back(node["value"]);
1449 for (const auto &child : node)
1450 collect_return_values(child, values);
1451}
1452
1453} // namespace json_utils
void base_type(type2tc &type, const namespacet &ns)
Definition base_type.cpp:6
static const twodig_t base
Definition bigint.cpp:28
Definition module.h:60
Definition symbol_id.h:6
Definition toml.hpp:1531
int count
Definition cuda.h:51
@ full
Alpha-rename every symbol: the digest sees structure and types only.
Definition fingerprint.h:23
nlohmann::json json
Definition json.cpp:12
literalt pos(literalt a)
Definition literal.h:130
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::string dotted_name(const nlohmann::json &node)
The dotted name a Name or Attribute node spells, e.g. "collections.abc.Set".
Definition json_utils.h:611
const JsonType get_var_value(const std::string &var_name, const std::string &function, const JsonType &ast)
Definition json_utils.h:1050
std::string imported_function_return_type(const std::string &name, const JsonType &ast_json)
The declared return-type name of a function name defined in a module this AST imports,...
Definition json_utils.h:1389
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
bool ast_location_key(const std::string &k)
Definition json_utils.h:22
JsonType find_function_by_path(const JsonType &ast, const std::vector< std::string > &path)
Definition json_utils.h:834
bool ast_arrays_equal_ignoring_location(const nlohmann::json &a, const nlohmann::json &b)
Definition json_utils.h:55
bool extract_constant_integer(const JsonType &node, const std::string &function, const JsonType &ast, long long &value)
Definition json_utils.h:1222
bool any_class_defines_method(const JsonType &node, const std::string &name)
True when a class anywhere under node defines a method named name.
Definition json_utils.h:284
bool is_property_def(const JsonType &function_def)
Definition json_utils.h:256
void collect_return_values(const nlohmann::json &node, std::vector< nlohmann::json > &values)
Definition json_utils.h:1438
std::size_t binds_by_field(const nlohmann::json &node, const std::string &kind, const std::string &name)
Definition json_utils.h:1065
bool is_class(const std::string &name, const JsonType &ast_json)
Definition json_utils.h:441
std::string extract_var_name_from_symbol_id(const std::string &symbol_id)
Extract the variable name from a symbol identifier Examples: "py:test.py@l" -> "l" "py:test....
Definition json_utils.h:1327
bool ast_contains_call(const nlohmann::json &n)
True for X = Y between two bare names, such as _Elem = int.
Definition json_utils.h:89
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 is_module(const std::string &module_name, const JsonType &ast)
Definition json_utils.h:511
std::string collections_abc_class(const nlohmann::json &ast, const std::string &name)
The collections.abc class that dotted name denotes, or empty.
Definition json_utils.h:662
bool ast_equal_ignoring_location(const nlohmann::json &a, const nlohmann::json &b)
Definition json_utils.h:74
const JsonType get_list_element(const JsonType &list_value, int pos)
Definition json_utils.h:1279
std::string get_annotation_type_name(const JsonType &annotation)
Definition json_utils.h:957
bool ast_objects_equal_ignoring_location(const nlohmann::json &a, const nlohmann::json &b)
Definition json_utils.h:34
bool is_name_alias(const nlohmann::json &stmt)
Definition json_utils.h:108
std::vector< std::string > split_function_path(const std::string &function)
Definition json_utils.h:792
bool any_class_has_property(const JsonType &ast_body, const std::string &name)
Definition json_utils.h:270
unsigned count_function_scope_classes(const JsonType &body, const std::string &class_name, bool in_function=false)
Counts the ClassDef nodes named class_name that sit inside a function body under body.
Definition json_utils.h:381
bool search_function_in_ast(const JsonType &node, const std::string &func_name)
Definition json_utils.h:125
bool is_imported_from(const JsonType &ast, const std::string &module, const std::string &entity)
True when from <module> import <entity> appears at the AST top level.
Definition json_utils.h:574
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
std::string dotted_to_path(const std::string &module_name)
Convert a dotted Python module name to a filesystem path segment.
Definition json_utils.h:117
const JsonType * class_method_scope(const JsonType &body, std::string &name)
Definition json_utils.h:815
JsonType build_char_sequence_node(const char *kind, const std::string &chars, const JsonType &loc_src)
Definition json_utils.h:1338
bool has_overload_decorator(const JsonType &func_node)
Definition json_utils.h:1367
void collect_class_definition_lines(const JsonType &node, const std::string &class_name, std::vector< int > &lines)
Source lines of every ClassDef named class_name under node that the converter registers: at any scope...
Definition json_utils.h:407
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
bool is_property_method(const JsonType &ast_body, const std::string &class_name, const std::string &method_name)
Definition json_utils.h:306
const JsonType find_return_node(const JsonType &block)
Definition json_utils.h:1312
std::string resolve_import(const nlohmann::json &ast, const std::string &name)
What dotted name denotes through the module's imports, e.g.
Definition json_utils.h:637
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
std::size_t count_class_defs(const JsonType &node)
Counts every ClassDef under node, at any depth.
Definition json_utils.h:366
Json find_attr_annotation(const Json &class_node, const std::string &attr)
The annotation of self.
Definition json_utils.h:173
const nlohmann::json * top_level_value(const nlohmann::json &module, const std::string &name)
Definition json_utils.h:1114
bool has_multiple_assignments_in_scope(const std::string &var_name, const std::string &function, const JsonType &ast)
Definition json_utils.h:1194
Definition path.py:1
uint256_t key
Definition solidity_mapping.c:1
unsigned stack_budget_guardt< Tag >::depth
Definition stack_budget.h:72
Definition module.h:12