summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/importer.nim1
-rw-r--r--compiler/lineinfos.nim9
-rw-r--r--compiler/patterns.nim2
-rw-r--r--compiler/sem.nim7
-rw-r--r--compiler/semcall.nim4
-rw-r--r--compiler/semdata.nim4
-rw-r--r--compiler/semexprs.nim41
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/semtempl.nim2
-rw-r--r--compiler/semtypes.nim6
-rw-r--r--compiler/sigmatch.nim6
-rw-r--r--compiler/suggest.nim17
12 files changed, 66 insertions, 35 deletions
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 2a5a1ac7c..426b79981 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -167,6 +167,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
         message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated")
     suggestSym(c.config, n.info, result, c.graph.usageSym, false)
     importStmtResult.add newSymNode(result, n.info)
+    c.unusedImports.add((result, n.info))
     #newStrNode(toFullPath(c.config, f), n.info)
 
 proc transformImportAs(c: PContext; n: PNode): PNode =
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index 121962014..42d33de7d 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -33,12 +33,14 @@ type
     warnFieldXNotSupported, warnCommentXIgnored,
     warnTypelessParam,
     warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
+    warnUnusedImportX,
     warnEachIdentIsTuple,
     warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
     warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
     warnInconsistentSpacing, warnCaseTransition, warnUser,
     hintSuccess, hintSuccessX, hintCC,
-    hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
+    hintLineTooLong, hintXDeclaredButNotUsed,
+    hintConvToBaseNotNeeded,
     hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
     hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
     hintConditionAlwaysTrue, hintConditionAlwaysFalse, hintName, hintPattern,
@@ -78,6 +80,7 @@ const
     warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
     warnWriteToForeignHeap: "write to foreign heap",
     warnUnsafeCode: "unsafe code: '$1'",
+    warnUnusedImportX: "imported and not used: '$1'",
     warnEachIdentIsTuple: "each identifier is a tuple",
     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",
@@ -133,6 +136,7 @@ const
     "LanguageXNotSupported", "FieldXNotSupported",
     "CommentXIgnored",
     "TypelessParam", "UseBase", "WriteToForeignHeap",
+    "UnusedModule",
     "UnsafeCode", "EachIdentIsTuple",
     "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
     "GcMem", "Destructor", "LockLevel", "ResultShadowed",
@@ -140,7 +144,8 @@ const
 
   HintsToStr* = [
     "Success", "SuccessX", "CC", "LineTooLong",
-    "XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
+    "XDeclaredButNotUsed",
+    "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
     "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
     "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency",
     "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "ExpandMacro",
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index e20c42b66..623a04406 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -295,7 +295,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
         # constraint not fulfilled:
         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:
     assert m.len == 3
     m.sons[1] = result
diff --git a/compiler/sem.nim b/compiler/sem.nim
index fa0f742f7..e33170553 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -450,7 +450,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   pushInfoContext(c.config, nOrig.info, sym.detailedInfo)
 
   let info = getCallLineInfo(n)
-  markUsed(c.config, info, sym, c.graph.usageSym)
+  markUsed(c, info, sym, c.graph.usageSym)
   onUse(info, sym)
   if sym == c.p.owner:
     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)
   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 =
   var c = PContext(context)
   if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
     suggestSentinel(c)
   closeScope(c)         # close module's scope
   rawCloseScope(c)      # imported symbols; don't check for unused ones!
+  reportUnusedModules(c)
   result = newNode(nkStmtList)
   if n != nil:
     internalError(c.config, n.info, "n is not nil") #result := n;
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 805a29303..d2f878831 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -474,7 +474,7 @@ proc semResolvedCall(c: PContext, x: TCandidate,
   assert x.state == csMatch
   var finalCallee = x.calleeSym
   let info = getCallLineInfo(n)
-  markUsed(c.config, info, finalCallee, c.graph.usageSym)
+  markUsed(c, info, finalCallee, c.graph.usageSym)
   onUse(info, finalCallee)
   assert finalCallee.ast != nil
   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)
   newInst.typ.flags.excl tfUnresolved
   let info = getCallLineInfo(n)
-  markUsed(c.config, info, s, c.graph.usageSym)
+  markUsed(c, info, s, c.graph.usageSym)
   onUse(info, s)
   result = newSymNode(newInst, info)
 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 6c7dd02ed..76eb468ed 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -71,7 +71,8 @@ type
   TExprFlags* = set[TExprFlag]
 
   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
     module*: PSym              # the module sym belonging to the context
     currentScope*: PScope      # current scope
@@ -136,6 +137,7 @@ type
       # the generic type has been constructed completely. See
       # tests/destructor/topttree.nim for an example that
       # would otherwise fail.
+    unusedImports*: seq[(PSym, TLineInfo)]
 
 template config*(c: PContext): ConfigRef = c.graph.config
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index bdf742bf1..929283bef 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -25,7 +25,7 @@ const
 proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
                      flags: TExprFlags = {}): PNode =
   let info = getCallLineInfo(n)
-  markUsed(c.config, info, s, c.graph.usageSym)
+  markUsed(c, info, s, c.graph.usageSym)
   onUse(info, s)
   # Note: This is n.info on purpose. It prevents template from creating an info
   # context when called from an another template
@@ -305,7 +305,7 @@ proc semConv(c: PContext, n: PNode): PNode =
       let it = op.sons[i]
       let status = checkConvertible(c, result.typ, it)
       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)
         markIndirect(c, it.sym)
         return it
@@ -1106,7 +1106,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
   let s = getGenSym(c, sym)
   case s.kind
   of skConst:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
+    markUsed(c, n.info, s, c.graph.usageSym)
     onUse(n.info, s)
     let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
     case typ.kind
