ucn 3.8.26 → 4.0.0

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.
package/languages/go.js CHANGED
@@ -10,7 +10,8 @@ const {
10
10
  traverseTreeCached,
11
11
  nodeToLocation,
12
12
  parseStructuredParams,
13
- extractGoDocstring
13
+ extractGoDocstring,
14
+ visitNameNodes,
14
15
  } = require('./utils');
15
16
  const { PARSE_OPTIONS, safeParse } = require('./index');
16
17
 
@@ -181,6 +182,28 @@ function _processClass(node, types, processedRanges, lines) {
181
182
  ...(embeddedBases.length > 0 && { extends: embeddedBases.join(', ') })
182
183
  });
183
184
  }
185
+ } else if (spec.type === 'type_alias') {
186
+ // `type A = B` — A IS B (compiler identity, methods carry over;
187
+ // unlike `type A B` defined types, which get NO methods from B).
188
+ // Record the aliased base so callers can treat A-qualified
189
+ // receivers as B (fix #208).
190
+ const nameNode = spec.childForFieldName('name');
191
+ const typeNode = spec.childForFieldName('type');
192
+ if (nameNode && typeNode) {
193
+ const aliasOf = typeNode.type === 'type_identifier' ? typeNode.text
194
+ : typeNode.type === 'qualified_type' ? typeNode.childForFieldName('name')?.text
195
+ : null;
196
+ const { startLine, endLine } = nodeToLocation(node, lines);
197
+ types.push({
198
+ name: nameNode.text,
199
+ startLine,
200
+ endLine,
201
+ type: 'type',
202
+ members: [],
203
+ modifiers: /^[A-Z]/.test(nameNode.text) ? ['export'] : [],
204
+ ...(aliasOf && { aliasOf }),
205
+ });
206
+ }
184
207
  }
185
208
  }
186
209
  return true;
@@ -555,6 +578,63 @@ const GO_BUILTINS = new Set([
555
578
  'println', 'real', 'recover'
556
579
  ]);
557
580
 
