fix: allow lexical import shadowing
This commit is contained in:
@@ -3642,13 +3642,16 @@ fn cgdot(c: *cgen, n: *syntax.node) void = {
|
||||
// → inline the pre-computed constant. `pkg.Enum.MEMBER` keeps
|
||||
// `pkg` so enumlookupmod can prefer the explicit module on a
|
||||
// leaf collision; bare `Enum.MEMBER` falls back to c.curmod via
|
||||
// enumlookup's same-module-first walk.
|
||||
// enumlookup's same-module-first walk. A live local with the same
|
||||
// spelling wins before this name-only registry fallback; valid local
|
||||
// enum selectors were already folded by the checker.
|
||||
if (lhs != nil) {
|
||||
let etname: str;
|
||||
let etmod: str;
|
||||
etname.ptr = nil; etname.len = 0;
|
||||
etmod.ptr = nil; etmod.len = 0;
|
||||
if (lhs.kind == syntax.nkind.N_IDENT) {
|
||||
if (lhs.kind == syntax.nkind.N_IDENT
|
||||
&& localfindnode(c, lhs.str) == nil) {
|
||||
etname = lhs.str;
|
||||
};
|
||||
if (lhs.kind == syntax.nkind.N_DOT) {
|
||||
|
||||
@@ -37,9 +37,8 @@ type checker = struct {
|
||||
// preference in bare-leaf lookups.
|
||||
cursource: i32, // lexical source-file scope of the current decl;
|
||||
// selects only that file's import bindings.
|
||||
file: *syntax.node, // N_FILE root; used by checkmoduleshadow
|
||||
// to consult the declaring source's own
|
||||
// `use` directives.
|
||||
file: *syntax.node, // N_FILE root; owns source-local `use`
|
||||
// bindings and their usage state.
|
||||
allococtx: *syntax.node, // #3/B': the one empty alloc([], n) call node
|
||||
// with let-declared slice context this walk;
|
||||
// any other empty alloc has no element hint
|
||||
@@ -224,6 +223,24 @@ fn usepathfor(file: *syntax.node, modtag: str, source: i32, alias: str) str = {
|
||||
// modkeyfor — the module key for a directly imported alias. Empty means
|
||||
// the referencing package did not itself declare that import.
|
||||
fn modkeyfor(c: *checker, alias: str) str = {
|
||||
let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, alias);
|
||||
if (found != nil && found.scope != c.top
|
||||
&& (found.skind == syntax.skind.SK_VAR
|
||||
|| found.skind == syntax.skind.SK_PARAM)) {
|
||||
let empty: str;
|
||||
return empty;
|
||||
};
|
||||
// Imported interface declarations may qualify their own package from the
|
||||
// source-0 section without a source import node. Preserve that bridge, but
|
||||
// only after a closer lexical binding has been excluded above.
|
||||
if (c.cursource == 0 && c.curmod.len != 0) {
|
||||
let (prefix, suffix) = strings.rcut(c.curmod, ".");
|
||||
let leaf: str = suffix;
|
||||
if (leaf.len == 0) { leaf = c.curmod; };
|
||||
if (syntax.streq(alias, leaf)) {
|
||||
return usepathfor(c.file, c.curmod, c.cursource, alias);
|
||||
};
|
||||
};
|
||||
return usepathfor(c.file, c.curmod, c.cursource, alias);
|
||||
};
|
||||
|
||||
@@ -264,6 +281,23 @@ fn srcimports(file: *syntax.node, modtag: str, source: i32, name: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// A package-name object remains in the file scope when a closer lexical value
|
||||
// wins lookup. Keep this predicate restricted to that newly reachable path so
|
||||
// unrelated WW selector extensions retain their established diagnostics.
|
||||
fn localshadowsimport(c: *checker, name: str) bool = {
|
||||
if (!srcimports(c.file, c.curmod, c.cursource, name)) { return false; };
|
||||
let s: *syntax.scope = c.cur;
|
||||
for (s != nil && s != c.top) {
|
||||
let found: *syntax.sym = syntax.scopelookuplocal(s, name);
|
||||
if (found != nil) {
|
||||
return found.skind == syntax.skind.SK_VAR
|
||||
|| found.skind == syntax.skind.SK_PARAM;
|
||||
};
|
||||
s = s.parent;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn lookupvisible(c: *checker, name: str) *syntax.sym = {
|
||||
let found: *syntax.sym = syntax.scopelookupprefer(c.cur, c.curmod, name);
|
||||
let builtin: *syntax.sym = nil;
|
||||
@@ -398,39 +432,6 @@ fn synthesizedtestrun(c: *checker, e: *syntax.node) bool = {
|
||||
return c.synthtestrun != nil && c.synthtestrun == e;
|
||||
};
|
||||
|
||||
// checkmoduleshadow — enforce "value names and module names are
|
||||
// disjoint" at nested-scope binds. Mirrors cstage check_module_shadow
|
||||
// (cmd/wcc/check.c). Fires for fn params / lets / forrange iters /
|
||||
// mcase bindings whose name matches an in-scope `use foo;` import
|
||||
// declared in the same source file. Top-level decls are exempt
|
||||
// (their same-leaf-as-module pattern is the intentional coexistence
|
||||
// shape — `use fnmatch; fn fnmatch(...)` etc.).
|
||||
fn checkmoduleshadow(c: *checker, name: str, kindstr: str) void = {
|
||||
if (name.len == 0) { return; };
|
||||
if (c.cur == c.top) { return; };
|
||||
let seen: bool = false;
|
||||
let s: *syntax.scope = c.cur;
|
||||
for (s != nil) {
|
||||
let r: *syntax.sym = syntax.scopelookuplocal(s, name);
|
||||
if (r != nil) {
|
||||
if (r.skind == syntax.skind.SK_USE) {
|
||||
seen = true;
|
||||
s = nil;
|
||||
};
|
||||
};
|
||||
if (s != nil) { s = s.parent; };
|
||||
};
|
||||
if (!seen) { return; };
|
||||
if (!srcimports(c.file, c.curmod, c.cursource, name)) { return; };
|
||||
cerr(kindstr);
|
||||
cerr(" '");
|
||||
cerr(name);
|
||||
cerr("' shadows imported module '");
|
||||
cerr(name);
|
||||
cerr("'\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
|
||||
// installdecl — install the top-level decl's name into the top scope.
|
||||
// We don't compute its type yet (that's the resolve pass) — just bind
|
||||
// the name so forward references resolve.
|
||||
@@ -593,9 +594,11 @@ fn installtop(c: *checker, d: *syntax.node, nm: str, mod: str, k: syntax.skind,
|
||||
// `let (a,b) = f()`, `for (let (a,b) .. s)`, and the ww-extension
|
||||
// multi-assign `a, _ = f()`.
|
||||
//
|
||||
// `define` (the binding contexts: let-unpack + for-range) installs each
|
||||
// named binder as a fresh SK_VAR and back-fills its declared type onto
|
||||
// .lhs so use sites resolve through the N_IDENT exprtype path. Multi-
|
||||
// `define` (the binding contexts: let-unpack + for-range) back-fills and
|
||||
// resolves every declared type before installing any named binder as a fresh
|
||||
// SK_VAR. This gives the whole declaration header the outer lexical scope,
|
||||
// matching the Cstage twin and Go's VarSpec/ShortVarDecl declaration point.
|
||||
// Multi-
|
||||
// assign targets are pre-declared lvalues, so it passes false: .lhs is
|
||||
// left untouched (an N_INDEX/N_DOT target carries a live operand there)
|
||||
// and only the type_ stamp fires on the still-untyped slots.
|
||||
@@ -614,23 +617,6 @@ fn stamptuplebinds(c: *checker, binds: *syntax.node, elems: *syntax.node,
|
||||
if (pt != nil) { et = pt.lhs; };
|
||||
if (define) {
|
||||
if (b.lhs == nil) { b.lhs = et; };
|
||||
let bnm: str = b.str;
|
||||
if (bnm.len > 0) {
|
||||
checkmoduleshadow(c, bnm, what);
|
||||
// first registration wins; a nil return is a
|
||||
// same-scope duplicate — `let (a, a) = ..` /
|
||||
// `for (let (a, a) .. xs)`. Mirrors cstage
|
||||
// scope_define == NULL → "redeclared".
|
||||
let ds: *syntax.sym = syntax.scopedefine(c.cur, bnm, syntax.skind.SK_VAR, nil, b);
|
||||
if (ds == nil) {
|
||||
cerr("error: ");
|
||||
cerr(what);
|
||||
cerr(" '");
|
||||
cerr(bnm);
|
||||
cerr("' redeclared in same scope\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (b.type_ == nil) {
|
||||
let src: *syntax.node = b.lhs;
|
||||
@@ -643,6 +629,27 @@ fn stamptuplebinds(c: *checker, binds: *syntax.node, elems: *syntax.node,
|
||||
b = b.next;
|
||||
if (pt != nil) { pt = pt.next; };
|
||||
};
|
||||
if (define) {
|
||||
b = binds;
|
||||
for (b != nil) {
|
||||
let bnm: str = b.str;
|
||||
if (bnm.len > 0) {
|
||||
// First registration wins; a nil return is a same-scope
|
||||
// duplicate within this declaration.
|
||||
let ds: *syntax.sym = syntax.scopedefine(c.cur, bnm,
|
||||
syntax.skind.SK_VAR, nil, b);
|
||||
if (ds == nil) {
|
||||
cerr("error: ");
|
||||
cerr(what);
|
||||
cerr(" '");
|
||||
cerr(bnm);
|
||||
cerr("' redeclared in same scope\n");
|
||||
c.errs += 1;
|
||||
};
|
||||
};
|
||||
b = b.next;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// resolvewalk — recursive AST walk that, for every nkind.N_IDENT and
|
||||
@@ -818,7 +825,6 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
} else {
|
||||
let bnm: str = n.str;
|
||||
if (bnm.len > 0) {
|
||||
checkmoduleshadow(c, bnm, "binding");
|
||||
// C4 (task #7): bind the ELEMENT type so field
|
||||
// reads off a by-value aggregate binding
|
||||
// (`for (let t .. threads) { t.pc }`) resolve —
|
||||
@@ -876,11 +882,13 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
};
|
||||
|
||||
// `for (init; cond; post) body` / `for (cond) body` — the body is the
|
||||
// break/continue target. Walk init/cond/post outside the loop count
|
||||
// (they hold no statements), bump only around the body, and keep the
|
||||
// `else` outside (it targets an enclosing loop, like N_FORRANGE above).
|
||||
// break/continue target. Its initializer scope covers cond/post/body/else;
|
||||
// only the body is inside the loop count, so `else` still targets an
|
||||
// enclosing loop.
|
||||
// Mirrors cstage cmd/wcc/check.c:2529 (N_FOR, c->loops++).
|
||||
if (k == syntax.nkind.N_FOR) {
|
||||
let forouter: *syntax.scope = c.cur;
|
||||
c.cur = syntax.newscope(forouter);
|
||||
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
|
||||
if (n.cond != nil) { resolvewalk(c, n.cond); };
|
||||
if (n.rhs != nil) { resolvewalk(c, n.rhs); };
|
||||
@@ -888,6 +896,7 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
if (n.body != nil) { resolvewalk(c, n.body); };
|
||||
c.loops -= 1;
|
||||
if (n.els != nil) { resolvewalk(c, n.els); };
|
||||
c.cur = forouter;
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -930,7 +939,6 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
c.cur = syntax.newscope(outer);
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
checkmoduleshadow(c, nm, "binding");
|
||||
syntax.scopedefine(c.cur, nm, syntax.skind.SK_VAR, nil, n);
|
||||
};
|
||||
if (n.body != nil) {
|
||||
@@ -997,8 +1005,6 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
// the un-annotated binder stays untyped and asserttyped aborts.
|
||||
// Mirrors cstage check.c:2017 (cexpr(rhs), unconditional).
|
||||
if (n.rhs != nil) {
|
||||
// `rt` would shadow the imported lib/rt module
|
||||
// (checkmoduleshadow errors); `rty` avoids it.
|
||||
// #99 alias transparency: a NAMED tuple alias rhs
|
||||
// (`type pair = (i64,i64)`) destructures like its
|
||||
// base — resolvealias mirrors cstage's
|
||||
@@ -1158,7 +1164,6 @@ fn resolvewalk(c: *checker, n: *syntax.node) void = {
|
||||
if (k == syntax.nkind.N_LET) {
|
||||
let nm: str = n.str;
|
||||
if (nm.len > 0) {
|
||||
checkmoduleshadow(c, nm, "let");
|
||||
let s: *syntax.sym = syntax.scopedefine(c.cur, nm, syntax.skind.SK_VAR, nil, n);
|
||||
// catB-22: flag a `const` binding so checkassign can
|
||||
// reject a later reassignment. The parser stamps
|
||||
@@ -4868,8 +4873,7 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
};
|
||||
}; };
|
||||
// A.6.1.5b stamp cases — mirror cstage check.c:833-866. Pure
|
||||
// type-AST stamps; never rewrite e.kind. Lenient on misses
|
||||
// (cstage errors); falls through to nil under scruttype L656.
|
||||
// type-AST stamps; never rewrite e.kind.
|
||||
//
|
||||
// Pseudo-fields .len/.cap/.ptr on slice/str/array. Cstage
|
||||
// L833-842. `str` lives as N_TNAME("str") in wwstage — no
|
||||
@@ -4938,6 +4942,23 @@ fn exprtype(c: *checker, e: *syntax.node, hint: *syntax.node) *syntax.node = {
|
||||
};
|
||||
};
|
||||
}; };
|
||||
// A same-spelled lexical receiver that fails every valid local
|
||||
// selector shape is the newly reachable shadowing error path. Stamp
|
||||
// recovery so asserttyped and cgen cannot replace the causal,
|
||||
// positioned diagnostic. Missing struct/tuple/enum members use this
|
||||
// same shadow-specific spelling in both compiler stages.
|
||||
if (lhsn != nil && lhsn.kind == syntax.nkind.N_IDENT
|
||||
&& localshadowsimport(c, lhsn.str)) {
|
||||
// Cstage's N_DOT position is the receiver start; use the
|
||||
// identifier position rather than WW parser's dot token.
|
||||
cerr(lhsn.file); cerr(":");
|
||||
cerr(strconv.i32tos(lhsn.line, strconv.base.DEC)); cerr(":");
|
||||
cerr(strconv.i32tos(lhsn.col, strconv.base.DEC));
|
||||
cerr(": error: selector '"); cerr(e.str);
|
||||
cerr("' undefined\n");
|
||||
c.errs += 1;
|
||||
e.type_ = c.tc.tyerr: *void;
|
||||
};
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
@@ -7631,7 +7652,6 @@ fn installparams(c: *checker, params: *syntax.node) void = {
|
||||
normalizevariadicparam(p);
|
||||
let nm: str = p.str;
|
||||
if (nm.len > 0) {
|
||||
checkmoduleshadow(c, nm, "param");
|
||||
syntax.scopedefine(c.cur, nm, syntax.skind.SK_PARAM, nil, p);
|
||||
};
|
||||
};
|
||||
@@ -7846,39 +7866,195 @@ fn topdeclkind(d: *syntax.node) bool = {
|
||||
|| d.kind == syntax.nkind.N_LET);
|
||||
};
|
||||
|
||||
// Record only qualified syntax in each owning source before name resolution.
|
||||
// This preserves source-position import diagnostics and never lets a failed
|
||||
// bare lookup consume an ordinary import.
|
||||
fn markimportusesnode(c: *checker, n: *syntax.node, owner: str,
|
||||
source: i32) void = {
|
||||
// Mark only a qualifier whose spelling still denotes the source-local package
|
||||
// binding. The scratch scope contains lexical binders only; package and import
|
||||
// symbols remain owned by the real checker scope.
|
||||
fn markimportqualifier(c: *checker, scope: *syntax.scope, owner: str,
|
||||
source: i32, alias: str) void = {
|
||||
if (alias.len == 0) { return; };
|
||||
if (syntax.scopelookup(scope, alias) != nil) { return; };
|
||||
if (!srcimports(c.file, owner, source, alias)) { return; };
|
||||
let marked: str = usepathfor(c.file, owner, source, alias);
|
||||
};
|
||||
|
||||
fn markimportusesnode(c: *checker, n: *syntax.node, scope: *syntax.scope,
|
||||
owner: str, source: i32, top: bool) void = {
|
||||
if (n == nil) { return; };
|
||||
if (n.kind == syntax.nkind.N_TNAME) {
|
||||
// strings.rcut returns the whole input as its head on a miss. Require
|
||||
// a real qualifier separator so a bare package TNAME never counts as use.
|
||||
let k: syntax.nkind = n.kind;
|
||||
if (k == syntax.nkind.N_USE) { return; };
|
||||
|
||||
if (k == syntax.nkind.N_TNAME) {
|
||||
if (strings.contains(n.str, ".")) {
|
||||
let (head, leaf) = strings.rcut(n.str, ".");
|
||||
findusepath(c.file, owner, source, head, true);
|
||||
markimportqualifier(c, scope, owner, source, head);
|
||||
};
|
||||
} else { if (n.kind == syntax.nkind.N_DOT && n.lhs != nil
|
||||
&& n.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
findusepath(c.file, owner, source, n.lhs.str, true);
|
||||
}; };
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_DOT) {
|
||||
if (n.lhs != nil && n.lhs.kind == syntax.nkind.N_IDENT) {
|
||||
markimportqualifier(c, scope, owner, source, n.lhs.str);
|
||||
} else {
|
||||
markimportusesnode(c, n.lhs, scope, owner, source, false);
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_FNDECL) {
|
||||
let p: *syntax.node = n.attr;
|
||||
for (p != nil) {
|
||||
markimportusesnode(c, p, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
markimportusesnode(c, n.lhs, scope, owner, source, false);
|
||||
p = n.list;
|
||||
for (p != nil) {
|
||||
let a: *syntax.node = p.attr;
|
||||
for (a != nil) {
|
||||
markimportusesnode(c, a, scope, owner, source, false);
|
||||
a = a.next;
|
||||
};
|
||||
markimportusesnode(c, p.lhs, scope, owner, source, false);
|
||||
markimportusesnode(c, p.rhs, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
let fnscope: *syntax.scope = syntax.newscope(scope);
|
||||
p = n.list;
|
||||
for (p != nil) {
|
||||
if (p.kind == syntax.nkind.N_PARAM && p.str.len > 0) {
|
||||
syntax.scopedefine(fnscope, p.str,
|
||||
syntax.skind.SK_PARAM, nil, p);
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
markimportusesnode(c, n.body, fnscope, owner, source, false);
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_BLOCK) {
|
||||
let inner: *syntax.scope = syntax.newscope(scope);
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) {
|
||||
markimportusesnode(c, p, inner, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_LET) {
|
||||
let p: *syntax.node = n.attr;
|
||||
for (p != nil) {
|
||||
markimportusesnode(c, p, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
markimportusesnode(c, n.lhs, scope, owner, source, false);
|
||||
markimportusesnode(c, n.rhs, scope, owner, source, false);
|
||||
if (!top && n.str.len > 0) {
|
||||
syntax.scopedefine(scope, n.str, syntax.skind.SK_VAR, nil, n);
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_MLET) {
|
||||
let a: *syntax.node = n.attr;
|
||||
for (a != nil) {
|
||||
markimportusesnode(c, a, scope, owner, source, false);
|
||||
a = a.next;
|
||||
};
|
||||
markimportusesnode(c, n.rhs, scope, owner, source, false);
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) {
|
||||
a = p.attr;
|
||||
for (a != nil) {
|
||||
markimportusesnode(c, a, scope, owner, source, false);
|
||||
a = a.next;
|
||||
};
|
||||
markimportusesnode(c, p.lhs, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
p = n.list;
|
||||
for (p != nil) {
|
||||
if (p.str.len > 0) {
|
||||
syntax.scopedefine(scope, p.str, syntax.skind.SK_VAR, nil, p);
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_FOR) {
|
||||
let inner: *syntax.scope = syntax.newscope(scope);
|
||||
markimportusesnode(c, n.lhs, inner, owner, source, false);
|
||||
markimportusesnode(c, n.cond, inner, owner, source, false);
|
||||
markimportusesnode(c, n.rhs, inner, owner, source, false);
|
||||
markimportusesnode(c, n.body, inner, owner, source, false);
|
||||
markimportusesnode(c, n.els, inner, owner, source, false);
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_FORRANGE) {
|
||||
markimportusesnode(c, n.lhs, scope, owner, source, false);
|
||||
let inner: *syntax.scope = syntax.newscope(scope);
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) {
|
||||
let a: *syntax.node = p.attr;
|
||||
for (a != nil) {
|
||||
markimportusesnode(c, a, scope, owner, source, false);
|
||||
a = a.next;
|
||||
};
|
||||
markimportusesnode(c, p.lhs, scope, owner, source, false);
|
||||
if (p.str.len > 0) {
|
||||
syntax.scopedefine(inner, p.str, syntax.skind.SK_VAR, nil, p);
|
||||
};
|
||||
p = p.next;
|
||||
};
|
||||
if (n.list == nil && n.str.len > 0) {
|
||||
syntax.scopedefine(inner, n.str, syntax.skind.SK_VAR, nil, n);
|
||||
};
|
||||
markimportusesnode(c, n.body, inner, owner, source, false);
|
||||
markimportusesnode(c, n.els, scope, owner, source, false);
|
||||
return;
|
||||
};
|
||||
|
||||
if (k == syntax.nkind.N_MCASE) {
|
||||
markimportusesnode(c, n.lhs, scope, owner, source, false);
|
||||
let p: *syntax.node = n.list;
|
||||
for (p != nil) {
|
||||
markimportusesnode(c, p, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
let inner: *syntax.scope = syntax.newscope(scope);
|
||||
if (n.str.len > 0) {
|
||||
syntax.scopedefine(inner, n.str, syntax.skind.SK_VAR, nil, n);
|
||||
};
|
||||
markimportusesnode(c, n.body, inner, owner, source, false);
|
||||
return;
|
||||
};
|
||||
|
||||
let p: *syntax.node = n.attr;
|
||||
for (p != nil) { markimportusesnode(c, p, owner, source); p = p.next; };
|
||||
markimportusesnode(c, n.lhs, owner, source);
|
||||
markimportusesnode(c, n.rhs, owner, source);
|
||||
markimportusesnode(c, n.cond, owner, source);
|
||||
markimportusesnode(c, n.body, owner, source);
|
||||
markimportusesnode(c, n.els, owner, source);
|
||||
for (p != nil) {
|
||||
markimportusesnode(c, p, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
markimportusesnode(c, n.lhs, scope, owner, source, false);
|
||||
markimportusesnode(c, n.rhs, scope, owner, source, false);
|
||||
markimportusesnode(c, n.cond, scope, owner, source, false);
|
||||
markimportusesnode(c, n.body, scope, owner, source, false);
|
||||
markimportusesnode(c, n.els, scope, owner, source, false);
|
||||
p = n.list;
|
||||
for (p != nil) { markimportusesnode(c, p, owner, source); p = p.next; };
|
||||
for (p != nil) {
|
||||
markimportusesnode(c, p, scope, owner, source, false);
|
||||
p = p.next;
|
||||
};
|
||||
};
|
||||
|
||||
fn markimportuses(c: *checker, file: *syntax.node) void = {
|
||||
let d: *syntax.node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind != syntax.nkind.N_USE) {
|
||||
markimportusesnode(c, d, declmod(file, d), d.sourceid);
|
||||
let scope: *syntax.scope = syntax.newscope(nil);
|
||||
markimportusesnode(c, d, scope, declmod(file, d),
|
||||
d.sourceid, true);
|
||||
};
|
||||
d = d.next;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user