Skip to content

Commit 0b495c5

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
fix: spec2cli — ambiguous types, nullable interfaces, array elements
- resolveTypeKey: proximity-based disambiguation for ambiguous short names - classifyType: strip pointer from nullable interfaces (Go interfaces are nilable) - classifyType: propagate kindUnsupported through array elements - classifyType: return kindUnsupported for truly unknown types - writeInterfaceTypeExtraction: handle *-prefixed interface types - classifyFieldType: same nullable + array fixes
1 parent e1b144b commit 0b495c5

1 file changed

Lines changed: 76 additions & 5 deletions

File tree

tools/cmd/spec2cli/main.go

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,22 @@ func convertMethodSpec(
10141014
// key ("importPath:TypeName") using the interface's import context.
10151015
// When the type is not found in the interface's own package, it falls
10161016
// back to the global typeByShortName index for cross-package resolution.
1017+
func commonPrefixLen(
1018+
a string,
1019+
b string,
1020+
) int {
1021+
n := len(a)
1022+
if len(b) < n {
1023+
n = len(b)
1024+
}
1025+
for i := 0; i < n; i++ {
1026+
if a[i] != b[i] {
1027+
return i
1028+
}
1029+
}
1030+
return n
1031+
}
1032+
10171033
func resolveTypeKey(
10181034
typeStr string,
10191035
ifaceImportPath string,
@@ -1045,6 +1061,37 @@ func resolveTypeKey(
10451061
return globalKey
10461062
}
10471063

1064+
// Ambiguous short name (exists in multiple packages) — pick the
1065+
// entry whose import path shares the longest prefix with the
1066+
// calling interface's package, so types resolve to nearby packages.
1067+
if _, ok := typeByShortName[bare]; ok {
1068+
bestKey := ""
1069+
bestPrefix := 0
1070+
for key := range knownStructs {
1071+
parts := strings.SplitN(key, ":", 2)
1072+
if len(parts) == 2 && parts[1] == bare {
1073+
prefix := commonPrefixLen(ifaceImportPath, parts[0])
1074+
if prefix > bestPrefix {
1075+
bestPrefix = prefix
1076+
bestKey = key
1077+
}
1078+
}
1079+
}
1080+
for key := range knownEnums {
1081+
parts := strings.SplitN(key, ":", 2)
1082+
if len(parts) == 2 && parts[1] == bare {
1083+
prefix := commonPrefixLen(ifaceImportPath, parts[0])
1084+
if prefix > bestPrefix {
1085+
bestPrefix = prefix
1086+
bestKey = key
1087+
}
1088+
}
1089+
}
1090+
if bestKey != "" {
1091+
return bestKey
1092+
}
1093+
}
1094+
10481095
return localKey
10491096
}
10501097

@@ -1068,6 +1115,9 @@ func classifyType(
10681115
// Fall through to nullable wrapping a JSON-serializable type
10691116
// rather than rejecting the entire method.
10701117
return kindNullable
1118+
case kindInterfaceType, kindInterface:
1119+
// Go interfaces are inherently nilable — strip the pointer.
1120+
return innerKind
10711121
default:
10721122
return kindNullable
10731123
}
@@ -1097,6 +1147,12 @@ func classifyType(
10971147
}
10981148

10991149
if strings.HasPrefix(typeStr, "[]") {
1150+
// Propagate unsupported element types so the method is skipped
1151+
// instead of generating broken array serialization code.
1152+
elemKind := classifyType(typeStr[2:], iface)
1153+
if elemKind == kindUnsupported {
1154+
return kindUnsupported
1155+
}
11001156
return kindComplexArray
11011157
}
11021158

@@ -1157,10 +1213,9 @@ func classifyType(
11571213
return kindUnsupported
11581214
}
11591215

1160-
// Unknown type: fall back to JSON serialization rather than
1161-
// rejecting the method. The type may exist in Go but not be
1162-
// in the spec's type registry (e.g., Java-only parcelables).
1163-
return kindInterface
1216+
// Truly unknown type — skip the method to avoid generating broken
1217+
// JSON-based `any` code for types we cannot resolve.
1218+
return kindUnsupported
11641219
}
11651220

11661221
// isAIDLInterfaceName returns true if the name follows the AIDL
@@ -1329,7 +1384,14 @@ func classifyFieldType(
13291384
if _, ok := primitiveTypes[inner]; ok {
13301385
return kindNullablePrimitive
13311386
}
1332-
return kindNullable
1387+
innerKind := classifyFieldType(inner, si)
1388+
switch innerKind {
1389+
case kindInterfaceType, kindInterface:
1390+
// Go interfaces are inherently nilable — strip the pointer.
1391+
return innerKind
1392+
default:
1393+
return kindNullable
1394+
}
13331395
}
13341396

13351397
switch typeStr {
@@ -1353,6 +1415,12 @@ func classifyFieldType(
13531415
return kindMap
13541416
}
13551417
if strings.HasPrefix(typeStr, "[]") {
1418+
// Propagate unsupported element types so the field is skipped
1419+
// instead of generating broken array serialization code.
1420+
elemKind := classifyFieldType(typeStr[2:], si)
1421+
if elemKind == kindUnsupported {
1422+
return kindUnsupported
1423+
}
13561424
return kindComplexArray
13571425
}
13581426

@@ -2500,6 +2568,9 @@ func writeInterfaceTypeExtraction(
25002568
varName string,
25012569
gc *genContext,
25022570
) {
2571+
// Nullable interface types (*IFoo) arrive here after classifyType
2572+
// strips the pointer (Go interfaces are inherently nilable).
2573+
typeStr = strings.TrimPrefix(typeStr, "*")
25032574
qualType := gc.resolveGoType(typeStr)
25042575

25052576
bareName := typeStr

0 commit comments

Comments
 (0)