added first version of a nimfind tool for the poor souls that don't have a good nimsuggest integretation

This commit is contained in:
Andreas Rumpf
2018-11-14 16:04:45 +01:00
parent b65c0c336c
commit 6e8ed8c6fa
11 changed files with 326 additions and 52 deletions

View File

@@ -63,6 +63,9 @@ type
cacheCounters*: Table[string, BiggestInt]
cacheTables*: Table[string, BTree[string, PNode]]
passes*: seq[TPass]
onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
TPassContext* = object of RootObj # the pass's context
PPassContext* = ref TPassContext
@@ -78,6 +81,32 @@ type
proc hash*(x: FileIndex): Hash {.borrow.}
when defined(nimfind):
template onUse*(info: TLineInfo; s: PSym) =
when compiles(c.c.graph):
if c.c.graph.onUsage != nil: c.c.graph.onUsage(c.c.graph, s, info)
else:
if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info)
template onDef*(info: TLineInfo; s: PSym) =
when compiles(c.c.graph):
if c.c.graph.onDefinition != nil: c.c.graph.onDefinition(c.c.graph, s, info)
else:
if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info)
template onDefResolveForward*(info: TLineInfo; s: PSym) =
when compiles(c.c.graph):
if c.c.graph.onDefinitionResolveForward != nil:
c.c.graph.onDefinitionResolveForward(c.c.graph, s, info)
else:
if c.graph.onDefinitionResolveForward != nil:
c.graph.onDefinitionResolveForward(c.graph, s, info)
else:
template onUse*(info: TLineInfo; s: PSym) = discard
template onDef*(info: TLineInfo; s: PSym) = discard
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
proc stopCompile*(g: ModuleGraph): bool {.inline.} =
result = g.doStopCompile != nil and g.doStopCompile()

View File

@@ -18,7 +18,7 @@ import
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, pluginsupport, plugins/active, rod, lineinfos
from modulegraphs import ModuleGraph, PPassContext
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
when defined(nimfix):
import nimfix/prettybase
@@ -450,7 +450,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
markUsed(c.config, n.info, sym, c.graph.usageSym)
styleCheckUse(n.info, sym)
onUse(n.info, sym)
if sym == c.p.owner:
globalError(c.config, n.info, "recursive dependency: '$1'" % sym.name.s)

View File

@@ -122,7 +122,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
if op == nil:
op = liftBody(c.c, t, c.kind, c.info)
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
onUse(c.info, op)
body.add newAsgnCall(c.c, op, x, y)
result = true
@@ -132,7 +132,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
let op = t.destructor
if op != nil:
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
onUse(c.info, op)
body.add destructorCall(c.c, op, x)
result = true
of attachedAsgn:
@@ -143,7 +143,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
let op = t.deepCopy
if op != nil:
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
onUse(c.info, op)
body.add newDeepCopyCall(op, x, y)
result = true

View File

