Initial dead code finding code
This commit is contained in:
parent
ff48dc3ea6
commit
5c0c39c0ae
4 changed files with 165 additions and 0 deletions
13
mort.nimble
Normal file
13
mort.nimble
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Joey Yakimowich-Payne"
|
||||||
|
description = "A dead code locator for Nim"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.0.0"
|
||||||
118
src/mort.nim
Normal file
118
src/mort.nim
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import macros, tables, strformat, algorithm
|
||||||
|
|
||||||
|
type
|
||||||
|
IndexStack[T] = seq[T]
|
||||||
|
|
||||||
|
template childAt(node: NimNode, indexList: seq[int]): NimNode =
|
||||||
|
var curNode = node
|
||||||
|
for i in indexList:
|
||||||
|
curNode = curNode[i]
|
||||||
|
curNode
|
||||||
|
|
||||||
|
iterator iterNode(node: NimNode): NimNode =
|
||||||
|
## Recursive iterator for NimNodes. There's weird index stuff going on
|
||||||
|
## because deep nodes can't be modified in place for some reason when iterating
|
||||||
|
var
|
||||||
|
stack: seq[IndexStack[int]] = @[newSeq[int]()]
|
||||||
|
curNode = node
|
||||||
|
|
||||||
|
# traverse the node tree inorder
|
||||||
|
while stack.len() > 0:
|
||||||
|
let indexList = stack.pop()
|
||||||
|
var newIndices: IndexStack[int] = indexList
|
||||||
|
|
||||||
|
var n = curNode.childAt(indexList)
|
||||||
|
yield n
|
||||||
|
for i in 0 ..< n.len:
|
||||||
|
var nstack = newIndices
|
||||||
|
nstack.add(i)
|
||||||
|
stack.add(nstack)
|
||||||
|
|
||||||
|
template sortedIter(iter: untyped): seq[(int, int)] =
|
||||||
|
## Iterate over a pairs iterator and return a sorted iterator
|
||||||
|
var arr: seq[(int, int)] = @[]
|
||||||
|
for k, v in iter:
|
||||||
|
arr.add((k, v))
|
||||||
|
sorted(arr, SortOrder.Ascending)
|
||||||
|
|
||||||
|
|
||||||
|
var UsageTable = newTable[string, TableRef[int, int]]()
|
||||||
|
|
||||||
|
proc initUsage(filename: string, line: int) =
|
||||||
|
## This needs to be called at the start of the profiled code
|
||||||
|
## in order to capture empty lines
|
||||||
|
## This is done automatically by the macro
|
||||||
|
var lineTable = UsageTable.mgetOrPut(filename, newTable[int, int]())
|
||||||
|
discard lineTable.hasKeyOrPut(line, 0)
|
||||||
|
|
||||||
|
proc logUsage(filename: string, line: int) =
|
||||||
|
## This needs to be sprinkled in between the code where a path begins
|
||||||
|
## This is done automatically by the macro
|
||||||
|
UsageTable[filename][line] += 1
|
||||||
|
|
||||||
|
######################## User API ################################
|
||||||
|
|
||||||
|
proc printCodeUsage*() =
|
||||||
|
## Prints usage for all code paths
|
||||||
|
for filename, lineTable in UsageTable.pairs:
|
||||||
|
echo fmt"Printing usage in: {filename}"
|
||||||
|
for _, (lineNum, count) in sortedIter(lineTable.pairs()):
|
||||||
|
echo fmt"{lineNum}: {count}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
proc printDeadLines*() =
|
||||||
|
## Prints only the locations where dead lines exist
|
||||||
|
for filename, lineTable in UsageTable.pairs:
|
||||||
|
for _, (lineNum, count) in sortedIter(lineTable.pairs()):
|
||||||
|
if count == 0:
|
||||||
|
echo fmt"Dead code found in {filename}"
|
||||||
|
echo fmt"at line {lineNum}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
macro findDeadCode*(code: untyped): untyped =
|
||||||
|
## This is the main macro. It can be used in the style of a pragma or
|
||||||
|
## as a block. To activate it, use the ``-d:findDeadCode`` compiler flag.
|
||||||
|
##
|
||||||
|
## After sprinkling it around your code, call ``printCodeUsage`` or
|
||||||
|
## ``printDeadLines`` to get output about where dead lines exist in
|
||||||
|
## your code.
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## .. code-block:: nim
|
||||||
|
##
|
||||||
|
## findDeadCode:
|
||||||
|
## # ... Put a bunch of Nim code here
|
||||||
|
## discard
|
||||||
|
##
|
||||||
|
## proc myFancyProc() {.findDeadCode.} =
|
||||||
|
## # ... Put a bunch of Nim code here
|
||||||
|
## discard
|
||||||
|
##
|
||||||
|
result = code
|
||||||
|
|
||||||
|
if not defined(findDeadCode):
|
||||||
|
return
|
||||||
|
|
||||||
|
template logUsageTemplate(lineInfo) =
|
||||||
|
logUsage(lineInfo.filename, lineInfo.line)
|
||||||
|
|
||||||
|
template initUsageTemplate(lineInfo) =
|
||||||
|
initUsage(lineInfo.filename, lineInfo.line)
|
||||||
|
|
||||||
|
var initUsages = newNimNode(nnkStmtList)
|
||||||
|
|
||||||
|
for n in iterNode(result):
|
||||||
|
if n.kind == nnkStmtList:
|
||||||
|
let
|
||||||
|
lineInfo = n.lineInfoObj()
|
||||||
|
callNode = getAst(logUsageTemplate(lineInfo))
|
||||||
|
n.insert(0, callNode)
|
||||||
|
initUsages.add(getAst(initUsageTemplate(lineInfo)))
|
||||||
|
|
||||||
|
if result.kind == nnkStmtList:
|
||||||
|
result.insert(0, initUsages)
|
||||||
|
else:
|
||||||
|
for child in result.children:
|
||||||
|
if child.kind == nnkStmtList:
|
||||||
|
child.insert(0, initUsages)
|
||||||
|
break
|
||||||
1
tests/config.nims
Normal file
1
tests/config.nims
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
switch("path", "$projectDir/../src")
|
||||||
33
tests/testmort.nim
Normal file
33
tests/testmort.nim
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import strutils
|
||||||
|
|
||||||
|
import mort
|
||||||
|
|
||||||
|
proc mysnakeToCamel(s: cstring): string {.findDeadCode.} =
|
||||||
|
var i, j: int
|
||||||
|
result = newString(s.len)
|
||||||
|
if s[i] == '_': inc(i)
|
||||||
|
while true:
|
||||||
|
if s[i] == '_' and (s[i + 1] == '\0' or s[i + 1] == '_'):
|
||||||
|
inc(i)
|
||||||
|
elif (s[i] == '_' or s[i] == '-') and s[i + 1].isLowerAscii and not s[i - 1].isUpperAscii:
|
||||||
|
inc(i)
|
||||||
|
result[j] = toUpperAscii(s[i])
|
||||||
|
inc(i)
|
||||||
|
inc(j)
|
||||||
|
else:
|
||||||
|
result[j] = s[i]
|
||||||
|
inc(i)
|
||||||
|
inc(j)
|
||||||
|
if s[i] == '\0':
|
||||||
|
result.setLen(j)
|
||||||
|
break
|
||||||
|
if result[0] == '\0': # this may result, so we emit a dummy name as marker
|
||||||
|
result = "QQQ"
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
discard mySnakeToCamel("diff_is_good")
|
||||||
|
discard mySnakeToCamel("_diff_is_good")
|
||||||
|
printCodeUsage()
|
||||||
|
printDeadLines()
|
||||||
|
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue