Skip to content

Commit

Permalink
new {.allowMissingCases.} pragma to allow growing enums
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Oct 20, 2020
1 parent f20e485 commit 266b354
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 11 deletions.
1 change: 1 addition & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ type
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
sfSingleUsedTemp # For temporaries that we know will only be used once
sfNoalias # 'noalias' annotation, means C's 'restrict'
sfAllowMissingCases # issue warnMissingCases instead of error

TSymFlags* = set[TSymFlag]

Expand Down
4 changes: 3 additions & 1 deletion compiler/lineinfos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type
warnInconsistentSpacing, warnCaseTransition, warnCycleCreated,
warnObservableStores,
warnUser,
warnMissingCases,
hintSuccess, hintSuccessX, hintCC,
hintLineTooLong, hintXDeclaredButNotUsed,
hintXCannotRaiseY,
Expand Down Expand Up @@ -126,6 +127,7 @@ const
warnCycleCreated: "$1",
warnObservableStores: "observable stores to '$1'",
warnUser: "$1",
warnMissingCases: "$1",
hintSuccess: "operation successful: $#",
# keep in sync with `testament.isSuccess`
hintSuccessX: "${loc} lines; ${sec}s; $mem; $build build; proj: $project; out: $output",
Expand Down Expand Up @@ -177,7 +179,7 @@ const
"IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "CaseTransition", "CycleCreated",
"ObservableStores", "User"]
"ObservableStores", "User", "MissingCases"]

HintsToStr* = [
"Success", "SuccessX", "CC", "LineTooLong",
Expand Down
8 changes: 7 additions & 1 deletion compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
wBorrow, wGcSafe, wPartial, wExplain, wPackage, wAllowMissingCases}
fieldPragmas* = declPragmas + {
wGuard, wBitsize, wCursor, wRequiresInit, wNoalias} - {wExportNims, wNodecl} # why exclude these?
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
Expand Down Expand Up @@ -875,6 +875,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
if sym != nil:
if k == wPure and sym.kind in routineKinds: invalidPragma(c, it)
else: incl(sym.flags, sfPure)
of wAllowMissingCases:
# let s = expectStrLit(c, it)
noVal(c, it) # TODO: allow allowMissingCases:false
if sym != nil:
incl(sym.flags, sfAllowMissingCases)
else: localError(c.config, it.info, "expected a `sym`")
of wVolatile:
noVal(c, it)
incl(sym.flags, sfVolatile)
Expand Down
15 changes: 11 additions & 4 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -991,11 +991,18 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
if chckCovered:
if covered == toCover(c, n[0].typ):
hasElse = true
elif n[0].typ.skipTypes(abstractRange).kind in {tyEnum, tyChar}:
localError(c.config, n.info, "not all cases are covered; missing: $1" %
formatMissingEnums(c, n))
else:
localError(c.config, n.info, "not all cases are covered")
let typ = n[0].typ.skipTypes(abstractRange)
template msg: untyped = "not all cases are covered; missing: $1" % formatMissingEnums(c, n)
template bail = localError(c.config, n.info, msg())
case typ.kind
of tyChar: bail()
of tyEnum:
if sfAllowMissingCases in typ.sym.flags:
message(c.config, n.info, warnMissingCases, msg())
else: bail()
else:
localError(c.config, n.info, "not all cases are covered")
popCaseContext(c)
closeScope(c)
if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
Expand Down
10 changes: 7 additions & 3 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,13 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
delSon(b, b.len - 1)
semRecordNodeAux(c, lastSon(n[i]), check, pos, b, rectype, hasCaseFields = true)
if chckCovered and covered != toCover(c, a[0].typ):
if a[0].typ.skipTypes(abstractRange).kind == tyEnum:
localError(c.config, a.info, "not all cases are covered; missing: $1" %
formatMissingEnums(c, a))
let typ = a[0].typ.skipTypes(abstractRange)
if typ.kind == tyEnum:
template msg: untyped = "not all cases are covered; missing: $1" % formatMissingEnums(c, a)
if sfAllowMissingCases in typ.sym.flags:
message(c.config, a.info, warnMissingCases, msg())
else:
localError(c.config, a.info, msg())
else:
localError(c.config, a.info, "not all cases are covered")
father.add a
Expand Down
4 changes: 2 additions & 2 deletions compiler/wordrecg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ type
wStdIn, wStdOut, wStdErr,

wInOut, wByCopy, wByRef, wOneWay,
wBitsize
wBitsize, wAllowMissingCases

TSpecialWords* = set[TSpecialWord]

Expand Down Expand Up @@ -175,7 +175,7 @@ const
"stdin", "stdout", "stderr",

"inout", "bycopy", "byref", "oneway",
"bitsize"
"bitsize", "allowMissingCases"
]

proc findStr*(a: openArray[string], s: string): int =
Expand Down
23 changes: 23 additions & 0 deletions tests/casestmt/tincompletecaseobject.nim
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
discard """
nimout: '''
tincompletecaseobject.nim(19, 3) Warning: not all cases are covered; missing: {f2} [MissingCases]
tincompletecaseobject.nim(24, 5) Warning: not all cases are covered; missing: {f2} [MissingCases]
'''
errormsg: '''
not all cases are covered; missing: {nnkComesFrom, nnkDotCall, nnkHiddenCallConv, nnkVarTuple, nnkCurlyExpr, nnkRange, nnkCheckedFieldExpr, nnkDerefExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkBind, nnkClosedSymChoice, nnkHiddenSubConv, nnkConv, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkImportAs, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkAsmStmt, nnkTypeDef, nnkFinally, nnkContinueStmt, nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkUsingStmt, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy, nnkEnumFieldDef, nnkArglist, nnkPattern, nnkReturnToken, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr}
'''
"""

# this isn't imported from macros.nim to make it robust against possible changes in the ast.

block:
type Foo {.allowMissingCases.} = enum
f0
f1
f2
var f: Foo
case f
of f0: discard
of f1: discard

type Bar = object
case k: Foo
of f0:
v0: string
of f1:
v1: string

var b: Bar

type
NimNodeKind* = enum
nnkNone, nnkEmpty, nnkIdent, nnkSym,
Expand Down

0 comments on commit 266b354

Please sign in to comment.