cg-code-graph 0.10.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. cg_code_graph-0.10.1.dist-info/METADATA +678 -0
  2. cg_code_graph-0.10.1.dist-info/RECORD +174 -0
  3. cg_code_graph-0.10.1.dist-info/WHEEL +5 -0
  4. cg_code_graph-0.10.1.dist-info/entry_points.txt +3 -0
  5. cg_code_graph-0.10.1.dist-info/licenses/LICENSE +21 -0
  6. cg_code_graph-0.10.1.dist-info/top_level.txt +1 -0
  7. codegraph/__init__.py +2 -0
  8. codegraph/aitools.py +129 -0
  9. codegraph/apps.py +76 -0
  10. codegraph/blindspots.py +428 -0
  11. codegraph/bridges.py +1701 -0
  12. codegraph/cli.py +725 -0
  13. codegraph/concepts.py +362 -0
  14. codegraph/config.py +559 -0
  15. codegraph/core/__init__.py +0 -0
  16. codegraph/core/cache.py +375 -0
  17. codegraph/core/detect.py +80 -0
  18. codegraph/core/extractors.py +187 -0
  19. codegraph/core/fsutil.py +61 -0
  20. codegraph/core/generated.py +575 -0
  21. codegraph/core/model.py +174 -0
  22. codegraph/core/paths.py +175 -0
  23. codegraph/core/plugin.py +160 -0
  24. codegraph/core/store.py +80 -0
  25. codegraph/core/syntax_errors.py +132 -0
  26. codegraph/coverage.py +928 -0
  27. codegraph/doctor.py +453 -0
  28. codegraph/external.py +613 -0
  29. codegraph/indexer.py +336 -0
  30. codegraph/link.py +434 -0
  31. codegraph/lint_async.py +524 -0
  32. codegraph/mcp_server.py +1303 -0
  33. codegraph/parity.py +473 -0
  34. codegraph/parity_structure.py +307 -0
  35. codegraph/payload.py +321 -0
  36. codegraph/plans.py +1285 -0
  37. codegraph/platform_scan.py +643 -0
  38. codegraph/platforms.py +1369 -0
  39. codegraph/plugins/__init__.py +0 -0
  40. codegraph/plugins/cfamily/__init__.py +0 -0
  41. codegraph/plugins/cfamily/plugin.py +930 -0
  42. codegraph/plugins/cfamily/syntax.py +881 -0
  43. codegraph/plugins/dart/__init__.py +0 -0
  44. codegraph/plugins/dart/bridges.py +345 -0
  45. codegraph/plugins/dart/extractor/bin/extract.dart +717 -0
  46. codegraph/plugins/dart/extractor/pubspec.lock +149 -0
  47. codegraph/plugins/dart/extractor/pubspec.yaml +7 -0
  48. codegraph/plugins/dart/http.py +904 -0
  49. codegraph/plugins/dart/models.py +308 -0
  50. codegraph/plugins/dart/plugin.py +625 -0
  51. codegraph/plugins/dart/program.py +907 -0
  52. codegraph/plugins/django/__init__.py +0 -0
  53. codegraph/plugins/django/extras.py +378 -0
  54. codegraph/plugins/django/models.py +508 -0
  55. codegraph/plugins/django/plugin.py +728 -0
  56. codegraph/plugins/django/schemas.py +339 -0
  57. codegraph/plugins/django/shapes.py +216 -0
  58. codegraph/plugins/django/urls.py +603 -0
  59. codegraph/plugins/express/__init__.py +0 -0
  60. codegraph/plugins/express/plugin.py +428 -0
  61. codegraph/plugins/flutter/__init__.py +0 -0
  62. codegraph/plugins/flutter/plugin.py +538 -0
  63. codegraph/plugins/kotlin/__init__.py +0 -0
  64. codegraph/plugins/kotlin/exact.py +457 -0
  65. codegraph/plugins/kotlin/plugin.py +1961 -0
  66. codegraph/plugins/kotlin/reparse.py +234 -0
  67. codegraph/plugins/laravel/__init__.py +0 -0
  68. codegraph/plugins/laravel/broadcast.py +351 -0
  69. codegraph/plugins/laravel/plugin.py +863 -0
  70. codegraph/plugins/laravel/tests.py +262 -0
  71. codegraph/plugins/laravel/values.py +728 -0
  72. codegraph/plugins/native/__init__.py +0 -0
  73. codegraph/plugins/native/gates.py +286 -0
  74. codegraph/plugins/native/runner.py +183 -0
  75. codegraph/plugins/native/scipread.py +194 -0
  76. codegraph/plugins/native/ts.py +54 -0
  77. codegraph/plugins/nest/__init__.py +0 -0
  78. codegraph/plugins/nest/plugin.py +654 -0
  79. codegraph/plugins/nextjs/__init__.py +0 -0
  80. codegraph/plugins/nextjs/plugin.py +336 -0
  81. codegraph/plugins/nuxt/__init__.py +0 -0
  82. codegraph/plugins/nuxt/plugin.py +308 -0
  83. codegraph/plugins/php/__init__.py +0 -0
  84. codegraph/plugins/php/extractor/composer.json +5 -0
  85. codegraph/plugins/php/extractor/composer.lock +76 -0
  86. codegraph/plugins/php/extractor/extract.php +743 -0
  87. codegraph/plugins/php/gating.py +573 -0
  88. codegraph/plugins/php/plugin.py +668 -0
  89. codegraph/plugins/php/strings.py +197 -0
  90. codegraph/plugins/python/__init__.py +0 -0
  91. codegraph/plugins/python/aitools.py +664 -0
  92. codegraph/plugins/python/external.py +245 -0
  93. codegraph/plugins/python/fields.py +107 -0
  94. codegraph/plugins/python/plugin.py +1733 -0
  95. codegraph/plugins/python/refs.py +485 -0
  96. codegraph/plugins/python/roots.py +412 -0
  97. codegraph/plugins/python/socketio.py +210 -0
  98. codegraph/plugins/python/subproc.py +864 -0
  99. codegraph/plugins/python/tests.py +1040 -0
  100. codegraph/plugins/python/values.py +179 -0
  101. codegraph/plugins/pyweb/__init__.py +0 -0
  102. codegraph/plugins/pyweb/plugin.py +1334 -0
  103. codegraph/plugins/pyweb/values.py +68 -0
  104. codegraph/plugins/rust/__init__.py +0 -0
  105. codegraph/plugins/rust/cargo.py +226 -0
  106. codegraph/plugins/rust/plugin.py +980 -0
  107. codegraph/plugins/rust/syntax.py +678 -0
  108. codegraph/plugins/scip/__init__.py +0 -0
  109. codegraph/plugins/scip/importer.py +129 -0
  110. codegraph/plugins/scip/scip.proto +962 -0
  111. codegraph/plugins/scip/scip_pb2.py +97 -0
  112. codegraph/plugins/stubs/__init__.py +0 -0
  113. codegraph/plugins/stubs/plugins.py +38 -0
  114. codegraph/plugins/swift/__init__.py +0 -0
  115. codegraph/plugins/swift/baseurl.py +109 -0
  116. codegraph/plugins/swift/exact.py +415 -0
  117. codegraph/plugins/swift/indexstore.py +209 -0
  118. codegraph/plugins/swift/packages.py +174 -0
  119. codegraph/plugins/swift/plugin.py +2890 -0
  120. codegraph/plugins/ts/__init__.py +0 -0
  121. codegraph/plugins/ts/baseurl.py +185 -0
  122. codegraph/plugins/ts/extractor/extract.mjs +2652 -0
  123. codegraph/plugins/ts/extractor/fw.mjs +685 -0
  124. codegraph/plugins/ts/extractor/package-lock.json +205 -0
  125. codegraph/plugins/ts/extractor/package.json +9 -0
  126. codegraph/plugins/ts/plugin.py +480 -0
  127. codegraph/plugins/tsweb/__init__.py +0 -0
  128. codegraph/plugins/tsweb/common.py +290 -0
  129. codegraph/plugins/tsweb/data.py +276 -0
  130. codegraph/presets/__init__.py +146 -0
  131. codegraph/presets/c_cpp.yaml +9 -0
  132. codegraph/presets/common.yaml +66 -0
  133. codegraph/presets/dart.yaml +9 -0
  134. codegraph/presets/django-ninja.yaml +15 -0
  135. codegraph/presets/django.yaml +25 -0
  136. codegraph/presets/djangorestframework.yaml +17 -0
  137. codegraph/presets/express.yaml +17 -0
  138. codegraph/presets/kotlin.yaml +11 -0
  139. codegraph/presets/laravel.yaml +40 -0
  140. codegraph/presets/nest.yaml +11 -0
  141. codegraph/presets/nextjs.yaml +15 -0
  142. codegraph/presets/nuxt.yaml +9 -0
  143. codegraph/presets/php.yaml +5 -0
  144. codegraph/presets/python.yaml +10 -0
  145. codegraph/presets/rust.yaml +5 -0
  146. codegraph/presets/swift.yaml +10 -0
  147. codegraph/presets/typescript.yaml +13 -0
  148. codegraph/process_runs.py +328 -0
  149. codegraph/protocols/__init__.py +299 -0
  150. codegraph/protocols/builtin.py +67 -0
  151. codegraph/protocols/matchers.py +144 -0
  152. codegraph/protocols/view.py +334 -0
  153. codegraph/query.py +2089 -0
  154. codegraph/realtime.py +260 -0
  155. codegraph/roundtrip.py +346 -0
  156. codegraph/routes.py +442 -0
  157. codegraph/starters.py +218 -0
  158. codegraph/tests_index.py +117 -0
  159. codegraph/viz/__init__.py +0 -0
  160. codegraph/viz/graph.py +369 -0
  161. codegraph/viz/server.py +198 -0
  162. codegraph/viz/static/app.css +148 -0
  163. codegraph/viz/static/app.js +1082 -0
  164. codegraph/viz/static/index.html +81 -0
  165. codegraph/viz/static/layered.js +237 -0
  166. codegraph/viz/static/vendor/VERSIONS.txt +4 -0
  167. codegraph/viz/static/vendor/cose-base.js +3214 -0
  168. codegraph/viz/static/vendor/cytoscape-fcose.js +1549 -0
  169. codegraph/viz/static/vendor/cytoscape.min.js +31 -0
  170. codegraph/viz/static/vendor/layout-base.js +5230 -0
  171. codegraph/viz/tools/package-lock.json +303 -0
  172. codegraph/viz/tools/package.json +7 -0
  173. codegraph/viz/tools/shoot.mjs +165 -0
  174. codegraph/xcode.py +251 -0
