Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new {.allowMissingCases.} pragma to allow growing enums #15646

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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