feat(lsp): parse own/parenthesized/nullable types and report implicit conversions

ParseType now understands `own T`, `(T)` and `T?`, so assignment checks reuse the existing int/pointer conversion codes (integer<->pointer, pointer mismatch). Value-typed targets reject pointer values, and duplicate nullable reporting is gone.
This commit is contained in:
TermiNexus committed 2026-11-03 19:49:04 +08:00
1 parent 295d78aa56
commit 2c65cea97f
2 files changed
+36 -7

No files matched your search

+11 -7
View File
@@ -1570,14 +1570,16 @@ def _WalkChecks(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*,
ann: str = _DeclAnnotationText(pool, doc, an.target)
dst: LspTypes.LspType* = LspTypes.ParseType(pool, ann)
_CheckAssignValue(pool, doc, a, ws, table_pool, dst, an.value, None, errors)
if _NullableDetail(ann) == 0 \
if an.value is not None and _AnnIsPointerLike(pool, doc, an) != 0 \
and _NullableDetail(ann) == 0 \
and _NullableDetail(_SymDetailAt(pool, a, an.value)) != 0:
_EmitTypeError(pool, doc, an.value,
"a nullable value may be None; declare the target as `T?` or unwrap it with `match` first", errors)
_WalkChecksExprs(pool, doc, a, ws, table_pool, an.value, errors)
if an.value is not None and _AnnIsPointerLike(pool, doc, an) == 0 \
and _NullableDetail(_SymDetailAt(pool, a, an.value)) == 0 \
and _ValueIsPointer(pool, a, an.value) != 0:
and _ValueIsPointer(pool, a, an.value) != 0 \
and (an.value.kind() == ast.ASTKind.New or dst is None
or dst.kind == LspTypes.LT_AGG or dst.kind == LspTypes.LT_UNKNOWN):
_EmitTypeError(pool, doc, an.value,
"a pointer/`own` value cannot be stored into a value-typed variable; declare the target as `own T` / `T*`, or dereference explicitly (`*p`)", errors)
if an.target is not None and an.target.kind() == ast.ASTKind.Name:
@@ -1604,15 +1606,17 @@ def _WalkChecks(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*,
continue
dst_t: LspTypes.LspType* = _TypeFromDetail(pool, tt.detail)
_CheckAssignValue(pool, doc, a, ws, table_pool, dst_t, asn.value, None, errors)
if _NullableDetail(tt.detail) == 0 \
if _DetailIsPointer(tt.detail) != 0 and _NullableDetail(tt.detail) == 0 \
and _NullableDetail(_SymDetailAt(pool, a, asn.value)) != 0:
_EmitTypeError(pool, doc, asn.value,
"a nullable value may be None; declare the target as `T?` or unwrap it with `match` first", errors)
if tt.detail is not None and _DetailIsPointer(tt.detail) == 0 \
and _NullableDetail(_SymDetailAt(pool, a, asn.value)) == 0 \
and _ValueIsPointer(pool, a, asn.value) != 0:
_EmitTypeError(pool, doc, asn.value,
"a pointer/`own` value cannot be stored into a value-typed variable; declare the target as `own T` / `T*`, or dereference explicitly (`*p`)", errors)
dst_a: LspTypes.LspType* = _TypeFromDetail(pool, tt.detail)
if asn.value.kind() == ast.ASTKind.New or dst_a is None \
or dst_a.kind == LspTypes.LT_AGG or dst_a.kind == LspTypes.LT_UNKNOWN:
_EmitTypeError(pool, doc, asn.value,
"a pointer/`own` value cannot be stored into a value-typed variable; declare the target as `own T` / `T*`, or dereference explicitly (`*p`)", errors)
_WalkChecksExprs(pool, doc, a, ws, table_pool, asn.value, errors)
if asn.value is not None and asn.value.kind() == ast.ASTKind.Name:
sn_a: ast.Name* = ast.Name*(asn.value)
+25
View File
@@ -243,6 +243,31 @@ def ParseType(pool: memhub.MemManager*, text: str) -> LspType*:
if p is not None:
p.pointee = inner
return p
# Parenthesized type group: `( T )` (suffixes such as `?` are handled below/outside).
w_len0: sizet = string.strlen(working)
if w_len0 >= 2 and working[0] == '(' and working[w_len0 - 1] == ')':
return ParseType(pool, _Trim(pool, _Slice(pool, working, 1, int(w_len0 - 1))))
# Ownership prefix: `own T` (one owned pointer layer; an existing pointer keeps its layer).
if _Starts(working, "own ") != 0:
rest_own: str = _Trim(pool, _Slice(pool, working, 4, int(string.strlen(working))))
inner_own: LspType* = ParseType(pool, rest_own)
if inner_own is not None and inner_own.kind == LT_PTR:
return inner_own
p_own: LspType* = _NewType(pool, LT_PTR)
if p_own is not None:
p_own.pointee = inner_own
return p_own
# Nullable suffix: `T?` (pointer niche; an existing pointer keeps its layer).
n_q: sizet = string.strlen(working)
if n_q > 0 and working[n_q - 1] == '?':
base_q: str = _Trim(pool, _Slice(pool, working, 0, int(n_q - 1)))
inner_q: LspType* = ParseType(pool, base_q)
if inner_q is not None and inner_q.kind == LT_PTR:
return inner_q
p_q: LspType* = _NewType(pool, LT_PTR)
if p_q is not None:
p_q.pointee = inner_q
return p_q
# Leading array: []T / [N]T.
if string.strlen(working) > 0 and working[0] == '[':
close: int = -1