@@ -454,7 +454,7 @@ proc semResolvedCall(c: PContext, x: TCandidate,
assert x.state == csMatch
var finalCallee = x.calleeSym
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
styleCheckUse(n.sons[0].info, finalCallee)
onUse(n.sons[0].info, finalCallee)
assert finalCallee.ast != nil
if x.hasFauxMatch:
result = x.call
@@ -562,7 +562,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
result = newSymNode(newInst, n.info)
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =

View File

@@ -25,7 +25,7 @@ const
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode =
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
pushInfoContext(c.config, n.info, s.detailedInfo)
result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
@@ -265,7 +265,7 @@ proc semConv(c: PContext, n: PNode): PNode =
let status = checkConvertible(c, result.typ, it.typ)
if status in {convOK, convNotNeedeed}:
markUsed(c.config, n.info, it.sym, c.graph.usageSym)
styleCheckUse(n.info, it.sym)
onUse(n.info, it.sym)
markIndirect(c, it.sym)
return it
errorUseQualifier(c, n.info, op.sons[0].sym)
@@ -1036,7 +1036,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
case s.kind
of skConst:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind
of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyTuple, tySet, tyUInt..tyUInt64:
@@ -1061,7 +1061,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
(n.kind notin nkCallKinds and s.requiredParams > 0):
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
result = symChoice(c, n, s, scClosed)
else:
result = semMacroExpr(c, n, n, s, flags)
@@ -1070,13 +1070,13 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
(n.kind notin nkCallKinds and s.requiredParams > 0) or
sfCustomPragma in sym.flags:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
result = symChoice(c, n, s, scClosed)
else:
result = semTemplateExpr(c, n, s, flags)
of skParam:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ...
return s.typ.n
@@ -1098,14 +1098,14 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
localError(c.config, n.info, "illegal context for 'nimvm' magic")
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
result = newSymNode(s, n.info)
# We cannot check for access to outer vars for example because it's still
# not sure the symbol really ends up being used:
# var len = 0 # but won't be called
# genericThatUsesLen(x) # marked as taking a closure?
of skGenericParam:
styleCheckUse(n.info, s)
onUse(n.info, s)
if s.typ.kind == tyStatic:
result = newSymNode(s, n.info)
result.typ = s.typ
@@ -1116,7 +1116,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
return n
of skType:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
return s.typ.n
result = newSymNode(s, n.info)
@@ -1138,7 +1138,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
# is the access to a public field or in the same module or in a friend?
doAssert f == s
markUsed(c.config, n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f)
onUse(n.info, f)
result = newNodeIT(nkDotExpr, n.info, f.typ)
result.add makeDeref(newSymNode(p.selfSym))
result.add newSymNode(f) # we now have the correct field
@@ -1151,11 +1151,11 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
ty = skipTypes(ty.sons[0], skipPtrs)
# old code, not sure if it's live code:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
result = newSymNode(s, n.info)
else:
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
onUse(n.info, s)
result = newSymNode(s, n.info)
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
@@ -1178,7 +1178,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
else:
markUsed(c.config, n.sons[1].info, s, c.graph.usageSym)
result = semSym(c, n, s, flags)
styleCheckUse(n.sons[1].info, s)
onUse(n.sons[1].info, s)
return
n.sons[0] = semExprWithType(c, n.sons[0], flags+{efDetermineType})
@@ -1241,7 +1241,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.info = n.info
result.typ = ty
markUsed(c.config, n.info, f, c.graph.usageSym)
styleCheckUse(n.info, f)
onUse(n.info, f)
return
of tyObject, tyTuple:
if ty.n != nil and ty.n.kind == nkRecList:
@@ -1272,7 +1272,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
if fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend?
markUsed(c.config, n.sons[1].info, f, c.graph.usageSym)
styleCheckUse(n.sons[1].info, f)
onUse(n.sons[1].info, f)
n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f) # we now have the correct field
n.typ = f.typ
@@ -1286,7 +1286,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
f = getSymFromList(ty.n, i)
if f != nil:
markUsed(c.config, n.sons[1].info, f, c.graph.usageSym)
styleCheckUse(n.sons[1].info, f)
onUse(n.sons[1].info, f)
n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f)
n.typ = f.typ
@@ -1758,7 +1758,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
markUsed(c.config, n.info, expandedSym, c.graph.usageSym)
styleCheckUse(n.info, expandedSym)
onUse(n.info, expandedSym)
if isCallExpr(macroCall):
for i in countup(1, macroCall.len-1):
@@ -1783,7 +1783,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
let info = macroCall.sons[0].info
macroCall.sons[0] = newSymNode(cand, info)
markUsed(c.config, info, cand, c.graph.usageSym)
styleCheckUse(info, cand)
onUse(info, cand)
# we just perform overloading resolution here:
#n.sons[1] = semOverloadedCall(c, macroCall, macroCall, {skTemplate, skMacro})
@@ -2265,6 +2265,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode =
n.sons[0] = newSymNode(labl, n.sons[0].info)
suggestSym(c.config, n.sons[0].info, labl, c.graph.usageSym)
styleCheckDef(c.config, labl)
onDef(n[0].info, labl)
n.sons[1] = semExpr(c, n.sons[1], flags)
n.typ = n.sons[1].typ
if isEmptyType(n.typ): n.kind = nkBlockStmt