@@ -1138,7 +1138,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
   of skMacro:
     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)
+      markUsed(c, n.info, s, c.graph.usageSym)
       onUse(n.info, s)
       result = symChoice(c, n, s, scClosed)
     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
        sfCustomPragma in sym.flags:
       let info = getCallLineInfo(n)
-      markUsed(c.config, info, s, c.graph.usageSym)
+      markUsed(c, info, s, c.graph.usageSym)
       onUse(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)
+    markUsed(c, n.info, s, c.graph.usageSym)
     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 ...
@@ -1178,7 +1178,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
     if s.magic == mNimvm:
       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)
     result = newSymNode(s, n.info)
     # 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
       return n
   of skType:
-    markUsed(c.config, n.info, s, c.graph.usageSym)
+    markUsed(c, n.info, s, c.graph.usageSym)
     onUse(n.info, s)
     if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil:
       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):
             # 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)
+            markUsed(c, n.info, f, c.graph.usageSym)
             onUse(n.info, f)
             result = newNodeIT(nkDotExpr, n.info, f.typ)
             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
           ty = skipTypes(ty.sons[0], skipPtrs)
     # 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)
     result = newSymNode(s, n.info)
   else:
     let info = getCallLineInfo(n)
-    markUsed(c.config, info, s, c.graph.usageSym)
+    markUsed(c, info, s, c.graph.usageSym)
     onUse(info, s)
     result = newSymNode(s, info)
 
@@ -1258,7 +1258,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       result = symChoice(c, n, s, scClosed)
       if result.kind == nkSym: result = semSym(c, n, s, flags)
     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)
     onUse(n.sons[1].info, s)
     return
@@ -1322,7 +1322,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         result = newSymNode(f)
         result.info = n.info
         result.typ = ty
-        markUsed(c.config, n.info, f, c.graph.usageSym)
+        markUsed(c, n.info, f, c.graph.usageSym)
         onUse(n.info, f)
         return
     of tyObject, tyTuple:
@@ -1357,7 +1357,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
         else: true
       if not visibilityCheckNeeded or 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)
+        markUsed(c, n.sons[1].info, f, c.graph.usageSym)
         onUse(n.sons[1].info, f)
         n.sons[0] = makeDeref(n.sons[0])
         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:
     f = getSymFromList(ty.n, i)
     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)
       n.sons[0] = makeDeref(n.sons[0])
       n.sons[1] = newSymNode(f)
@@ -1905,7 +1905,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
     if expandedSym.kind == skError: return n
 
     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)
 
   if isCallExpr(macroCall):
@@ -1930,7 +1930,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
     else:
       let info = macroCall.sons[0].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)
 
     # we just perform overloading resolution here:
@@ -2453,9 +2453,10 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
   var s = initTabIter(i, exported.tab)
   while s != nil:
     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)
       result.add newSymNode(s, n.info)
+      markUsed(c, n.info, s, c.graph.usageSym)
     s = nextIter(i, exported.tab)
 
 proc semExport(c: PContext, n: PNode): PNode =
@@ -2476,14 +2477,16 @@ proc semExport(c: PContext, n: PNode): PNode =
           strTableAdd(c.module.tab, it)
           result.add newSymNode(it, a.info)
         it = nextIter(ti, s.tab)
+      markUsed(c, n.info, s, c.graph.usageSym)
     else:
       while s != nil:
         if s.kind == skEnumField:
           localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a) &
             "; 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))
           strTableAdd(c.module.tab, s)
+          markUsed(c, n.info, s, c.graph.usageSym)
         s = nextOverloadIter(o, c, a)
 
 proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 2020f10f8..402816078 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -812,7 +812,7 @@ proc handleCaseStmtMacro(c: PContext; n: PNode): PNode =
                            errors, false)
   if r.state == csMatch:
     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)
 
     # but pass 'n' to the 'match' macro, not 'n[0]':
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 7a56f8c45..1b9b9e8a9 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -64,7 +64,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode =
     # (s.kind notin routineKinds or s.magic != mNone):
     # for instance 'nextTry' is both in tables.nim and astalgo.nim ...
     result = newSymNode(s, info)
-    markUsed(c.config, info, s, c.graph.usageSym)
+    markUsed(c, info, s, c.graph.usageSym)
     onUse(info, s)
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index e927be417..e7fbc83ec 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -353,7 +353,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
     if result.isNil:
       result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
     if result != nil:
-      markUsed(c.config, n.info, result, c.graph.usageSym)
+      markUsed(c, n.info, result, c.graph.usageSym)
       onUse(n.info, result)
 
       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))
 
   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)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
@@ -1751,7 +1751,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       else:
         assignType(prev, t)
         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)
     else:
       if s.kind != skError:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index b331b6f1f..828d25fe8 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -107,7 +107,7 @@ type
 const
   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
 
@@ -1891,7 +1891,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       dest = generateTypeInstance(c, m.bindings, arg, dest)
     let fdest = typeRel(m, f, dest)
     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])
       s.typ = c.converters[i].typ
       s.info = arg.info
@@ -2220,7 +2220,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
       else: result = nil
     else:
       # 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)
       result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
                                   argOrig)
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 2f92825c0..59e3a7242 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -528,7 +528,21 @@ proc userError(conf: ConfigRef; info: TLineInfo; s: PSym) =
         return
   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)
   if s.kind == skEnumField and s.owner != nil:
     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)
   if {optStyleHint, optStyleError} * conf.globalOptions != {}:
     styleCheckUse(conf, info, s)
+  markOwnerModuleAsUsed(c, s)
 
 proc safeSemExpr*(c: PContext, n: PNode): PNode =
   # use only for idetools support!