From 478773ffb12f578ee15a67ab8234cb82d1caeb3a Mon Sep 17 00:00:00 2001 From: Nikolay Nikolov Date: Fri, 18 Jul 2025 09:44:36 +0300 Subject: [PATCH] NimSuggest: Fix for the inlay exception hints with generic procs (#23610) Based on the fix, started by SirOlaf in #23414 --------- Co-authored-by: SirOlaf <> Co-authored-by: Andreas Rumpf Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- compiler/modulegraphs.nim | 4 +- compiler/semcall.nim | 15 ++++-- compiler/sempass2.nim | 2 +- compiler/sigmatch.nim | 2 +- compiler/suggest.nim | 96 ++++++++++++++++++++------------------- compiler/suggestsymdb.nim | 35 +++++++++++++- compiler/types.nim | 2 +- nimsuggest/nimsuggest.nim | 46 +++++++++++-------- 8 files changed, 126 insertions(+), 76 deletions(-) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 6010e93947..51b9e5e4eb 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -457,10 +457,10 @@ template getPContext(): untyped = else: c.c when defined(nimsuggest): - template onUse*(info: TLineInfo; s: PSym) = discard + template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard template onDefResolveForward*(info: TLineInfo; s: PSym) = discard else: - template onUse*(info: TLineInfo; s: PSym) = discard + template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard template onDef*(info: TLineInfo; s: PSym) = discard template onDefResolveForward*(info: TLineInfo; s: PSym) = discard diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 038d7b0131..a80b58be7b 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -836,9 +836,12 @@ proc semResolvedCall(c: PContext, x: var TCandidate, assert x.state == csMatch var finalCallee = x.calleeSym let info = getCallLineInfo(n) - markUsed(c, info, finalCallee) - onUse(info, finalCallee) + markUsed(c, info, finalCallee, isGenericInstance = false) + onUse(info, finalCallee, isGenericInstance = false) assert finalCallee.ast != nil + if x.matchedErrorType: + markUsed(c, info, finalCallee, isGenericInstance = true) + onUse(info, finalCallee, isGenericInstance = true) if x.matchedErrorType: result = x.call result[0] = newSymNode(finalCallee, getCallLineInfo(result[0])) @@ -874,6 +877,8 @@ proc semResolvedCall(c: PContext, x: var TCandidate, x.call.add tn else: internalAssert c.config, false + markUsed(c, info, finalCallee, isGenericInstance = true) + onUse(info, finalCallee, isGenericInstance = true) result = x.call instGenericConvertersSons(c, result, x) @@ -942,8 +947,10 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErr var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved let info = getCallLineInfo(n) - markUsed(c, info, s) - onUse(info, s) + markUsed(c, info, s, isGenericInstance = false) + onUse(info, s, isGenericInstance = false) + markUsed(c, info, newInst, isGenericInstance = true) + onUse(info, newInst, isGenericInstance = true) result = newSymNode(newInst, info) proc setGenericParams(c: PContext, n, expectedParams: PNode) = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b5e600ad8a..65d216b831 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -971,7 +971,7 @@ proc checkForSink(tracked: PEffects; n: PNode) = proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) = when defined(nimsuggest): proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) = - var si = q.findSymInfoIndex(info) + var si = q.findSymInfoIndex(info, true) if si != -1: q.caughtExceptionsSet[si] = true for w1 in tracked.caughtExceptions.nodes: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5094e61aed..d3d99a355a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -104,7 +104,7 @@ const isNilConversion = isConvertible # maybe 'isIntConv' fits better? maxInheritancePenalty = high(int) div 2 -proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true) +proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true; isGenericInstance = false) proc markOwnerModuleAsUsed*(c: PContext; s: PSym) proc initCandidateAux(ctx: PContext, diff --git a/compiler/suggest.nim b/compiler/suggest.nim index a1a477ec8a..1317fb2e48 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -624,41 +624,43 @@ proc ensureIdx[T](x: var T, y: int) = proc ensureSeq[T](x: var seq[T]) = if x == nil: newSeq(x, 0) -proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = +proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true; isGenericInstance=false) {.inline.} = ## misnamed: should be 'symDeclared' let conf = g.config when defined(nimsuggest): - g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions + if optIdeExceptionInlayHints in conf.globalOptions or not isGenericInstance: + g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl, isGenericInstance: isGenericInstance), optIdeExceptionInlayHints in g.config.globalOptions - if conf.suggestVersion == 0: - if s.allUsages.len == 0: - s.allUsages = @[info] - else: - s.addNoDup(info) + if not isGenericInstance: + if conf.suggestVersion == 0: + if s.allUsages.len == 0: + s.allUsages = @[info] + else: + s.addNoDup(info) - if conf.ideCmd == ideUse: - findUsages(g, info, s, usageSym) - elif conf.ideCmd == ideDef: - findDefinition(g, info, s, usageSym) - elif conf.ideCmd == ideDus and s != nil: - if isTracked(info, conf.m.trackPos, s.name.s.len): - suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) - findUsages(g, info, s, usageSym) - elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex: - suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) - elif conf.ideCmd == ideOutline and isDecl: - # if a module is included then the info we have is inside the include and - # we need to walk up the owners until we find the outer most module, - # which will be the last skModule prior to an skPackage. - var - parentFileIndex = info.fileIndex # assume we're in the correct module - parentModule = s.owner - while parentModule != nil and parentModule.kind == skModule: - parentFileIndex = parentModule.info.fileIndex - parentModule = parentModule.owner + if conf.ideCmd == ideUse: + findUsages(g, info, s, usageSym) + elif conf.ideCmd == ideDef: + findDefinition(g, info, s, usageSym) + elif conf.ideCmd == ideDus and s != nil: + if isTracked(info, conf.m.trackPos, s.name.s.len): + suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + findUsages(g, info, s, usageSym) + elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex: + suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) + elif conf.ideCmd == ideOutline and isDecl: + # if a module is included then the info we have is inside the include and + # we need to walk up the owners until we find the outer most module, + # which will be the last skModule prior to an skPackage. + var + parentFileIndex = info.fileIndex # assume we're in the correct module + parentModule = s.owner + while parentModule != nil and parentModule.kind == skModule: + parentFileIndex = parentModule.info.fileIndex + parentModule = parentModule.owner - if parentFileIndex == conf.m.trackPos.fileIndex: - suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) + if parentFileIndex == conf.m.trackPos.fileIndex: + suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = var pragmaNode: PNode @@ -702,26 +704,28 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) = else: inc i -proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) = - let conf = c.config - incl(s.flags, sfUsed) - if s.kind == skEnumField and s.owner != nil: - incl(s.owner.flags, sfUsed) - if sfDeprecated in s.owner.flags: - warnAboutDeprecated(conf, info, s) - if {sfDeprecated, sfError} * s.flags != {}: - if sfDeprecated in s.flags: - if not (c.lastTLineInfo.line == info.line and - c.lastTLineInfo.col == info.col): +proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true; isGenericInstance = false) = + if not isGenericInstance: + let conf = c.config + incl(s.flags, sfUsed) + if s.kind == skEnumField and s.owner != nil: + incl(s.owner.flags, sfUsed) + if sfDeprecated in s.owner.flags: warnAboutDeprecated(conf, info, s) - c.lastTLineInfo = info + if {sfDeprecated, sfError} * s.flags != {}: + if sfDeprecated in s.flags: + if not (c.lastTLineInfo.line == info.line and + c.lastTLineInfo.col == info.col): + warnAboutDeprecated(conf, info, s) + c.lastTLineInfo = info - if sfError in s.flags: userError(conf, info, s) + if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): - suggestSym(c.graph, info, s, c.graph.usageSym, false) - if checkStyle: - styleCheckUse(c, info, s) - markOwnerModuleAsUsed(c, s) + suggestSym(c.graph, info, s, c.graph.usageSym, isDecl = false, isGenericInstance = isGenericInstance) + if not isGenericInstance: + if checkStyle: + styleCheckUse(c, info, s) + markOwnerModuleAsUsed(c, s) proc safeSemExpr*(c: PContext, n: PNode): PNode = # use only for idetools support! diff --git a/compiler/suggestsymdb.nim b/compiler/suggestsymdb.nim index e1e67afbe4..9ec3ac3839 100644 --- a/compiler/suggestsymdb.nim +++ b/compiler/suggestsymdb.nim @@ -16,6 +16,7 @@ type caughtExceptions*: seq[PType] caughtExceptionsSet*: bool isDecl*: bool + isGenericInstance*: bool SuggestFileSymbolDatabase* = object lineInfo*: seq[TinyLineInfo] @@ -23,6 +24,7 @@ type caughtExceptions*: seq[seq[PType]] caughtExceptionsSet*: PackedBoolArray isDecl*: PackedBoolArray + isGenericInstance*: PackedBoolArray fileIndex*: FileIndex trackCaughtExceptions*: bool isSorted*: bool @@ -82,6 +84,11 @@ proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair = s.caughtExceptionsSet[idx] else: false, + isGenericInstance: + if s.trackCaughtExceptions: + s.isGenericInstance[idx] + else: + false, isDecl: s.isDecl[idx] ) @@ -90,6 +97,7 @@ proc reverse*(s: var SuggestFileSymbolDatabase) = s.sym.reverse() s.caughtExceptions.reverse() s.caughtExceptionsSet.reverse() + s.isGenericInstance.reverse() s.isDecl.reverse() proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase = @@ -99,6 +107,7 @@ proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions caughtExceptions: @[], caughtExceptionsSet: newPackedBoolArray(), isDecl: newPackedBoolArray(), + isGenericInstance: newPackedBoolArray(), fileIndex: aFileIndex, trackCaughtExceptions: aTrackCaughtExceptions, isSorted: true @@ -119,6 +128,8 @@ func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int = result = cmp(s.lineInfo[i], s.lineInfo[j]) if result == 0: result = cmp(s.isDecl[i], s.isDecl[j]) + if result == 0 and s.trackCaughtExceptions: + result = cmp(s.isGenericInstance[i], s.isGenericInstance[j]) proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) = if i == j: @@ -133,6 +144,9 @@ proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) = var tmp3 = s.caughtExceptionsSet[i] s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j] s.caughtExceptionsSet[j] = tmp3 + var tmp6 = s.isGenericInstance[i] + s.isGenericInstance[i] = s.isGenericInstance[j] + s.isGenericInstance[j] = tmp6 var tmp4 = s.isDecl[i] s.isDecl[i] = s.isDecl[j] s.isDecl[j] = tmp4 @@ -196,12 +210,17 @@ proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) = if s.trackCaughtExceptions: s.caughtExceptions.add(v.caughtExceptions) s.caughtExceptionsSet.add(v.caughtExceptionsSet) + s.isGenericInstance.add(v.isGenericInstance) s.isSorted = false proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) = s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v) -proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int = +proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo; isGenericInstance: bool): int = + # if trackCaughtExceptions is false, then all records in the database are not generic instances, so + # if we're searching for a generic instance, we find none + if isGenericInstance and not s.trackCaughtExceptions: + return -1 doAssert(li.fileIndex == s.fileIndex) if not s.isSorted: s.sort() @@ -210,3 +229,17 @@ proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int = col: li.col ) result = binarySearch(s.lineInfo, q, cmp) + # if trackCaughtExceptions is false, then all records in the database are not generic instances, so + # if we're a searching for a non-generic instance, then we're done, we return what we have found + if not isGenericInstance and not s.trackCaughtExceptions: + return + # in this case trackCaughtExceptions is true, and the database contains both generic and non-generic instances, so we need + # to check the isGenericInstance flag also + if result != -1: + # search through a sequence of equal lineInfos to find a matching isGenericInstance + while result > 0 and s.isGenericInstance[result] != isGenericInstance and cmp(s.lineInfo[result], s.lineInfo[result - 1]) == 0: + dec result + while result < (s.lineInfo.len - 1) and s.isGenericInstance[result] != isGenericInstance and cmp(s.lineInfo[result], s.lineInfo[result + 1]) == 0: + inc result + if s.isGenericInstance[result] != isGenericInstance: + result = -1 diff --git a/compiler/types.nim b/compiler/types.nim index 8744f173ce..bd65c3f331 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -766,7 +766,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = prag.add("effectsOf: ") prag.add(effectsOfStr) if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0): - let effects = t.owner.typ.n[0] + let effects = t.n[0] if effects.kind == nkEffectList and effects.len == effectListLen: var inferredRaisesStr = "" let effs = effects[exceptionEffects] diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index f77e03aeee..6144352f05 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -822,7 +822,7 @@ func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair] result.add(itm) result.reverse() -func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDatabase = +func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase, isGenericInstance: bool): SuggestFileSymbolDatabase = # xs contains duplicate items and we want to filter them by range because the # sym may not match. This can happen when xs contains the same definition but # with different signature because suggestSym might be called multiple times @@ -833,6 +833,7 @@ func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDat isDecl: newPackedBoolArray(), caughtExceptions: newSeqOfCap[seq[PType]](xs.caughtExceptions.len), caughtExceptionsSet: newPackedBoolArray(), + isGenericInstance: newPackedBoolArray(), fileIndex: xs.fileIndex, trackCaughtExceptions: xs.trackCaughtExceptions, isSorted: false @@ -846,14 +847,16 @@ func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDat found = true break if not found: - result.add(xs.getSymInfoPair(i)) + let q = xs.getSymInfoPair(i) + if q.isGenericInstance == isGenericInstance: + result.add(q) dec i result.reverse() -proc findSymData(graph: ModuleGraph, trackPos: TLineInfo): +proc findSymData(graph: ModuleGraph, trackPos: TLineInfo, isGenericInstance: bool = false): ref SymInfoPair = result = nil - let db = graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair + let db = graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair(isGenericInstance) doAssert(db.fileIndex == trackPos.fileIndex) for i in db.lineInfo.low..db.lineInfo.high: if isTracked(db.lineInfo[i], TinyLineInfo(line: trackPos.line, col: trackPos.col), db.sym[i].name.s.len): @@ -867,28 +870,28 @@ func isInRange*(current, startPos, endPos: TinyLineInfo, tokenLen: int): bool = (current.line > startPos.line or (current.line == startPos.line and current.col>=startPos.col)) and (current.line < endPos.line or (current.line == endPos.line and current.col <= endPos.col)) -proc findSymDataInRange(graph: ModuleGraph, startPos, endPos: TLineInfo): +proc findSymDataInRange(graph: ModuleGraph, startPos, endPos: TLineInfo, isGenericInstance: bool = false): seq[SymInfoPair] = result = newSeq[SymInfoPair]() - let db = graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair + let db = graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair(isGenericInstance) for i in db.lineInfo.low..db.lineInfo.high: if isInRange(db.lineInfo[i], TinyLineInfo(line: startPos.line, col: startPos.col), TinyLineInfo(line: endPos.line, col: endPos.col), db.sym[i].name.s.len): result.add(db.getSymInfoPair(i)) -proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int): +proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int, isGenericInstance: bool = false): ref SymInfoPair = let fileIdx = fileInfoIdx(graph.config, file) trackPos = newLineInfo(fileIdx, line, col) - result = findSymData(graph, trackPos) + result = findSymData(graph, trackPos, isGenericInstance) -proc findSymDataInRange(graph: ModuleGraph, file: AbsoluteFile; startLine, startCol, endLine, endCol: int): +proc findSymDataInRange(graph: ModuleGraph, file: AbsoluteFile; startLine, startCol, endLine, endCol: int, isGenericInstance: bool = false): seq[SymInfoPair] = let fileIdx = fileInfoIdx(graph.config, file) startPos = newLineInfo(fileIdx, startLine, startCol) endPos = newLineInfo(fileIdx, endLine, endCol) - result = findSymDataInRange(graph, startPos, endPos) + result = findSymDataInRange(graph, startPos, endPos, isGenericInstance) proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) = let sha = $sha1.secureHashFile(file) @@ -937,7 +940,7 @@ proc suggestInlayHintResultException(graph: ModuleGraph, sym: PSym, info: TLineI if sym.kind == skParam and sfEffectsDelayed in sym.flags: return - var raisesList: seq[PType] = @[getEbase(graph, info)] + var raisesList: seq[PType] = @[] let t = sym.typ if not isNil(t) and not isNil(t.n) and t.n.len > 0 and t.n[0].len > exceptionEffects: @@ -945,7 +948,6 @@ proc suggestInlayHintResultException(graph: ModuleGraph, sym: PSym, info: TLineI if effects.kind == nkEffectList and effects.len == effectListLen: let effs = effects[exceptionEffects] if not isNil(effs): - raisesList = @[] for eff in items(effs): if not isNil(eff): raisesList.add(eff.typ) @@ -1154,7 +1156,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, incl m.flags, sfDirty of ideOutline: let n = parseFile(fileIndex, graph.cache, graph.config) - graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair) + graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair(false)) of ideChk: myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)" for sug in graph.suggestErrorsIter: @@ -1206,7 +1208,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, # find first mention of the symbol in the file containing the definition. # It is either the definition or the declaration. var first: SymInfoPair = default(SymInfoPair) - let db = graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair + let db = graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair(false) for i in db.lineInfo.low..db.lineInfo.high: if s.sym.symbolEqual(db.sym[i]): first = db.getSymInfoPair(i) @@ -1279,12 +1281,16 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, else: myLog fmt "Discarding unknown inlay hint parameter {token}" - let s = graph.findSymDataInRange(file, line, col, endLine, endCol) - for q in s: - if typeHints and q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType: - graph.suggestInlayHintResultType(q.sym, q.info, ideInlayHints) - if exceptionHints and q.sym.kind in {skProc, skFunc, skMethod, skVar, skLet, skParam} and not q.isDecl: - graph.suggestInlayHintResultException(q.sym, q.info, ideInlayHints, caughtExceptions = q.caughtExceptions, caughtExceptionsSet = q.caughtExceptionsSet) + if typeHints: + let s = graph.findSymDataInRange(file, line, col, endLine, endCol, false) + for q in s: + if typeHints and q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType: + graph.suggestInlayHintResultType(q.sym, q.info, ideInlayHints) + if exceptionHints: + let sGen = graph.findSymDataInRange(file, line, col, endLine, endCol, true) + for q in sGen: + if q.sym.kind in {skProc, skFunc, skMethod, skVar, skLet, skParam} and not q.isDecl: + graph.suggestInlayHintResultException(q.sym, q.info, ideInlayHints, caughtExceptions = q.caughtExceptions, caughtExceptionsSet = q.caughtExceptionsSet) else: myLog fmt "Discarding {cmd}"