View File

@@ -76,14 +76,14 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result = symChoice(c, n, s, scOpen)
of skTemplate:
if macroToExpandSym(s):
styleCheckUse(n.info, s)
onUse(n.info, s)
result = semTemplateExpr(c, n, s, {efNoSemCheck})
result = semGenericStmt(c, result, {}, ctx)
else:
result = symChoice(c, n, s, scOpen)
of skMacro:
if macroToExpandSym(s):
styleCheckUse(n.info, s)
onUse(n.info, s)
result = semMacroExpr(c, n, n, s, {efNoSemCheck})
result = semGenericStmt(c, result, {}, ctx)
else:
@@ -96,20 +96,20 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result = n
else:
result = newSymNodeTypeDesc(s, n.info)
styleCheckUse(n.info, s)
onUse(n.info, s)
of skParam:
result = n
styleCheckUse(n.info, s)
onUse(n.info, s)
of skType:
if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
result = newSymNodeTypeDesc(s, n.info)
else:
result = n
styleCheckUse(n.info, s)
onUse(n.info, s)
else:
result = newSymNode(s, n.info)
styleCheckUse(n.info, s)
onUse(n.info, s)
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var GenericCtx): PNode =
@@ -172,6 +172,7 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
let s = newSymS(skUnknown, getIdentNode(c, n), c)
addPrelimDecl(c, s)
styleCheckDef(c.config, n.info, s, kind)
onDef(n.info, s)
proc semGenericStmt(c: PContext, n: PNode,
flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
@@ -230,7 +231,7 @@ proc semGenericStmt(c: PContext, n: PNode,
case s.kind
of skMacro:
if macroToExpand(s) and sc.safeLen <= 1:
styleCheckUse(fn.info, s)
onUse(fn.info, s)
result = semMacroExpr(c, n, n, s, {efNoSemCheck})
result = semGenericStmt(c, result, flags, ctx)
else:
@@ -239,7 +240,7 @@ proc semGenericStmt(c: PContext, n: PNode,
mixinContext = true
of skTemplate:
if macroToExpand(s) and sc.safeLen <= 1:
styleCheckUse(fn.info, s)
onUse(fn.info, s)
result = semTemplateExpr(c, n, s, {efNoSemCheck})
result = semGenericStmt(c, result, flags, ctx)
else:
@@ -262,17 +263,17 @@ proc semGenericStmt(c: PContext, n: PNode,
inc first
of skGenericParam:
result.sons[0] = newSymNodeTypeDesc(s, fn.info)
styleCheckUse(fn.info, s)
onUse(fn.info, s)
first = 1
of skType:
# bad hack for generics:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result.sons[0] = newSymNodeTypeDesc(s, fn.info)
styleCheckUse(fn.info, s)
onUse(fn.info, s)
first = 1
else:
result.sons[0] = newSymNode(s, fn.info)
styleCheckUse(fn.info, s)
onUse(fn.info, s)
first = 1
elif fn.kind == nkDotExpr:
result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)

View File

@@ -65,7 +65,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
incl(s.flags, sfUsed)
n.sons[0] = x
suggestSym(c.config, x.info, s, c.graph.usageSym)
styleCheckUse(x.info, s)
onUse(x.info, s)
else:
localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
else:
@@ -363,6 +363,7 @@ proc semUsing(c: PContext; n: PNode): PNode =
for j in countup(0, length-3):
let v = semIdentDef(c, a.sons[j], skParam)
styleCheckDef(c.config, v)
onDef(a[j].info, v)
v.typ = typ
strTableIncl(c.signatures, v)
else:
@@ -494,6 +495,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
continue
var v = semIdentDef(c, a.sons[j], symkind)
styleCheckDef(c.config, v)
onDef(a[j].info, v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addInterfaceDecl(c, v)
when oKeepVariableNames:
@@ -548,6 +550,7 @@ proc semConst(c: PContext, n: PNode): PNode =
checkSonsLen(a, 3, c.config)
var v = semIdentDef(c, a.sons[0], skConst)
styleCheckDef(c.config, v)
onDef(a[0].info, v)
var typ: PType = nil
if a.sons[1].kind != nkEmpty: typ = semTypeNode(c, a.sons[1], nil)
@@ -591,6 +594,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
let m = if n.kind == nkPragmaExpr: n.sons[0] else: n
result = newSymG(skForVar, m, c)
styleCheckDef(c.config, result)
onDef(n.info, result)
if n.kind == nkPragmaExpr:
pragma(c, result, n.sons[1], forVarPragmas)
@@ -696,7 +700,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
if r.state == csMatch:
var match = r.calleeSym
markUsed(c.config, n[0].info, match, c.graph.usageSym)
styleCheckUse(n[0].info, match)
onUse(n[0].info, match)
# but pass 'n' to the 'match' macro, not 'n[0]':
r.call.sons[1] = n
@@ -880,6 +884,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
if typsym.isNil:
s = semIdentDef(c, name[1], skType)
styleCheckDef(c.config, s)
onDef(name[1].info, s)
s.typ = newTypeS(tyObject, c)
s.typ.sym = s
s.flags.incl sfForward
@@ -894,6 +899,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
else:
s = semIdentDef(c, name, skType)
styleCheckDef(c.config, s)
onDef(name.info, s)
s.typ = newTypeS(tyForward, c)
s.typ.sym = s # process pragmas:
if name.kind == nkPragmaExpr:
@@ -1625,6 +1631,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
else:
implicitPragmas(c, s, n, validPragmas)
styleCheckDef(c.config, s)
onDef(n[namePos].info, s)
else:
if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, s, n.sons[pragmasPos], validPragmas)
@@ -1638,6 +1645,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX %
("'" & proto.name.s & "' from " & c.config$proto.info))
styleCheckDef(c.config, s)
onDefResolveForward(n[namePos].info, proto)
if sfForward notin proto.flags and proto.magic == mNone:
wrongRedefinition(c, n.info, proto.name.s, proto.info)
excl(proto.flags, sfForward)

View File

@@ -160,7 +160,7 @@ proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
if s.owner == c.owner and s.kind == skParam:
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
styleCheckUse(n.info, s)
onUse(n.info, s)
else:
for i in 0 ..< n.safeLen:
result.sons[i] = onlyReplaceParams(c, n.sons[i])
@@ -208,19 +208,20 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
# So we need only check the *current* scope.
let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident))
if s != nil and s.owner == c.owner and sfGenSym in s.flags:
styleCheckUse(n.info, s)
onUse(n.info, s)
replaceIdentBySym(c.c, n, newSymNode(s, n.info))
elif not (n.kind == nkSym and sfGenSym in n.sym.flags):
let local = newGenSym(k, ident, c)
addPrelimDecl(c.c, local)
styleCheckDef(c.c.config, n.info, local)
onDef(n.info, local)
replaceIdentBySym(c.c, n, newSymNode(local, n.info))
else:
replaceIdentBySym(c.c, n, ident)
proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
incl(s.flags, sfUsed)
# we do not call styleCheckUse here, as the identifier is not really
# we do not call onUse here, as the identifier is not really
# resolved here. We will fixup the used identifiers later.
case s.kind
of skUnknown:
@@ -245,7 +246,7 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags):
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
styleCheckUse(n.info, s)
onUse(n.info, s)
else:
for i in countup(0, safeLen(n) - 1):
result.sons[i] = semRoutineInTemplName(c, n.sons[i])
@@ -261,6 +262,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
s.ast = n
addPrelimDecl(c.c, s)
styleCheckDef(c.c.config, n.info, s)
onDef(n.info, s)
n.sons[namePos] = newSymNode(s, n.sons[namePos].info)
else:
n.sons[namePos] = ident
@@ -314,7 +316,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
if s.owner == c.owner and s.kind == skParam:
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
styleCheckUse(n.info, s)
onUse(n.info, s)
elif contains(c.toBind, s.id):
result = symChoice(c.c, n, s, scClosed)
elif contains(c.toMixin, s.name.id):
@@ -324,7 +326,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
# var yz: T
incl(s.flags, sfUsed)
result = newSymNode(s, n.info)
styleCheckUse(n.info, s)
onUse(n.info, s)
else:
result = semTemplSymbol(c.c, n, s)
of nkBind:
@@ -382,6 +384,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
let s = newGenSym(skLabel, n.sons[0], c)
addPrelimDecl(c.c, s)
styleCheckDef(c.c.config, s)
onDef(n[0].info, s)
n.sons[0] = newSymNode(s, n.sons[0].info)
n.sons[1] = semTemplBody(c, n.sons[1])
closeScope(c)
@@ -505,7 +508,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
if s.owner == c.owner and s.kind == skParam and
n.kind == nkAccQuoted and n.len == 1:
incl(s.flags, sfUsed)
styleCheckUse(n.info, s)
onUse(n.info, s)
return newSymNode(s, n.info)
elif contains(c.toBind, s.id):
return symChoice(c.c, n, s, scClosed)
@@ -553,6 +556,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else:
s = semIdentVis(c, skTemplate, n.sons[0], {})
styleCheckDef(c.config, s)
onDef(n[0].info, s)
# check parameter list:
#s.scope = c.currentScope
pushOwner(c, s)
@@ -635,7 +639,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
# semtypes.addParamOrResult). Within the pattern we have to ensure
# to use the param with the proper type though:
incl(s.flags, sfUsed)
styleCheckUse(n.info, s)
onUse(n.info, s)
let x = c.owner.typ.n.sons[s.position+1].sym
assert x.name == s.name
result = newSymNode(x, n.info)

