* better procvar ambiguity errors, clean up after #20457 fixes #6359, fixes #13849 * only trigger on closedsymchoice again * new approach * add manual entry for ambiguous enums too * add indent [skip ci] * move to proc
This commit is contained in:
parent
b36f5119ae
commit
2449c37137
5 changed files with 76 additions and 36 deletions
|
|
@ -70,10 +70,11 @@ type
|
||||||
efWantStmt, efAllowStmt, efDetermineType, efExplain,
|
efWantStmt, efAllowStmt, efDetermineType, efExplain,
|
||||||
efWantValue, efOperand, efNoSemCheck,
|
efWantValue, efOperand, efNoSemCheck,
|
||||||
efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
|
efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
|
||||||
efNoUndeclared, efIsDotCall, efCannotBeDotCall
|
efNoUndeclared, efIsDotCall, efCannotBeDotCall,
|
||||||
# Use this if undeclared identifiers should not raise an error during
|
# Use this if undeclared identifiers should not raise an error during
|
||||||
# overload resolution.
|
# overload resolution.
|
||||||
efNoDiagnostics
|
efNoDiagnostics,
|
||||||
|
efTypeAllowed # typeAllowed will be called after
|
||||||
|
|
||||||
TExprFlags* = set[TExprFlag]
|
TExprFlags* = set[TExprFlag]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,18 +83,39 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
|
||||||
# do not produce another redundant error message:
|
# do not produce another redundant error message:
|
||||||
result = errorNode(c, n)
|
result = errorNode(c, n)
|
||||||
|
|
||||||
|
proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
|
||||||
|
let first = n[0].sym
|
||||||
|
if first.kind == skEnumField:
|
||||||
|
# choose the first resolved enum field, i.e. the latest in scope
|
||||||
|
# to mirror behavior before overloadable enums
|
||||||
|
if hintAmbiguousEnum in c.config.notes:
|
||||||
|
var err = "ambiguous enum field '" & first.name.s &
|
||||||
|
"' assumed to be of type " & typeToString(first.typ) &
|
||||||
|
" -- use one of the following:\n"
|
||||||
|
for child in n:
|
||||||
|
let candidate = child.sym
|
||||||
|
err.add " " & candidate.owner.name.s & "." & candidate.name.s & "\n"
|
||||||
|
message(c.config, orig.info, hintAmbiguousEnum, err)
|
||||||
|
result = n[0]
|
||||||
|
else:
|
||||||
|
var err = "ambiguous identifier '" & first.name.s &
|
||||||
|
"' -- use one of the following:\n"
|
||||||
|
for child in n:
|
||||||
|
let candidate = child.sym
|
||||||
|
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||||
|
err.add ": " & typeToString(candidate.typ) & "\n"
|
||||||
|
localError(c.config, orig.info, err)
|
||||||
|
n.typ = errorType(c)
|
||||||
|
result = n
|
||||||
|
|
||||||
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
|
||||||
result = semExprCheck(c, n, flags, expectedType)
|
result = semExprCheck(c, n, flags, expectedType)
|
||||||
if result.typ == nil and efInTypeof in flags:
|
if result.typ == nil and efInTypeof in flags:
|
||||||
result.typ = c.voidType
|
result.typ = c.voidType
|
||||||
elif (result.typ == nil or result.typ.kind == tyNone) and
|
elif (result.typ == nil or result.typ.kind == tyNone) and
|
||||||
result.kind == nkClosedSymChoice and
|
efTypeAllowed in flags and
|
||||||
result[0].sym.kind == skEnumField:
|
result.kind == nkClosedSymChoice and result.len > 0:
|
||||||
# if overloaded enum field could not choose a type from a closed list,
|
result = ambiguousSymChoice(c, n, result)
|
||||||
# choose the first resolved enum field, i.e. the latest in scope
|
|
||||||
# to mirror old behavior
|
|
||||||
msgSymChoiceUseQualifier(c, result, hintAmbiguousEnum)
|
|
||||||
result = result[0]
|
|
||||||
elif result.typ == nil or result.typ == c.enforceVoidContext:
|
elif result.typ == nil or result.typ == c.enforceVoidContext:
|
||||||
localError(c.config, n.info, errExprXHasNoType %
|
localError(c.config, n.info, errExprXHasNoType %
|
||||||
renderTree(result, {renderNoComments}))
|
renderTree(result, {renderNoComments}))
|
||||||
|
|
@ -634,7 +655,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||||
lastValidIndex = lastOrd(c.config, indexType)
|
lastValidIndex = lastOrd(c.config, indexType)
|
||||||
x = x[1]
|
x = x[1]
|
||||||
|
|
||||||
let yy = semExprWithType(c, x, expectedType = expectedElementType)
|
let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
|
||||||
var typ = yy.typ
|
var typ = yy.typ
|
||||||
if expectedElementType == nil:
|
if expectedElementType == nil:
|
||||||
expectedElementType = typ
|
expectedElementType = typ
|
||||||
|
|
@ -655,7 +676,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
|
||||||
localError(c.config, x.info, "invalid order in array constructor")
|
localError(c.config, x.info, "invalid order in array constructor")
|
||||||
x = x[1]
|
x = x[1]
|
||||||
|
|
||||||
let xx = semExprWithType(c, x, {}, expectedElementType)
|
let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
|
||||||
result.add xx
|
result.add xx
|
||||||
typ = commonType(c, typ, xx.typ)
|
typ = commonType(c, typ, xx.typ)
|
||||||
#n[i] = semExprWithType(c, x, {})
|
#n[i] = semExprWithType(c, x, {})
|
||||||
|
|
@ -1806,7 +1827,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||||
renderTree(a, {renderNoComments}))
|
renderTree(a, {renderNoComments}))
|
||||||
else:
|
else:
|
||||||
let lhs = n[0]
|
let lhs = n[0]
|
||||||
let rhs = semExprWithType(c, n[1], {}, le)
|
let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le)
|
||||||
if lhs.kind == nkSym and lhs.sym.kind == skResult:
|
if lhs.kind == nkSym and lhs.sym.kind == skResult:
|
||||||
n.typ = c.enforceVoidContext
|
n.typ = c.enforceVoidContext
|
||||||
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
|
if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
|
||||||
|
|
@ -2502,8 +2523,8 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
if isRange(n[i]):
|
if isRange(n[i]):
|
||||||
checkSonsLen(n[i], 3, c.config)
|
checkSonsLen(n[i], 3, c.config)
|
||||||
n[i][1] = semExprWithType(c, n[i][1], {}, expectedElementType)
|
n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
|
||||||
n[i][2] = semExprWithType(c, n[i][2], {}, expectedElementType)
|
n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
|
||||||
if typ == nil:
|
if typ == nil:
|
||||||
typ = skipTypes(n[i][1].typ,
|
typ = skipTypes(n[i][1].typ,
|
||||||
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
{tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||||
|
|
@ -2518,7 +2539,7 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
|
||||||
if expectedElementType == nil:
|
if expectedElementType == nil:
|
||||||
expectedElementType = typ
|
expectedElementType = typ
|
||||||
else:
|
else:
|
||||||
n[i] = semExprWithType(c, n[i], {}, expectedElementType)
|
n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
|
||||||
if typ == nil:
|
if typ == nil:
|
||||||
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||||
if expectedElementType == nil:
|
if expectedElementType == nil:
|
||||||
|
|
|
||||||
|
|
@ -585,22 +585,6 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
|
||||||
pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas)
|
pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
proc msgSymChoiceUseQualifier(c: PContext; n: PNode; note = errGenerated) =
|
|
||||||
assert n.kind in nkSymChoices
|
|
||||||
var err =
|
|
||||||
if note == hintAmbiguousEnum:
|
|
||||||
"ambiguous enum field '$1' assumed to be of type $2, this will become an error in the future" % [$n[0], typeToString(n[0].typ)]
|
|
||||||
else:
|
|
||||||
"ambiguous identifier: '" & $n[0] & "'"
|
|
||||||
var i = 0
|
|
||||||
for child in n:
|
|
||||||
let candidate = child.sym
|
|
||||||
if i == 0: err.add " -- use one of the following:\n"
|
|
||||||
else: err.add "\n"
|
|
||||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
|
||||||
inc i
|
|
||||||
message(c.config, n.info, note, err)
|
|
||||||
|
|
||||||
template isLocalVarSym(n: PNode): bool =
|
template isLocalVarSym(n: PNode): bool =
|
||||||
n.kind == nkSym and
|
n.kind == nkSym and
|
||||||
(n.sym.kind in {skVar, skLet} and not
|
(n.sym.kind in {skVar, skLet} and not
|
||||||
|
|
@ -645,11 +629,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
|
|
||||||
var def: PNode = c.graph.emptyNode
|
var def: PNode = c.graph.emptyNode
|
||||||
if a[^1].kind != nkEmpty:
|
if a[^1].kind != nkEmpty:
|
||||||
def = semExprWithType(c, a[^1], {}, typ)
|
def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
|
||||||
|
|
||||||
if def.kind in nkSymChoices and def[0].sym.kind == skEnumField:
|
if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
|
||||||
msgSymChoiceUseQualifier(c, def, errGenerated)
|
|
||||||
elif def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
|
|
||||||
typFlags.incl taIsTemplateOrMacro
|
typFlags.incl taIsTemplateOrMacro
|
||||||
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
||||||
typFlags.incl taProcContextIsNotMacro
|
typFlags.incl taProcContextIsNotMacro
|
||||||
|
|
@ -799,7 +781,7 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||||
var typFlags: TTypeAllowedFlags
|
var typFlags: TTypeAllowedFlags
|
||||||
|
|
||||||
# don't evaluate here since the type compatibility check below may add a converter
|
# don't evaluate here since the type compatibility check below may add a converter
|
||||||
var def = semExprWithType(c, a[^1], {}, typ)
|
var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
|
||||||
|
|
||||||
if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
|
if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
|
||||||
typFlags.incl taIsTemplateOrMacro
|
typFlags.incl taIsTemplateOrMacro
|
||||||
|
|
|
||||||
|
|
@ -1365,6 +1365,23 @@ ambiguous, a static error will be produced.
|
||||||
p value2
|
p value2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In some cases, ambiguity of enums is resolved depending on the relation
|
||||||
|
between the current scope and the scope the enums were defined in.
|
||||||
|
|
||||||
|
```nim
|
||||||
|
# a.nim
|
||||||
|
type Foo* = enum abc
|
||||||
|
|
||||||
|
# b.nim
|
||||||
|
import a
|
||||||
|
type Bar = enum abc
|
||||||
|
echo abc is Bar # true
|
||||||
|
|
||||||
|
block:
|
||||||
|
type Baz = enum abc
|
||||||
|
echo abc is Baz # true
|
||||||
|
```
|
||||||
|
|
||||||
To implement bit fields with enums see [Bit fields].
|
To implement bit fields with enums see [Bit fields].
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
19
tests/ambsym/tambprocvar.nim
Normal file
19
tests/ambsym/tambprocvar.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
discard """
|
||||||
|
action: reject
|
||||||
|
cmd: "nim check $file"
|
||||||
|
nimout: '''
|
||||||
|
tambprocvar.nim(15, 11) Error: ambiguous identifier 'foo' -- use one of the following:
|
||||||
|
tambprocvar.foo: proc (x: int){.noSideEffect, gcsafe.}
|
||||||
|
tambprocvar.foo: proc (x: float){.noSideEffect, gcsafe.}
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
block:
|
||||||
|
proc foo(x: int) = discard
|
||||||
|
proc foo(x: float) = discard
|
||||||
|
|
||||||
|
let x = foo
|
||||||
|
|
||||||
|
block:
|
||||||
|
let x = `+` #[tt.Error
|
||||||
|
^ ambiguous identifier '+' -- use one of the following:]#
|
||||||
Loading…
Add table
Add a link
Reference in a new issue