diff --git a/compiler/main.nim b/compiler/main.nim index efbf496d4f..2a90765350 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -34,6 +34,7 @@ from icconfig import produceIcConfig when not defined(nimKochBootstrap): import nifbackend import deps + import idetools when not defined(leanCompiler): import docgen @@ -415,7 +416,26 @@ proc mainCommand*(graph: ModuleGraph) = for it in conf.searchPaths: msgWriteln(conf, it.string) of cmdCheck: - commandCheck(graph) + when not defined(nimKochBootstrap): + if conf.ideCmd in {ideDef, ideUse}: + # `nim check --def:`/`--usages:`: run a whole-project check that also + # emits each cleanly-compiled module's `.s.bif` (see + # pipelines.shouldWriteNif), then scan those NIF files to answer the + # goto-definition / find-usages query. The NIF writer needs the IC setup + # `cmdM` uses (disabled rod files + `useIc`) to serialize full module + # content; we keep `cmd == cmdCheck` for its error-tolerant sem and the + # VM guards that run compile-time code faithfully for this mode (see + # vm.nim/vmgen.nim), so the emitted NIF reflects macro/gorge symbols. + graph.config.symbolFiles = disabledSf + setUseIc(true) + excl conf.features, Feature.vtables + createDir(getNimcacheDir(conf).string) + commandCheck(graph) + runIdeQuery(conf) + else: + commandCheck(graph) + else: + commandCheck(graph) of cmdM: # cmdM uses NIF files, not ROD files graph.config.symbolFiles = disabledSf diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim index aed7944b1c..7e9bd3a8ea 100644 --- a/compiler/pipelines.nim +++ b/compiler/pipelines.nim @@ -40,9 +40,10 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): of GenDependPass: result = addDotDependency(bModule, semNode) of SemPass: - # Return the semantic node for cmdM (NIF generation needs it) - # For regular check, we don't need the result - if graph.config.cmd == cmdM: + # Return the semantic node for cmdM (NIF generation needs it), and likewise + # for a `--def`/`--usages` query under cmdCheck which emits `.s.bif` too. + # For regular check, we don't need the result. + if graph.config.cmd == cmdM or graph.config.ideCmd in {ideDef, ideUse}: result = semNode else: result = graph.emptyNode @@ -167,7 +168,10 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator s = stream graph.interactive = stream.kind == llsStdIn var topLevelStmts = - if optCompress in graph.config.globalOptions or graph.config.cmd == cmdM: + if optCompress in graph.config.globalOptions or graph.config.cmd == cmdM or + graph.config.ideCmd in {ideDef, ideUse}: + # A `--def`/`--usages` query emits every module's `.s.bif` under cmdCheck + # (see shouldWriteNif below), which needs the collected top-level stmts. newNodeI(nkStmtList, module.info) else: nil @@ -247,7 +251,18 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator # current strongly-connected import group (`--icGroup`) are the exception: # they are compiled from source here, so each must write its own NIF. let shouldWriteNif = - if graph.config.ideActive: + if graph.config.ideCmd in {ideDef, ideUse}: + # `nim check --def:`/`--usages:` query: this whole-project check runs + # under cmdCheck and emits each cleanly-compiled, non-dirty PROJECT + # module's `.s.bif` so `idetools.runIdeQuery` can scan them afterwards. + # Stdlib modules are skipped: they serialize robustly only via `nim ic`'s + # per-module `cmdM` build, and the common query targets a project symbol + # (whose def + uses all live in project modules). Skip the edited buffer + # and any module reached after an error (incomplete NIF). + graph.config.errorCounter == 0 and + not belongsToStdlib(graph, module) and + graph.config.m.fileInfos[module.position].dirtyFile.isEmpty + elif graph.config.ideActive: # nimsuggest (cmdM): persist NIF for cleanly-compiled, SAVED modules so # later queries load them instead of recompiling. Never persist the # actively edited buffer (it may hold unsaved/incomplete code) nor a diff --git a/compiler/sem.nim b/compiler/sem.nim index 7f777a7add..cf33a68d98 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -926,7 +926,7 @@ proc semWithPContext*(c: PContext, n: PNode): PNode = #if c.config.ideActive: findSuggest(c, n) proc reportUnusedModules(c: PContext) = - if c.config.cmd == cmdM: return + if c.config.cmd == cmdM or c.config.ideCmd in {ideDef, ideUse}: return for (s, info) in c.unusedImports: if sfUsed notin s.flags: message(c.config, info, warnUnusedImportX, s.name.s) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ab3a30a29e..8ec82bd3cf 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2892,9 +2892,10 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult, resolvedIncStmt proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) var resolvedIncStmt: PNode = nil - if optCompress in c.config.globalOptions or c.config.cmd == cmdM: + if optCompress in c.config.globalOptions or c.config.cmd == cmdM or + c.config.ideCmd in {ideDef, ideUse}: # New resolve the include filenames to string literals that contain absolute paths, - # nicer for IC: + # nicer for IC (also for a `--def`/`--usages` query, which emits `.s.bif`): resolvedIncStmt = newNodeI(nkIncludeStmt, n.info) result.add resolvedIncStmt else: diff --git a/compiler/vm.nim b/compiler/vm.nim index 0d9ab99b2e..90f6d43880 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2033,7 +2033,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = inc pc let rd = c.code[pc].regA createStr regs[ra] - if defined(nimsuggest) or c.config.cmd == cmdCheck: + if defined(nimsuggest) or (c.config.cmd == cmdCheck and c.config.ideCmd == ideNone): + # Don't run staticExec for plain `nim check` / `nim suggest`. A + # `--def`/`--usages` query (ideCmd != ideNone) is the exception: it wants + # faithful compile-time execution so the emitted `.bif` — which the query + # then scans — reflects macro/gorge-produced symbols. See idetools.nim. discard "don't run staticExec for 'nim suggest'" regs[ra].node.strVal = "" else: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a560665b68..c49380a31e 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -359,7 +359,9 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) = #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}: if i != dest: when not defined(release): - if c.config.cmd != cmdCheck: + # A `--def`/`--usages` query runs the VM faithfully (ideCmd != ideNone), + # so re-arm the leaking-temporary assert for it as in a real compile. + if c.config.cmd != cmdCheck or c.config.ideCmd != ideNone: if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown, slotTempInt, slotTempFloat, @@ -1598,9 +1600,11 @@ proc setSlot(c: PCtx; v: PSym) = v.positionImpl = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1) template cannotEval(c: PCtx; n: PNode) = - if c.config.cmd == cmdCheck and c.config.m.errorOutputs != {}: + if c.config.cmd == cmdCheck and c.config.ideCmd == ideNone and c.config.m.errorOutputs != {}: # nim check command with no error outputs doesn't need to cascade here, - # includes `tryConstExpr` case which should not continue generating code + # includes `tryConstExpr` case which should not continue generating code. + # A `--def`/`--usages` query (ideCmd != ideNone) falls through to the hard + # `globalError` below so its compile-time evaluation is faithful. localError(c.config, n.info, "cannot evaluate at compile time: " & n.renderTree) c.cannotEval = true return