mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 14:00:35 +00:00
less verbose type mismatch messages (#21191)
* less verbose type mismatch messages * Update compiler/types.nim * fixes i386 * fixes i386
This commit is contained in:
@@ -135,6 +135,8 @@
|
||||
|
||||
- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
||||
|
||||
- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
@@ -231,6 +231,7 @@ type
|
||||
## are not anymore.
|
||||
laxEffects
|
||||
## Lax effects system prior to Nim 2.0.
|
||||
verboseTypeMismatch
|
||||
|
||||
SymbolFilesOption* = enum
|
||||
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
## This module implements semantic checking for calls.
|
||||
# included from sem.nim
|
||||
|
||||
from algorithm import sort
|
||||
from std/algorithm import sort
|
||||
|
||||
|
||||
proc sameMethodDispatcher(a, b: PSym): bool =
|
||||
result = false
|
||||
@@ -192,7 +193,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
# argument in order to remove plenty of candidates. This is
|
||||
# comparable to what C# does and C# is doing fine.
|
||||
var filterOnlyFirst = false
|
||||
if optShowAllMismatches notin c.config.globalOptions:
|
||||
if optShowAllMismatches notin c.config.globalOptions and verboseTypeMismatch in c.config.legacyFeatures:
|
||||
for err in errors:
|
||||
if err.firstMismatch.arg > 1:
|
||||
filterOnlyFirst = true
|
||||
@@ -208,6 +209,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
if filterOnlyFirst and err.firstMismatch.arg == 1:
|
||||
inc skipped
|
||||
continue
|
||||
|
||||
if verboseTypeMismatch notin c.config.legacyFeatures:
|
||||
candidates.add "[" & $err.firstMismatch.arg & "] "
|
||||
|
||||
if err.sym.kind in routineKinds and err.sym.ast != nil:
|
||||
candidates.add(renderTree(err.sym.ast,
|
||||
{renderNoBody, renderNoComments, renderNoPragmas}))
|
||||
@@ -217,7 +222,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
candidates.add("\n")
|
||||
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
|
||||
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
|
||||
if n.len > 1:
|
||||
if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures:
|
||||
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||
case err.firstMismatch.kind
|
||||
@@ -274,11 +279,28 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||
const
|
||||
errTypeMismatch = "type mismatch: got <"
|
||||
errButExpected = "but expected one of:"
|
||||
errExpectedPosition = "Expected one of (first mismatch at position [#]):"
|
||||
errUndeclaredField = "undeclared field: '$1'"
|
||||
errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
|
||||
errBadRoutine = "attempting to call routine: '$1'$2"
|
||||
errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3"
|
||||
|
||||
proc describeParamList(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
|
||||
result = "Expression: " & $n
|
||||
for i in startIdx..<n.len:
|
||||
result.add "\n [" & $i & "] " & renderTree(n[i]) & ": "
|
||||
result.add describeArg(c, n, i, startIdx, prefer)
|
||||
result.add "\n"
|
||||
|
||||
template legacynotFoundError(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
|
||||
var result = errTypeMismatch
|
||||
result.add(describeArgs(c, n, 1, prefer))
|
||||
result.add('>')
|
||||
if candidates != "":
|
||||
result.add("\n" & errButExpected & "\n" & candidates)
|
||||
localError(c.config, n.info, result & "\nexpression: " & $n)
|
||||
|
||||
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||
# Gives a detailed error message; this is separated from semOverloadedCall,
|
||||
# as semOverloadedCall is already pretty slow (and we need this information
|
||||
@@ -306,13 +328,15 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||
localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
|
||||
return
|
||||
|
||||
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
|
||||
var result = errTypeMismatch
|
||||
result.add(describeArgs(c, n, 1, prefer))
|
||||
result.add('>')
|
||||
if candidates != "":
|
||||
result.add("\n" & errButExpected & "\n" & candidates)
|
||||
localError(c.config, n.info, result & "\nexpression: " & $n)
|
||||
if verboseTypeMismatch in c.config.legacyFeatures:
|
||||
legacynotFoundError(c, n, errors)
|
||||
else:
|
||||
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
|
||||
var result = "type mismatch\n"
|
||||
result.add describeParamList(c, n, 1, prefer)
|
||||
if candidates != "":
|
||||
result.add("\n" & errExpectedPosition & "\n" & candidates)
|
||||
localError(c.config, n.info, result)
|
||||
|
||||
proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||
var errors: CandidateErrors = @[]
|
||||
|
||||
@@ -322,26 +322,32 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
|
||||
else:
|
||||
result = arg.typ.typeToString(prefer)
|
||||
|
||||
template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName) =
|
||||
var arg = n[i]
|
||||
if n[i].kind == nkExprEqExpr:
|
||||
result.add renderTree(n[i][0])
|
||||
result.add ": "
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
|
||||
# XXX we really need to 'tryExpr' here!
|
||||
arg = c.semOperand(c, n[i][1])
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
else:
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
|
||||
nkOfBranch, nkElifBranch,
|
||||
nkExceptBranch}:
|
||||
arg = c.semOperand(c, n[i])
|
||||
n[i] = arg
|
||||
if arg.typ != nil and arg.typ.kind == tyError: return
|
||||
result.add argTypeToString(arg, prefer)
|
||||
|
||||
proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string =
|
||||
describeArgImpl(c, n, i, startIdx, prefer)
|
||||
|
||||
proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
|
||||
result = ""
|
||||
for i in startIdx..<n.len:
|
||||
var arg = n[i]
|
||||
if n[i].kind == nkExprEqExpr:
|
||||
result.add renderTree(n[i][0])
|
||||
result.add ": "
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
|
||||
# XXX we really need to 'tryExpr' here!
|
||||
arg = c.semOperand(c, n[i][1])
|
||||
n[i].typ = arg.typ
|
||||
n[i][1] = arg
|
||||
else:
|
||||
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
|
||||
nkOfBranch, nkElifBranch,
|
||||
nkExceptBranch}:
|
||||
arg = c.semOperand(c, n[i])
|
||||
n[i] = arg
|
||||
if arg.typ != nil and arg.typ.kind == tyError: return
|
||||
result.add argTypeToString(arg, prefer)
|
||||
describeArgImpl(c, n, i, startIdx, prefer)
|
||||
if i != n.len - 1: result.add ", "
|
||||
|
||||
proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
|
||||
|
||||
@@ -44,3 +44,4 @@ when defined(windows):
|
||||
switch("tlsEmulation", "off")
|
||||
|
||||
switch("warningAserror", "UnnamedBreak")
|
||||
switch("legacy", "verboseTypeMismatch")
|
||||
|
||||
23
tests/errmsgs/tconcisetypemismatch.nim
Normal file
23
tests/errmsgs/tconcisetypemismatch.nim
Normal file
@@ -0,0 +1,23 @@
|
||||
discard """
|
||||
cmd: "nim c --hints:off --skipParentCfg $file"
|
||||
errormsg: "type mismatch"
|
||||
nimout: '''
|
||||
tconcisetypemismatch.nim(23, 47) Error: type mismatch
|
||||
Expression: int(inNanoseconds(t2 - t1)) / 100.5
|
||||
[1] int(inNanoseconds(t2 - t1)): int
|
||||
[2] 100.5: float64
|
||||
|
||||
Expected one of (first mismatch at position [#]):
|
||||
[1] proc `/`(x, y: float): float
|
||||
[1] proc `/`(x, y: float32): float32
|
||||
[2] proc `/`(x, y: int): float
|
||||
'''
|
||||
"""
|
||||
|
||||
import std/monotimes
|
||||
from times import inNanoseconds
|
||||
|
||||
let t1 = getMonotime()
|
||||
let result = 1 + 2
|
||||
let t2 = getMonotime()
|
||||
echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5
|
||||
21
tests/errmsgs/tconcisetypemismatch.nims
Normal file
21
tests/errmsgs/tconcisetypemismatch.nims
Normal file
@@ -0,0 +1,21 @@
|
||||
switch("path", "$lib/../testament/lib")
|
||||
# so we can `import stdtest/foo` inside tests
|
||||
# Using $lib/../ instead of $nim/ so you can use a different nim to run tests
|
||||
# during local testing, e.g. nim --lib:lib.
|
||||
|
||||
## prevent common user config settings to interfere with testament expectations
|
||||
## Indifidual tests can override this if needed to test for these options.
|
||||
switch("colors", "off")
|
||||
|
||||
switch("excessiveStackTrace", "off")
|
||||
|
||||
when (NimMajor, NimMinor, NimPatch) >= (1,5,1):
|
||||
# to make it easier to test against older nim versions, (best effort only)
|
||||
switch("filenames", "legacyRelProj")
|
||||
switch("spellSuggest", "0")
|
||||
|
||||
# for std/unittest
|
||||
switch("define", "nimUnittestOutputLevel:PRINT_FAILURES")
|
||||
switch("define", "nimUnittestColor:off")
|
||||
|
||||
hint("Processing", off)
|
||||
Reference in New Issue
Block a user