compiler: implement blank package name semantics
This commit is contained in:
@@ -62,6 +62,8 @@ static const char *use_path(Node *file, const char *curmod, int source,
|
||||
const char *alias);
|
||||
static const char *find_use_path(Node *file, const char *curmod, int source,
|
||||
const char *alias, int mark);
|
||||
static Node *find_use_binding(Node *file, const char *curmod, int source,
|
||||
const char *alias, int mark);
|
||||
static int src_imports(Node *file, const char *modtag, int source,
|
||||
const char *name);
|
||||
static Sym *lookup_visible(Checker *c, const char *name);
|
||||
@@ -96,6 +98,11 @@ resolve_typename(Checker *c, Node *n)
|
||||
char *head = astrndup(c->a, nm, hl);
|
||||
Sym *m = scope_lookup(c->cur, head);
|
||||
if (m && (m->kind == SK_USE || m->use_alias)) {
|
||||
Node *u = find_use_binding(c->file, c->cur_mod,
|
||||
c->cur_source, head, 1);
|
||||
if (u != NULL && u->usepkgname != NULL
|
||||
&& strcmp(u->usepkgname, "_") == 0)
|
||||
return ty_err;
|
||||
/* M1 #22: map the qualifier alias to its dotted
|
||||
* import path (symbols are path-keyed). */
|
||||
const char *mk = use_path(c->file, c->cur_mod,
|
||||
@@ -1521,6 +1528,11 @@ cexpr(Checker *c, Node *n)
|
||||
if (n->lhs && n->lhs->kind == N_IDENT) {
|
||||
Sym *ms = lookup_visible(c, n->lhs->str);
|
||||
if (ms && (ms->kind == SK_USE || ms->use_alias)) {
|
||||
Node *u = find_use_binding(c->file, c->cur_mod,
|
||||
c->cur_source, n->lhs->str, 1);
|
||||
if (u != NULL && u->usepkgname != NULL
|
||||
&& strcmp(u->usepkgname, "_") == 0)
|
||||
return n->type = ty_err;
|
||||
/* Module-qualified ref. `use_alias` covers
|
||||
* the self-import case where the module's
|
||||
* type name shadowed the SK_USE; the leaf
|
||||
@@ -3016,6 +3028,18 @@ import_binding_pos(Node *u)
|
||||
return p;
|
||||
}
|
||||
|
||||
static Pos
|
||||
import_path_pos(Node *u)
|
||||
{
|
||||
Pos p = u->pos;
|
||||
if (u->usepathfile != NULL) {
|
||||
p.file = u->usepathfile;
|
||||
p.line = u->usepathline;
|
||||
p.col = u->usepathcol;
|
||||
}
|
||||
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
|
||||
@@ -3056,6 +3080,33 @@ find_use_path(Node *file, const char *curmod, int source, const char *alias,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Node *
|
||||
find_use_binding(Node *file, const char *curmod, int source,
|
||||
const char *alias, int mark)
|
||||
{
|
||||
if (file == NULL || alias == NULL) return NULL;
|
||||
/* Source-zero self-qualification wins before import lookup in
|
||||
* find_use_path and therefore cannot denote an imported package object. */
|
||||
if (source == 0 && curmod != NULL) {
|
||||
const char *dot = strrchr(curmod, '.');
|
||||
const char *leaf = dot ? dot + 1 : curmod;
|
||||
if (strcmp(alias, leaf) == 0) return NULL;
|
||||
}
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->str == NULL || invalid_init_import(u)
|
||||
|| u->sourceid != source || strcmp(u->str, alias) != 0)
|
||||
continue;
|
||||
const char *um = decl_mod(file, u);
|
||||
int same = (um == NULL) ? (curmod == NULL)
|
||||
: (curmod != NULL && strcmp(um, curmod) == 0);
|
||||
if (same) {
|
||||
if (mark) u->used = 1;
|
||||
return u;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
use_path(Node *file, const char *curmod, int source, const char *alias)
|
||||
{
|
||||
@@ -4379,11 +4430,48 @@ check_test_target(const Checker *c, const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
reject_blank_package_names(Checker *c, Node *file)
|
||||
{
|
||||
for (Node *p = file->body; p; p = p->next)
|
||||
if (p->kind == N_FILE && p->pkgname != NULL
|
||||
&& strcmp(p->pkgname, "_") == 0)
|
||||
err(c, p->pos, "invalid package name _");
|
||||
}
|
||||
|
||||
static void
|
||||
reject_invalid_imports(Checker *c, Node *file)
|
||||
{
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->usepath == NULL
|
||||
|| u->usepkgname == NULL
|
||||
|| strcmp(u->usepkgname, "_") != 0)
|
||||
continue;
|
||||
u->used = 1;
|
||||
int seen = 0;
|
||||
for (Node *v = file->list; v != u; v = v->next) {
|
||||
if (v->kind == N_USE && v->usepath != NULL
|
||||
&& v->usepkgname != NULL
|
||||
&& strcmp(v->usepkgname, "_") == 0
|
||||
&& strcmp(v->usepath, u->usepath) == 0) {
|
||||
seen = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!seen)
|
||||
err(c, import_path_pos(u),
|
||||
"could not import %s (invalid package name: \"_\")",
|
||||
u->usepath);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
check_file(Checker *c, Node *file)
|
||||
{
|
||||
if (file == NULL || file->kind != N_FILE) return;
|
||||
c->file = file;
|
||||
reject_blank_package_names(c, file);
|
||||
reject_invalid_imports(c, file);
|
||||
|
||||
/* Under -T, prepend the dispatcher support import before pass 1 so
|
||||
* decl_mod keys the runner under the selected support module. A pure
|
||||
|
||||
@@ -92,6 +92,24 @@ expectident(Parser *p)
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Package declarations admit the blank identifier syntactically. Keep this
|
||||
* private to the package-name slot: every ordinary identifier production must
|
||||
* retain expectident's rejection of TK_UNDER. The checker owns the semantic
|
||||
* BlankPkgName rejection. */
|
||||
static const char *
|
||||
expectpackagename(Parser *p)
|
||||
{
|
||||
if (p->cur.kind != TK_IDENT && p->cur.kind != TK_UNDER) {
|
||||
errorf(p->cur.pos, "expected identifier, got %s",
|
||||
tokname(p->cur.kind));
|
||||
p->errs++;
|
||||
return "<err>";
|
||||
}
|
||||
const char *s = p->cur.text;
|
||||
advance(p);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Like expectident but also accepts a bare `_` discard marker. The
|
||||
* returned string is the empty string "" so the checker skips
|
||||
* scope_define. Callers that care can detect this with `s[0] == '\0'`. */
|
||||
@@ -1340,19 +1358,25 @@ parseuse(Parser *p)
|
||||
n->usefile = p->cur.pos.file;
|
||||
n->useline = p->cur.pos.line;
|
||||
n->usecol = p->cur.pos.col;
|
||||
Pos pathpos = p->cur.pos;
|
||||
const char *alias = NULL;
|
||||
const char *first;
|
||||
if (p->cur.kind == TK_UNDER) {
|
||||
n->useblank = 1;
|
||||
advance(p);
|
||||
pathpos = p->cur.pos;
|
||||
first = expectident(p);
|
||||
} else {
|
||||
first = expectident(p);
|
||||
if (p->cur.kind == TK_IDENT) {
|
||||
alias = first;
|
||||
pathpos = p->cur.pos;
|
||||
first = expectident(p);
|
||||
}
|
||||
}
|
||||
n->usepathfile = pathpos.file;
|
||||
n->usepathline = pathpos.line;
|
||||
n->usepathcol = pathpos.col;
|
||||
const char *leaf = first;
|
||||
const char *path = leaf;
|
||||
while (accept(p, TK_DOT)) {
|
||||
@@ -1383,11 +1407,13 @@ parseheaderuse(Parser *p)
|
||||
n->usefile = p->cur.pos.file;
|
||||
n->useline = p->cur.pos.line;
|
||||
n->usecol = p->cur.pos.col;
|
||||
Pos pathpos = p->cur.pos;
|
||||
const char *alias = NULL;
|
||||
const char *first;
|
||||
if (p->cur.kind == TK_UNDER) {
|
||||
n->useblank = 1;
|
||||
advance(p);
|
||||
pathpos = p->cur.pos;
|
||||
} else if (p->cur.kind != TK_IDENT) {
|
||||
errorf(p->cur.pos, "expected identifier, got %s",
|
||||
tokname(p->cur.kind));
|
||||
@@ -1404,9 +1430,13 @@ parseheaderuse(Parser *p)
|
||||
advance(p);
|
||||
if (!n->useblank && p->cur.kind == TK_IDENT) {
|
||||
alias = first;
|
||||
pathpos = p->cur.pos;
|
||||
first = p->cur.text;
|
||||
advance(p);
|
||||
}
|
||||
n->usepathfile = pathpos.file;
|
||||
n->usepathline = pathpos.line;
|
||||
n->usepathcol = pathpos.col;
|
||||
const char *leaf = first;
|
||||
const char *path = leaf;
|
||||
while (p->cur.kind == TK_DOT) {
|
||||
@@ -1467,13 +1497,12 @@ parsepackageheader(Parser *p)
|
||||
}
|
||||
Pos pp = p->cur.pos;
|
||||
advance(p);
|
||||
if (p->cur.kind != TK_IDENT) {
|
||||
if (p->cur.kind != TK_IDENT && p->cur.kind != TK_UNDER) {
|
||||
errorf(p->cur.pos, "invalid or missing package clause");
|
||||
p->errs++;
|
||||
return file;
|
||||
}
|
||||
const char *name = p->cur.text;
|
||||
advance(p);
|
||||
const char *name = expectpackagename(p);
|
||||
if (p->cur.kind != TK_SEMI) {
|
||||
errorf(p->cur.pos, "expected ';' after package name");
|
||||
p->errs++;
|
||||
@@ -1548,14 +1577,14 @@ parseimports(Parser *p)
|
||||
Pos pp = p->cur.pos;
|
||||
previmport = 1;
|
||||
advance(p);
|
||||
if (p->cur.kind != TK_IDENT) {
|
||||
if (p->cur.kind != TK_IDENT && p->cur.kind != TK_UNDER) {
|
||||
errorf(p->cur.pos, "invalid or missing package clause");
|
||||
p->errs++;
|
||||
sawpackage = 1;
|
||||
skipdecl(p);
|
||||
continue;
|
||||
}
|
||||
const char *name = expectident(p);
|
||||
const char *name = expectpackagename(p);
|
||||
expect(p, TK_SEMI);
|
||||
p->curpkg = name;
|
||||
if (p->pathmod == NULL && p->resetmod == NULL)
|
||||
@@ -1754,13 +1783,16 @@ parsefile(Parser *p)
|
||||
sawpackage = 1;
|
||||
previmport = 1;
|
||||
advance(p);
|
||||
const char *name = expectident(p);
|
||||
Pos namepos = p->cur.pos;
|
||||
const char *name = expectpackagename(p);
|
||||
expect(p, TK_SEMI);
|
||||
p->curpkg = name;
|
||||
if (p->pathmod == NULL && p->resetmod == NULL) {
|
||||
p->curmod = name;
|
||||
}
|
||||
Node *package = newnode(p->a, N_FILE, packagepos);
|
||||
Pos markerpos = strcmp(name, "_") == 0
|
||||
? namepos : packagepos;
|
||||
Node *package = newnode(p->a, N_FILE, markerpos);
|
||||
package->module = p->curmod;
|
||||
package->pkgname = name;
|
||||
package->sourceid = p->sourceid;
|
||||
|
||||
@@ -354,6 +354,10 @@ struct Node {
|
||||
* alias when explicit, path otherwise. */
|
||||
int useline;
|
||||
int usecol;
|
||||
const char *usepathfile; /* N_USE: first path-token position,
|
||||
* independent of an explicit/blank alias. */
|
||||
int usepathline;
|
||||
int usepathcol;
|
||||
const char *usepkgname; /* N_USE: imported declared package name,
|
||||
* independent of the visible binding in `str`. */
|
||||
int useblank; /* N_USE: `_` spelling; no source binding. */
|
||||
|
||||
Reference in New Issue
Block a user