# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module implements lookup helpers. import intsets, ast, astalgo, idents, semdata, types, msgs, options, renderer, nimfix/prettybase, lineinfos, strutils, modulegraphs proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) proc noidentError(conf: ConfigRef; n, origin: PNode) = var m = "" if origin != nil: m.add "in expression '" & origin.renderTree & "': " m.add "identifier expected, but found '" & n.renderTree & "'" localError(conf, n.info, m) proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = ## Retrieve a PIdent from a PNode, taking into account accent nodes. ## ``origin`` can be nil. If it is not nil, it is used for a better ## error message. template handleError(n, origin: PNode) = noidentError(c.config, n, origin) result = getIdent(c.cache, "") case n.kind of nkIdent: result = n.ident of nkSym: result = n.sym.name of nkAccQuoted: case n.len of 0: handleError(n, origin) of 1: result = considerQuotedIdent(c, n[0], origin) else: var id = "" for i in 0.. 0: err.add "\nThis might be caused by a recursive module dependency:\n" err.add c.recursiveDep # prevent excessive errors for 'nim check' c.recursiveDep = "" localError(c.config, info, errGenerated, err) proc lookUp*(c: PContext, n: PNode): PSym = # Looks up a symbol. Generates an error in case of nil. var amb = false case n.kind of nkIdent: result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config) if result == nil: fixSpelling(n, n.ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, n.ident.s) result = errorSym(c, n) of nkSym: result = n.sym of nkAccQuoted: var ident = considerQuotedIdent(c, n) result = searchInScopes(c, ident, amb).skipAlias(n, c.config) if result == nil: fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) result = errorSym(c, n) else: internalError(c.config, n.info, "lookUp") return if amb: #contains(c.ambiguousSymbols, result.id): errorUseQualifier(c, n.info, result) when false: if result.kind == skStub: loadStub(result) type TLookupFlag* = enum checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage} case n.kind of nkIdent, nkAccQuoted: var amb = false var ident = considerQuotedIdent(c, n) if checkModule in flags: result = searchInScopes(c, ident, amb).skipAlias(n, c.config) else: let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config) if candidates.len > 0: result = candidates[0] amb = candidates.len > 1 if amb and checkAmbiguity in flags: errorUseQualifier(c, n.info, candidates) if result == nil: let candidates = allPureEnumFields(c, ident) if candidates.len > 0: result = candidates[0] amb = candidates.len > 1 if amb and checkAmbiguity in flags: errorUseQualifier(c, n.info, candidates) if result == nil and checkUndeclared in flags: fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) result = errorSym(c, n) elif checkAmbiguity in flags and result != nil and amb: errorUseQualifier(c, n.info, result) c.isAmbiguous = amb of nkSym: result = n.sym of nkDotExpr: result = nil var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule}) if m != nil and m.kind == skModule: var ident: PIdent = nil if n[1].kind == nkIdent: ident = n[1].ident elif n[1].kind == nkAccQuoted: ident = considerQuotedIdent(c, n[1]) if ident != nil: if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) else: result = someSym(c.graph, m, ident).skipAlias(n, c.config) if result == nil and checkUndeclared in flags: fixSpelling(n[1], ident, searchInScopes) errorUndeclaredIdentifier(c, n[1].info, ident.s) result = errorSym(c, n[1]) elif n[1].kind == nkSym: result = n[1].sym elif checkUndeclared in flags and n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}: localError(c.config, n[1].info, "identifier expected, but got: " & renderTree(n[1])) result = errorSym(c, n[1]) else: result = nil when false: if result != nil and result.kind == skStub: loadStub(result) proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.importIdx = -1 o.marked = initIntSet() case n.kind of nkIdent, nkAccQuoted: var ident = considerQuotedIdent(c, n) var scope = c.currentScope o.mode = oimNoQualifier while true: result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config) if result != nil: o.currentScope = scope break else: scope = scope.parent if scope == nil: for i in 0..c.imports.high: result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config) if result != nil: o.currentScope = nil o.importIdx = i return result return nil of nkSym: result = n.sym o.mode = oimDone of nkDotExpr: o.mode = oimOtherModule o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule}) if o.m != nil and o.m.kind == skModule: var ident: PIdent = nil if n[1].kind == nkIdent: ident = n[1].ident elif n[1].kind == nkAccQuoted: ident = considerQuotedIdent(c, n[1], n) if ident != nil: if o.m == c.module: # a module may access its private members: result = initIdentIter(o.it, c.topLevelScope.symbols, ident).skipAlias(n, c.config) o.mode = oimSelfModule else: result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config) else: noidentError(c.config, n[1], n) result = errorSym(c, n[1]) of nkClosedSymChoice, nkOpenSymChoice: o.mode = oimSymChoice if n[0].kind == nkSym: result = n[0].sym else: o.mode = oimDone return nil o.symChoiceIndex = 1 o.marked = initIntSet() incl(o.marked, result.id) else: discard when false: if result != nil and result.kind == skStub: loadStub(result) proc lastOverloadScope*(o: TOverloadIter): int = case o.mode of oimNoQualifier: result = if o.importIdx >= 0: 0 elif o.currentScope.isNil: -1 else: o.currentScope.depthLevel of oimSelfModule: result = 1 of oimOtherModule: result = 0 else: result = -1 proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym = assert o.currentScope == nil var idx = o.importIdx+1 o.importIdx = c.imports.len # assume the other imported modules lack this symbol too while idx < c.imports.len: result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config) if result != nil: # oh, we were wrong, some other module had the symbol, so remember that: o.importIdx = idx break inc idx proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym = assert o.currentScope == nil while o.importIdx < c.imports.len: result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config) #while result != nil and result.id in o.marked: # result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]) if result != nil: #assert result.id notin o.marked return result inc o.importIdx proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = case o.mode of oimDone: result = nil of oimNoQualifier: if o.currentScope != nil: assert o.importIdx < 0 result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config) while result == nil: o.currentScope = o.currentScope.parent if o.currentScope != nil: result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config) # BUGFIX: o.it.name <-> n.ident else: o.importIdx = 0 if c.imports.len > 0: result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config) if result == nil: result = nextOverloadIterImports(o, c, n) break elif o.importIdx < c.imports.len: result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config) if result == nil: result = nextOverloadIterImports(o, c, n) else: result = nil of oimSelfModule: result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config) of oimOtherModule: result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config) of oimSymChoice: if o.symChoiceIndex < n.len: result = n[o.symChoiceIndex].sym incl(o.marked, result.id) inc o.symChoiceIndex elif n.kind == nkOpenSymChoice: # try 'local' symbols too for Koenig's lookup: o.mode = oimSymChoiceLocalLookup o.currentScope = c.currentScope result = firstIdentExcluding(o.it, o.currentScope.symbols, n[0].sym.name, o.marked).skipAlias(n, c.config) while result == nil: o.currentScope = o.currentScope.parent if o.currentScope != nil: result = firstIdentExcluding(o.it, o.currentScope.symbols, n[0].sym.name, o.marked).skipAlias(n, c.config) else: o.importIdx = 0 result = symChoiceExtension(o, c, n) break if result != nil: incl o.marked, result.id of oimSymChoiceLocalLookup: if o.currentScope != nil: result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config) while result == nil: o.currentScope = o.currentScope.parent if o.currentScope != nil: result = firstIdentExcluding(o.it, o.currentScope.symbols, n[0].sym.name, o.marked).skipAlias(n, c.config) else: o.importIdx = 0 result = symChoiceExtension(o, c, n) break if result != nil: incl o.marked, result.id elif o.importIdx < c.imports.len: result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config) #assert result.id notin o.marked #while result != nil and result.id in o.marked: # result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config) if result == nil: inc o.importIdx result = symChoiceExtension(o, c, n) when false: if result != nil and result.kind == skStub: loadStub(result) proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; flags: TSymFlags = {}): PSym = var o: TOverloadIter var a = initOverloadIter(o, c, n) while a != nil: if a.kind in kinds and flags <= a.flags: if result == nil: result = a else: return nil # ambiguous a = nextOverloadIter(o, c, n)