581
+ /**
582
+ * Variable receiving this call's result (fix #207 return-type flow):
583
+ * bb := balancer.Get(n) → { assignedTo: 'bb' }
584
+ * x, err := pkg.Make() → { assignedTo: 'x', assignedTuple: true }
585
+ * (tuple unpack — the flow map pairs the first
586
+ * return element with the first variable)
587
+ * y = q() → { assignedTo: 'y' } (plain `=` only — `+=` etc.
588
+ * don't bind the call's type to the variable)
589
+ * a, b := g(), h() → parallel assignment: each call pairs with its
590
+ * own LHS position, single-value semantics
591
+ * Identifier targets only; blank (`_`) targets return undefined.
592
+ */
593
+ function goAssignmentTargetOf(callNode) {
594
+ let n = callNode;
595
+ let p = n.parent;
596
+ let rhsIndex = 0;
597
+ let rhsCount = 1;
598
+ if (p && p.type === 'expression_list') {
599
+ rhsCount = p.namedChildCount;
600
+ for (let i = 0; i < p.namedChildCount; i++) {
601
+ if (p.namedChild(i).id === n.id) { rhsIndex = i; break; }
602
+ }
603
+ n = p; p = n.parent;
604
+ }
605
+ if (!p || (p.type !== 'short_var_declaration' && p.type !== 'assignment_statement')) return undefined;
606
+ if (p.type === 'assignment_statement') {
607
+ const op = p.childForFieldName('operator');
608
+ if (op && op.text !== '=') return undefined;
609
+ }
610
+ const right = p.childForFieldName('right');
611
+ if (!right || right.id !== n.id) return undefined;
612
+ const left = p.childForFieldName('left');
613
+ if (!left) return undefined;
614
+ const names = left.type === 'expression_list'
615
+ ? Array.from({ length: left.namedChildCount }, (_, i) => left.namedChild(i))
616
+ : [left];
617
+ if (rhsCount > 1) {
618
+ const target = names[rhsIndex];
619
+ return target?.type === 'identifier' && target.text !== '_'
620
+ ? { assignedTo: target.text } : undefined;
621
+ }
622
+ const target = names[0];
623
+ if (target?.type !== 'identifier' || target.text === '_') return undefined;
624
+ if (names.length > 1) {
625
+ // All LHS names (fix #220): an EXTERNAL producer decides every tuple
626
+ // element's type, not just the first — `tmpFile, err := os.CreateTemp`
627
+ // marks err external-flow too. The TYPED flow keeps pairing only
628
+ // element 0 with the producer's return tuple (#207).
629
+ const rest = names.slice(1)
630
+ .filter(t => t.type === 'identifier' && t.text !== '_')
631
+ .map(t => t.text);
632
+ return { assignedTo: target.text, assignedTuple: true,
633
+ ...(rest.length > 0 && { assignedTupleRest: rest }) };
634
+ }
635
+ return { assignedTo: target.text };
636
+ }
637
+
558
638
  function findCallsInCode(code, parser, options = {}) {
559
639
  const tree = parseTree(parser, code);
560
640
  const calls = [];
@@ -736,6 +816,104 @@ function findCallsInCode(code, parser, options = {}) {
736
816
  return undefined;
737
817
  };
738
818
 
819
+ // fix #203 (Go): is a bare-identifier function REFERENCE shadowed by an
820
+ // enclosing func-literal/function parameter, method receiver, range/init
821
+ // binding, or a := / var local declared before use? grpc-go-measured:
822
+ // `&clusterInfo{unsubscribe: unsubscribe}` inside `func(ref int32,
823
+ // unsubscribe func())` references the parameter, never a same-name
824
+ // package symbol. The enclosing INDEXED symbol's params are checked at
825
+ // query time in findCallers — func-literal params and block locals are
826
+ // only visible here. (Rust/Java need no equivalent: their parsers emit
827
+ // no bare-identifier callback references — Rust only obj.method field
828
+ // expressions, Java only :: method references.)
829
+ const _paramListDeclares = (paramsNode, name) => {
830
+ if (!paramsNode) return false;
831
+ for (let i = 0; i < paramsNode.namedChildCount; i++) {
832
+ const pd = paramsNode.namedChild(i);
833
+ if (pd.type !== 'parameter_declaration' && pd.type !== 'variadic_parameter_declaration') continue;
834
+ // Go allows several names per declaration: `a, b int`
835
+ for (let j = 0; j < pd.namedChildCount; j++) {
836
+ const c = pd.namedChild(j);
837
+ if (c.type === 'identifier' && c.text === name) return true;
838
+ }
839
+ }
840
+ return false;
841
+ };
842
+ const _declaresLocal = (stmt, name, refNode) => {
843
+ if (!stmt) return false;
844
+ // The declaration CONTAINING the reference is not a shadow: in
845
+ // `unsubscribe := unsubscribe` the RHS names the OUTER binding —
846
+ // Go's := declares the LHS only after the statement.
847
+ if (stmt.startIndex <= refNode.startIndex && stmt.endIndex >= refNode.endIndex) return false;
848
+ if (stmt.type === 'short_var_declaration') {
849
+ const left = stmt.childForFieldName('left');
850
+ if (left) {
851
+ for (let i = 0; i < left.namedChildCount; i++) {
852
+ const id = left.namedChild(i);
853
+ if (id.type === 'identifier' && id.text === name) return true;
854
+ }
855
+ }
856
+ } else if (stmt.type === 'var_declaration') {
857
+ for (let i = 0; i < stmt.namedChildCount; i++) {
858
+ const spec = stmt.namedChild(i);
859
+ if (spec.type !== 'var_spec') continue;
860
+ for (let j = 0; j < spec.namedChildCount; j++) {
861
+ const id = spec.namedChild(j);
862
+ if (id.type === 'identifier' && id.text === name) return true;
863
+ }
864
+ }
865
+ }
866
+ return false;
867
+ };
868
+ const isShadowedByLocal = (refNode, name) => {
869
+ for (let p = refNode.parent; p; p = p.parent) {
870
+ if (p.type === 'block') {
871
+ for (let i = 0; i < p.namedChildCount; i++) {
872
+ const stmt = p.namedChild(i);
873
+ if (stmt.startIndex >= refNode.startIndex) break; // declaration-before-use
874
+ if (_declaresLocal(stmt, name, refNode)) return true;
875
+ }
876
+ } else if (p.type === 'for_statement') {
877
+ for (let i = 0; i < p.namedChildCount; i++) {
878
+ const c = p.namedChild(i);
879
+ if (c.type === 'range_clause') {
880
+ const left = c.childForFieldName('left');
881
+ if (left) {
882
+ for (let j = 0; j < left.namedChildCount; j++) {
883
+ const id = left.namedChild(j);
884
+ if (id.type === 'identifier' && id.text === name) return true;
885
+ }
886
+ }
887
+ } else if (c.type === 'for_clause') {
888
+ if (_declaresLocal(c.childForFieldName('initializer'), name, refNode)) return true;
889
+ }
890
+ }
891
+ } else if (p.type === 'if_statement' || p.type === 'expression_switch_statement' ||
892
+ p.type === 'type_switch_statement') {
893
+ if (_declaresLocal(p.childForFieldName('initializer'), name, refNode)) return true;
894
+ // if/switch initializers are plain named children in some
895
+ // grammar versions; type switches bind `v := x.(type)`
896
+ for (let i = 0; i < p.namedChildCount; i++) {
897
+ const c = p.namedChild(i);
898
+ if (c.type === 'short_var_declaration' && _declaresLocal(c, name, refNode)) return true;
899
+ if (p.type === 'type_switch_statement' && c.type === 'expression_list' &&
900
+ c.nextSibling?.type === ':=') {
901
+ for (let j = 0; j < c.namedChildCount; j++) {
902
+ const id = c.namedChild(j);
903
+ if (id.type === 'identifier' && id.text === name) return true;
904
+ }
905
+ }
906
+ }
907
+ } else if (p.type === 'func_literal' || p.type === 'function_declaration' ||
908
+ p.type === 'method_declaration') {
909
+ if (_paramListDeclares(p.childForFieldName('parameters'), name)) return true;
910
+ if (p.type === 'method_declaration' &&
911
+ _paramListDeclares(p.childForFieldName('receiver'), name)) return true;
912
+ }
913
+ }
914
+ return false;
915
+ };
916
+
739
917
  traverseTree(tree.rootNode, (node) => {
740
918
  // Track function entry
741
919
  if (isFunctionNode(node)) {
@@ -795,6 +973,23 @@ function findCallsInCode(code, parser, options = {}) {
795
973
  if (callName && /^New[A-Z]/.test(callName)) {
796
974
  typeName = callName.slice(3);
797
975
  if (!typeName || !/^[A-Z]/.test(typeName)) typeName = null;
976
+ } else if (callFuncNode.type === 'identifier' &&
977
+ callFuncNode.text === 'new') {
978
+ // buf := new(bytes.Buffer) — the builtin
979
+ // allocator returns *T (fix #220,
980
+ // cobra-measured: buf.String() is
981
+ // bytes.Buffer's, never a project method).
982
+ // The argument parses as an expression:
983
+ // identifier or selector_expression.
984
+ const args = val.childForFieldName('arguments');
985
+ const argNode = args && args.namedChild(0);
986
+ if (argNode) {
987
+ typeName = argNode.type === 'identifier'
988
+ ? argNode.text
989
+ : argNode.type === 'selector_expression'
990
+ ? argNode.childForFieldName('field')?.text || null
991
+ : extractTypeName(argNode);
992
+ }
798
993
  }
799
994
  }
800
995
  }
@@ -804,6 +999,34 @@ function findCallsInCode(code, parser, options = {}) {
804
999
  }
805
1000
  }
806
1001
 
1002
+ // Explicitly typed var declarations: `var buf bytes.Buffer`,
1003
+ // `var sb strings.Builder` (fix #220, cobra-measured — sb.String()
1004
+ // on an untyped receiver fell to single-owner confirmation). Same
1005
+ // semantics as parameter annotations: the declared type is the
1006
+ // receiver's compile-time type.
1007
+ if (node.type === 'var_declaration' && functionStack.length > 0) {
1008
+ const scopeKey = functionStack[functionStack.length - 1].startLine;
1009
+ const varTypeMap = scopeTypes.get(scopeKey);
1010
+ if (varTypeMap) {
1011
+ const recordSpec = (spec) => {
1012
+ if (spec.type !== 'var_spec') return;
1013
+ const typeName = extractTypeName(spec.childForFieldName('type'));
1014
+ if (!typeName) return;
1015
+ for (let j = 0; j < spec.namedChildCount; j++) {
1016
+ const id = spec.namedChild(j);
1017
+ if (id.type === 'identifier') varTypeMap.set(id.text, typeName);
1018
+ }
1019
+ };
1020
+ for (let i = 0; i < node.namedChildCount; i++) {
1021
+ const c = node.namedChild(i);
1022
+ if (c.type === 'var_spec') recordSpec(c);
1023
+ else if (c.type === 'var_spec_list') {
1024
+ for (let j = 0; j < c.namedChildCount; j++) recordSpec(c.namedChild(j));
1025
+ }
1026
+ }
1027
+ }
1028
+ }
1029
+
807
1030
  // Track local closures: atoi := func(...) { ... } or var handler = func(...) { ... }
808
1031
  if (node.type === 'short_var_declaration' || node.type === 'var_declaration') {
809
1032
  // Check if a subtree contains a func_literal
@@ -861,6 +1084,26 @@ function findCallsInCode(code, parser, options = {}) {
861
1084
  const enclosingFunction = getCurrentEnclosingFunction();
862
1085
  let uncertain = false;
863
1086
 
1087
+ // Call-site arg count for arity pruning. Slice-spread (`xs...`)
1088
+ // makes the count open-ended — flag it so pruning skips the site.
1089
+ const argsNode = node.childForFieldName('arguments');
1090
+ let argCount = 0;
1091
+ let argSpread = false;
1092
+ if (argsNode) {
1093
+ for (let i = 0; i < argsNode.namedChildCount; i++) {
1094
+ if (argsNode.namedChild(i).type === 'comment') continue;
1095
+ argCount++;
1096
+ }
1097
+ for (let i = 0; i < argsNode.childCount; i++) {
1098
+ if (argsNode.child(i).type === '...') { argSpread = true; break; }
1099
+ }
1100
+ }
1101
+
1102
+ // Assignment target for return-type flow (fix #207):
1103
+ // bb := balancer.Get(n) lets findCallers type bb from Get's
1104
+ // declared return type at query time.
1105
+ const assigned = goAssignmentTargetOf(node);
1106
+
864
1107
  if (funcNode.type === 'identifier') {
865
1108
  const callName = funcNode.text;
866
1109
  // Skip Go built-in function calls
@@ -877,6 +1120,11 @@ function findCallsInCode(code, parser, options = {}) {
877
1120
  name: callName,
878
1121
  line: node.startPosition.row + 1,
879
1122
  isMethod: false,
1123
+ argCount,
1124
+ ...(argSpread && { argSpread: true }),
1125
+ ...(assigned && { assignedTo: assigned.assignedTo }),
1126
+ ...(assigned?.assignedTuple && { assignedTuple: true }),
1127
+ ...(assigned?.assignedTupleRest && { assignedTupleRest: assigned.assignedTupleRest }),
880
1128
  enclosingFunction,
881
1129
  uncertain,
882
1130
  ...(firstArg && { firstStringArg: firstArg.value, firstStringArgInterp: firstArg.interp })
@@ -892,13 +1140,67 @@ function findCallsInCode(code, parser, options = {}) {
892
1140
  // If receiver is a known import alias, this is a package call, not a method call
893
1141
  const isPkgCall = receiver && importAliases.has(receiver);
894
1142
  const receiverType = (!isPkgCall && receiver) ? getReceiverType(receiver) : undefined;
1143
+ // fix #202: one-hop declared-field receivers — h.inner.Run().
1144
+ // receiverRoot/Field/RootType let findCallers hop to the
1145
+ // field's declared struct-field type cross-file.
1146
+ let receiverRoot, receiverFieldName, receiverRootType;
1147
+ if (!receiver && operandNode?.type === 'selector_expression') {
1148
+ const rootNode = operandNode.childForFieldName('operand');
1149
+ const fldNode = operandNode.childForFieldName('field');
1150
+ if (rootNode?.type === 'identifier' && fldNode &&
1151
+ !importAliases.has(rootNode.text)) {
1152
+ receiverRoot = rootNode.text;
1153
+ receiverFieldName = fldNode.text;
1154
+ receiverRootType = getReceiverType(rootNode.text);
1155
+ }
1156
+ }
1157
+ // Chained receiver (fix #220, cobra-measured): the receiver
1158
+ // IS a call — rootCmd.Flags().String(...) — record the
1159
+ // producer so findCallers can type the receiver from its
1160
+ // declared return (*pflag.FlagSet → external → routed).
1161
+ // Package-qualified producers (os.CreateTemp().Name())
1162
+ // carry the qualifier for strict import-package resolution.
1163
+ let receiverCall, receiverCallIsMethod, receiverCallReceiver;
1164
+ if (!receiver && !receiverFieldName && operandNode?.type === 'call_expression') {
1165
+ const prodFunc = operandNode.childForFieldName('function');
1166
+ if (prodFunc?.type === 'identifier') {
1167
+ receiverCall = prodFunc.text;
1168
+ } else if (prodFunc?.type === 'selector_expression') {
1169
+ const pf = prodFunc.childForFieldName('field');
1170
+ const po = prodFunc.childForFieldName('operand');
1171
+ if (pf) {
1172
+ receiverCall = pf.text;
1173
+ if (po?.type === 'identifier' && importAliases.has(po.text)) {
1174
+ receiverCallReceiver = po.text;
1175
+ } else {
1176
+ receiverCallIsMethod = true;
1177
+ }
1178
+ }
1179
+ }
1180
+ }
895
1181
  const firstArg = getFirstStringArg(node);
896
1182
  calls.push({
897
1183
  name: fieldNode.text,
898
- line: node.startPosition.row + 1,
1184
+ // Name-node line convention (#201/RUST-2, fix #223):
1185
+ // a method call on a multi-line receiver —
1186
+ // (&pkg.Name{...}).String() — reports the FIELD's own
1187
+ // line, not the chain-start line. The account's ground
1188
+ // set and the oracles key by the name's line; Go was
1189
+ // the only parser still using the call node's start.
1190
+ line: fieldNode.startPosition.row + 1,
899
1191
  isMethod: !isPkgCall,
900
1192
  receiver,
901
1193
  ...(receiverType && { receiverType }),
1194
+ ...(receiverFieldName && { receiverRoot, receiverField: receiverFieldName }),
1195
+ ...(receiverFieldName && receiverRootType && { receiverRootType }),
1196
+ ...(receiverCall && { receiverCall }),
1197
+ ...(receiverCallIsMethod && { receiverCallIsMethod: true }),
1198
+ ...(receiverCallReceiver && { receiverCallReceiver }),
1199
+ argCount,
1200
+ ...(argSpread && { argSpread: true }),
1201
+ ...(assigned && { assignedTo: assigned.assignedTo }),
1202
+ ...(assigned?.assignedTuple && { assignedTuple: true }),
1203
+ ...(assigned?.assignedTupleRest && { assignedTupleRest: assigned.assignedTupleRest }),
902
1204
  enclosingFunction,
903
1205
  uncertain,
904
1206
  ...(firstArg && { firstStringArg: firstArg.value, firstStringArgInterp: firstArg.interp })
@@ -926,13 +1228,18 @@ function findCallsInCode(code, parser, options = {}) {
926
1228
  const typeNode = node.childForFieldName('type');
927
1229
  if (typeNode) {
928
1230
  let typeName = null;
1231
+ let typeQualifier = null;
929
1232
  if (typeNode.type === 'type_identifier') {
930
1233
  // Foo{...}
931
1234
  typeName = typeNode.text;
932
1235
  } else if (typeNode.type === 'qualified_type') {
933
- // pkg.Foo{...}
1236
+ // pkg.Foo{...} — keep the package qualifier as receiver: a
1237
+ // package-qualified type can never resolve to a same-file
1238
+ // binding (Go cannot self-import), so resolution must not
1239
+ // claim local same-name symbols for it.
934
1240
  const tn = typeNode.childForFieldName('name');
935
1241
  if (tn) typeName = tn.text;
1242
+ typeQualifier = typeNode.childForFieldName('package')?.text || null;
936
1243
  }
937
1244
  // Skip anonymous types (slice_type, map_type, array_type, struct_type, etc.)
938
1245
  if (typeName) {
@@ -942,6 +1249,7 @@ function findCallsInCode(code, parser, options = {}) {
942
1249
  line: node.startPosition.row + 1,
943
1250
  isMethod: false,
944
1251
  isConstructor: true,
1252
+ ...(typeQualifier && { receiver: typeQualifier }),
945
1253
  enclosingFunction,
946
1254
  uncertain: false
947
1255
  });
@@ -988,7 +1296,8 @@ function findCallsInCode(code, parser, options = {}) {
988
1296
  isFunctionReference: true,
989
1297
  isPotentialCallback: true,
990
1298
  enclosingFunction,
991
- uncertain: false
1299
+ uncertain: false,
1300
+ ...(isShadowedByLocal(node, name) && { localShadow: true }),
992
1301
  });
993
1302
  }
994
1303
  }
@@ -1039,7 +1348,8 @@ function findCallsInCode(code, parser, options = {}) {
1039
1348
  isFunctionReference: true,
1040
1349
  isPotentialCallback: true,
1041
1350
  enclosingFunction,
1042
- uncertain: false
1351
+ uncertain: false,
1352
+ ...(isShadowedByLocal(rhs, name) && { localShadow: true }),
1043
1353
  });
1044
1354
  }
1045
1355
  }
@@ -1097,6 +1407,7 @@ function findCallsInCode(code, parser, options = {}) {
1097
1407
  uncertain: false,
1098
1408
  ...(compositeType && { compositeType }),
1099
1409
  ...(fieldName && { fieldName }),
1410
+ ...(isShadowedByLocal(valueNode, name) && { localShadow: true }),
1100
1411
  });
1101
1412
  }
1102
1413
  }
@@ -1329,7 +1640,7 @@ function findUsagesInCode(code, name, parser) {
1329
1640
  const tree = parseTree(parser, code);
1330
1641
  const usages = [];
1331
1642
 
1332
- traverseTreeCached(tree.rootNode, (node) => {
1643
+ visitNameNodes(tree, code, name, (node) => {
1333
1644
  // Look for identifier, field_identifier (method names in selector expressions),
1334
1645
  // and type_identifier (type references in params, return types, composite literals, etc.)
1335
1646
  const isIdentifier = node.type === 'identifier' || node.type === 'field_identifier' || node.type === 'type_identifier';
@@ -21,6 +21,25 @@ const STRUCTURAL_TRAITS = {
21
21
  exportVisibility: 'keyword',
22
22
  hasDynamicImports: true,
23
23
  testDirs: [],
24
+ allMethodsVirtual: false,
25
+ hasArityOverloads: false,
26
+ // Class members are public unless marked otherwise (#name, _name,
27
+ // `private`). An exported class therefore exposes its non-private methods
28
+ // as public API — deadcode treats them as exported (fix #211). Languages
29
+ // with explicit member visibility (Rust `pub`, Java `public` — already
30
+ // captured as modifiers) or capitalization rules (Go) set false: there
31
+ // the member's own marker decides.
32
+ implicitlyPublicMembers: true,
33
+ // A bare (receiver-less) name can never denote a METHOD here — JS/Python
34
+ // methods are reached through their receiver; only a rebound alias could,
35
+ // which is separate name-level evidence. Java sets true: `execute()`
36
+ // inside a class means this.execute(), and static imports bind foreign
37
+ // class methods to bare names (fix #220).
38
+ bareCallReachesMethods: false,
39
+ // A method-shaped call CAN reach a standalone function here: attribute
40
+ // assignment rebinds functions onto objects (obj.print = print), so the
41
+ // #218b gate routes such calls visible instead of excluding (fix #220).
42
+ methodCallReachesFunctions: true,
24
43
  };
25
44
  const NOMINAL_TRAITS = {
26
45
  typeSystem: 'nominal',
@@ -30,6 +49,58 @@ const NOMINAL_TRAITS = {
30
49
  exportVisibility: 'keyword',
31
50
  hasDynamicImports: true,
32
51
  testDirs: [],
52
+ // Whether ANY instance method call can dynamically dispatch to a subtype
53
+ // override (Java: all instance methods are virtual). Go struct method
54
+ // sets and Rust inherent methods bind statically — only interface/trait
55
+ // receivers dispatch there, which is detected per-type, not per-language.
56
+ allMethodsVirtual: false,
57
+ // Whether one class can define several same-name methods differing only
58
+ // in parameters (Java overloading). Drives the overload discipline in
59
+ // the caller contract: a pinned overload is only confirmed when the call
60
+ // site provably binds it.
61
+ hasArityOverloads: false,
62
+ // What a TYPE-QUALIFIED method call looks like, so a receiver that merely
63
+ // shares the target type's NAME isn't mistaken for the type itself:
64
+ // 'static' — Type.method() static form, any arity (Java).
65
+ // 'method-expr' — Go method expressions T.M(recv, ...): the receiver
66
+ // instance is the FIRST argument, so a zero-arg call on
67
+ // a type-named receiver must be a variable (grpc-go
68
+ // names builder structs and Builder locals both `bb`).
69
+ // 'path' — Rust Type::method (isPathCall); a DOT-call receiver
70
+ // matching a type name is a variable, never the type.
71
+ typeQualifiedCallStyle: 'static',
72
+ implicitlyPublicMembers: false,
73
+ // The implicit root supertype every class extends without declaring it
74
+ // (Java `Object`). A receiver declared with this type can hold ANY project
75
+ // instance, so it is dispatch-capable toward every override — but the
76
+ // edge is invisible to declared-ancestry walks (fix #212). Routing only,
77
+ // never exclusion evidence. Go/Rust: null — Go's interface{}/any cannot
78
+ // receive method calls without an assertion, Rust has no universal
79
+ // supertype. Structural languages: null — any/object/unknown receivers
80
+ // are already refused as exclusion evidence by the trust gate.
81
+ universalSupertype: null,
82
+ // A bare (receiver-less) call or reference can never denote a METHOD:
83
+ // Go method values/expressions require an explicit receiver or type
84
+ // qualifier (m.Helper(), T.Helper); Rust requires self./Type:: and `use`
85
+ // cannot import associated functions. A bare MarkFlagDirname(...) inside
86
+ // Command.MarkFlagDirname denotes the package FUNCTION (fix #220,
87
+ // cobra/grpc-go-measured). Java overrides true (implicit this-calls,
88
+ // static imports).
89
+ bareCallReachesMethods: false,
90
+ // Whether a method-shaped call (x.f()) can reach a standalone FUNCTION:
91
+ // Go func-typed fields are name-callable (s.Run() may invoke a stored
92
+ // function), so exclusion needs !bindingId there; Rust requires (s.f)()
93
+ // parens and Java requires .apply() — a dot-call provably never binds a
94
+ // free function (fix #220, ripgrep-measured).
95
+ methodCallReachesFunctions: false,
96
+ // Whether a paren-less member access (x.name) can denote a METHOD. Rust
97
+ // sets true for the inverse — `x.name` is ALWAYS a field there (method
98
+ // values are path-only: Type::method), so a member-access reference
99
+ // against a method target is excluded (fix #220, ripgrep-measured:
100
+ // `self.paths.has_implicit_path` is the bool FIELD, not the method).
101
+ // Go method values (obj.Method) and Java `::` references DO denote
102
+ // methods — false. Per-language override, not preset-wide.
103
+ memberAccessNeverMethod: false,
33
104
  };
34
105
 
35
106
  // Language configurations
@@ -99,6 +170,8 @@ const LANGUAGES = {
99
170
  hasReceiverPackageCalls: true,
100
171
  exportVisibility: 'capitalization',
101
172
  hasDynamicImports: false,
173
+ typeQualifiedCallStyle: 'method-expr',
174
+ methodCallReachesFunctions: true,
102
175
  testFileCandidates: (base, ext) => [`${base}_test.go`],
103
176
  },
104
177
  },
@@ -112,6 +185,8 @@ const LANGUAGES = {
112
185
  ...NOMINAL_TRAITS,
113
186
  selfParam: ['self', '&self', '&mut self', 'mut self'],
114
187
  hasDynamicImports: false,
188
+ typeQualifiedCallStyle: 'path',
189
+ memberAccessNeverMethod: true,
115
190
  testFileCandidates: (base, ext) => [`${base}_test.rs`],
116
191
  testDirs: ['tests'],
117
192
  },
@@ -125,6 +200,10 @@ const LANGUAGES = {
125
200
  traits: {
126
201
  ...NOMINAL_TRAITS,
127
202
  selfParam: ['this'],
203
+ allMethodsVirtual: true,
204
+ hasArityOverloads: true,
205
+ universalSupertype: 'Object',
206
+ bareCallReachesMethods: true,
128
207
  testFileCandidates: (base, ext) => [`${base}Test.java`, `${base}Tests.java`, `${base}TestCase.java`],
129
208
  },
130
209
  },