View File

@@ -122,6 +122,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
if not isPure: strTableAdd(c.module.tab, e)
addSon(result.n, newSymNode(e))
styleCheckDef(c.config, e)
onDef(e.info, e)
if sfGenSym notin e.flags:
if not isPure: addDecl(c, e)
else: importPureEnumField(c, e)
@@ -377,7 +378,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
if result != nil:
markUsed(c.config, n.info, result, c.graph.usageSym)
styleCheckUse(n.info, result)
onUse(n.info, result)
if result.kind == skParam and result.typ.kind == tyTypeDesc:
# This is a typedesc param. is it already bound?
@@ -466,6 +467,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
addSon(result.n, newSymNode(field))
addSonSkipIntLit(result, typ)
styleCheckDef(c.config, a.sons[j].info, field)
onDef(field.info, field)
if result.n.len == 0: result.n = nil
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
@@ -688,7 +690,6 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
else: rectype.sym
for i in countup(0, sonsLen(n)-3):
var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported})
styleCheckDef(c.config, n.sons[i].info, f)
suggestSym(c.config, n.sons[i].info, f, c.graph.usageSym)
f.typ = typ
f.position = pos
@@ -703,6 +704,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
if a.kind == nkEmpty: addSon(father, newSymNode(f))
else: addSon(a, newSymNode(f))
styleCheckDef(c.config, f)
onDef(f.info, f)
if a.kind != nkEmpty: addSon(father, a)
of nkSym:
# This branch only valid during generic object
@@ -988,7 +990,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyGenericParam:
markUsed(c.config, info, paramType.sym, c.graph.usageSym)
styleCheckUse(info, paramType.sym)
onUse(info, paramType.sym)
if tfWildcard in paramType.flags:
paramType.flags.excl tfWildcard
paramType.sym.kind = skType
@@ -1110,6 +1112,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
rawAddSon(result, finalType)
addParamOrResult(c, arg, kind)
styleCheckDef(c.config, a.sons[j].info, arg)
onDef(a[j].info, arg)
var r: PType
if n.sons[0].kind != nkEmpty:
@@ -1637,7 +1640,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
assignType(prev, t)
result = prev
markUsed(c.config, n.info, n.sym, c.graph.usageSym)
styleCheckUse(n.info, n.sym)
onUse(n.info, n.sym)
else:
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)

