summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semstmts.nim3
-rw-r--r--compiler/semtypes.nim24
-rw-r--r--tests/casestmt/tcaseexpr1.nim2
-rw-r--r--tests/casestmt/tincompletecaseobject.nim115
4 files changed, 142 insertions, 2 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 5e9c88f2b..5b7556b2e 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -873,6 +873,9 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
   if chckCovered:
     if covered == toCover(c, n.sons[0].typ):
       hasElse = true
+    elif n.sons[0].typ.kind == tyEnum:
+      localError(c.config, n.info, "not all cases are covered; missing: {$1}" %
+                 formatMissingEnums(n))
     else:
       localError(c.config, n.info, "not all cases are covered")
   closeScope(c)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 744746323..d62bc664e 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -608,6 +608,24 @@ proc toCover(c: PContext, t: PType): BiggestInt =
 
 proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
                       father: PNode, rectype: PType, hasCaseFields = false)
+
+proc formatMissingEnums(n: PNode): string =
+  var coveredCases = initIntSet()
+  for i in 1 ..< n.len:
+    let ofBranch = n[i]
+    for j in 0 ..< ofBranch.len - 1:
+      let child = ofBranch[j]
+      if child.kind == nkIntLit:
+        coveredCases.incl(child.intVal.int)
+      elif child.kind == nkRange:
+        for k in child[0].intVal.int .. child[1].intVal.int:
+          coveredCases.incl k
+  for child in n[0].typ.n.sons:
+    if child.sym.position notin coveredCases:
+      if result.len > 0:
+        result.add ", "
+      result.add child.sym.name.s
+
 proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
                    father: PNode, rectype: PType) =
   var a = copyNode(n)
@@ -644,7 +662,11 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
     delSon(b, sonsLen(b) - 1)
     semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true)
   if chckCovered and covered != toCover(c, a.sons[0].typ):
-    localError(c.config, a.info, "not all cases are covered")
+    if a.sons[0].typ.kind == tyEnum:
+      localError(c.config, a.info, "not all cases are covered; missing: {$1}" %
+        formatMissingEnums(a))
+    else:
+      localError(c.config, a.info, "not all cases are covered")
   addSon(father, a)
 
 proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
diff --git a/tests/casestmt/tcaseexpr1.nim b/tests/casestmt/tcaseexpr1.nim
index 4fdac8191..3d4b6c087 100644
--- a/tests/casestmt/tcaseexpr1.nim
+++ b/tests/casestmt/tcaseexpr1.nim
@@ -3,7 +3,7 @@ discard """
   line: 33
   file: "tcaseexpr1.nim"
 
-  errormsg: "not all cases are covered"
+  errormsg: "not all cases are covered; missing: {C}"
   line: 27
   file: "tcaseexpr1.nim"
 """
