diff options
-rw-r--r-- | compiler/semstmts.nim | 3 | ||||
-rw-r--r-- | compiler/semtypes.nim | 24 | ||||
-rw-r--r-- | tests/casestmt/tcaseexpr1.nim | 2 | ||||
-rw-r--r-- | tests/casestmt/tincompletecaseobject.nim | 115 |
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 |