Skip to content

Commit

Permalink
better error messages for dot operators [backport]
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Nov 26, 2022
1 parent b57a963 commit 9bb5d2c
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 23 deletions.
48 changes: 25 additions & 23 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# fail fast:
globalError(c.config, n.info, "type mismatch")
return
if {nfDotField, nfDotSetter} * n.flags != {}:
let ident = considerQuotedIdent(c, n[0], n).s
let sym = n[1].typ.typSym
var typeHint = ""
if sym == nil:
discard
else:
typeHint = " for type " & getProcHeader(c.config, sym)
localError(c.config, n.info, errUndeclaredField % ident & typeHint)
return
if errors.len == 0:
localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
return
Expand Down Expand Up @@ -330,7 +340,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
sym = nextOverloadIter(o, c, f)

let ident = considerQuotedIdent(c, f, n).s
if {nfDotField, nfExplicitCall} * n.flags == {nfDotField}:
if {nfDotField, nfDotSetter, nfExplicitCall} * n.flags <= {nfDotField, nfDotSetter}:
let sym = n[1].typ.typSym
var typeHint = ""
if sym == nil:
Expand Down Expand Up @@ -363,11 +373,14 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
else:
initialBinding = nil

template pickBest(headSymbol) =
pickBestCandidate(c, f, n, orig, initialBinding,
filter, result, alt, errors, efExplain in flags,
errorsEnabled, flags)

template pickBestSpecialOp(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors, efExplain in flags,
errorsEnabled, flags)
pickBest(f)
filter, result, alt, (var dummyErrors: CandidateErrors; dummyErrors), efExplain in flags,
false, flags)

let overloadsState = result.state
if overloadsState != csMatch:
Expand All @@ -383,7 +396,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let op = newIdentNode(getIdent(c.cache, x), n.info)
n[0] = op
orig[0] = op
pickBest(op)
pickBestSpecialOp(op)

if nfExplicitCall in n.flags:
tryOp ".()"
Expand All @@ -397,26 +410,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
n.sons[0..1] = [callOp, n[1], calleeName]
orig.sons[0..1] = [callOp, orig[1], calleeName]
pickBest(callOp)
pickBestSpecialOp(callOp)

if overloadsState == csEmpty and result.state == csEmpty:
if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
template impl() =
result.state = csNoMatch
if efNoDiagnostics in flags:
return
# xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
if n[0].kind == nkIdent and n[0].ident.s == ".=" and n[2].kind == nkIdent:
let sym = n[1].typ.sym
if sym == nil:
impl()
else:
let field = n[2].ident.s
let msg = errUndeclaredField % field & " for type " & getProcHeader(c.config, sym)
localError(c.config, orig[2].info, msg)
else:
impl()
result.state = csNoMatch
if efNoDiagnostics in flags:
return
# xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
return
elif result.state != csMatch:
if nfExprCall in n.flags:
Expand Down
56 changes: 56 additions & 0 deletions tests/specialops/terrmsgs.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
discard """
action: reject
cmd: '''nim check --hints:off $file'''
"""

{.experimental: "dotOperators".}
{.experimental: "callOperator".}

# issue #13063

block:
template `.`(a: int, b: untyped): untyped = 123
var b: float
echo b.x #[tt.Error
^ undeclared field: 'x' for type system.float [type declared in ..\..\lib\system\basic_types.nim(15, 3)]]#

block:
template `()`(a: int, b: untyped): untyped = 123
var b: float
echo b.x #[tt.Error
^ undeclared field: 'x' for type system.float [type declared in ..\..\lib\system\basic_types.nim(15, 3)]]#
echo b.x() #[tt.Error
^ attempting to call undeclared routine: 'x']#

block:
type Foo = object
type Bar = object
x1: int
var b: Bar
block:
template `.`(a: Foo, b: untyped): untyped = 123
echo b.x #[tt.Error
^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(27, 8)]]#
block:
template `.()`(a: Foo, b: untyped): untyped = 123
echo b.x() #[tt.Error
^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(27, 8)]]#
block:
template `.=`(a: Foo, b: untyped, c: untyped) = b = c
b.x = 123 #[tt.Error
^ undeclared field: 'x=' for type terrmsgs.Bar [type declared in terrmsgs.nim(27, 8)]]#
# yeah it says x= but does it matter in practice
block:
template `()`(a: Foo, b: untyped, c: untyped) = echo "something"

# completely undeclared::
xyz(123) #[tt.Error
^ undeclared identifier: 'xyz']#

# already declared routine:
min(123) #[tt.Error
^ type mismatch: got <int literal(123)>]#

# non-routine type shows `()` overloads:
b(123) #[tt.Error
^ type mismatch: got <Bar, int literal(123)>]#

0 comments on commit 9bb5d2c

Please sign in to comment.