View File

@@ -13,7 +13,7 @@
import
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
linter, lineinfos
linter, lineinfos, modulegraphs
when (defined(booting) or defined(nimsuggest)) and not defined(leanCompiler):
import docgen
@@ -2149,7 +2149,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
else:
# only one valid interpretation found:
markUsed(m.c.config, arg.info, arg.sons[best].sym, m.c.graph.usageSym)
styleCheckUse(arg.info, arg.sons[best].sym)
onUse(arg.info, arg.sons[best].sym)
result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
argOrig)
when false:

228
tools/nimfind.nim Normal file
View File

@@ -0,0 +1,228 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Nimfind is a tool that helps to give editors IDE like capabilities.
when not defined(nimcore):
{.error: "nimcore MUST be defined for Nim's core tooling".}
when not defined(nimfind):
{.error: "nimfind MUST be defined for Nim's nimfind tool".}
const Usage = """
Nimfind - Tool to find declarations or usages for Nim symbols
Usage:
nimfind [options] file.nim:line:col
Options:
--help, -h show this help
--rebuild rebuild the index
--project:file.nim use file.nim as the entry point
In addition, all command line options of Nim that do not affect code generation
are supported.
"""
import strutils, os, parseopt, parseutils
import "../compiler" / [options, commands, modules, sem,
passes, passaux, msgs, nimconf,
extccomp, condsyms,
ast, scriptconfig,
idents, modulegraphs, vm, prefixmatches, lineinfos, cmdlinehelper,
pathutils]
import db_sqlite
proc createDb(db: DbConn) =
db.exec(sql"""
create table if not exists filenames(
id integer primary key,
fullpath varchar(8000) not null
);
""")
db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
# every sym can have potentially 2 different definitions due to forward
# declarations.
db.exec(sql"""
create table if not exists syms(
id integer primary key,
nimid integer not null,
name varchar(256) not null,
defline integer not null,
defcol integer not null,
deffile integer not null,
deflineB integer not null default 0,
defcolB integer not null default 0,
deffileB integer not null default 0,
foreign key (deffile) references filenames(id),
foreign key (deffileB) references filenames(id)
);
""")
db.exec(sql"""
create table if not exists usages(
id integer primary key,
nimid integer not null,
line integer not null,
col integer not null,
file integer not null,
foreign key (file) references filenames(id),
foreign key (nimid) references syms(nimid)
);
""")
proc toDbFileId*(db: DbConn; conf: ConfigRef; fileIdx: FileIndex): int =
if fileIdx == FileIndex(-1): return -1
let fullpath = toFullPath(conf, fileIdx)
let row = db.getRow(sql"select id from filenames where fullpath = ?", fullpath)
let id = row[0]
if id.len == 0:
result = int db.insertID(sql"insert into filenames(fullpath) values (?)",
fullpath)
else:
result = parseInt(id)
type
FinderRef = ref object of RootObj
db: DbConn
proc writeDef(graph: ModuleGraph; s: PSym; info: TLineInfo) =
let f = FinderRef(graph.backend)
f.db.exec(sql"""insert into syms(nimid, name, defline, defcol, deffile) values (?, ?, ?, ?, ?)""",
s.id, s.name.s, info.line, info.col,
toDbFileId(f.db, graph.config, info.fileIndex))
proc writeDefResolveForward(graph: ModuleGraph; s: PSym; info: TLineInfo) =
let f = FinderRef(graph.backend)
f.db.exec(sql"""update syms set deflineB = ?, defcolB = ?, deffileB = ?
where nimid = ?""", info.line, info.col,
toDbFileId(f.db, graph.config, info.fileIndex), s.id)
proc writeUsage(graph: ModuleGraph; s: PSym; info: TLineInfo) =
let f = FinderRef(graph.backend)
f.db.exec(sql"""insert into usages(nimid, line, col, file) values (?, ?, ?, ?)""",
s.id, info.line, info.col,
toDbFileId(f.db, graph.config, info.fileIndex))
proc performSearch(conf: ConfigRef; dbfile: AbsoluteFile) =
var db = open(connection=string dbfile, user="nim", password="",
database="nim")
let pos = conf.m.trackPos
let fid = toDbFileId(db, conf, pos.fileIndex)
var row = db.getRow(sql"""select max(col) from usages where line = ? and file = ? and ? >= col""",
pos.line, fid, pos.col)
if row.len > 0:
let known = toFullPath(conf, pos.fileIndex)
let nimid = db.getRow(sql"""select nimid from usages where line = ? and file = ? and col = ?""",
pos.line, fid, row[0])
for r in db.rows(sql"""select line, col, filenames.fullpath from usages
inner join filenames on filenames.id = file
where nimid = ?""", nimid):
let line = parseInt(r[0])
let col = parseInt(r[1])
let file = r[2]
if file == known and line == pos.line.int:
discard "don't output the line we already know"
else:
echo file, ":", line, ":", col+1
close(db)
proc setupDb(g: ModuleGraph; dbfile: AbsoluteFile) =
var f = FinderRef()
removeFile(dbfile)
f.db = open(connection=string dbfile, user="nim", password="",
database="nim")
createDb(f.db)
f.db.exec(sql"pragma journal_mode=off")
# This MUST be turned off, otherwise it's way too slow even for testing purposes:
f.db.exec(sql"pragma SYNCHRONOUS=off")
f.db.exec(sql"pragma LOCKING_MODE=exclusive")
g.backend = f
proc mainCommand(graph: ModuleGraph) =
let conf = graph.config
let dbfile = getNimcacheDir(conf) / RelativeFile"nimfind.db"
if not fileExists(dbfile) or optForceFullMake in conf.globalOptions:
clearPasses(graph)
registerPass graph, verbosePass
registerPass graph, semPass
conf.cmd = cmdIdeTools
wantMainModule(conf)
setupDb(graph, dbfile)
graph.onDefinition = writeUsage # writeDef
graph.onDefinitionResolveForward = writeUsage # writeDefResolveForward
graph.onUsage = writeUsage
if not fileExists(conf.projectFull):
quit "cannot find file: " & conf.projectFull.string
add(conf.searchPaths, conf.libpath)
# do not stop after the first error:
conf.errorMax = high(int)
compileProject(graph)
close(FinderRef(graph.backend).db)
performSearch(conf, dbfile)
proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
var p = parseopt.initOptParser(cmd)
while true:
parseopt.next(p)
case p.kind
of cmdEnd: break
of cmdLongoption, cmdShortOption:
case p.key.normalize
of "help", "h":
stdout.writeline(Usage)
quit()
of "project":
conf.projectName = p.val
of "rebuild":
incl conf.globalOptions, optForceFullMake
else: processSwitch(pass, p, conf)
of cmdArgument:
let info = p.key.split(':')
if info.len == 3:
let (dir, file, ext) = info[0].splitFile()
conf.projectName = findProjectNimFile(conf, dir)
if conf.projectName.len == 0: conf.projectName = info[0]
try:
conf.m.trackPos = newLineInfo(conf, AbsoluteFile info[0],
parseInt(info[1]), parseInt(info[2]))
except ValueError:
quit "invalid command line"
else:
quit "invalid command line"
proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
let self = NimProg(
suggestMode: true,
processCmdLine: processCmdLine,
mainCommand: mainCommand
)
self.initDefinesProg(conf, "nimfind")
if paramCount() == 0:
stdout.writeline(Usage)
return
self.processCmdLineAndProjectPath(conf)
# Find Nim's prefix dir.
let binaryPath = findExe("nim")
if binaryPath == "":
raise newException(IOError,
"Cannot find Nim standard library: Nim compiler not in PATH")
conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir()
if not dirExists(conf.prefixDir / RelativeDir"lib"):
conf.prefixDir = AbsoluteDir""
discard self.loadConfigsAndRunMainCommand(cache, conf)
handleCmdline(newIdentCache(), newConfigRef())