From 985b6109b3c5aff1c40e6caa25aed7dc8f22ef8d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 1 May 2021 19:52:07 -0700 Subject: [PATCH 01/14] {.used: symbol} --- compiler/pragmas.nim | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3a2d9cede743..b8138fb9e10f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1212,9 +1212,26 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wBoolDefine: sym.magic = mBoolDefine of wUsed: - noVal(c, it) - if sym == nil: invalidPragma(c, it) - else: sym.flags.incl sfUsed + dbg sym + if sym == nil or sym.kind == skModule: + dbg it.kind, it + if it.kind != nkExprColonExpr: + # if it.kind notin nkCallKinds: + invalidPragma(c, it) + else: + let ni = it[1] + block: + # for i in 1..it.len: + # let ni = it[i] + # dbg ni, ni.kind + let sym2 = qualifiedLookUp(c, ni, {checkUndeclared, checkModule}) + # let sym2 = considerQuotedIdent(c, it) + dbg sym2 + dbg sym2, sym2.kind + sym2.flags.incl sfUsed + else: + noVal(c, it) + sym.flags.incl sfUsed of wLiftLocals: discard of wRequires, wInvariant, wAssume, wAssert: pragmaProposition(c, it) From ac3e2d43f9647c2c4ce4d9efddf20c23430bad81 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 2 May 2021 09:57:52 -0700 Subject: [PATCH 02/14] add tests --- changelog.md | 3 +++ compiler/importer.nim | 8 +++++--- compiler/pragmas.nim | 9 --------- tests/pragmas/mused2a.nim | 29 +++++++++++++++++++++++++++++ tests/pragmas/mused2b.nim | 3 +++ tests/pragmas/mused2c.nim | 1 + tests/pragmas/mused2d.nim | 0 tests/pragmas/mused2e.nim | 0 tests/pragmas/tused2.nim | 36 ++++++++++++++++++++++++++++++++++++ 9 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 tests/pragmas/mused2a.nim create mode 100644 tests/pragmas/mused2b.nim create mode 100644 tests/pragmas/mused2c.nim create mode 100644 tests/pragmas/mused2d.nim create mode 100644 tests/pragmas/mused2e.nim create mode 100644 tests/pragmas/tused2.nim diff --git a/changelog.md b/changelog.md index 29b3237d0faa..58e923c2daed 100644 --- a/changelog.md +++ b/changelog.md @@ -344,6 +344,9 @@ - `typeof(voidStmt)` now works and returns `void`. +- `{.used.}` now accepts symbols, e.g. `{.used: mymodule.}` or `{.used: myFun.}`. + + ## Compiler changes - Added `--declaredlocs` to show symbol declaration location in messages. diff --git a/compiler/importer.nim b/compiler/importer.nim index 692cdb604f9b..92f3a7241a6b 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -224,7 +224,6 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = result = realModule - c.unusedImports.add((realModule, n.info)) template createModuleAliasImpl(ident): untyped = createModuleAlias(realModule, nextSymId c.idgen, ident, realModule.info, c.config.options) if n.kind != nkImportAs: discard @@ -233,10 +232,13 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool) elif n[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... result = createModuleAliasImpl(n[1].ident) + if result == realModule: + # avoids modifying `realModule`, see D20201209T194412 for `import {.all.}` + # and also for `import foo; {.used: foo.}` + result = createModuleAliasImpl(realModule.name) if importHidden: - if result == realModule: # avoids modifying `realModule`, see D20201209T194412. - result = createModuleAliasImpl(realModule.name) result.options.incl optImportHidden + c.unusedImports.add((result, n.info)) proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = var ret: typeof(result) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b8138fb9e10f..1ddf3481ba7a 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1212,22 +1212,13 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wBoolDefine: sym.magic = mBoolDefine of wUsed: - dbg sym if sym == nil or sym.kind == skModule: - dbg it.kind, it if it.kind != nkExprColonExpr: - # if it.kind notin nkCallKinds: invalidPragma(c, it) else: let ni = it[1] block: - # for i in 1..it.len: - # let ni = it[i] - # dbg ni, ni.kind let sym2 = qualifiedLookUp(c, ni, {checkUndeclared, checkModule}) - # let sym2 = considerQuotedIdent(c, it) - dbg sym2 - dbg sym2, sym2.kind sym2.flags.incl sfUsed else: noVal(c, it) diff --git a/tests/pragmas/mused2a.nim b/tests/pragmas/mused2a.nim new file mode 100644 index 000000000000..3fa28e376cb5 --- /dev/null +++ b/tests/pragmas/mused2a.nim @@ -0,0 +1,29 @@ +import std/strutils +import std/sugar +from std/os import fileExists +import std/enumutils as enumutils2 +import std/typetraits as typetraits2 +from std/setutils import complement + +{.used: strutils.} +{.used: enumutils2.} +{.used: complement.} + +proc fn1() = discard +proc fn2*() = discard +proc fn3() = discard + +let fn4 = 0 +let fn5* = 0 +let fn6 = 0 + +const fn7 = 0 +const fn8* = 0 +const fn9 = 0 +type T1 = object +type T2 = object + +{.used: fn3.} +{.used: fn6.} +{.used: fn9.} +{.used: T2.} diff --git a/tests/pragmas/mused2b.nim b/tests/pragmas/mused2b.nim new file mode 100644 index 000000000000..39c92b964615 --- /dev/null +++ b/tests/pragmas/mused2b.nim @@ -0,0 +1,3 @@ +import mused2c +export mused2c + diff --git a/tests/pragmas/mused2c.nim b/tests/pragmas/mused2c.nim new file mode 100644 index 000000000000..a374e634e758 --- /dev/null +++ b/tests/pragmas/mused2c.nim @@ -0,0 +1 @@ +proc baz*() = discard \ No newline at end of file diff --git a/tests/pragmas/mused2d.nim b/tests/pragmas/mused2d.nim new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/pragmas/mused2e.nim b/tests/pragmas/mused2e.nim new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim new file mode 100644 index 000000000000..9f5aec4cd0fb --- /dev/null +++ b/tests/pragmas/tused2.nim @@ -0,0 +1,36 @@ +discard """ + matrix: "--hint:conf:off --hint:link:off --hint:cc:off --hint:SuccessX:off --import:tests/pragmas/mused2e" + joinable: false + nimoutFull: true + nimout: ''' +mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(2, 11) Warning: imported and not used: 'sugar' [UnusedImport] +mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] +mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] +mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] +tused2.nim(31, 8) Warning: imported and not used: 'mused2a' [UnusedImport] +tused2.nim(32, 8) Warning: imported and not used: 'mused2b' [UnusedImport] +tused2.nim(34, 11) Warning: imported and not used: 'strutils' [UnusedImport] +Hint: ***SLOW, DEBUG BUILD***; -d:release makes code run faster. [BuildMode] +''' +""" + +#[ +xxx the `testament.isSuccess` logic makes `nimoutFull` awkward to use, forcing it to show `BuildMode`; +we should improve this. + +xxx tused2.nim(32, 8) Warning: imported and not used: 'mused2b' [UnusedImport] +should not be generated (refs bug #17510) +]# + + +# line 30 +import mused2a +import mused2b +import mused2d +import std/strutils +baz() +{.used: mused2d.} From 2f429058caeeb40d25f8628b478afd64966fc99c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 01:46:00 -0700 Subject: [PATCH 03/14] fix tests with --import --- tests/pragmas/mused2e.nim | 1 + tests/pragmas/tused2.nim | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/pragmas/mused2e.nim b/tests/pragmas/mused2e.nim index e69de29bb2d1..494fce414242 100644 --- a/tests/pragmas/mused2e.nim +++ b/tests/pragmas/mused2e.nim @@ -0,0 +1 @@ +proc fnMused2e*() = discard diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim index 9f5aec4cd0fb..ce52673e0713 100644 --- a/tests/pragmas/tused2.nim +++ b/tests/pragmas/tused2.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--hint:conf:off --hint:link:off --hint:cc:off --hint:SuccessX:off --import:tests/pragmas/mused2e" + matrix: "--hint:conf:off --hint:link:off --hint:cc:off --hint:SuccessX:off --path:. --import:tests/pragmas/mused2e" joinable: false nimoutFull: true nimout: ''' @@ -7,16 +7,22 @@ mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed] mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed] mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed] mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] mused2a.nim(2, 11) Warning: imported and not used: 'sugar' [UnusedImport] mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] -tused2.nim(31, 8) Warning: imported and not used: 'mused2a' [UnusedImport] -tused2.nim(32, 8) Warning: imported and not used: 'mused2b' [UnusedImport] -tused2.nim(34, 11) Warning: imported and not used: 'strutils' [UnusedImport] +mused2c.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] +mused2b.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] +mused2d.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] +tused2.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] +tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport] +tused2.nim(43, 8) Warning: imported and not used: 'mused2b' [UnusedImport] +tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] Hint: ***SLOW, DEBUG BUILD***; -d:release makes code run faster. [BuildMode] ''' """ + # matrix: "--hint:conf:off --hint:link:off --hint:cc:off --hint:SuccessX:off --import:tests/pragmas/mused2e" #[ xxx the `testament.isSuccess` logic makes `nimoutFull` awkward to use, forcing it to show `BuildMode`; @@ -27,7 +33,12 @@ should not be generated (refs bug #17510) ]# -# line 30 + + + + +# line 40 +fnMused2e() # ensures `--import:tests/pragmas/mused2e` works import mused2a import mused2b import mused2d From 98ec1259f84f490790f7a90863f22c7c47bce4b2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 11:41:09 -0700 Subject: [PATCH 04/14] --import works without giving spurious unused warnings --- compiler/passes.nim | 20 ++++++++++++++------ compiler/pragmas.nim | 1 + tests/pragmas/tused2.nim | 10 +++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/compiler/passes.nim b/compiler/passes.nim index 3debce1f65bd..730a7b60e37a 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,7 +14,11 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, pathutils + lineinfos, pathutils, wordrecg +from parser import parseString +from sugar import dup +from strutils import `%` +from os import splitFile type TPassData* = tuple[input: PNode, closeOutput: PNode] @@ -88,11 +92,15 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod for module in items(implicits): # implicit imports should not lead to a module importing itself if m.position != resolveMod(graph.config, module, relativeTo).int32: - var importStmt = newNodeI(nodeKind, m.info) - var str = newStrNode(nkStrLit, module) - str.info = m.info - importStmt.add str - if not processTopLevelStmt(graph, importStmt, a): break + var code = "" + let quoted = "".dup(addQuoted(module)) + let name = module.splitFile.name + case nodeKind + of nkImportStmt: code = "import $1 as $2\n{.used: $2.}" % [quoted, name] + of nkIncludeStmt: code = "include $1" % quoted + else: assert false + let node = parseString(code, graph.cache, graph.config, filename = "", line = 0, errorHandler = nil) + if not processTopLevelStmt(graph, node, a): break const imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef, diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1ddf3481ba7a..17bb3b967a12 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1219,6 +1219,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, let ni = it[1] block: let sym2 = qualifiedLookUp(c, ni, {checkUndeclared, checkModule}) + # dbg sym2, it, ni sym2.flags.incl sfUsed else: noVal(c, it) diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim index ce52673e0713..2641d355bdce 100644 --- a/tests/pragmas/tused2.nim +++ b/tests/pragmas/tused2.nim @@ -7,15 +7,10 @@ mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed] mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed] mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed] mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed] -mused2a.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] mused2a.nim(2, 11) Warning: imported and not used: 'sugar' [UnusedImport] mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] -mused2c.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] -mused2b.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] -mused2d.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] -tused2.nim(1, 2) Warning: imported and not used: 'mused2e' [UnusedImport] tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport] tused2.nim(43, 8) Warning: imported and not used: 'mused2b' [UnusedImport] tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] @@ -37,6 +32,11 @@ should not be generated (refs bug #17510) + + + + + # line 40 fnMused2e() # ensures `--import:tests/pragmas/mused2e` works import mused2a From f4eee1d09719bd4529f617752ad12fc7d4941ba2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 14:33:33 -0700 Subject: [PATCH 05/14] new warning warnDuplicateModuleImport for `import foo; import foo` --- compiler/lineinfos.nim | 2 ++ compiler/lookups.nim | 20 ++++++++++++-------- compiler/pragmas.nim | 23 +++++++++++------------ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index c7057e7db4f3..dc4805dddf3e 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -62,6 +62,7 @@ type warnStrictNotNil = "StrictNotNil", warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", + warnDuplicateModuleImport = "DuplicateModuleImport", warnUser = "User", hintSuccess = "Success", hintSuccessX = "SuccessX", hintBuildMode = "BuildMode", @@ -140,6 +141,7 @@ const warnStrictNotNil: "$1", warnCannotOpen: "cannot open: $1", warnFileChanged: "file changed: $1", + warnDuplicateModuleImport: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c4f506a68861..6f47f524cdf1 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -59,8 +59,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = template addSym*(scope: PScope, s: PSym) = strTableAdd(scope.symbols, s) -proc addUniqueSym*(scope: PScope, s: PSym): PSym = - result = strTableInclReportConflict(scope.symbols, s) +proc addUniqueSym*(scope: PScope, s: PSym, onConflictKeepOld: bool): PSym = + result = strTableInclReportConflict(scope.symbols, s, onConflictKeepOld) proc openScope*(c: PContext): PScope {.discardable.} = result = PScope(parent: c.currentScope, @@ -286,17 +286,21 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = s = nextIter(it, scope.symbols) proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; - conflictsWith: TLineInfo) = + conflictsWith: TLineInfo, note = errGenerated) = ## Emit a redefinition error if in non-interactive mode if c.config.cmd != cmdInteractive: - localError(c.config, info, + localError(c.config, info, note, "redefinition of '$1'; previous declaration here: $2" % [s, c.config $ conflictsWith]) proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) = - let conflict = c.currentScope.addUniqueSym(sym) + let conflict = c.currentScope.addUniqueSym(sym, onConflictKeepOld = true) if conflict != nil: - wrongRedefinition(c, info, sym.name.s, conflict.info) + var note = errGenerated + if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner: + # import foo; import foo + note = warnDuplicateModuleImport + wrongRedefinition(c, info, sym.name.s, conflict.info, note) proc addDecl*(c: PContext, sym: PSym) = let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true) @@ -304,10 +308,10 @@ proc addDecl*(c: PContext, sym: PSym) = wrongRedefinition(c, sym.info, sym.name.s, conflict.info) proc addPrelimDecl*(c: PContext, sym: PSym) = - discard c.currentScope.addUniqueSym(sym) + discard c.currentScope.addUniqueSym(sym, onConflictKeepOld = false) proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = - let conflict = scope.addUniqueSym(sym) + let conflict = scope.addUniqueSym(sym, onConflictKeepOld = true) if conflict != nil: wrongRedefinition(c, sym.info, sym.name.s, conflict.info) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 17bb3b967a12..1860dfec53b2 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1212,18 +1212,17 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wBoolDefine: sym.magic = mBoolDefine of wUsed: - if sym == nil or sym.kind == skModule: - if it.kind != nkExprColonExpr: - invalidPragma(c, it) - else: - let ni = it[1] - block: - let sym2 = qualifiedLookUp(c, ni, {checkUndeclared, checkModule}) - # dbg sym2, it, ni - sym2.flags.incl sfUsed - else: - noVal(c, it) - sym.flags.incl sfUsed + case it.kind + of nkExprColonExpr: # {.used: mysym.} + # if sym != nil or sym.kind != skModule: + # localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") + let sym2 = qualifiedLookUp(c, it[1], {checkUndeclared, checkModule}) + assert sym2 != nil # PRTEMP: localError + sym2.flags.incl sfUsed + of nkIdent: # {.used.} + if sym == nil: invalidPragma(c, it) + else: sym.flags.incl sfUsed + else: invalidPragma(c, it) of wLiftLocals: discard of wRequires, wInvariant, wAssume, wAssert: pragmaProposition(c, it) From 46d51cc671b008a282239787ab6a79f66e7eb46c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 15:13:21 -0700 Subject: [PATCH 06/14] fix test, add resolveModuleAlias, use proper line info for module aliases --- compiler/ast.nim | 9 +++++++++ compiler/importer.nim | 11 +++++------ tests/modules/tselfimport.nim | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 7090bc7ee787..8347c3d5b2a0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -851,6 +851,8 @@ type TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym # proc and type instantiations are cached in the generic symbol case kind*: TSymKind + of skModule: + realModule*: PSym # for `createModuleAlias` of routineKinds: #procInstCache*: seq[PInstantiation] gcUnsafetyReason*: PSym # for better error messages wrt gcsafe @@ -1493,6 +1495,13 @@ proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo; result.position = s.position result.loc = s.loc result.annex = s.annex + result.realModule = s # xxx can we just use id ? + +proc resolveModuleAlias*(s: PSym): PSym = + assert s.kind == skModule + result = s + if result.realModule != nil: # owner is unrelated + result = result.realModule proc initStrTable*(x: var TStrTable) = x.counter = 0 diff --git a/compiler/importer.nim b/compiler/importer.nim index 92f3a7241a6b..03754a71d4bc 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -13,6 +13,7 @@ import intsets, ast, astalgo, msgs, options, idents, lookups, semdata, modulepaths, sigmatch, lineinfos, sets, modulegraphs, wordrecg +from strutils import `%` proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} @@ -225,7 +226,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = result = realModule template createModuleAliasImpl(ident): untyped = - createModuleAlias(realModule, nextSymId c.idgen, ident, realModule.info, c.config.options) + createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options) if n.kind != nkImportAs: discard elif n.len != 2 or n[1].kind != nkIdent: localError(c.config, n.info, "module alias must be an identifier") @@ -283,11 +284,9 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = #echo "set back to ", L c.graph.importStack.setLen(L) # we cannot perform this check reliably because of - # test: modules/import_in_config) - when true: - if result.info.fileIndex == c.module.info.fileIndex and - result.info.fileIndex == n.info.fileIndex: - localError(c.config, n.info, "A module cannot import itself") + # test: modules/import_in_config) # xxx is that still true? + if result.resolveModuleAlias == c.module: + localError(c.config, n.info, "module '$1' cannot import itself" % c.module.name.s) if sfDeprecated in result.flags: if result.constraint != nil: message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated") diff --git a/tests/modules/tselfimport.nim b/tests/modules/tselfimport.nim index 7e50bef7c52a..ba5d9b4cf80d 100644 --- a/tests/modules/tselfimport.nim +++ b/tests/modules/tselfimport.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "A module cannot import itself" + errormsg: "module 'tselfimport' cannot import itself" file: "tselfimport.nim" line: 7 """ From b235191568d10f0e3646612b9ef6645a9d78b9b2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 16:37:54 -0700 Subject: [PATCH 07/14] fix spurious warnings --- compiler/passaux.nim | 2 +- compiler/passes.nim | 2 +- compiler/pragmas.nim | 2 -- compiler/suggest.nim | 3 ++- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 9abc97be402c..87cb3a730889 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -10,7 +10,7 @@ ## implements some little helper passes import - ast, passes, idents, msgs, options, lineinfos + ast, passes, msgs, options, lineinfos from modulegraphs import ModuleGraph, PPassContext diff --git a/compiler/passes.nim b/compiler/passes.nim index 730a7b60e37a..6376d2581c1b 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,7 +14,7 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, pathutils, wordrecg + lineinfos, pathutils from parser import parseString from sugar import dup from strutils import `%` diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1860dfec53b2..f123e5a9afdf 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1214,8 +1214,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wUsed: case it.kind of nkExprColonExpr: # {.used: mysym.} - # if sym != nil or sym.kind != skModule: - # localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") let sym2 = qualifiedLookUp(c, it[1], {checkUndeclared, checkModule}) assert sym2 != nil # PRTEMP: localError sym2.flags.incl sfUsed diff --git a/compiler/suggest.nim b/compiler/suggest.nim index a5b4ac87d9f3..b440b055266a 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -572,7 +572,8 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) = var i = 0 while i <= high(c.unusedImports): let candidate = c.unusedImports[i][0] - if candidate == module or c.exportIndirections.contains((candidate.id, s.id)): + let candidate2 = candidate.resolveModuleAlias + if candidate2 == module or c.exportIndirections.contains((candidate.id, s.id)): # mark it as used: c.unusedImports.del(i) else: From fcb2cca1ebfcd945500ac4f3cf998ec043459323 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 17:56:42 -0700 Subject: [PATCH 08/14] fix deprecation msg for deprecated modules even with `import foo as bar` --- compiler/importer.nim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/importer.nim b/compiler/importer.nim index 03754a71d4bc..f401a789c31e 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -285,13 +285,14 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = c.graph.importStack.setLen(L) # we cannot perform this check reliably because of # test: modules/import_in_config) # xxx is that still true? - if result.resolveModuleAlias == c.module: + let realModuule = result.resolveModuleAlias + if realModuule == c.module: localError(c.config, n.info, "module '$1' cannot import itself" % c.module.name.s) - if sfDeprecated in result.flags: - if result.constraint != nil: - message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated") + if sfDeprecated in realModuule.flags: + if realModuule.constraint != nil: + message(c.config, n.info, warnDeprecated, realModuule.constraint.strVal & "; " & realModuule.name.s & " is deprecated") else: - message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated") + message(c.config, n.info, warnDeprecated, realModuule.name.s & " is deprecated") suggestSym(c.graph, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) From 6f116fbb58c858c01b00d9da46bfd576a3dbe8c2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 20:03:31 -0700 Subject: [PATCH 09/14] disable a test for i386 pending sorting XDeclaredButNotUsed errors --- compiler/lookups.nim | 2 ++ tests/pragmas/tused2.nim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 6f47f524cdf1..16d2d927a798 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -282,6 +282,8 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # maybe they can be made skGenericParam as well. if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and s.typ.kind != tyGenericParam: + # xxx D20210504T200053:here these should be sorted to have reproducible errors, in particular + # across 32 vs 64 bit; can be done by buffering those and then sorting. message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) s = nextIter(it, scope.symbols) diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim index 2641d355bdce..d30994f9e352 100644 --- a/tests/pragmas/tused2.nim +++ b/tests/pragmas/tused2.nim @@ -16,8 +16,8 @@ tused2.nim(43, 8) Warning: imported and not used: 'mused2b' [UnusedImport] tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] Hint: ***SLOW, DEBUG BUILD***; -d:release makes code run faster. [BuildMode] ''' + disabled: "i386" # see D20210504T200053 for reason and how to fix """ - # matrix: "--hint:conf:off --hint:link:off --hint:cc:off --hint:SuccessX:off --import:tests/pragmas/mused2e" #[ xxx the `testament.isSuccess` logic makes `nimoutFull` awkward to use, forcing it to show `BuildMode`; From f6afeae97eca7d338b5cb81c128ed86eb9f0471b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 4 May 2021 17:22:03 -0700 Subject: [PATCH 10/14] UnusedImport now works with re-exported symbols --- compiler/importer.nim | 3 +++ tests/pragmas/tused2.nim | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/importer.nim b/compiler/importer.nim index f401a789c31e..9f13e30fc3e5 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -305,6 +305,9 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = addDecl(c, m, it.info) # add symbol to symbol table of module importAllSymbols(c, m) #importForwarded(c, m.ast, emptySet, m) + for s in allSyms(c.graph, m): # fixes bug #17510, for re-exported symbols + if s.owner != m.resolveModuleAlias: + c.exportIndirections.incl((m.id, s.id)) proc evalImport*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim index d30994f9e352..83fc0b4678f3 100644 --- a/tests/pragmas/tused2.nim +++ b/tests/pragmas/tused2.nim @@ -12,7 +12,6 @@ mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport] -tused2.nim(43, 8) Warning: imported and not used: 'mused2b' [UnusedImport] tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] Hint: ***SLOW, DEBUG BUILD***; -d:release makes code run faster. [BuildMode] ''' @@ -22,9 +21,6 @@ Hint: ***SLOW, DEBUG BUILD***; -d:release makes code run faster. [BuildMode] #[ xxx the `testament.isSuccess` logic makes `nimoutFull` awkward to use, forcing it to show `BuildMode`; we should improve this. - -xxx tused2.nim(32, 8) Warning: imported and not used: 'mused2b' [UnusedImport] -should not be generated (refs bug #17510) ]# @@ -37,6 +33,10 @@ should not be generated (refs bug #17510) + + + + # line 40 fnMused2e() # ensures `--import:tests/pragmas/mused2e` works import mused2a From 414b3b2a29e35c6b264d78bf9cee02e5613a9454 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 5 May 2021 09:52:02 -0700 Subject: [PATCH 11/14] fix typo [skip ci] --- compiler/importer.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/importer.nim b/compiler/importer.nim index 9f13e30fc3e5..68743a92e37a 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -285,14 +285,14 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = c.graph.importStack.setLen(L) # we cannot perform this check reliably because of # test: modules/import_in_config) # xxx is that still true? - let realModuule = result.resolveModuleAlias - if realModuule == c.module: + let realModule = result.resolveModuleAlias + if realModule == c.module: localError(c.config, n.info, "module '$1' cannot import itself" % c.module.name.s) - if sfDeprecated in realModuule.flags: - if realModuule.constraint != nil: - message(c.config, n.info, warnDeprecated, realModuule.constraint.strVal & "; " & realModuule.name.s & " is deprecated") + if sfDeprecated in realModule.flags: + if realModule.constraint != nil: + message(c.config, n.info, warnDeprecated, realModule.constraint.strVal & "; " & realModule.name.s & " is deprecated") else: - message(c.config, n.info, warnDeprecated, realModuule.name.s & " is deprecated") + message(c.config, n.info, warnDeprecated, realModule.name.s & " is deprecated") suggestSym(c.graph, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) From 9428b31aba49821d00cd7259eb48de134ab2f353 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 5 May 2021 10:56:18 -0700 Subject: [PATCH 12/14] ic support --- compiler/ic/ic.nim | 12 ++++++++++-- compiler/ic/packed_ast.nim | 1 + testament/categories.nim | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 6530cb6c27cb..6b24169320c7 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -394,11 +394,15 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId storeNode(p, s, ast) storeNode(p, s, constraint) - if s.kind in {skLet, skVar, skField, skForVar}: + case s.kind + of {skLet, skVar, skField, skForVar}: c.addMissing s.guard p.guard = s.guard.safeItemId(c, m) p.bitsize = s.bitsize p.alignment = s.alignment + of skModule: + p.realModule = s.realModule.safeItemId(c, m) + else: discard p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, m) p.locFlags = s.loc.flags @@ -847,10 +851,14 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; when hasFFI: result.cname = g[si].fromDisk.strings[s.cname] - if s.kind in {skLet, skVar, skField, skForVar}: + case s.kind + of {skLet, skVar, skField, skForVar}: result.guard = loadSym(c, g, si, s.guard) result.bitsize = s.bitsize result.alignment = s.alignment + of skModule: + result.realModule = loadSym(c, g, si, s.realModule) + else: discard result.owner = loadSym(c, g, si, s.owner) let externalName = g[si].fromDisk.strings[s.externalName] if externalName != "": diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 17beda2c18b3..cfc2e266e1e9 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -67,6 +67,7 @@ type when hasFFI: cname*: LitId constraint*: NodeId + realModule*: PackedItemId PackedType* = object kind*: TTypeKind diff --git a/testament/categories.nim b/testament/categories.nim index 1253a2b8ce53..a0057f810c6c 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -519,6 +519,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; const tempExt = "_temp.nim" for it in walkDirRec(testsDir): + # for it in ["tests/ic/timports.nim"]: # debugging: to try a specific test if isTestFile(it) and not it.endsWith(tempExt): let nimcache = nimcacheDir(it, options, getTestSpecTarget()) removeDir(nimcache) From 621b2d970269f01f2947b6ea3647989e52de538f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 5 May 2021 15:15:48 -0700 Subject: [PATCH 13/14] add genPNode to allow writing PNode-based compiler code similarly to `genAst` --- compiler/passes.nim | 21 ++++++----- compiler/vmconv.nim | 90 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/compiler/passes.nim b/compiler/passes.nim index 6376d2581c1b..347268419d46 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,10 +14,7 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, pathutils -from parser import parseString -from sugar import dup -from strutils import `%` + lineinfos, pathutils, vmconv from os import splitFile type @@ -92,14 +89,18 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod for module in items(implicits): # implicit imports should not lead to a module importing itself if m.position != resolveMod(graph.config, module, relativeTo).int32: - var code = "" - let quoted = "".dup(addQuoted(module)) - let name = module.splitFile.name + var c = GenContext(cache: graph.cache, info: m.info) + var node: PNode case nodeKind - of nkImportStmt: code = "import $1 as $2\n{.used: $2.}" % [quoted, name] - of nkIncludeStmt: code = "include $1" % quoted + of nkImportStmt: + let name = graph.cache.getIdent(module.splitFile.name) + node = genPNode(c, module, name): + import module as name + {.used: name.} + of nkIncludeStmt: + node = genPNode(c, module): + include module else: assert false - let node = parseString(code, graph.cache, graph.config, filename = "", line = 0, errorHandler = nil) if not processTopLevelStmt(graph, node, a): break const diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim index b82fb2ff3199..2914198d5057 100644 --- a/compiler/vmconv.nim +++ b/compiler/vmconv.nim @@ -1,4 +1,8 @@ +from std/strutils import cmpIgnoreStyle +import std/macros import ast +from idents import PIdent, IdentCache +from lineinfos import TLineInfo template elementType*(T: typedesc): typedesc = typeof(block: @@ -19,7 +23,12 @@ proc fromLit*(a: PNode, T: typedesc): auto = proc toLit*[T](a: T): PNode = ## generic type => PNode ## see also reverse operation `fromLit` - when T is string: newStrNode(nkStrLit, a) + # xxx also allow an optional `info` param + when false: discard + elif T is string: newStrNode(nkStrLit, a) + elif T is PIdent: + result = newNode(nkIdent) + result.ident = a elif T is Ordinal: newIntNode(nkIntLit, a.ord) elif T is (proc): newNode(nkNilLit) elif T is ref: @@ -28,7 +37,7 @@ proc toLit*[T](a: T): PNode = elif T is tuple: result = newTree(nkTupleConstr) for ai in fields(a): result.add toLit(ai) - elif T is seq: + elif T is seq | array: result = newNode(nkBracket) for ai in a: result.add toLit(ai) @@ -43,3 +52,80 @@ proc toLit*[T](a: T): PNode = else: static: doAssert false, "not yet supported: " & $T # add as needed +type + GenContext* = object + cache*: IdentCache + info*: TLineInfo + ContextVars = seq[tuple[name: string, val: NimNode]] + +proc genPNodeImpl(c: NimNode, code: var NimNode, vals: ContextVars, n: NimNode): NimNode = + if n.kind == nnkIdent: + for v in vals: + if n.strVal.cmpIgnoreStyle(v.name) == 0: + return v.val + result = genSym(nskVar, "ret") + let kind2 = n.kind.ord.TNodeKind.newLit + code.add quote do: + # alternatively, get `info` from `n.info`, which requires exposing this in macros. + var `result` = newNodeI(`kind2`, `c`.info) + # keep in sync with `ast.TNode` + case n.kind + of nnkCharLit..nnkUInt64Lit: + let val = n.intVal.newLit + code.add quote do: + `result`.intVal = `val` + of nnkFloatLit..nnkFloat128Lit: + let val = n.floatVal.newLit + code.add quote do: + `result`.floatVal = `val` + let tmp = quote do: + `result`.floatVal = `val` + of nnkStrLit..nnkTripleStrLit: + let val = n.strVal.newLit + code.add quote do: + `result`.strVal = `val` + of nnkIdent: + let val = n.strVal.newLit + code.add quote do: + `result`.ident = getIdent(`c`.cache, `val`) + of nnkSym: doAssert false # not implemented, but shouldn't be needed + else: + for ni in n: + let reti = genPNodeImpl(c, code, vals, ni) + code.add quote do: + `result`.add `reti` + +macro genPNode*(c: GenContext, args: varargs[untyped]): PNode = + ## Converts an AST into a PNode, and works similarly to std/genasts. + ## This can simplify writing compiler code, avoiding to manually write `PNode` ASTs. + runnableExamples: + import idents, renderer + let cache = newIdentCache() + let a = [1,2] + let b = cache.getIdent("foo") + var c = GenContext(cache: cache) + let node = genPNode(c, a, b): + for i in 0..<3: + let b = @[i, 1] + echo (a, b, i, "abc") + let s = node.renderTree + assert s == """ + +for i in 0 ..< 3: + let foo = @[i, 1] + echo ([1, 2], foo, i, "abc")""", s + + result = newStmtList() + let m = args.len - 1 + var vals: ContextVars + vals.setLen m + for i in 0.. Date: Fri, 7 May 2021 19:33:34 -0700 Subject: [PATCH 14/14] fix DuplicateModuleImport warning --- tests/statictypes/tstatictypes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index c0eb62e213b7..79ae987b8027 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -274,7 +274,7 @@ block: fails(foo) -import macros, tables +import tables var foo{.compileTime.} = [ "Foo",