diff --git a/tests/casestmt/tincompletecaseobject.nim b/tests/casestmt/tincompletecaseobject.nim
new file mode 100644
index 000000000..909ee4e1c
--- /dev/null
+++ b/tests/casestmt/tincompletecaseobject.nim
@@ -0,0 +1,115 @@
+discard """
+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.
+
+type
+  NimNodeKind* = enum
+    nnkNone, nnkEmpty, nnkIdent, nnkSym,
+    nnkType, nnkCharLit, nnkIntLit, nnkInt8Lit,
+    nnkInt16Lit, nnkInt32Lit, nnkInt64Lit, nnkUIntLit, nnkUInt8Lit,
+    nnkUInt16Lit, nnkUInt32Lit, nnkUInt64Lit, nnkFloatLit,
+    nnkFloat32Lit, nnkFloat64Lit, nnkFloat128Lit, nnkStrLit, nnkRStrLit,
+    nnkTripleStrLit, nnkNilLit, nnkComesFrom, nnkDotCall,
+    nnkCommand, nnkCall, nnkCallStrLit, nnkInfix,
+    nnkPrefix, nnkPostfix, nnkHiddenCallConv,
+    nnkExprEqExpr,
+    nnkExprColonExpr, nnkIdentDefs, nnkVarTuple,
+    nnkPar, nnkObjConstr, nnkCurly, nnkCurlyExpr,
+    nnkBracket, nnkBracketExpr, nnkPragmaExpr, nnkRange,
+    nnkDotExpr, nnkCheckedFieldExpr, nnkDerefExpr, nnkIfExpr,
+    nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkAccQuoted,
+    nnkTableConstr, nnkBind,
+    nnkClosedSymChoice,
+    nnkOpenSymChoice,
+    nnkHiddenStdConv,
+    nnkHiddenSubConv, nnkConv, nnkCast, nnkStaticExpr,
+    nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv,
+    nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange,
+    nnkStringToCString, nnkCStringToString, nnkAsgn,
+    nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit,
+    nnkImportAs, nnkProcDef, nnkMethodDef, nnkConverterDef,
+    nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch,
+    nnkElifBranch, nnkExceptBranch, nnkElse,
+    nnkAsmStmt, nnkPragma, nnkPragmaBlock, nnkIfStmt, nnkWhenStmt,
+    nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkCaseStmt,
+    nnkTypeSection, nnkVarSection, nnkLetSection, nnkConstSection,
+    nnkConstDef, nnkTypeDef,
+    nnkYieldStmt, nnkDefer, nnkTryStmt, nnkFinally, nnkRaiseStmt,
+    nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt,
+    nnkDiscardStmt, nnkStmtList,
+    nnkImportStmt = 1337, # make a hole just for fun
+    nnkImportExceptStmt,
+    nnkExportStmt,
+    nnkExportExceptStmt,
+    nnkFromStmt,
+    nnkIncludeStmt,
+    nnkBindStmt, nnkMixinStmt, nnkUsingStmt,
+    nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr,
+    nnkStmtListType, nnkBlockType,
+    nnkWith, nnkWithout,
+    nnkTypeOfExpr, nnkObjectTy,
+    nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
+    nnkRecList, nnkRecCase, nnkRecWhen,
+    nnkRefTy, nnkPtrTy, nnkVarTy,
+    nnkConstTy, nnkMutableTy,
+    nnkDistinctTy,
+    nnkProcTy,
+    nnkIteratorTy,         # iterator type
+    nnkSharedTy,           # 'shared T'
+    nnkEnumTy,
+    nnkEnumFieldDef,
+    nnkArglist, nnkPattern
+    nnkReturnToken,
+    nnkClosure,
+    nnkGotoState,
+    nnkState,
+    nnkBreakState,
+    nnkFuncDef,
+    nnkTupleConstr
+
+const
+  nnkLiterals* = {nnkCharLit..nnkNilLit}
+
+  nnkSomething* = {nnkStmtList, nnkStmtListExpr, nnkDiscardStmt, nnkVarSection, nnkLetSection,
+       nnkConstSection, nnkPar, nnkAccQuoted, nnkAsgn, nnkDefer, nnkCurly, nnkBracket,
+       nnkStaticStmt, nnkTableConstr, nnkExprColonExpr, nnkInfix, nnkPrefix,
+       nnkRaiseStmt, nnkYieldStmt, nnkBracketExpr, nnkDotExpr, nnkCast, nnkBlockStmt,
+       nnkExprEqExpr}
+
+type
+  MyFictionalType = object
+    a: int
+    case n: NimNodeKind
+    of nnkLiterals, nnkCommentStmt, nnkNone, nnkEmpty, nnkIdent, nnkSym,
+       nnkType, nnkBindStmt, nnkMixinStmt, nnkTypeSection, nnkPragmaBlock,
+       nnkPragmaExpr, nnkPragma, nnkBreakStmt, nnkCallStrLit, nnkPostfix,
+       nnkOpenSymChoice:
+      b: int
+    of nnkCall, nnkCommand:
+      c: int
+    of nnkReturnStmt:
+      d: int
+    of nnkForStmt, nnkParForStmt, nnkWhileStmt, nnkProcDef, nnkMethodDef:
+      e: int
+    of nnkSomething, nnkRefTy, nnkPtrTy, nnkHiddenStdConv:
+      f: int
+    of nnkObjConstr:
+      g: int
+    of nnkIfStmt, nnkIfExpr, nnkWhenStmt:
+      # if when and case statements are branching statements. So a
+      # single function call is allowed to be in all of the braches and
+      # the entire expression can still be considered as a forwarding
+      # template.
+      h: int
+    of nnkCaseStmt:
+      i: int
+    of nnkTryStmt:
+      j: int
+    of nnkIdentDefs:
+      k: int
+    of nnkConstDef:
+      l: int