less verbose type mismatch messages (#21191)

* less verbose type mismatch messages

* Update compiler/types.nim

* fixes i386

* fixes i386
This commit is contained in:
ringabout
2022-12-29 03:54:15 +08:00
committed by GitHub
parent 646932b3f3
commit 761c5a0830
7 changed files with 105 additions and 27 deletions

View File

@@ -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:"

View File

@@ -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

View File

@@ -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 = @[]

View File

@@ -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 =

View File

@@ -44,3 +44,4 @@ when defined(windows):
switch("tlsEmulation", "off")
switch("warningAserror", "UnnamedBreak")
switch("legacy", "verboseTypeMismatch")

View 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

View 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)