@@ -0,0 +1,717 @@
1
+ // Dart -> JSON facts for code-graph. Parse-only (package:analyzer unresolved AST, no pub get of the
2
+ // target project needed): declarations, directives and per-body facts (calls, instance creations, index
3
+ // reads, local vars, assignments, returns, type checks, try ranges) with source lines. Name/type
4
+ // resolution happens in Python (codegraph/plugins/dart/plugin.py).
5
+ //
6
+ // dart run bin/extract.dart --config cfg.json cfg: {"root": "...", "out": "...", "skip_dirs": [...]}
7
+ import 'dart:convert';
8
+ import 'dart:io';
9
+
10
+ import 'package:analyzer/dart/analysis/features.dart';
11
+ import 'package:analyzer/dart/analysis/utilities.dart';
12
+ import 'package:analyzer/dart/ast/ast.dart';
13
+ import 'package:analyzer/dart/ast/visitor.dart';
14
+ import 'package:analyzer/source/line_info.dart';
15
+
16
+ const maxDepth = 7;
17
+
18
+ // latest language + primary constructors (Dart 3.13 projects already use them)
19
+ final features = FeatureSet.latestLanguageVersion(flags: ['primary-constructors']);
20
+ final baseFeatures = FeatureSet.latestLanguageVersion();
21
+
22
+ late LineInfo li;
23
+ int ln(AstNode n) => li.getLocation(n.offset).lineNumber;
24
+ int lnEnd(AstNode n) => li.getLocation(n.end).lineNumber;
25
+
26
+ String src(AstNode? n, [int max = 160]) {
27
+ if (n == null) return '';
28
+ final s = n.toSource();
29
+ return s.length > max ? '${s.substring(0, max)}…' : s;
30
+ }
31
+
32
+ String typeText(TypeAnnotation? t) => t == null ? '' : t.toSource();
33
+
34
+ // conditional imports / exports: `import 'stub.dart' if (dart.library.io) 'io.dart'`
35
+ List<Map<String, dynamic>> configs(NodeList<Configuration> cs) => [
36
+ for (final c in cs)
37
+ {'name': c.name.toSource(), if (c.value != null) 'value': c.value!.stringValue, 'uri': c.uri.stringValue}
38
+ ];
39
+
40
+ Map<String, dynamic>? repr(Expression? e, [int d = 0]) {
41
+ if (e == null) return null;
42
+ if (d > maxDepth) return {'k': 'other', 'v': src(e, 60)};
43
+ final n = d + 1;
44
+ if (e is SimpleStringLiteral) return {'k': 'str', 'v': e.value};
45
+ if (e is StringInterpolation) {
46
+ final parts = <dynamic>[];
47
+ for (final el in e.elements) {
48
+ if (el is InterpolationString) {
49
+ if (el.value.isNotEmpty) parts.add(el.value);
50
+ } else if (el is InterpolationExpression) {
51
+ parts.add({'e': repr(el.expression, n)});
52
+ }
53
+ }
54
+ return {'k': 'tpl', 'parts': parts};
55
+ }
56
+ if (e is AdjacentStrings) return {'k': 'cat', 'parts': [for (final s in e.strings) repr(s, n)]};
57
+ if (e is IntegerLiteral) return {'k': 'num', 'v': e.value};
58
+ if (e is DoubleLiteral) return {'k': 'num', 'v': e.value};
59
+ if (e is BooleanLiteral) return {'k': 'bool', 'v': e.value};
60
+ if (e is NullLiteral) return {'k': 'null'};
61
+ if (e is SimpleIdentifier) return {'k': 'id', 'v': e.name};
62
+ if (e is PrefixedIdentifier) return {'k': 'prop', 't': {'k': 'id', 'v': e.prefix.name}, 'n': e.identifier.name};
63
+ if (e is PropertyAccess) {
64
+ return {'k': 'prop', 't': e.isCascaded ? {'k': 'cascade_t'} : repr(e.target, n), 'n': e.propertyName.name,
65
+ if (e.isNullAware) 'qa': true};
66
+ }
67
+ if (e is ThisExpression) return {'k': 'this'};
68
+ if (e is SuperExpression) return {'k': 'super'};
69
+ if (e is MethodInvocation) return callRepr(e, n);
70
+ if (e is InstanceCreationExpression) return newRepr(e, n);
71
+ if (e is FunctionExpressionInvocation) {
72
+ return {'k': 'call', 't': repr(e.function, n), 'n': null, 'a': args(e.argumentList, n), 'na': named(e.argumentList, n), 'l': ln(e)};
73
+ }
74
+ if (e is IndexExpression) return {'k': 'idx', 't': e.isCascaded ? {'k': 'cascade_t'} : repr(e.target, n), 'i': repr(e.index, n), 'l': ln(e)};
75
+ if (e is AsExpression) return {'k': 'as', 'e': repr(e.expression, n), 'type': typeText(e.type)};
76
+ if (e is ParenthesizedExpression) return repr(e.expression, d);
77
+ if (e is AwaitExpression) return {'k': 'await', 'e': repr(e.expression, n)};
78
+ if (e is PostfixExpression) return {'k': 'nn', 'e': repr(e.operand, n), 'op': e.operator.lexeme};
79
+ if (e is ConditionalExpression) return {'k': 'cond', 'c': src(e.condition, 80), 'a': repr(e.thenExpression, n), 'b': repr(e.elseExpression, n)};
80
+ if (e is BinaryExpression) return {'k': 'bin', 'op': e.operator.lexeme, 'l': repr(e.leftOperand, n), 'r': repr(e.rightOperand, n)};
81
+ if (e is SetOrMapLiteral) {
82
+ if (e.isMap || (e.elements.isNotEmpty && e.elements.first is MapLiteralEntry) || (e.elements.isEmpty && !e.isSet)) {
83
+ return {'k': 'map', 'entries': [for (final el in e.elements.take(60)) elemRepr(el, n)], 'l': ln(e)};
84
+ }
85
+ return {'k': 'list', 'items': [for (final el in e.elements.take(30)) elemRepr(el, n)]};
86
+ }
87
+ if (e is ListLiteral) return {'k': 'list', 'items': [for (final el in e.elements.take(30)) elemRepr(el, n)], 'l': ln(e)};
88
+ if (e is FunctionExpression) return fnRepr(e, n);
89
+ if (e is CascadeExpression) {
90
+ return {'k': 'cascade', 't': repr(e.target, n), 'sections': [for (final s in e.cascadeSections.take(20)) repr(s, n)]};
91
+ }
92
+ if (e is AssignmentExpression) return {'k': 'assign', 'lhs': repr(e.leftHandSide, n), 'rhs': repr(e.rightHandSide, n)};
93
+ if (e is TypeLiteral) return {'k': 'id', 'v': e.type.toSource()};
94
+ if (e is NamedExpression) return repr(e.expression, d);
95
+ if (e is SwitchExpression) {
96
+ return {'k': 'switch', 'e': repr(e.expression, n), 'cases': [for (final c in e.cases.take(30)) {'p': src(c.guardedPattern, 80), 'v': repr(c.expression, n)}]};
97
+ }
98
+ return {'k': 'other', 'v': src(e, 80)};
99
+ }
100
+
101
+ dynamic elemRepr(CollectionElement el, int d) {
102
+ if (el is MapLiteralEntry) return {'key': repr(el.key, d), 'value': repr(el.value, d), 'l': ln(el)};
103
+ if (el is Expression) return repr(el, d);
104
+ if (el is SpreadElement) return {'k': 'spread', 'e': repr(el.expression, d)};
105
+ if (el is IfElement) return {'k': 'if', 'c': src(el.expression, 60), 'then': elemRepr(el.thenElement, d), if (el.elseElement != null) 'else': elemRepr(el.elseElement!, d)};
106
+ if (el is ForElement) return {'k': 'for', 'body': elemRepr(el.body, d)};
107
+ return {'k': 'other', 'v': src(el, 60)};
108
+ }
109
+
110
+ List<dynamic> args(ArgumentList al, int d) => [for (final a in al.arguments.where((a) => a is! NamedExpression).take(12)) repr(a, d)];
111
+ Map<String, dynamic> named(ArgumentList al, int d) => {
112
+ for (final a in al.arguments.whereType<NamedExpression>().take(30)) a.name.label.name: repr(a.expression, d)
113
+ };
114
+
115
+ Map<String, dynamic> callRepr(MethodInvocation e, int d) => {
116
+ 'k': 'call',
117
+ 't': e.isCascaded ? {'k': 'cascade_t'} : repr(e.target, d),
118
+ 'n': e.methodName.name,
119
+ if (e.typeArguments != null) 'ta': [for (final t in e.typeArguments!.arguments) t.toSource()],
120
+ 'a': args(e.argumentList, d),
121
+ 'na': named(e.argumentList, d),
122
+ if (e.isNullAware) 'qa': true,
123
+ 'l': ln(e),
124
+ };
125
+
126
+ Map<String, dynamic> newRepr(InstanceCreationExpression e, int d) {
127
+ final cn = e.constructorName;
128
+ final nt = cn.type;
129
+ final prefix = nt.importPrefix?.name.lexeme;
130
+ return {
131
+ 'k': 'new',
132
+ 'type': prefix != null ? '$prefix.${nt.name.lexeme}' : nt.name.lexeme,
133
+ if (cn.name != null) 'ctor': cn.name!.name,
134
+ if (nt.typeArguments != null) 'ta': [for (final t in nt.typeArguments!.arguments) t.toSource()],
135
+ 'a': args(e.argumentList, d),
136
+ 'na': named(e.argumentList, d),
137
+ 'l': ln(e),
138
+ };
139
+ }
140
+
141
+ Map<String, dynamic> fnRepr(FunctionExpression e, int d) {
142
+ final body = e.body;
143
+ final out = <String, dynamic>{
144
+ 'k': 'fn',
145
+ 'params': [for (final p in e.parameters?.parameters ?? <FormalParameter>[]) p.name?.lexeme ?? '_'],
146
+ 'l': ln(e),
147
+ };
148
+ if (body is ExpressionFunctionBody) {
149
+ out['ret'] = [repr(body.expression, d)];
150
+ } else if (body is BlockFunctionBody) {
151
+ final rets = <dynamic>[];
152
+ body.block.accept(_ReturnCollector(rets, d));
153
+ out['ret'] = rets.take(8).toList();
154
+ }
155
+ return out;
156
+ }
157
+
158
+ class _ReturnCollector extends RecursiveAstVisitor<void> {
159
+ final List<dynamic> rets;
160
+ final int d;
161
+ _ReturnCollector(this.rets, this.d);
162
+ @override
163
+ void visitReturnStatement(ReturnStatement node) {
164
+ if (node.expression != null) rets.add(repr(node.expression, d));
165
+ }
166
+
167
+ @override
168
+ void visitFunctionExpression(FunctionExpression node) {} // returns of nested closures are theirs
169
+ }
170
+
171
+ List<Map<String, dynamic>> annotations(NodeList<Annotation> md) => [
172
+ for (final a in md)
173
+ {
174
+ 'name': a.name.toSource() + (a.constructorName != null ? '.${a.constructorName!.name}' : ''),
175
+ if (a.arguments != null) 'a': args(a.arguments!, 1),
176
+ if (a.arguments != null) 'na': named(a.arguments!, 1),
177
+ 'l': ln(a),
178
+ }
179
+ ];
180
+
181
+ List<ClassMember> bodyMembers(ClassBody b) => b is BlockClassBody ? b.members.toList() : <ClassMember>[];
182
+
183
+ Map<String, dynamic> param(FormalParameter p) {
184
+ FormalParameter base = p;
185
+ Expression? def;
186
+ if (p is DefaultFormalParameter) {
187
+ def = p.defaultValue;
188
+ base = p.parameter;
189
+ }
190
+ String type = '';
191
+ bool isThis = false, isSuper = false;
192
+ if (base is SimpleFormalParameter) type = typeText(base.type);
193
+ if (base is FieldFormalParameter) {
194
+ type = typeText(base.type);
195
+ isThis = true;
196
+ }
197
+ if (base is SuperFormalParameter) {
198
+ type = typeText(base.type);
199
+ isSuper = true;
200
+ }
201
+ if (base is FunctionTypedFormalParameter) type = 'Function';
202
+ return {
203
+ 'name': p.name?.lexeme ?? '_',
204
+ if (type.isNotEmpty) 'type': type,
205
+ if (p.isNamed) 'named': true,
206
+ if (p.isRequired) 'required': true,
207
+ if (isThis) 'this': true,
208
+ if (isSuper) 'super': true,
209
+ if (def != null) 'default': repr(def),
210
+ if (p.metadata.isNotEmpty) 'ann': annotations(p.metadata),
211
+ };
212
+ }
213
+
214
+ bool enumLike(Expression e) {
215
+ if (e is PrefixedIdentifier) return RegExp(r'^[A-Z]').hasMatch(e.prefix.name);
216
+ if (e is PropertyAccess) {
217
+ final t = e.target;
218
+ return t is SimpleIdentifier && RegExp(r'^[A-Z]').hasMatch(t.name) || t is PrefixedIdentifier && RegExp(r'^[A-Z]').hasMatch(t.identifier.name);
219
+ }
220
+ return false;
221
+ }
222
+
223
+ /// Facts inside one executable body. `on<E>(closure)` handlers inside a bloc constructor are split out
224
+ /// into their own synthetic member so each event's handler has its own calls.
225
+ class BodyFacts extends RecursiveAstVisitor<void> {
226
+ final List<Map<String, dynamic>> facts = [];
227
+ final List<Map<String, dynamic>> extraMembers;
228
+ BodyFacts(this.extraMembers);
229
+
230
+ @override
231
+ void visitMethodInvocation(MethodInvocation node) {
232
+ if (node.methodName.name == 'on' && node.target == null && node.typeArguments != null &&
233
+ node.argumentList.arguments.isNotEmpty && node.argumentList.arguments.first is FunctionExpression) {
234
+ final fe = node.argumentList.arguments.first as FunctionExpression;
235
+ final sub = BodyFacts(extraMembers);
236
+ fe.body.accept(sub);
237
+ final ev = node.typeArguments!.arguments.first.toSource();
238
+ facts.add({'t': 'call', ...callRepr(node, 1)});
239
+ extraMembers.add({
240
+ 'name': 'on<$ev>@${ln(node)}',
241
+ 'kind': 'handler',
242
+ 'event': ev,
243
+ 'params': [for (final p in fe.parameters?.parameters ?? <FormalParameter>[]) param(p)],
244
+ 'line': ln(node),
245
+ 'end': lnEnd(node),
246
+ 'facts': sub.facts,
247
+ });
248
+ for (final a in node.argumentList.arguments.skip(1)) {
249
+ a.accept(this);
250
+ }
251
+ return;
252
+ }
253
+ facts.add({'t': 'call', ...callRepr(node, 1)});
254
+ super.visitMethodInvocation(node);
255
+ }
256
+
257
+ @override
258
+ void visitInstanceCreationExpression(InstanceCreationExpression node) {
259
+ facts.add({'t': 'new', ...newRepr(node, 1)});
260
+ super.visitInstanceCreationExpression(node);
261
+ }
262
+
263
+ @override
264
+ void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
265
+ facts.add({'t': 'call', ...repr(node, 1)!});
266
+ super.visitFunctionExpressionInvocation(node);
267
+ }
268
+
269
+ @override
270
+ void visitIndexExpression(IndexExpression node) {
271
+ final key = node.index;
272
+ if (key is SimpleStringLiteral || key is StringInterpolation) {
273
+ final f = <String, dynamic>{'t': 'index', 'target': node.isCascaded ? {'k': 'cascade_t'} : repr(node.target, 1), 'key': repr(key, 1), 'l': ln(node)};
274
+ AstNode p = node.parent!;
275
+ while (p is ParenthesizedExpression) {
276
+ p = p.parent!;
277
+ }
278
+ if (p is AsExpression) {
279
+ f['cast'] = typeText(p.type);
280
+ AstNode q = p.parent!;
281
+ while (q is ParenthesizedExpression) {
282
+ q = q.parent!;
283
+ }
284
+ if (q is MethodInvocation && q.target != null) f['post'] = q.methodName.name;
285
+ if (q is PropertyAccess) f['post'] = q.propertyName.name;
286
+ } else if (p is MethodInvocation && p.target == node) {
287
+ f['post'] = p.methodName.name;
288
+ } else if (p is PropertyAccess && p.target == node) {
289
+ f['post'] = p.propertyName.name;
290
+ } else if (p is ArgumentList && p.parent is MethodInvocation) {
291
+ final mi = p.parent as MethodInvocation;
292
+ f['wrap'] = mi.target != null ? '${src(mi.target, 40)}.${mi.methodName.name}' : mi.methodName.name;
293
+ } else if (p is ArgumentList && p.parent is InstanceCreationExpression) {
294
+ f['wrap'] = src((p.parent as InstanceCreationExpression).constructorName, 40);
295
+ } else if (p is BinaryExpression && p.operator.lexeme == '??' && p.leftOperand == node) {
296
+ f['coalesce'] = true;
297
+ } else if (p is AssignmentExpression && p.leftHandSide == node) {
298
+ f['write'] = true;
299
+ }
300
+ if (p is BinaryExpression && p.operator.lexeme == '??') f['coalesce'] = true;
301
+ facts.add(f);
302
+ }
303
+ super.visitIndexExpression(node);
304
+ }
305
+
306
+ @override
307
+ void visitVariableDeclaration(VariableDeclaration node) {
308
+ final list = node.parent;
309
+ facts.add({
310
+ 't': 'var',
311
+ 'name': node.name.lexeme,
312
+ if (list is VariableDeclarationList && list.type != null) 'type': typeText(list.type),
313
+ if (node.initializer != null) 'init': repr(node.initializer),
314
+ 'l': ln(node),
315
+ });
316
+ super.visitVariableDeclaration(node);
317
+ }
318
+
319
+ @override
320
+ void visitAssignmentExpression(AssignmentExpression node) {
321
+ facts.add({'t': 'assign', 'lhs': repr(node.leftHandSide, 1), 'rhs': repr(node.rightHandSide, 1), 'op': node.operator.lexeme, 'l': ln(node)});
322
+ super.visitAssignmentExpression(node);
323
+ }
324
+
325
+ @override
326
+ void visitReturnStatement(ReturnStatement node) {
327
+ if (node.expression != null) facts.add({'t': 'return', 'v': repr(node.expression, 1), 'l': ln(node)});
328
+ super.visitReturnStatement(node);
329
+ }
330
+
331
+ @override
332
+ void visitExpressionFunctionBody(ExpressionFunctionBody node) {
333
+ if (node.parent is! FunctionExpression || node.parent!.parent is FunctionDeclaration) {
334
+ facts.add({'t': 'return', 'v': repr(node.expression, 1), 'l': ln(node)});
335
+ }
336
+ super.visitExpressionFunctionBody(node);
337
+ }
338
+
339
+ @override
340
+ void visitIsExpression(IsExpression node) {
341
+ facts.add({'t': 'is', 'type': typeText(node.type), 'e': src(node.expression, 60), if (node.notOperator != null) 'not': true, 'l': ln(node)});
342
+ super.visitIsExpression(node);
343
+ }
344
+
345
+ @override
346
+ void visitObjectPattern(ObjectPattern node) {
347
+ facts.add({'t': 'is', 'type': node.type.toSource(), 'e': 'pattern', 'l': ln(node)});
348
+ super.visitObjectPattern(node);
349
+ }
350
+
351
+ @override
352
+ void visitDeclaredVariablePattern(DeclaredVariablePattern node) {
353
+ if (node.type != null) facts.add({'t': 'is', 'type': typeText(node.type), 'e': 'pattern', 'l': ln(node)});
354
+ super.visitDeclaredVariablePattern(node);
355
+ }
356
+
357
+ @override
358
+ void visitTryStatement(TryStatement node) {
359
+ facts.add({
360
+ 't': 'try',
361
+ 'l': ln(node),
362
+ 'end': lnEnd(node.body),
363
+ 'catches': [for (final c in node.catchClauses) c.exceptionType?.toSource() ?? '*'],
364
+ if (node.finallyBlock != null) 'finally': true,
365
+ });
366
+ super.visitTryStatement(node);
367
+ }
368
+
369
+ @override
370
+ void visitBinaryExpression(BinaryExpression node) {
371
+ const ops = {'==', '!=', '>=', '<=', '<', '>'};
372
+ if (ops.contains(node.operator.lexeme)) {
373
+ final l = node.leftOperand, r = node.rightOperand;
374
+ Expression? side;
375
+ IntegerLiteral? lit;
376
+ if (r is IntegerLiteral) {
377
+ side = l;
378
+ lit = r;
379
+ } else if (l is IntegerLiteral) {
380
+ side = r;
381
+ lit = l;
382
+ }
383
+ if ((node.operator.lexeme == '==' || node.operator.lexeme == '!=') && (enumLike(l) || enumLike(r))) {
384
+ facts.add({'t': 'cmp', 'a': src(l, 80), 'b': src(r, 80), 'op': node.operator.lexeme, 'l': ln(node)});
385
+ }
386
+ if (side != null && lit != null && RegExp('(statusCode|status)[\'"]?\\]?\$').hasMatch(src(side, 80))) {
387
+ facts.add({'t': 'status', 'e': src(side, 60), 'op': node.operator.lexeme, 'v': lit.value, 'lit_left': l is IntegerLiteral, 'l': ln(node)});
388
+ }
389
+ }
390
+ super.visitBinaryExpression(node);
391
+ }
392
+
393
+ @override
394
+ void visitSwitchStatement(SwitchStatement node) {
395
+ facts.add({
396
+ 't': 'switch',
397
+ 'e': src(node.expression, 80),
398
+ 'cases': [
399
+ for (final m in node.members)
400
+ if (m is SwitchPatternCase) src(m.guardedPattern, 80) else if (m is SwitchCase) src(m.expression, 80) else 'default'
401
+ ],
402
+ 'l': ln(node)
403
+ });
404
+ super.visitSwitchStatement(node);
405
+ }
406
+
407
+ @override
408
+ void visitSwitchExpression(SwitchExpression node) {
409
+ facts.add({'t': 'switch', 'e': src(node.expression, 80), 'cases': [for (final c in node.cases) src(c.guardedPattern, 80)], 'l': ln(node)});
410
+ super.visitSwitchExpression(node);
411
+ }
412
+
413
+ @override
414
+ void visitThrowExpression(ThrowExpression node) {
415
+ facts.add({'t': 'throw', 'v': repr(node.expression, 1), 'l': ln(node)});
416
+ super.visitThrowExpression(node);
417
+ }
418
+ }
419
+
420
+ Map<String, dynamic> member(ClassMember m, List<Map<String, dynamic>> extra) {
421
+ if (m is FieldDeclaration) {
422
+ return {
423
+ 'kind': 'field',
424
+ if (m.isStatic) 'static': true,
425
+ if (m.fields.isFinal) 'final': true,
426
+ if (m.fields.isConst) 'const': true,
427
+ if (m.fields.isLate) 'late': true,
428
+ if (m.fields.type != null) 'type': typeText(m.fields.type),
429
+ 'vars': [
430
+ for (final v in m.fields.variables)
431
+ {'name': v.name.lexeme, if (v.initializer != null) 'init': repr(v.initializer), 'l': ln(v)}
432
+ ],
433
+ 'ann': annotations(m.metadata),
434
+ 'line': ln(m),
435
+ };
436
+ }
437
+ final bf = BodyFacts(extra);
438
+ if (m is ConstructorDeclaration) {
439
+ m.body.accept(bf);
440
+ for (final i in m.initializers) {
441
+ i.accept(bf);
442
+ }
443
+ for (final p in m.parameters.parameters) {
444
+ if (p is DefaultFormalParameter && p.defaultValue != null) p.defaultValue!.accept(bf);
445
+ }
446
+ final inits = <Map<String, dynamic>>[];
447
+ for (final i in m.initializers) {
448
+ if (i is ConstructorFieldInitializer) inits.add({'field': i.fieldName.name, 'v': repr(i.expression), 'l': ln(i)});
449
+ if (i is SuperConstructorInvocation) inits.add({'super': i.constructorName?.name ?? '', 'a': args(i.argumentList, 1), 'na': named(i.argumentList, 1), 'l': ln(i)});
450
+ }
451
+ return {
452
+ 'kind': 'ctor',
453
+ 'name': m.name?.lexeme ?? '',
454
+ if (m.factoryKeyword != null) 'factory': true,
455
+ if (m.constKeyword != null) 'const': true,
456
+ if (m.redirectedConstructor != null) 'redirect': m.redirectedConstructor!.toSource(),
457
+ 'params': [for (final p in m.parameters.parameters) param(p)],
458
+ 'inits': inits,
459
+ 'ann': annotations(m.metadata),
460
+ 'line': ln(m),
461
+ 'end': lnEnd(m),
462
+ 'facts': bf.facts,
463
+ };
464
+ }
465
+ if (m is MethodDeclaration) {
466
+ m.body.accept(bf);
467
+ return {
468
+ 'kind': m.isGetter ? 'getter' : (m.isSetter ? 'setter' : (m.isOperator ? 'operator' : 'method')),
469
+ 'name': m.name.lexeme,
470
+ if (m.isStatic) 'static': true,
471
+ if (m.isAbstract) 'abstract': true,
472
+ if (m.returnType != null) 'ret': typeText(m.returnType),
473
+ if (m.body.isAsynchronous) 'async': true,
474
+ 'params': [for (final p in m.parameters?.parameters ?? <FormalParameter>[]) param(p)],
475
+ 'ann': annotations(m.metadata),
476
+ 'line': ln(m),
477
+ 'end': lnEnd(m),
478
+ 'facts': bf.facts,
479
+ };
480
+ }
481
+ return {'kind': 'other', 'line': ln(m)};
482
+ }
483
+
484
+ List<String> types(Iterable<NamedType>? ts) => [for (final t in ts ?? <NamedType>[]) t.toSource()];
485
+
486
+ Map<String, dynamic> fileFacts(String rel, String content) {
487
+ var res = parseString(content: content, path: rel, throwIfDiagnostics: false, featureSet: baseFeatures);
488
+ if (res.errors.isNotEmpty) {
489
+ // newer syntax (primary constructors, `factory name(...)` shorthand): re-parse with the experiment and keep the
490
+ // better parse (the experiment changes other rules, e.g. `final` params, so it is not the default)
491
+ final res2 = parseString(content: content, path: rel, throwIfDiagnostics: false, featureSet: features);
492
+ if (res2.errors.length < res.errors.length) res = res2;
493
+ }
494
+ final cu = res.unit;
495
+ li = res.lineInfo;
496
+ final out = <String, dynamic>{
497
+ 'file': rel,
498
+ 'errors': res.errors.length,
499
+ if (res.errors.isNotEmpty) 'first_error': '${res.errors.first.message} @${li.getLocation(res.errors.first.offset).lineNumber}',
500
+ if (res.errors.isNotEmpty) 'error_lines': [for (final e in res.errors.take(20)) li.getLocation(e.offset).lineNumber],
501
+ 'imports': [],
502
+ 'exports': [],
503
+ 'parts': [],
504
+ 'classes': [],
505
+ 'functions': [],
506
+ 'vars': [],
507
+ 'lines': li.lineCount,
508
+ };
509
+ for (final d in cu.directives) {
510
+ if (d is ImportDirective) {
511
+ out['imports'].add({
512
+ 'uri': d.uri.stringValue,
513
+ if (d.prefix != null) 'prefix': d.prefix!.name,
514
+ 'show': [for (final c in d.combinators.whereType<ShowCombinator>()) for (final n in c.shownNames) n.name],
515
+ 'hide': [for (final c in d.combinators.whereType<HideCombinator>()) for (final n in c.hiddenNames) n.name],
516
+ 'l': ln(d),
517
+ if (d.configurations.isNotEmpty) 'configs': configs(d.configurations),
518
+ });
519
+ } else if (d is ExportDirective) {
520
+ out['exports'].add({
521
+ 'uri': d.uri.stringValue,
522
+ 'show': [for (final c in d.combinators.whereType<ShowCombinator>()) for (final n in c.shownNames) n.name],
523
+ 'hide': [for (final c in d.combinators.whereType<HideCombinator>()) for (final n in c.hiddenNames) n.name],
524
+ 'l': ln(d),
525
+ if (d.configurations.isNotEmpty) 'configs': configs(d.configurations),
526
+ });
527
+ } else if (d is PartDirective) {
528
+ out['parts'].add(d.uri.stringValue);
529
+ } else if (d is PartOfDirective) {
530
+ out['part_of'] = d.uri?.stringValue ?? d.libraryName?.toSource();
531
+ } else if (d is LibraryDirective) {
532
+ out['library'] = d.name?.toSource();
533
+ }
534
+ }
535
+ for (final decl in cu.declarations) {
536
+ if (decl is ClassDeclaration || decl is MixinDeclaration || decl is EnumDeclaration || decl is ExtensionDeclaration ||
537
+ decl is ExtensionTypeDeclaration) {
538
+ final extra = <Map<String, dynamic>>[];
539
+ final c = <String, dynamic>{'line': ln(decl), 'end': lnEnd(decl), 'ann': annotations(decl.metadata)};
540
+ List<ClassMember> members = [];
541
+ if (decl is ClassDeclaration) {
542
+ c.addAll({
543
+ 'kind': 'class',
544
+ 'name': decl.name.lexeme,
545
+ if (decl.abstractKeyword != null) 'abstract': true,
546
+ if (decl.sealedKeyword != null) 'sealed': true,
547
+ if (decl.extendsClause != null) 'extends': decl.extendsClause!.superclass.toSource(),
548
+ 'with': types(decl.withClause?.mixinTypes),
549
+ 'implements': types(decl.implementsClause?.interfaces),
550
+ });
551
+ members = bodyMembers(decl.body);
552
+ final np = decl.namePart;
553
+ if (np is PrimaryConstructorDeclaration) {
554
+ // `class C(final A a, {var B? b})`: declaring params become fields + an unnamed (or named) constructor
555
+ final ps = np.formalParameters.parameters;
556
+ final pc = <Map<String, dynamic>>[];
557
+ for (final p in ps) {
558
+ final base = p is DefaultFormalParameter ? p.parameter : p;
559
+ final declaring = base is SimpleFormalParameter && base.keyword != null;
560
+ final pm = param(p);
561
+ if (declaring) {
562
+ pm['this'] = true;
563
+ c.putIfAbsent('pc_fields', () => <Map<String, dynamic>>[]);
564
+ (c['pc_fields'] as List).add({
565
+ 'kind': 'field',
566
+ if (base.keyword!.lexeme == 'final') 'final': true,
567
+ if (base.type != null) 'type': typeText(base.type),
568
+ 'vars': [{'name': p.name?.lexeme ?? '_', 'l': ln(p)}],
569
+ 'ann': annotations(p.metadata),
570
+ 'line': ln(p),
571
+ });
572
+ }
573
+ pc.add(pm);
574
+ }
575
+ c['pc_ctor'] = {
576
+ 'kind': 'ctor',
577
+ 'name': np.constructorName?.name.lexeme ?? '',
578
+ if (np.constKeyword != null) 'const': true,
579
+ 'params': pc,
580
+ 'inits': <Map<String, dynamic>>[],
581
+ 'ann': <Map<String, dynamic>>[],
582
+ 'line': ln(np),
583
+ 'end': lnEnd(np),
584
+ 'facts': <Map<String, dynamic>>[],
585
+ };
586
+ }
587
+ } else if (decl is MixinDeclaration) {
588
+ c.addAll({
589
+ 'kind': 'mixin',
590
+ 'name': decl.name.lexeme,
591
+ 'on': types(decl.onClause?.superclassConstraints),
592
+ 'implements': types(decl.implementsClause?.interfaces),
593
+ });
594
+ members = decl.members.toList();
595
+ } else if (decl is EnumDeclaration) {
596
+ c.addAll({
597
+ 'kind': 'enum',
598
+ 'name': decl.name.lexeme,
599
+ 'with': types(decl.withClause?.mixinTypes),
600
+ 'implements': types(decl.implementsClause?.interfaces),
601
+ 'values': [
602
+ for (final v in decl.constants)
603
+ {
604
+ 'name': v.name.lexeme,
605
+ if (v.arguments != null) 'a': args(v.arguments!.argumentList, 1),
606
+ 'ann': annotations(v.metadata),
607
+ 'l': ln(v)
608
+ }
609
+ ],
610
+ });
611
+ members = decl.members.toList();
612
+ } else if (decl is ExtensionDeclaration) {
613
+ c.addAll({'kind': 'extension', 'name': decl.name?.lexeme ?? '_ext${ln(decl)}', 'on_type': decl.onClause?.extendedType.toSource()});
614
+ members = decl.members.toList();
615
+ } else if (decl is ExtensionTypeDeclaration) {
616
+ c.addAll({'kind': 'extension_type', 'name': decl.name.lexeme, 'implements': types(decl.implementsClause?.interfaces)});
617
+ members = bodyMembers(decl.body);
618
+ }
619
+ c['members'] = [for (final m in members) member(m, extra)];
620
+ (c['members'] as List).addAll(extra);
621
+ if (c.containsKey('pc_fields')) (c['members'] as List).addAll(c.remove('pc_fields') as List);
622
+ if (c.containsKey('pc_ctor')) (c['members'] as List).add(c.remove('pc_ctor'));
623
+ out['classes'].add(c);
624
+ } else if (decl is ClassTypeAlias) {
625
+ out['classes'].add({
626
+ 'kind': 'class',
627
+ 'name': decl.name.lexeme,
628
+ 'extends': decl.superclass.toSource(),
629
+ 'with': types(decl.withClause.mixinTypes),
630
+ 'implements': types(decl.implementsClause?.interfaces),
631
+ 'ann': annotations(decl.metadata),
632
+ 'members': [],
633
+ 'line': ln(decl),
634
+ 'end': lnEnd(decl),
635
+ });
636
+ } else if (decl is FunctionDeclaration) {
637
+ final extra = <Map<String, dynamic>>[];
638
+ final bf = BodyFacts(extra);
639
+ decl.functionExpression.body.accept(bf);
640
+ out['functions'].add({
641
+ 'name': decl.name.lexeme,
642
+ 'kind': decl.isGetter ? 'getter' : 'function',
643
+ if (decl.returnType != null) 'ret': typeText(decl.returnType),
644
+ 'params': [for (final p in decl.functionExpression.parameters?.parameters ?? <FormalParameter>[]) param(p)],
645
+ 'ann': annotations(decl.metadata),
646
+ if (decl.functionExpression.body.isAsynchronous) 'async': true,
647
+ 'line': ln(decl),
648
+ 'end': lnEnd(decl),
649
+ 'facts': bf.facts,
650
+ });
651
+ } else if (decl is TopLevelVariableDeclaration) {
652
+ for (final v in decl.variables.variables) {
653
+ final bf = BodyFacts([]);
654
+ v.initializer?.accept(bf);
655
+ out['vars'].add({
656
+ 'name': v.name.lexeme,
657
+ if (decl.variables.type != null) 'type': typeText(decl.variables.type),
658
+ if (decl.variables.isConst) 'const': true,
659
+ if (decl.variables.isFinal) 'final': true,
660
+ if (v.initializer != null) 'init': repr(v.initializer),
661
+ 'l': ln(v),
662
+ 'facts': bf.facts,
663
+ });
664
+ }
665
+ }
666
+ }
667
+ return out;
668
+ }
669
+
670
+ void main(List<String> argv) {
671
+ final cfgPath = argv[argv.indexOf('--config') + 1];
672
+ final cfg = jsonDecode(File(cfgPath).readAsStringSync()) as Map<String, dynamic>;
673
+ final root = Directory(cfg['root'] as String);
674
+ // directory rules from the presets and .cg.yaml (codegraph/core/paths.py PathRules.extractor_cfg): names to skip,
675
+ // names kept (hidden directories are skipped unless kept), include directories walked although a rule covers them
676
+ final skip = {...(cfg['skip_names'] as List? ?? []).cast<String>(), ...(cfg['skip_dirs'] as List? ?? []).cast<String>()};
677
+ final keep = {...(cfg['keep_names'] as List? ?? []).cast<String>()};
678
+ final include = (cfg['include'] as List? ?? []).cast<String>().map((i) => i.replaceAll(RegExp(r'^/+|/+$'), '')).where((i) => i.isNotEmpty).toList();
679
+ bool included(String r) => include.any((i) => r == i || r.startsWith('$i/'));
680
+ bool onIncludePath(String r) => included(r) || include.any((i) => i.startsWith('$r/'));
681
+ bool skippedName(String name) => skip.contains(name) || (name.startsWith('.') && !keep.contains(name));
682
+ final skipSuffix = (cfg['skip_suffixes'] as List? ?? ['.freezed.dart', '.mocks.dart']).cast<String>();
683
+ final files = <Map<String, dynamic>>[];
684
+ final failures = <Map<String, dynamic>>[];
685
+ var skipped = 0;
686
+ final rootPath = root.absolute.path;
687
+ // forced: `d` is skipped by itself and only walked to reach an include directory
688
+ void walk(Directory d, [bool forced = false]) {
689
+ final ents = d.listSync(followLinks: false)..sort((a, b) => a.path.compareTo(b.path));
690
+ for (final e in ents) {
691
+ final name = e.uri.pathSegments.where((s) => s.isNotEmpty).last;
692
+ final rel = e.absolute.path.substring(rootPath.length).replaceFirst(RegExp(r'^/'), '').replaceFirst(RegExp(r'/$'), '');
693
+ if (e is Directory) {
694
+ final own = forced || skippedName(name);
695
+ if (onIncludePath(rel) || !own) walk(e, own && !included(rel));
696
+ } else if (e is File && name.endsWith('.dart')) {
697
+ if (forced && !included(rel)) continue;
698
+ if (skipSuffix.any((s) => name.endsWith(s))) {
699
+ skipped++;
700
+ continue;
701
+ }
702
+ if (e.lengthSync() > 1500000) {
703
+ skipped++;
704
+ continue;
705
+ }
706
+ try {
707
+ files.add(fileFacts(rel, e.readAsStringSync()));
708
+ } catch (ex) {
709
+ failures.add({'file': rel, 'error': ex.toString().split('\n').first});
710
+ }
711
+ }
712
+ }
713
+ }
714
+
715
+ walk(root);
716
+ File(cfg['out'] as String).writeAsStringSync(jsonEncode({'files': files, 'failures': failures, 'skipped': skipped}));
717
+ }