add compile time check to detect duplicate abbr and duplicate name
This commit is contained in:
parent
ab4ba1cbfd
commit
0cd09d75c8
|
@ -659,6 +659,31 @@ proc generateFieldSetters(RecordType: NimNode): NimNode =
|
|||
result.add settersArray
|
||||
debugMacroResult "Field Setters"
|
||||
|
||||
proc checkDuplicate(cmd: CmdInfo, opt: OptInfo, fieldName: NimNode) =
|
||||
for x in cmd.opts:
|
||||
if opt.name == x.name:
|
||||
error "duplicate name detected: " & opt.name, fieldName
|
||||
if opt.abbr.len > 0 and opt.abbr == x.abbr:
|
||||
error "duplicate abbr detected: " & opt.abbr, fieldName
|
||||
|
||||
proc validPath(path: var seq[CmdInfo], parent, node: CmdInfo): bool =
|
||||
for x in parent.opts:
|
||||
if x.kind != Discriminator: continue
|
||||
for y in x.subCmds:
|
||||
if y == node:
|
||||
path.add y
|
||||
return true
|
||||
if validPath(path, y, node):
|
||||
path.add y
|
||||
return true
|
||||
false
|
||||
|
||||
proc findPath(parent, node: CmdInfo): seq[CmdInfo] =
|
||||
# find valid path from parent to node
|
||||
result = newSeq[CmdInfo]()
|
||||
doAssert validPath(result, parent, node)
|
||||
result.add parent
|
||||
|
||||
proc cmdInfoFromType(T: NimNode): CmdInfo =
|
||||
result = CmdInfo()
|
||||
|
||||
|
@ -741,9 +766,14 @@ proc cmdInfoFromType(T: NimNode): CmdInfo =
|
|||
if branchEnumVal.kind == nnkDotExpr:
|
||||
branchEnumVal = branchEnumVal[1]
|
||||
var cmd = findCmd(discriminator.subCmds, $branchEnumVal)
|
||||
# we respect subcommand hierarchy when looking for duplicate
|
||||
let path = findPath(result, cmd)
|
||||
for n in path:
|
||||
checkDuplicate(n, opt, field.name)
|
||||
cmd.opts.add opt
|
||||
|
||||
else:
|
||||
checkDuplicate(result, opt, field.name)
|
||||
result.opts.add opt
|
||||
|
||||
macro configurationRtti(RecordType: type): untyped =
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
mode = ScriptMode.Verbose
|
||||
|
||||
packageName = "confutils"
|
||||
|
@ -13,3 +14,18 @@ requires "nim >= 1.0.0",
|
|||
task test, "Run all tests":
|
||||
exec "nim c -r --threads:off -d:release tests/test_all"
|
||||
exec "nim c -r --threads:on -d:release tests/test_all"
|
||||
|
||||
exec "nim c --threads:off -d:release tests/test_duplicates"
|
||||
exec "nim c --threads:on -d:release tests/test_duplicates"
|
||||
|
||||
#Also iterate over every test in tests/fail, and verify they fail to compile.
|
||||
echo "\r\nTest Fail to Compile:"
|
||||
for path in listFiles(thisDir() / "tests" / "fail"):
|
||||
if path.split(".")[^1] != "nim":
|
||||
continue
|
||||
|
||||
if gorgeEx("nim c " & path).exitCode != 0:
|
||||
echo " [OK] ", path.split(DirSep)[^1]
|
||||
else:
|
||||
echo " [FAILED] ", path.split(DirSep)[^1]
|
||||
exec "exit 1"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import
|
||||
../../confutils,
|
||||
../../confutils/defs
|
||||
|
||||
type
|
||||
TestConf* = object
|
||||
dataDir* {.abbr: "d" }: OutDir
|
||||
importDir* {.abbr: "d" }: OutDir
|
||||
|
||||
let c = TestConf.load()
|
|
@ -0,0 +1,19 @@
|
|||
import
|
||||
../../confutils,
|
||||
../../confutils/defs
|
||||
|
||||
type
|
||||
Command = enum
|
||||
noCommand
|
||||
|
||||
TestConf* = object
|
||||
dataDir* {.abbr: "d" }: OutDir
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: Command
|
||||
|
||||
of noCommand:
|
||||
importDir* {.abbr: "d" }: OutDir
|
||||
|
||||
let c = TestConf.load()
|
|
@ -0,0 +1,20 @@
|
|||
import
|
||||
../../confutils,
|
||||
../../confutils/defs
|
||||
|
||||
type
|
||||
Command = enum
|
||||
noCommand
|
||||
|
||||
TestConf* = object
|
||||
dataDir* {.abbr: "d" }: OutDir
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: Command
|
||||
|
||||
of noCommand:
|
||||
importDir* {.abbr: "i" }: OutDir
|
||||
importKey* {.abbr: "i" }: OutDir
|
||||
|
||||
let c = TestConf.load()
|
|
@ -0,0 +1,10 @@
|
|||
import
|
||||
../../confutils,
|
||||
../../confutils/defs
|
||||
|
||||
type
|
||||
TestConf* = object
|
||||
dataDir* {.name: "data-dir" }: OutDir
|
||||
importDir* {.name: "data-dir" }: OutDir
|
||||
|
||||
let c = TestConf.load()
|
|
@ -0,0 +1,19 @@
|
|||
import
|
||||
../../confutils,
|
||||
../../confutils/defs
|
||||
|
||||
type
|
||||
Command = enum
|
||||
noCommand
|
||||
|
||||
TestConf* = object
|
||||
dataDir* {.name: "data-dir" }: OutDir
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: Command
|
||||
|
||||
of noCommand:
|
||||
importDir* {.name: "data-dir" }: OutDir
|
||||
|
||||
let c = TestConf.load()
|
|
@ -0,0 +1,20 @@
|
|||
import
|
||||
../../confutils,
|
||||
../../confutils/defs
|
||||
|
||||
type
|
||||
Command = enum
|
||||
noCommand
|
||||
|
||||
TestConf* = object
|
||||
dataDir* {.name: "data-dir" }: OutDir
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: Command
|
||||
|
||||
of noCommand:
|
||||
importDir* {.name: "import-dir" }: OutDir
|
||||
importKey* {.name: "import-dir" }: OutDir
|
||||
|
||||
let c = TestConf.load()
|
|
@ -0,0 +1,60 @@
|
|||
import
|
||||
../confutils,
|
||||
../confutils/defs
|
||||
|
||||
# duplicate name and abbr from different subcommand
|
||||
# at the same level is allowed
|
||||
|
||||
# but hierarchical duplicate is not allowed
|
||||
|
||||
type
|
||||
Command = enum
|
||||
noCommand
|
||||
subCommand
|
||||
|
||||
BranchCmd = enum
|
||||
branchA
|
||||
branchB
|
||||
|
||||
TestConf* = object
|
||||
dataDir* {.abbr: "d" }: OutDir
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: Command
|
||||
|
||||
of noCommand:
|
||||
importDir* {.
|
||||
abbr: "i"
|
||||
name: "import"
|
||||
}: OutDir
|
||||
|
||||
outputDir* {.
|
||||
abbr: "o"
|
||||
name: "output"
|
||||
}: OutDir
|
||||
|
||||
of subCommand:
|
||||
importKey* {.
|
||||
abbr: "i"
|
||||
name: "import"
|
||||
}: OutDir
|
||||
|
||||
case subcmd* {.
|
||||
command
|
||||
defaultValue: branchA }: BranchCmd
|
||||
|
||||
of branchA:
|
||||
outputFolder* {.
|
||||
abbr: "o"
|
||||
name: "output"
|
||||
}: OutDir
|
||||
|
||||
of branchB:
|
||||
importFolder* {.
|
||||
abbr: "f"
|
||||
name: "import-folder"
|
||||
}: OutDir
|
||||
|
||||
let c = TestConf.load()
|
||||
discard c
|
Loading…
Reference in New Issue