ww imports: reject init bindings before recovery

This commit is contained in:
2026-08-22 04:27:23 +09:00
parent 4069eda942
commit 78c9eee82d
10 changed files with 789 additions and 15 deletions

View File

@@ -2907,6 +2907,25 @@ decl_mod(Node *file, Node *d)
return NULL;
}
static int
invalid_init_import(Node *u)
{
return u != NULL && u->kind == N_USE && !u->useblank
&& u->str != NULL && strcmp(u->str, "init") == 0;
}
static Pos
import_binding_pos(Node *u)
{
Pos p = u->pos;
if (u->usefile != NULL) {
p.file = u->usefile;
p.line = u->useline;
p.col = u->usecol;
}
return p;
}
/*
* use_path — map a source-file default qualifier (the imported package's
* declared name) to the full canonical import path it binds, for
@@ -2932,7 +2951,7 @@ find_use_path(Node *file, const char *curmod, int source, const char *alias,
if (strcmp(alias, leaf) == 0) return curmod;
}
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->str == NULL
if (u->kind != N_USE || u->str == NULL || invalid_init_import(u)
|| u->sourceid != source || strcmp(u->str, alias) != 0)
continue;
const char *p = u->usepath ? u->usepath : u->str;
@@ -3960,6 +3979,19 @@ check_import_alt(Node *d, const char *name)
d->pos.file ? d->pos.file : "?", d->pos.line, d->pos.col, name);
}
/* Pinned Go 1.26.5 types2 rejects an effective import binding named init and
* immediately continues before creating its PkgName. Diagnose every resolved
* occurrence at the first import-spec token, then keep it out of all binding
* recovery below while retaining the real loader-owned dependency edge. */
static void
reject_init_imports(Checker *c, Node *file)
{
for (Node *u = file->list; u; u = u->next)
if (invalid_init_import(u))
err(c, import_binding_pos(u),
"cannot import package as init - init must be a func");
}
/* Go's default import binding lives in the importing file's scope. Reject
* only another binding in that same source section; equal names in sibling
* files are independent even though their canonical edges are package-wide. */
@@ -3968,11 +4000,11 @@ check_import_redeclarations(Checker *c, Node *file)
{
if (!c->sep_mode) return;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->useblank
if (u->kind != N_USE || u->useblank || invalid_init_import(u)
|| u->imported || u->str == NULL)
continue;
for (Node *v = file->list; v != u; v = v->next) {
if (v->kind != N_USE || v->useblank
if (v->kind != N_USE || v->useblank || invalid_init_import(v)
|| v->imported || v->str == NULL
|| v->sourceid != u->sourceid)
continue;
@@ -3994,7 +4026,7 @@ check_import_usage_and_collisions(Checker *c, Node *file)
{
if (!c->sep_mode) return;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->useblank
if (u->kind != N_USE || u->useblank || invalid_init_import(u)
|| u->imported || u->used || u->str == NULL)
continue;
const char *path = u->usesource ? u->usesource
@@ -4011,7 +4043,7 @@ check_import_usage_and_collisions(Checker *c, Node *file)
if (d->imported || !top_decl_kind(d) || d->str == NULL)
continue;
for (Node *u = file->list; u; u = u->next) {
if (u->kind != N_USE || u->useblank
if (u->kind != N_USE || u->useblank || invalid_init_import(u)
|| u->imported || u->str == NULL
|| strcmp(d->str, u->str) != 0)
continue;
@@ -4078,6 +4110,7 @@ check_file(Checker *c, Node *file)
file->list = usenode;
}
}
reject_init_imports(c, file);
reject_nonfunction_main_decls(c, file);
mark_import_uses(c, file);
check_import_redeclarations(c, file);
@@ -4106,10 +4139,8 @@ check_file(Checker *c, Node *file)
"'%s' cannot import itself", owner);
if (d->useblank)
continue;
if (d->str != NULL && strcmp(d->str, "init") == 0) {
err(c, d->pos, "cannot import package as init - init must be a func");
if (invalid_init_import(d))
continue;
}
Sym *prev = scope_lookup_local(c->cur, d->str);
if (prev != NULL) {
/* Self-import: the driver concatenates the

View File

@@ -1323,6 +1323,12 @@ parseuse(Parser *p)
Pos pp = p->cur.pos;
expect(p, TK_USE);
Node *n = newnode(p->a, N_USE, pp);
/* Keep n->pos at the import keyword for structural diagnostics. Go's
* import declaration position is the first spec token: the explicit alias
* when present, otherwise the path. */
n->usefile = p->cur.pos.file;
n->useline = p->cur.pos.line;
n->usecol = p->cur.pos.col;
const char *alias = NULL;
const char *first;
if (p->cur.kind == TK_UNDER) {

View File

@@ -349,6 +349,10 @@ struct Node {
const char *usepath; /* N_USE: canonical, vendor-expanded identity;
* initially equal to `usesource`. */
const char *usealias; /* N_USE: explicit file-local alias, or NULL. */
const char *usefile; /* N_USE: first import-spec token position;
* alias when explicit, path otherwise. */
int useline;
int usecol;
const char *usepkgname; /* N_USE: imported declared package name,
* independent of the visible binding in `str`. */
int useblank; /* N_USE: `_` spelling; no source binding. */