For really cluttered files like 
[https://github.com/StefanSalewski/gintro/blob/master/tests/gen.nim](https://github.com/StefanSalewski/gintro/blob/master/tests/gen.nim)
 it is not easy to find code segments which are never executed. In the past I 
added 'echo' or 'assert false' statements when I assumed that branches are 
never executed. Yesterday I tried to create a macro for this:
    
    
    # module activeLines
    import macros, algorithm, strformat
    
    type
      SortObj = object
        line, cnt: int
    
    var
      lineCT {.compileTime.}: seq[int]
      line: seq[int]
      ec = newSeq[int](1024 * 8) # we may use a hash table?
    
    macro excnt*(): untyped =
      let ll = lineinfoObj(result).line
      lineCT.add(ll) # add the entry when macro is instantiated
      result = quote do:
        while line.len < `lineCT`.len: # copy CT seq entries to runtime seq
          line.add(`lineCT`[line.len])
        let (file, line, col) = instantiationInfo() # works at runtime, but is 
not really needed
        assert line == `ll`
        if ec.len <= line:
          ec.setLen(line + 1)
        ec[line] += 1
    
    proc excntResult* =
      var s = newSeq[SortObj](line.len)
      for i in 0 .. s.high:
        s[i].line = line[i]
        s[i].cnt = ec[line[i]]
      s.sort(proc (x, y: SortObj): int = cmp((x.cnt, x.line), (y.cnt, y.line)))
      echo "line : activity"
      template print = echo fmt"{s[i].line:>5}", ": ", s[i].cnt
      if s.len < 11:
        for i in 0 .. s.high:
          print
      else:
        for i in 0 .. 4:
          print
        echo ".."
        for i in s.high - 4 .. s.high:
          print
    
    when isMainModule:
      
      proc main =
        excnt()
        var sum: int
        excnt()
        for i in 0 .. 9:
          excnt()
          sum += i
          excnt()
          if sum < 0:
            sum = 0; excnt()
          if sum > 50:
            sum = 50; excnt()
          if sum mod 7 == 7:
            sum = 13; excnt()
        if 1 > 2:
          excnt()
        var a, b: int
        if a > -1:
          excnt()
        if a + b > 0: # to get output for this last always false statement we 
have to add one additional excnt()
          excnt()
          echo "a + b"
        excnt() # additional dummy one
      
      main()
      excntResult()
    
    
    
    
    Run
    
    
    $ nim c --gc:arc -r activeLines/activeLines.nim
    
    line : activity
       54: 0
       56: 0
       58: 0
       60: 0
       65: 0
    ..
       48: 1
       63: 1
       67: 1
       50: 10
       52: 10
    
    
    Run

Seems to be not that bad. Can we improve it? The copying of the compile time 
seq into the runtime seq line.add(`lineCT`[line.len]) is a bit strange, but I 
have found no other way. Also a bit ugly is that to see the last never executed 
line we have to add on more executed macro call.

Reply via email to