summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/importer.nim23
-rw-r--r--compiler/semdata.nim3
-rw-r--r--compiler/semexprs.nim47
-rw-r--r--compiler/suggest.nim3
-rw-r--r--tests/tools/tunused_imports.nim27
5 files changed, 76 insertions, 27 deletions
diff --git a/compiler/importer.nim b/compiler/importer.nim
index e42a4d816..d048d7fe1 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -31,7 +31,7 @@ proc importPureEnumField*(c: PContext; s: PSym) =
       incl(c.ambiguousSymbols, checkB.id)
       incl(c.ambiguousSymbols, s.id)
 
-proc rawImportSymbol(c: PContext, s: PSym) =
+proc rawImportSymbol(c: PContext, s, origin: PSym) =
   # This does not handle stubs, because otherwise loading on demand would be
   # pointless in practice. So importing stubs is fine here!
   # check if we have already a symbol of the same name:
@@ -63,13 +63,14 @@ proc rawImportSymbol(c: PContext, s: PSym) =
           check = nextIdentIter(it, c.importTable.symbols)
         if e != nil:
           if sfPure notin s.flags:
-            rawImportSymbol(c, e)
+            rawImportSymbol(c, e, origin)
           else:
             importPureEnumField(c, e)
   else:
-    # rodgen assures that converters and patterns are no stubs
     if s.kind == skConverter: addConverter(c, s)
     if hasPattern(s): addPattern(c, s)
+  if s.owner != origin:
+    c.exportIndirections.incl(idPairToInt(origin.id, s.id))
 
 proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
   let ident = lookups.considerQuotedIdent(c, n)
@@ -88,10 +89,10 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
       while e != nil:
         if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
         if s.kind in ExportableSymKinds:
-          rawImportSymbol(c, e)
+          rawImportSymbol(c, e, fromMod)
         e = nextIdentIter(it, fromMod.tab)
     else:
-      rawImportSymbol(c, s)
+      rawImportSymbol(c, s, fromMod)
     suggestSym(c.config, n.info, s, c.graph.usageSym, false)
 
 proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
@@ -103,14 +104,14 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
         if s.kind notin ExportableSymKinds:
           internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s)
         if exceptSet.isNil or s.name.id notin exceptSet:
-          rawImportSymbol(c, s)
+          rawImportSymbol(c, s, fromMod)
     s = nextIter(i, fromMod.tab)
 
 proc importAllSymbols*(c: PContext, fromMod: PSym) =
   var exceptSet: IntSet
   importAllSymbolsExcept(c, fromMod, exceptSet)
 
-proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
+proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym) =
   if n.isNil: return
   case n.kind
   of nkExportStmt:
@@ -120,12 +121,12 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
       if s.kind == skModule:
         importAllSymbolsExcept(c, s, exceptSet)
       elif exceptSet.isNil or s.name.id notin exceptSet:
-        rawImportSymbol(c, s)
+        rawImportSymbol(c, s, fromMod)
   of nkExportExceptStmt:
     localError(c.config, n.info, "'export except' not implemented")
   else:
     for i in 0..safeLen(n)-1:
-      importForwarded(c, n.sons[i], exceptSet)
+      importForwarded(c, n.sons[i], exceptSet, fromMod)
 
 proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
   result = realModule
@@ -186,7 +187,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
     # ``addDecl`` needs to be done before ``importAllSymbols``!
     addDecl(c, m, it.info) # add symbol to symbol table of module
     importAllSymbolsExcept(c, m, emptySet)
-    #importForwarded(c, m.ast, emptySet)
+    #importForwarded(c, m.ast, emptySet, m)
 
 proc evalImport*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkImportStmt, n.info)
@@ -233,4 +234,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode =
     n.sons[0] = newSymNode(m)
     addDecl(c, m, n.info)               # add symbol to symbol table of module
     importAllSymbolsExcept(c, m, readExceptSet(c, n))
-    #importForwarded(c, m.ast, exceptSet)
+    #importForwarded(c, m.ast, exceptSet, m)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 5638fd29e..464c55b7e 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -138,6 +138,9 @@ type
       # tests/destructor/topttree.nim for an example that
       # would otherwise fail.
     unusedImports*: seq[(PSym, TLineInfo)]
+    exportIndirections*: IntSet
+
+template idPairToInt*(a, b: int): int = a * 10_000_000 + b
 
 template config*(c: PContext): ConfigRef = c.graph.config
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 439a0070e..dc3076ceb 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -2130,30 +2130,42 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   result = n
   case s.magic # magics that need special treatment
   of mAddr:
+    markUsed(c, n.info, s, c.graph.usageSym)
     checkSonsLen(n, 2, c.config)
     result[0] = newSymNode(s, n[0].info)
     result[1] = semAddrArg(c, n.sons[1], s.name.s == "unsafeAddr")
     result.typ = makePtrType(c, result[1].typ)
   of mTypeOf:
+    markUsed(c, n.info, s, c.graph.usageSym)
     result = semTypeOf(c, n)
-  #of mArrGet: result = semArrGet(c, n, flags)
-  #of mArrPut: result = semArrPut(c, n, flags)
-  #of mAsgn: result = semAsgnOpr(c, n)
-  of mDefined: result = semDefined(c, setMs(n, s), false)
-  of mDefinedInScope: result = semDefined(c, setMs(n, s), true)
-  of mCompiles: result = semCompiles(c, setMs(n, s), flags)
-  #of mLow: result = semLowHigh(c, setMs(n, s), mLow)
-  #of mHigh: result = semLowHigh(c, setMs(n, s), mHigh)
-  of mIs: result = semIs(c, setMs(n, s), flags)
-  #of mOf: result = semOf(c, setMs(n, s))
-  of mShallowCopy: result = semShallowCopy(c, n, flags)
-  of mExpandToAst: result = semExpandToAst(c, n, s, flags)
-  of mQuoteAst: result = semQuoteAst(c, n)
+  of mDefined:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semDefined(c, setMs(n, s), false)
+  of mDefinedInScope:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semDefined(c, setMs(n, s), true)
+  of mCompiles:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semCompiles(c, setMs(n, s), flags)
+  of mIs:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semIs(c, setMs(n, s), flags)
+  of mShallowCopy:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semShallowCopy(c, n, flags)
+  of mExpandToAst:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semExpandToAst(c, n, s, flags)
+  of mQuoteAst:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semQuoteAst(c, n)
   of mAstToStr:
+    markUsed(c, n.info, s, c.graph.usageSym)
     checkSonsLen(n, 2, c.config)
     result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
     result.typ = getSysType(c.graph, n.info, tyString)
   of mParallel:
+    markUsed(c, n.info, s, c.graph.usageSym)
     if parallel notin c.features:
       localError(c.config, n.info, "use the {.experimental.} pragma to enable 'parallel'")
     result = setMs(n, s)
@@ -2163,6 +2175,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     result.sons[1] = semStmt(c, x, {})
     dec c.inParallelStmt
   of mSpawn:
+    markUsed(c, n.info, s, c.graph.usageSym)
     when defined(leanCompiler):
       localError(c.config, n.info, "compiler was built without 'spawn' support")
       result = n
@@ -2180,10 +2193,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       else:
         result.add c.graph.emptyNode
   of mProcCall:
+    markUsed(c, n.info, s, c.graph.usageSym)
     result = setMs(n, s)
     result.sons[1] = semExpr(c, n.sons[1])
     result.typ = n[1].typ
   of mPlugin:
+    markUsed(c, n.info, s, c.graph.usageSym)
     # semDirectOp with conditional 'afterCallActions':
     let nOrig = n.copyTree
     #semLazyOpAux(c, n)
@@ -2200,6 +2215,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       if callee.magic != mNone:
         result = magicsAfterOverloadResolution(c, result, flags)
   of mRunnableExamples:
+    markUsed(c, n.info, s, c.graph.usageSym)
     if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
       when false:
         # some of this dead code was moved to `prepareExamples`
@@ -2216,8 +2232,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
       result = setMs(n, s)
     else:
       result = c.graph.emptyNode
-  of mSizeOf: result =
-    semSizeof(c, setMs(n, s))
+  of mSizeOf:
+    markUsed(c, n.info, s, c.graph.usageSym)
+    result = semSizeof(c, setMs(n, s))
   else:
     result = semDirectOp(c, n, flags)
 
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 59e3a7242..620f4830a 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -535,7 +535,8 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
   if module != nil and module != c.module:
     var i = 0
     while i <= high(c.unusedImports):
-      if c.unusedImports[i][0] == module:
+      let candidate = c.unusedImports[i][0]
+      if candidate == module or c.exportIndirections.contains(idPairToInt(candidate.id, s.id)):
         # mark it as used:
         c.unusedImports.del(i)
       else:
diff --git a/tests/tools/tunused_imports.nim b/tests/tools/tunused_imports.nim
new file mode 100644
index 000000000..c9cfcfe90
--- /dev/null
+++ b/tests/tools/tunused_imports.nim
@@ -0,0 +1,27 @@
+discard """
+  cmd: '''nim c --hint[Processing]:off $file'''
+  nimout: '''
+tunused_imports.nim(11, 10) Warning: BEGIN [User]
+tunused_imports.nim(27, 10) Warning: END [User]
+tunused_imports.nim(25, 8) Warning: imported and not used: 'strutils' [UnusedImport]
+'''
+  action: "compile"
+"""
+
+{.warning: "BEGIN".}
+
+import net
+
+echo AF_UNIX
+
+import macros
+# bug #11809
+macro bar(): untyped =
+  template baz() = discard
+  result = getAst(baz())
+
+bar()
+
+import strutils
+
+{.warning: "END".}