Merge pull request #11766 from nim-lang/araq-detect-unused-imports

[feature] detect unused imports
This commit is contained in:
Andreas Rumpf 2019-07-19 01:06:45 +02:00 • committed by GitHub
commit a5d6080a81
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 66 additions and 35 deletions

View file

@ -167,6 +167,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated") message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
suggestSym(c.config, n.info, result, c.graph.usageSym, false) suggestSym(c.config, n.info, result, c.graph.usageSym, false)
importStmtResult.add newSymNode(result, n.info) importStmtResult.add newSymNode(result, n.info)
c.unusedImports.add((result, n.info))
#newStrNode(toFullPath(c.config, f), n.info) #newStrNode(toFullPath(c.config, f), n.info)
proc transformImportAs(c: PContext; n: PNode): PNode = proc transformImportAs(c: PContext; n: PNode): PNode =

View file

@ -33,12 +33,14 @@ type
warnFieldXNotSupported, warnCommentXIgnored, warnFieldXNotSupported, warnCommentXIgnored,
warnTypelessParam, warnTypelessParam,
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnUnusedImportX,
warnEachIdentIsTuple, warnEachIdentIsTuple,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnInconsistentSpacing, warnCaseTransition, warnUser, warnInconsistentSpacing, warnCaseTransition, warnUser,
hintSuccess, hintSuccessX, hintCC, hintSuccess, hintSuccessX, hintCC,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, hintLineTooLong, hintXDeclaredButNotUsed,
hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
hintConditionAlwaysTrue, hintConditionAlwaysFalse, hintName, hintPattern, hintConditionAlwaysTrue, hintConditionAlwaysFalse, hintName, hintPattern,
@ -78,6 +80,7 @@ const
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap", warnWriteToForeignHeap: "write to foreign heap",
warnUnsafeCode: "unsafe code: '$1'", warnUnsafeCode: "unsafe code: '$1'",
warnUnusedImportX: "imported and not used: '$1'",
warnEachIdentIsTuple: "each identifier is a tuple", warnEachIdentIsTuple: "each identifier is a tuple",
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
warnProveField: "cannot prove that field '$1' is accessible", warnProveField: "cannot prove that field '$1' is accessible",
@ -133,6 +136,7 @@ const
"LanguageXNotSupported", "FieldXNotSupported", "LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored", "CommentXIgnored",
"TypelessParam", "UseBase", "WriteToForeignHeap", "TypelessParam", "UseBase", "WriteToForeignHeap",
"UnusedModule",
"UnsafeCode", "EachIdentIsTuple", "UnsafeCode", "EachIdentIsTuple",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "GcMem", "Destructor", "LockLevel", "ResultShadowed",
@ -140,7 +144,8 @@ const
HintsToStr* = [ HintsToStr* = [
"Success", "SuccessX", "CC", "LineTooLong", "Success", "SuccessX", "CC", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "XDeclaredButNotUsed",
"ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency", "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "ExpandMacro", "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "ExpandMacro",

View file

@ -295,7 +295,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
# constraint not fulfilled: # constraint not fulfilled:
if not ok: return nil if not ok: return nil
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
if ctx.subMatch: if ctx.subMatch:
assert m.len == 3 assert m.len == 3
m.sons[1] = result m.sons[1] = result

View file

@ -450,7 +450,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
pushInfoContext(c.config, nOrig.info, sym.detailedInfo) pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
markUsed(c.config, info, sym, c.graph.usageSym) markUsed(c, info, sym, c.graph.usageSym)
onUse(info, sym) onUse(info, sym)
if sym == c.p.owner: if sym == c.p.owner:
globalError(c.config, info, "recursive dependency: '$1'" % sym.name.s) globalError(c.config, info, "recursive dependency: '$1'" % sym.name.s)
@ -618,12 +618,17 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
#if c.config.cmd == cmdIdeTools: findSuggest(c, n) #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
rod.storeNode(c.graph, c.module, result) rod.storeNode(c.graph, c.module, result)
proc reportUnusedModules(c: PContext) =
for i in 0..high(c.unusedImports):
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
var c = PContext(context) var c = PContext(context)
if c.config.cmd == cmdIdeTools and not c.suggestionsMade: if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
suggestSentinel(c) suggestSentinel(c)
closeScope(c) # close module's scope closeScope(c) # close module's scope
rawCloseScope(c) # imported symbols; don't check for unused ones! rawCloseScope(c) # imported symbols; don't check for unused ones!
reportUnusedModules(c)
result = newNode(nkStmtList) result = newNode(nkStmtList)
if n != nil: if n != nil:
internalError(c.config, n.info, "n is not nil") #result := n; internalError(c.config, n.info, "n is not nil") #result := n;

View file

@ -474,7 +474,7 @@ proc semResolvedCall(c: PContext, x: TCandidate,
assert x.state == csMatch assert x.state == csMatch
var finalCallee = x.calleeSym var finalCallee = x.calleeSym
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
markUsed(c.config, info, finalCallee, c.graph.usageSym) markUsed(c, info, finalCallee, c.graph.usageSym)
onUse(info, finalCallee) onUse(info, finalCallee)
assert finalCallee.ast != nil assert finalCallee.ast != nil
if x.hasFauxMatch: if x.hasFauxMatch:
@ -584,7 +584,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
var newInst = generateInstance(c, s, m.bindings, n.info) var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved newInst.typ.flags.excl tfUnresolved
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
markUsed(c.config, info, s, c.graph.usageSym) markUsed(c, info, s, c.graph.usageSym)
onUse(info, s) onUse(info, s)
result = newSymNode(newInst, info) result = newSymNode(newInst, info)

View file

@ -71,7 +71,8 @@ type
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]
PContext* = ref TContext PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module TContext* = object of TPassContext # a context represents the module
# that is currently being compiled
enforceVoidContext*: PType enforceVoidContext*: PType
module*: PSym # the module sym belonging to the context module*: PSym # the module sym belonging to the context
currentScope*: PScope # current scope currentScope*: PScope # current scope
@ -136,6 +137,7 @@ type
# the generic type has been constructed completely. See # the generic type has been constructed completely. See
# tests/destructor/topttree.nim for an example that # tests/destructor/topttree.nim for an example that
# would otherwise fail. # would otherwise fail.
unusedImports*: seq[(PSym, TLineInfo)]
template config*(c: PContext): ConfigRef = c.graph.config template config*(c: PContext): ConfigRef = c.graph.config

View file

@ -25,7 +25,7 @@ const
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
flags: TExprFlags = {}): PNode = flags: TExprFlags = {}): PNode =
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
markUsed(c.config, info, s, c.graph.usageSym) markUsed(c, info, s, c.graph.usageSym)
onUse(info, s) onUse(info, s)
# Note: This is n.info on purpose. It prevents template from creating an info # Note: This is n.info on purpose. It prevents template from creating an info
# context when called from an another template # context when called from an another template
@ -305,7 +305,7 @@ proc semConv(c: PContext, n: PNode): PNode =
let it = op.sons[i] let it = op.sons[i]
let status = checkConvertible(c, result.typ, it) let status = checkConvertible(c, result.typ, it)
if status in {convOK, convNotNeedeed}: if status in {convOK, convNotNeedeed}:
markUsed(c.config, n.info, it.sym, c.graph.usageSym) markUsed(c, n.info, it.sym, c.graph.usageSym)
onUse(n.info, it.sym) onUse(n.info, it.sym)
markIndirect(c, it.sym) markIndirect(c, it.sym)
return it return it
@ -1106,7 +1106,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
let s = getGenSym(c, sym) let s = getGenSym(c, sym)
case s.kind case s.kind
of skConst: of skConst:
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
onUse(n.info, s) onUse(n.info, s)
let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc}) let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
case typ.kind case typ.kind
@ -1138,7 +1138,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
of skMacro: of skMacro:
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
(n.kind notin nkCallKinds and s.requiredParams > 0): (n.kind notin nkCallKinds and s.requiredParams > 0):
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
onUse(n.info, s) onUse(n.info, s)
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
else: else:
@ -1148,13 +1148,13 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
(n.kind notin nkCallKinds and s.requiredParams > 0) or (n.kind notin nkCallKinds and s.requiredParams > 0) or
sfCustomPragma in sym.flags: sfCustomPragma in sym.flags:
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
markUsed(c.config, info, s, c.graph.usageSym) markUsed(c, info, s, c.graph.usageSym)
onUse(info, s) onUse(info, s)
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
else: else:
result = semTemplateExpr(c, n, s, flags) result = semTemplateExpr(c, n, s, flags)
of skParam: of skParam:
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
onUse(n.info, s) onUse(n.info, s)
if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil: if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ... # XXX see the hack in sigmatch.nim ...
@ -1178,7 +1178,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
if s.magic == mNimvm: if s.magic == mNimvm:
localError(c.config, n.info, "illegal context for 'nimvm' magic") localError(c.config, n.info, "illegal context for 'nimvm' magic")
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
onUse(n.info, s) onUse(n.info, s)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
# We cannot check for access to outer vars for example because it's still # We cannot check for access to outer vars for example because it's still
@ -1196,7 +1196,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
n.typ = s.typ n.typ = s.typ
return n return n
of skType: of skType:
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
onUse(n.info, s) onUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
return s.typ.n return s.typ.n
@ -1218,7 +1218,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
if f != nil and fieldVisible(c, f): if f != nil and fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend? # is the access to a public field or in the same module or in a friend?
doAssert f == s doAssert f == s
markUsed(c.config, n.info, f, c.graph.usageSym) markUsed(c, n.info, f, c.graph.usageSym)
onUse(n.info, f) onUse(n.info, f)
result = newNodeIT(nkDotExpr, n.info, f.typ) result = newNodeIT(nkDotExpr, n.info, f.typ)
result.add makeDeref(newSymNode(p.selfSym)) result.add makeDeref(newSymNode(p.selfSym))
@ -1231,12 +1231,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
if ty.sons[0] == nil: break if ty.sons[0] == nil: break
ty = skipTypes(ty.sons[0], skipPtrs) ty = skipTypes(ty.sons[0], skipPtrs)
# old code, not sure if it's live code: # old code, not sure if it's live code:
markUsed(c.config, n.info, s, c.graph.usageSym) markUsed(c, n.info, s, c.graph.usageSym)
onUse(n.info, s) onUse(n.info, s)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
else: else:
let info = getCallLineInfo(n) let info = getCallLineInfo(n)
markUsed(c.config, info, s, c.graph.usageSym) markUsed(c, info, s, c.graph.usageSym)
onUse(info, s) onUse(info, s)
result = newSymNode(s, info) result = newSymNode(s, info)
@ -1258,7 +1258,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
if result.kind == nkSym: result = semSym(c, n, s, flags) if result.kind == nkSym: result = semSym(c, n, s, flags)
else: else:
markUsed(c.config, n.sons[1].info, s, c.graph.usageSym) markUsed(c, n.sons[1].info, s, c.graph.usageSym)
result = semSym(c, n, s, flags) result = semSym(c, n, s, flags)
onUse(n.sons[1].info, s) onUse(n.sons[1].info, s)
return return
@ -1322,7 +1322,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newSymNode(f) result = newSymNode(f)
result.info = n.info result.info = n.info
result.typ = ty result.typ = ty
markUsed(c.config, n.info, f, c.graph.usageSym) markUsed(c, n.info, f, c.graph.usageSym)
onUse(n.info, f) onUse(n.info, f)
return return
of tyObject, tyTuple: of tyObject, tyTuple:
@ -1357,7 +1357,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: true else: true
if not visibilityCheckNeeded or fieldVisible(c, f): if not visibilityCheckNeeded or fieldVisible(c, f):
# is the access to a public field or in the same module or in a friend? # 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) markUsed(c, n.sons[1].info, f, c.graph.usageSym)
onUse(n.sons[1].info, f) onUse(n.sons[1].info, f)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f) # we now have the correct field n.sons[1] = newSymNode(f) # we now have the correct field
@ -1371,7 +1371,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
elif ty.kind == tyTuple and ty.n != nil: elif ty.kind == tyTuple and ty.n != nil:
f = getSymFromList(ty.n, i) f = getSymFromList(ty.n, i)
if f != nil: if f != nil:
markUsed(c.config, n.sons[1].info, f, c.graph.usageSym) markUsed(c, n.sons[1].info, f, c.graph.usageSym)
onUse(n.sons[1].info, f) onUse(n.sons[1].info, f)
n.sons[0] = makeDeref(n.sons[0]) n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f) n.sons[1] = newSymNode(f)
@ -1905,7 +1905,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
if expandedSym.kind == skError: return n if expandedSym.kind == skError: return n
macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) macroCall.sons[0] = newSymNode(expandedSym, macroCall.info)
markUsed(c.config, n.info, expandedSym, c.graph.usageSym) markUsed(c, n.info, expandedSym, c.graph.usageSym)
onUse(n.info, expandedSym) onUse(n.info, expandedSym)
if isCallExpr(macroCall): if isCallExpr(macroCall):
@ -1930,7 +1930,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
else: else:
let info = macroCall.sons[0].info let info = macroCall.sons[0].info
macroCall.sons[0] = newSymNode(cand, info) macroCall.sons[0] = newSymNode(cand, info)
markUsed(c.config, info, cand, c.graph.usageSym) markUsed(c, info, cand, c.graph.usageSym)
onUse(info, cand) onUse(info, cand)
# we just perform overloading resolution here: # we just perform overloading resolution here:
@ -2453,9 +2453,10 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
var s = initTabIter(i, exported.tab) var s = initTabIter(i, exported.tab)
while s != nil: while s != nil:
if s.kind in ExportableSymKinds+{skModule} and if s.kind in ExportableSymKinds+{skModule} and
s.name.id notin exceptSet: s.name.id notin exceptSet and sfError notin s.flags:
strTableAdd(c.module.tab, s) strTableAdd(c.module.tab, s)
result.add newSymNode(s, n.info) result.add newSymNode(s, n.info)
markUsed(c, n.info, s, c.graph.usageSym)
s = nextIter(i, exported.tab) s = nextIter(i, exported.tab)
proc semExport(c: PContext, n: PNode): PNode = proc semExport(c: PContext, n: PNode): PNode =
@ -2476,14 +2477,16 @@ proc semExport(c: PContext, n: PNode): PNode =
strTableAdd(c.module.tab, it) strTableAdd(c.module.tab, it)
result.add newSymNode(it, a.info) result.add newSymNode(it, a.info)
it = nextIter(ti, s.tab) it = nextIter(ti, s.tab)
markUsed(c, n.info, s, c.graph.usageSym)
else: else:
while s != nil: while s != nil:
if s.kind == skEnumField: if s.kind == skEnumField:
localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) & localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) &
"; enum field cannot be exported individually") "; enum field cannot be exported individually")
if s.kind in ExportableSymKinds+{skModule}: if s.kind in ExportableSymKinds+{skModule} and sfError notin s.flags:
result.add(newSymNode(s, a.info)) result.add(newSymNode(s, a.info))
strTableAdd(c.module.tab, s) strTableAdd(c.module.tab, s)
markUsed(c, n.info, s, c.graph.usageSym)
s = nextOverloadIter(o, c, a) s = nextOverloadIter(o, c, a)
proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =

View file

@ -812,7 +812,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
errors, false) errors, false)
if r.state == csMatch: if r.state == csMatch:
var match = r.calleeSym var match = r.calleeSym
markUsed(c.config, n[0].info, match, c.graph.usageSym) markUsed(c, n[0].info, match, c.graph.usageSym)
onUse(n[0].info, match) onUse(n[0].info, match)
# but pass 'n' to the 'match' macro, not 'n[0]': # but pass 'n' to the 'match' macro, not 'n[0]':

View file

@ -64,7 +64,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
# (s.kind notin routineKinds or s.magic != mNone): # (s.kind notin routineKinds or s.magic != mNone):
# for instance 'nextTry' is both in tables.nim and astalgo.nim ... # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
result = newSymNode(s, info) result = newSymNode(s, info)
markUsed(c.config, info, s, c.graph.usageSym) markUsed(c, info, s, c.graph.usageSym)
onUse(info, s) onUse(info, s)
else: else:
# semantic checking requires a type; ``fitNode`` deals with it # semantic checking requires a type; ``fitNode`` deals with it

View file

@ -353,7 +353,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
if result.isNil: if result.isNil:
result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
if result != nil: if result != nil:
markUsed(c.config, n.info, result, c.graph.usageSym) markUsed(c, n.info, result, c.graph.usageSym)
onUse(n.info, result) onUse(n.info, result)
if result.kind == skParam and result.typ.kind == tyTypeDesc: if result.kind == skParam and result.typ.kind == tyTypeDesc:
@ -1063,7 +1063,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false)) result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false))
of tyGenericParam: of tyGenericParam:
markUsed(c.config, paramType.sym.info, paramType.sym, c.graph.usageSym) markUsed(c, paramType.sym.info, paramType.sym, c.graph.usageSym)
onUse(paramType.sym.info, paramType.sym) onUse(paramType.sym.info, paramType.sym)
if tfWildcard in paramType.flags: if tfWildcard in paramType.flags:
paramType.flags.excl tfWildcard paramType.flags.excl tfWildcard
@ -1751,7 +1751,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else: else:
assignType(prev, t) assignType(prev, t)
result = prev result = prev
markUsed(c.config, n.info, n.sym, c.graph.usageSym) markUsed(c, n.info, n.sym, c.graph.usageSym)
onUse(n.info, n.sym) onUse(n.info, n.sym)
else: else:
if s.kind != skError: if s.kind != skError:

View file

@ -107,7 +107,7 @@ type
const const
isNilConversion = isConvertible # maybe 'isIntConv' fits better? isNilConversion = isConvertible # maybe 'isIntConv' fits better?
proc markUsed*(conf: ConfigRef; info: TLineInfo, s: PSym; usageSym: var PSym) proc markUsed*(c: PContext; info: TLineInfo, s: PSym; usageSym: var PSym)
template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
@ -1891,7 +1891,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
dest = generateTypeInstance(c, m.bindings, arg, dest) dest = generateTypeInstance(c, m.bindings, arg, dest)
let fdest = typeRel(m, f, dest) let fdest = typeRel(m, f, dest)
if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind == tyVar): if fdest in {isEqual, isGeneric} and not (dest.kind == tyLent and f.kind == tyVar):
markUsed(c.config, arg.info, c.converters[i], c.graph.usageSym) markUsed(c, arg.info, c.converters[i], c.graph.usageSym)
var s = newSymNode(c.converters[i]) var s = newSymNode(c.converters[i])
s.typ = c.converters[i].typ s.typ = c.converters[i].typ
s.info = arg.info s.info = arg.info
@ -2220,7 +2220,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
else: result = nil else: result = nil
else: else:
# only one valid interpretation found: # only one valid interpretation found:
markUsed(m.c.config, arg.info, arg.sons[best].sym, m.c.graph.usageSym) markUsed(m.c, arg.info, arg.sons[best].sym, m.c.graph.usageSym)
onUse(arg.info, arg.sons[best].sym) onUse(arg.info, arg.sons[best].sym)
result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
argOrig) argOrig)

View file

@ -528,7 +528,21 @@ proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
return return
localError(conf, info, "usage of '$1' is a user-defined error" % s.name.s) localError(conf, info, "usage of '$1' is a user-defined error" % s.name.s)
proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
var module = s
while module != nil and module.kind != skModule:
module = module.owner
if module != nil and module != c.module:
var i = 0
while i <= high(c.unusedImports):
if c.unusedImports[i][0] == module:
# mark it as used:
c.unusedImports.del(i)
else:
inc i
proc markUsed(c: PContext; info: TLineInfo; s: PSym; usageSym: var PSym) =
let conf = c.config
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
if s.kind == skEnumField and s.owner != nil: if s.kind == skEnumField and s.owner != nil:
incl(s.owner.flags, sfUsed) incl(s.owner.flags, sfUsed)
@ -541,6 +555,7 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
suggestSym(conf, info, s, usageSym, false) suggestSym(conf, info, s, usageSym, false)
if {optStyleHint, optStyleError} * conf.globalOptions != {}: if {optStyleHint, optStyleError} * conf.globalOptions != {}:
styleCheckUse(conf, info, s) styleCheckUse(conf, info, s)
markOwnerModuleAsUsed(c, s)
proc safeSemExpr*(c: PContext, n: PNode): PNode = proc safeSemExpr*(c: PContext, n: PNode): PNode =
# use only for idetools support! # use only for idetools support!