diff --git a/App/lib/lsp/LspChecks.vp b/App/lib/lsp/LspChecks.vp index b5c88a9..bf8891a 100644 --- a/App/lib/lsp/LspChecks.vp +++ b/App/lib/lsp/LspChecks.vp @@ -1020,6 +1020,16 @@ def _CheckAssignValue(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*, _EmitTypeError(pool, doc, value, msg, errors) +def _RefDetail(det: str) -> int: + """1 when an annotation text is a reference (`&T` / `&mut T`, optional trailing `?`).""" + if det is None: return 0 + s: str = det + while string.strlen(s) > 0 and s[0] == ' ': + s += 1 + if string.strlen(s) == 0: return 0 + return 1 if s[0] == '&' else 0 + + def _NullableDetail(det: str) -> int: """1 when an annotation text is the nullable `T?` form.""" if det is None: return 0 @@ -1609,6 +1619,25 @@ def _WalkChecks(pool: memhub.MemManager*, doc: LspDocuments.LspDocument*, continue if tgt.kind() == ast.ASTKind.Attribute: _WalkChecksExprs(pool, doc, a, ws, table_pool, ast.Attribute*(tgt).value, errors) + # Reference field: the slot holds a borrow, not an owner. + det_f: str = _SymDetailAt(pool, a, tgt) + if _RefDetail(det_f) != 0: + ok_f: int = 0 + rv_f: ast.AST* = asn.value + if rv_f is not None: + if rv_f.kind() == ast.ASTKind.UnaryOp: + if (ast.UnaryOp*)(rv_f).op == ast.OpKind.AddrOf: ok_f = 1 + elif rv_f.kind() == ast.ASTKind.Constant: + if (ast.Constant*)(rv_f).const_kind == ast.CONST_NONE: ok_f = 1 + elif _RefDetail(_SymDetailAt(pool, a, rv_f)) != 0: + ok_f = 1 + if ok_f == 0: + at_f: ast.Attribute* = ast.Attribute*(tgt) + msg_f: bytes = pool.alloc(sizet(320)) + if msg_f is not None and at_f.attr is not None: + stdio.snprintf(msg_f, sizet(320), + "field '.%s' is a reference: assign a borrow (`&value`), not an owner", at_f.attr) + _EmitTypeError(pool, doc, rv_f, str(msg_f), errors) continue if tgt.kind() == ast.ASTKind.Subscript: sb_t: ast.Subscript* = ast.Subscript*(tgt)