* fix #19580; add warning for bare except: clause * fixes some easy ones * Update doc/manual.md * fixes docs * Update changelog.md * addition * Apply suggestions from code review Co-authored-by: Jacek Sieka <arnetheduck@gmail.com> * Update doc/tut2.md Co-authored-by: Jacek Sieka <arnetheduck@gmail.com>
This commit is contained in:
parent
9a50033d5b
commit
91ce8c385d
14 changed files with 50 additions and 26 deletions
|
|
@ -133,6 +133,8 @@
|
||||||
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
||||||
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
||||||
|
|
||||||
|
- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered.
|
||||||
|
|
||||||
## Standard library additions and changes
|
## Standard library additions and changes
|
||||||
|
|
||||||
[//]: # "Changes:"
|
[//]: # "Changes:"
|
||||||
|
|
|
||||||
|
|
@ -151,3 +151,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
defineSymbol("nimHasWarnUnnamedBreak")
|
defineSymbol("nimHasWarnUnnamedBreak")
|
||||||
defineSymbol("nimHasGenericDefine")
|
defineSymbol("nimHasGenericDefine")
|
||||||
defineSymbol("nimHasDefineAliases")
|
defineSymbol("nimHasDefineAliases")
|
||||||
|
defineSymbol("nimHasWarnBareExcept")
|
||||||
|
|
|
||||||
|
|
@ -1746,7 +1746,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
|
||||||
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
|
let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
|
||||||
try:
|
try:
|
||||||
writeFile(filename, content)
|
writeFile(filename, content)
|
||||||
except:
|
except IOError:
|
||||||
rawMessage(conf, errCannotOpenFile, filename.string)
|
rawMessage(conf, errCannotOpenFile, filename.string)
|
||||||
|
|
||||||
proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
||||||
|
|
@ -1768,7 +1768,7 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
||||||
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
|
let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
|
||||||
try:
|
try:
|
||||||
writeFile(filename, content)
|
writeFile(filename, content)
|
||||||
except:
|
except IOError:
|
||||||
rawMessage(conf, errCannotOpenFile, filename.string)
|
rawMessage(conf, errCannotOpenFile, filename.string)
|
||||||
|
|
||||||
proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
|
proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
|
||||||
|
|
@ -1789,7 +1789,7 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
writeFile(filename, code)
|
writeFile(filename, code)
|
||||||
except:
|
except IOError:
|
||||||
rawMessage(conf, errCannotOpenFile, filename.string)
|
rawMessage(conf, errCannotOpenFile, filename.string)
|
||||||
|
|
||||||
proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
|
proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
|
||||||
|
|
@ -1803,5 +1803,5 @@ proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile
|
||||||
|
|
||||||
try:
|
try:
|
||||||
writeFile(filename, $body)
|
writeFile(filename, $body)
|
||||||
except:
|
except IOError:
|
||||||
rawMessage(conf, errCannotOpenFile, filename.string)
|
rawMessage(conf, errCannotOpenFile, filename.string)
|
||||||
|
|
|
||||||
|
|
@ -528,7 +528,7 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool =
|
||||||
var exe = getConfigVar(conf, conf.cCompiler, ".exe")
|
var exe = getConfigVar(conf, conf.cCompiler, ".exe")
|
||||||
if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
|
if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
|
||||||
# NOTE: should we need the full version, use -dumpfullversion
|
# NOTE: should we need the full version, use -dumpfullversion
|
||||||
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except: ("", 1)
|
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
|
||||||
if exitCode == 0:
|
if exitCode == 0:
|
||||||
var major: int
|
var major: int
|
||||||
discard parseInt(s, major)
|
discard parseInt(s, major)
|
||||||
|
|
@ -1018,7 +1018,7 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
|
||||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||||
var bcache: BuildCache
|
var bcache: BuildCache
|
||||||
try: bcache.fromJson(jsonFile.string.parseFile)
|
try: bcache.fromJson(jsonFile.string.parseFile)
|
||||||
except:
|
except ValueError, KeyError, JsonKindError:
|
||||||
let e = getCurrentException()
|
let e = getCurrentException()
|
||||||
conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
|
conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
|
||||||
[e.msg, e.getStackTrace(), jsonFile.string]
|
[e.msg, e.getStackTrace(), jsonFile.string]
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ type
|
||||||
warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
|
warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
|
||||||
warnUnnamedBreak = "UnnamedBreak",
|
warnUnnamedBreak = "UnnamedBreak",
|
||||||
warnStmtListLambda = "StmtListLambda",
|
warnStmtListLambda = "StmtListLambda",
|
||||||
|
warnBareExcept = "BareExcept",
|
||||||
warnUser = "User",
|
warnUser = "User",
|
||||||
# hints
|
# hints
|
||||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||||
|
|
@ -185,6 +186,7 @@ const
|
||||||
warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
|
warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
|
||||||
warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
|
warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
|
||||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||||
|
warnBareExcept: "$1",
|
||||||
warnUser: "$1",
|
warnUser: "$1",
|
||||||
hintSuccess: "operation successful: $#",
|
hintSuccess: "operation successful: $#",
|
||||||
# keep in sync with `testament.isSuccess`
|
# keep in sync with `testament.isSuccess`
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,7 @@ define:useStdoutAsStdmsg
|
||||||
@if nimHasWarnUnnamedBreak:
|
@if nimHasWarnUnnamedBreak:
|
||||||
warningAserror[UnnamedBreak]:on
|
warningAserror[UnnamedBreak]:on
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@if nimHasWarnBareExcept:
|
||||||
|
warningAserror[BareExcept]:on
|
||||||
|
@end
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||||
isImported = true
|
isImported = true
|
||||||
elif not isException(typ):
|
elif not isException(typ):
|
||||||
localError(c.config, typeNode.info, errExprCannotBeRaised)
|
localError(c.config, typeNode.info, errExprCannotBeRaised)
|
||||||
|
elif not isDefectOrCatchableError(typ):
|
||||||
|
message(c.config, a.info, warnBareExcept, "catch a more precise Exception deriving from CatchableError or Defect.")
|
||||||
|
|
||||||
if containsOrIncl(check, typ.id):
|
if containsOrIncl(check, typ.id):
|
||||||
localError(c.config, typeNode.info, errExceptionAlreadyHandled)
|
localError(c.config, typeNode.info, errExceptionAlreadyHandled)
|
||||||
|
|
@ -251,7 +253,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
|
||||||
elif a.len == 1:
|
elif a.len == 1:
|
||||||
# count number of ``except: body`` blocks
|
# count number of ``except: body`` blocks
|
||||||
inc catchAllExcepts
|
inc catchAllExcepts
|
||||||
|
message(c.config, a.info, warnBareExcept,
|
||||||
|
"The bare except clause is deprecated; use `except CatchableError:` instead")
|
||||||
else:
|
else:
|
||||||
# support ``except KeyError, ValueError, ... : body``
|
# support ``except KeyError, ValueError, ... : body``
|
||||||
if catchAllExcepts > 0:
|
if catchAllExcepts > 0:
|
||||||
|
|
|
||||||
|
|
@ -1721,6 +1721,18 @@ proc isDefectException*(t: PType): bool =
|
||||||
t = skipTypes(t[0], abstractPtrs)
|
t = skipTypes(t[0], abstractPtrs)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
proc isDefectOrCatchableError*(t: PType): bool =
|
||||||
|
var t = t.skipTypes(abstractPtrs)
|
||||||
|
while t.kind == tyObject:
|
||||||
|
if t.sym != nil and t.sym.owner != nil and
|
||||||
|
sfSystemModule in t.sym.owner.flags and
|
||||||
|
(t.sym.name.s == "Defect" or
|
||||||
|
t.sym.name.s == "CatchableError"):
|
||||||
|
return true
|
||||||
|
if t[0] == nil: break
|
||||||
|
t = skipTypes(t[0], abstractPtrs)
|
||||||
|
return false
|
||||||
|
|
||||||
proc isSinkTypeForParam*(t: PType): bool =
|
proc isSinkTypeForParam*(t: PType): bool =
|
||||||
# a parameter like 'seq[owned T]' must not be used only once, but its
|
# a parameter like 'seq[owned T]' must not be used only once, but its
|
||||||
# elements must, so we detect this case here:
|
# elements must, so we detect this case here:
|
||||||
|
|
|
||||||
|
|
@ -4773,8 +4773,8 @@ Example:
|
||||||
echo "overflow!"
|
echo "overflow!"
|
||||||
except ValueError, IOError:
|
except ValueError, IOError:
|
||||||
echo "catch multiple exceptions!"
|
echo "catch multiple exceptions!"
|
||||||
except:
|
except CatchableError:
|
||||||
echo "Unknown exception!"
|
echo "Catchable exception!"
|
||||||
finally:
|
finally:
|
||||||
close(f)
|
close(f)
|
||||||
```
|
```
|
||||||
|
|
@ -4786,9 +4786,6 @@ listed in an `except` clause, the corresponding statements are executed.
|
||||||
The statements following the `except` clauses are called
|
The statements following the `except` clauses are called
|
||||||
`exception handlers`:idx:.
|
`exception handlers`:idx:.
|
||||||
|
|
||||||
The empty `except`:idx: clause is executed if there is an exception that is
|
|
||||||
not listed otherwise. It is similar to an `else` clause in `if` statements.
|
|
||||||
|
|
||||||
If there is a `finally`:idx: clause, it is always executed after the
|
If there is a `finally`:idx: clause, it is always executed after the
|
||||||
exception handlers.
|
exception handlers.
|
||||||
|
|
||||||
|
|
@ -4806,11 +4803,11 @@ Try can also be used as an expression; the type of the `try` branch then
|
||||||
needs to fit the types of `except` branches, but the type of the `finally`
|
needs to fit the types of `except` branches, but the type of the `finally`
|
||||||
branch always has to be `void`:
|
branch always has to be `void`:
|
||||||
|
|
||||||
```nim
|
```nim test
|
||||||
from std/strutils import parseInt
|
from std/strutils import parseInt
|
||||||
|
|
||||||
let x = try: parseInt("133a")
|
let x = try: parseInt("133a")
|
||||||
except: -1
|
except ValueError: -1
|
||||||
finally: echo "hi"
|
finally: echo "hi"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -4818,8 +4815,9 @@ branch always has to be `void`:
|
||||||
To prevent confusing code there is a parsing limitation; if the `try`
|
To prevent confusing code there is a parsing limitation; if the `try`
|
||||||
follows a `(` it has to be written as a one liner:
|
follows a `(` it has to be written as a one liner:
|
||||||
|
|
||||||
```nim
|
```nim test
|
||||||
let x = (try: parseInt("133a") except: -1)
|
from std/strutils import parseInt
|
||||||
|
let x = (try: parseInt("133a") except ValueError: -1)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4867,7 +4865,7 @@ error message from `e`, and for such situations, it is enough to use
|
||||||
```nim
|
```nim
|
||||||
try:
|
try:
|
||||||
# ...
|
# ...
|
||||||
except:
|
except CatchableError:
|
||||||
echo getCurrentExceptionMsg()
|
echo getCurrentExceptionMsg()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -5055,7 +5053,7 @@ An empty `raises` list (`raises: []`) means that no exception may be raised:
|
||||||
try:
|
try:
|
||||||
unsafeCall()
|
unsafeCall()
|
||||||
result = true
|
result = true
|
||||||
except:
|
except CatchableError:
|
||||||
result = false
|
result = false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -393,7 +393,7 @@ The `try` statement handles exceptions:
|
||||||
echo "could not convert string to integer"
|
echo "could not convert string to integer"
|
||||||
except IOError:
|
except IOError:
|
||||||
echo "IO error!"
|
echo "IO error!"
|
||||||
except:
|
except CatchableError:
|
||||||
echo "Unknown exception!"
|
echo "Unknown exception!"
|
||||||
# reraise the unknown exception:
|
# reraise the unknown exception:
|
||||||
raise
|
raise
|
||||||
|
|
@ -425,7 +425,7 @@ module. Example:
|
||||||
```nim
|
```nim
|
||||||
try:
|
try:
|
||||||
doSomethingHere()
|
doSomethingHere()
|
||||||
except:
|
except CatchableError:
|
||||||
let
|
let
|
||||||
e = getCurrentException()
|
e = getCurrentException()
|
||||||
msg = getCurrentExceptionMsg()
|
msg = getCurrentExceptionMsg()
|
||||||
|
|
|
||||||
|
|
@ -1610,7 +1610,7 @@ proc getFootnoteType(label: PRstNode): (FootnoteType, int) =
|
||||||
elif label.len == 1 and label.sons[0].kind == rnLeaf:
|
elif label.len == 1 and label.sons[0].kind == rnLeaf:
|
||||||
try:
|
try:
|
||||||
result = (fnManualNumber, parseInt(label.sons[0].text))
|
result = (fnManualNumber, parseInt(label.sons[0].text))
|
||||||
except:
|
except ValueError:
|
||||||
result = (fnCitation, -1)
|
result = (fnCitation, -1)
|
||||||
else:
|
else:
|
||||||
result = (fnCitation, -1)
|
result = (fnCitation, -1)
|
||||||
|
|
@ -2899,13 +2899,13 @@ proc parseEnumList(p: var RstParser): PRstNode =
|
||||||
let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol
|
let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol
|
||||||
# check that it's in sequence: enumerator == next(prevEnum)
|
# check that it's in sequence: enumerator == next(prevEnum)
|
||||||
if "n" in wildcards[w]: # arabic numeral
|
if "n" in wildcards[w]: # arabic numeral
|
||||||
let prevEnumI = try: parseInt(prevEnum) except: 1
|
let prevEnumI = try: parseInt(prevEnum) except ValueError: 1
|
||||||
if enumerator in autoEnums:
|
if enumerator in autoEnums:
|
||||||
if prevAE != "" and enumerator != prevAE:
|
if prevAE != "" and enumerator != prevAE:
|
||||||
break
|
break
|
||||||
prevAE = enumerator
|
prevAE = enumerator
|
||||||
curEnum = prevEnumI + 1
|
curEnum = prevEnumI + 1
|
||||||
else: curEnum = (try: parseInt(enumerator) except: 1)
|
else: curEnum = (try: parseInt(enumerator) except ValueError: 1)
|
||||||
if curEnum - prevEnumI != 1:
|
if curEnum - prevEnumI != 1:
|
||||||
break
|
break
|
||||||
prevEnum = enumerator
|
prevEnum = enumerator
|
||||||
|
|
|
||||||
|
|
@ -394,10 +394,10 @@ proc entityToRune*(entity: string): Rune =
|
||||||
case entity[1]
|
case entity[1]
|
||||||
of '0'..'9':
|
of '0'..'9':
|
||||||
try: runeValue = parseInt(entity[1..^1])
|
try: runeValue = parseInt(entity[1..^1])
|
||||||
except: discard
|
except ValueError: discard
|
||||||
of 'x', 'X': # not case sensitive here
|
of 'x', 'X': # not case sensitive here
|
||||||
try: runeValue = parseHexInt(entity[2..^1])
|
try: runeValue = parseHexInt(entity[2..^1])
|
||||||
except: discard
|
except ValueError: discard
|
||||||
else: discard # other entities are not defined with prefix `#`
|
else: discard # other entities are not defined with prefix `#`
|
||||||
if runeValue notin 0..0x10FFFF: runeValue = 0 # only return legal values
|
if runeValue notin 0..0x10FFFF: runeValue = 0 # only return legal values
|
||||||
return Rune(runeValue)
|
return Rune(runeValue)
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) =
|
||||||
const begin = "expected raising '" & astToStr(exception) & "', instead"
|
const begin = "expected raising '" & astToStr(exception) & "', instead"
|
||||||
const msgEnd = " by: " & astToStr(code)
|
const msgEnd = " by: " & astToStr(code)
|
||||||
template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd)
|
template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd)
|
||||||
|
{.warning[BareExcept]:off.}
|
||||||
when Exception is exception:
|
when Exception is exception:
|
||||||
try:
|
try:
|
||||||
if true:
|
if true:
|
||||||
|
|
@ -116,5 +117,6 @@ template doAssertRaises*(exception: typedesc, code: untyped) =
|
||||||
mixin `$` # alternatively, we could define $cstring in this module
|
mixin `$` # alternatively, we could define $cstring in this module
|
||||||
raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd)
|
raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd)
|
||||||
except: raisedForeign()
|
except: raisedForeign()
|
||||||
|
{.warning[BareExcept]:on.}
|
||||||
if wrong:
|
if wrong:
|
||||||
raiseAssert(begin & " nothing was raised" & msgEnd)
|
raiseAssert(begin & " nothing was raised" & msgEnd)
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ when true: # issue #12746
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
try:
|
try:
|
||||||
discard
|
discard
|
||||||
except:
|
except CatchableError:
|
||||||
# just the general except will work
|
# just the general except will work
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue