compiler: implement blank package name semantics
This commit is contained in:
232
cmd/w6c/main.c
232
cmd/w6c/main.c
@@ -282,62 +282,92 @@ consider_pkgname(const char *path, const char *candidate,
|
||||
*name = candidate;
|
||||
}
|
||||
|
||||
/* A paired interface owns PATH's declared name. Compiler-private names keep
|
||||
* transitive fact sections semantic and are ignored when a real name exists. */
|
||||
static const char *
|
||||
enum pkgname_state {
|
||||
PKGNAME_MISSING,
|
||||
PKGNAME_VALID,
|
||||
PKGNAME_CONFLICT,
|
||||
PKGNAME_INVALID,
|
||||
};
|
||||
|
||||
/* Standalone interfaces may carry closure facts for several canonical
|
||||
* packages. Marker.module, rather than the containing --import path, owns
|
||||
* each declared name. Compiler-private names keep transitive fact sections
|
||||
* semantic and are ignored when a real name exists. */
|
||||
static enum pkgname_state
|
||||
import_pkgname(struct importin *imports, int nimports, const char *path,
|
||||
Node *primary, int *conflict)
|
||||
Node *primary, const char **resolved)
|
||||
{
|
||||
const char *name = NULL;
|
||||
const char *placeholder = NULL;
|
||||
int conflict = 0;
|
||||
for (int i = 0; i < nimports; i++) {
|
||||
if (strcmp(imports[i].path, path) != 0) continue;
|
||||
Node *file = imports[i].ast;
|
||||
for (Node *p = file ? file->body : NULL; p; p = p->next) {
|
||||
if (p->module == NULL || p->pkgname == NULL
|
||||
|| strcmp(p->module, path) != 0)
|
||||
continue;
|
||||
consider_pkgname(path, p->pkgname, &name, &placeholder,
|
||||
conflict);
|
||||
if (*conflict) return NULL;
|
||||
&conflict);
|
||||
if (conflict) return PKGNAME_CONFLICT;
|
||||
}
|
||||
}
|
||||
for (Node *p = primary ? primary->body : NULL; p; p = p->next) {
|
||||
if (p->module == NULL || p->pkgname == NULL
|
||||
|| strcmp(p->module, path) != 0)
|
||||
continue;
|
||||
consider_pkgname(path, p->pkgname, &name, &placeholder, conflict);
|
||||
if (*conflict) return NULL;
|
||||
consider_pkgname(path, p->pkgname, &name, &placeholder, &conflict);
|
||||
if (conflict) return PKGNAME_CONFLICT;
|
||||
}
|
||||
return name ? name : placeholder;
|
||||
*resolved = name ? name : placeholder;
|
||||
if (*resolved == NULL) return PKGNAME_MISSING;
|
||||
if (strcmp(*resolved, "_") == 0) return PKGNAME_INVALID;
|
||||
return PKGNAME_VALID;
|
||||
}
|
||||
|
||||
static const char *
|
||||
path_leaf(const char *path)
|
||||
{
|
||||
const char *dot = path ? strrchr(path, '.') : NULL;
|
||||
return dot ? dot + 1 : path;
|
||||
}
|
||||
|
||||
static int
|
||||
bind_import_names(Node *list, struct importin *imports, int nimports,
|
||||
Node *primary, const char *testsupport)
|
||||
Node *primary, const char *testsupport, Node *external_support)
|
||||
{
|
||||
for (Node *u = list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->usepath == NULL) continue;
|
||||
/* The reserved test-support spelling is a compiler-owned alias, not
|
||||
* source default-import syntax. */
|
||||
if (testsupport != NULL && strcmp(testsupport, "__wwtest") == 0
|
||||
&& strcmp(u->usepath, testsupport) == 0)
|
||||
continue;
|
||||
int conflict = 0;
|
||||
const char *name = import_pkgname(imports, nimports, u->usepath,
|
||||
primary, &conflict);
|
||||
if (conflict) {
|
||||
int reserved = testsupport != NULL
|
||||
&& strcmp(testsupport, "__wwtest") == 0
|
||||
&& strcmp(u->usepath, testsupport) == 0;
|
||||
const char *name = NULL;
|
||||
enum pkgname_state state = import_pkgname(imports, nimports,
|
||||
u->usepath, primary, &name);
|
||||
if (state == PKGNAME_CONFLICT) {
|
||||
fprintf(stderr,
|
||||
"w6c: package %s has conflicting declared names in export data\n",
|
||||
u->usepath);
|
||||
return -1;
|
||||
}
|
||||
if (name != NULL) {
|
||||
if (state == PKGNAME_VALID || state == PKGNAME_INVALID) {
|
||||
u->usepkgname = name;
|
||||
if (!u->useblank) {
|
||||
if (state == PKGNAME_INVALID) {
|
||||
u->used = 1;
|
||||
if (!u->useblank && !reserved) {
|
||||
u->str = u->usealias ? u->usealias
|
||||
: path_leaf(u->usepath);
|
||||
u->strlen = strlen(u->str);
|
||||
}
|
||||
} else if (!u->useblank && !reserved) {
|
||||
u->str = u->usealias ? u->usealias : name;
|
||||
u->strlen = strlen(u->str);
|
||||
}
|
||||
} else if (u == external_support) {
|
||||
/* A bare direct -T compiler invocation deliberately leaves the
|
||||
* compiler-generated support hook external. */
|
||||
u->used = 1;
|
||||
} else if (!u->imported) {
|
||||
fprintf(stderr,
|
||||
"w6c: import %s has no declared package name in direct export data\n",
|
||||
@@ -356,6 +386,121 @@ bind_import_names(Node *list, struct importin *imports, int nimports,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct pathset {
|
||||
const char **v;
|
||||
int n;
|
||||
int cap;
|
||||
};
|
||||
|
||||
static int
|
||||
pathset_has(const struct pathset *s, const char *path)
|
||||
{
|
||||
if (path == NULL) return 0;
|
||||
for (int i = 0; i < s->n; i++)
|
||||
if (strcmp(s->v[i], path) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pathset_add(struct pathset *s, const char *path)
|
||||
{
|
||||
if (path == NULL || pathset_has(s, path)) return 0;
|
||||
if (s->n == s->cap) {
|
||||
int cap = s->cap ? s->cap * 2 : 16;
|
||||
const char **v = realloc(s->v, (size_t)cap * sizeof *v);
|
||||
if (v == NULL) return -1;
|
||||
s->v = v;
|
||||
s->cap = cap;
|
||||
}
|
||||
s->v[s->n++] = path;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static Node *
|
||||
materialize_test_support(Arena *a, Node *file, int testmode,
|
||||
const char *testsupport)
|
||||
{
|
||||
if (!testmode || testsupport == NULL) return NULL;
|
||||
for (Node *u = file->list; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->imported
|
||||
|| u->sourceid != file->sourceid || u->usepath == NULL
|
||||
|| strcmp(u->usepath, testsupport) != 0)
|
||||
continue;
|
||||
u->used = 1;
|
||||
return NULL;
|
||||
}
|
||||
Node *u = newnode(a, N_USE, file->pos);
|
||||
u->str = testsupport;
|
||||
u->strlen = strlen(testsupport);
|
||||
u->usesource = testsupport;
|
||||
u->usepath = testsupport;
|
||||
u->usefile = file->pos.file;
|
||||
u->useline = file->pos.line;
|
||||
u->usecol = file->pos.col;
|
||||
u->usepathfile = file->pos.file;
|
||||
u->usepathline = file->pos.line;
|
||||
u->usepathcol = file->pos.col;
|
||||
u->pkgname = file->pkgname;
|
||||
u->sourceid = file->sourceid;
|
||||
u->used = 1;
|
||||
u->next = file->list;
|
||||
file->list = u;
|
||||
return u;
|
||||
}
|
||||
|
||||
static int
|
||||
compute_reached_imports(struct pathset *reached, Node *primary,
|
||||
struct importin *imports, int nimports)
|
||||
{
|
||||
for (Node *u = primary ? primary->list : NULL; u; u = u->next) {
|
||||
if (u->kind != N_USE || u->imported || u->usepath == NULL) continue;
|
||||
if (pathset_add(reached, u->usepath) < 0) return -1;
|
||||
}
|
||||
int changed;
|
||||
do {
|
||||
changed = 0;
|
||||
for (int i = 0; i < nimports; i++) {
|
||||
for (Node *u = imports[i].ast ? imports[i].ast->list : NULL;
|
||||
u; u = u->next) {
|
||||
if (u->kind != N_USE || !u->imported
|
||||
|| u->module == NULL || u->usepath == NULL
|
||||
|| !pathset_has(reached, u->module))
|
||||
continue;
|
||||
const char *name = NULL;
|
||||
if (import_pkgname(imports, nimports, u->module,
|
||||
NULL, &name) != PKGNAME_VALID)
|
||||
continue;
|
||||
int added = pathset_add(reached, u->usepath);
|
||||
if (added < 0) return -1;
|
||||
if (added) changed = 1;
|
||||
}
|
||||
}
|
||||
} while (changed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
filter_reached_imports(Node **list, const struct pathset *reached,
|
||||
struct importin *imports, int nimports)
|
||||
{
|
||||
Node *prev = NULL;
|
||||
for (Node *d = *list; d; ) {
|
||||
Node *next = d->next;
|
||||
const char *name = NULL;
|
||||
int keep = d->imported && d->module != NULL
|
||||
&& pathset_has(reached, d->module)
|
||||
&& import_pkgname(imports, nimports, d->module, NULL,
|
||||
&name) == PKGNAME_VALID;
|
||||
if (keep)
|
||||
prev = d;
|
||||
else if (prev == NULL)
|
||||
*list = next;
|
||||
else
|
||||
prev->next = next;
|
||||
d = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
appendnodes(Node **head, Node **tail, Node *list)
|
||||
{
|
||||
@@ -563,7 +708,6 @@ main(int argc, char **argv)
|
||||
Checker c;
|
||||
Cg cg;
|
||||
|
||||
Node *head = NULL, *tail = NULL;
|
||||
for (int i = 0; i < nimports; i++) {
|
||||
if (slurp(imports[i].file, &imports[i].buf,
|
||||
&imports[i].len) < 0) {
|
||||
@@ -583,10 +727,6 @@ main(int argc, char **argv)
|
||||
imports[i].len, imports[i].path, testsupport, 0, &bad);
|
||||
if (bad) return 1;
|
||||
imports[i].ast = f;
|
||||
if (bind_import_names(f->list, imports, i + 1, NULL,
|
||||
testsupport) < 0)
|
||||
return 1;
|
||||
appendnodes(&head, &tail, f->list);
|
||||
}
|
||||
|
||||
char *buf;
|
||||
@@ -619,14 +759,33 @@ main(int argc, char **argv)
|
||||
fputs("w6c: --import-map source is not in primary input\n", stderr);
|
||||
return 2;
|
||||
}
|
||||
/* Later direct interfaces may supply names for origin sections referenced
|
||||
* by an earlier interface, so perform one complete metadata pass now. */
|
||||
/* The checker normally synthesizes this compiler-required edge. Make it
|
||||
* an explicit primary root before interface reachability and name
|
||||
* resolution so a supplied support interface cannot bypass validation. */
|
||||
Node *external_support = materialize_test_support(a, file, testmode,
|
||||
testsupport);
|
||||
|
||||
/* Interface containers and embedded origin sections are metadata, not
|
||||
* roots. Only primary uses seed the closure, and only a reached valid
|
||||
* owner may contribute its transitive uses. */
|
||||
struct pathset reached = {0};
|
||||
if (compute_reached_imports(&reached, file, imports, nimports) < 0) {
|
||||
fputs("w6c: out of memory\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
for (int i = 0; i < nimports; i++)
|
||||
filter_reached_imports(&imports[i].ast->list, &reached, imports,
|
||||
nimports);
|
||||
|
||||
/* Bind only retained facts, while consulting the complete package-marker
|
||||
* metadata. Keeping the standalone lists separate here prevents one
|
||||
* interface from escaping its reachability boundary through concatenation. */
|
||||
for (int i = 0; i < nimports; i++)
|
||||
if (bind_import_names(imports[i].ast->list, imports, nimports,
|
||||
NULL, testsupport) < 0)
|
||||
NULL, testsupport, NULL) < 0)
|
||||
return 1;
|
||||
if (bind_import_names(file->list, imports, nimports, file,
|
||||
testsupport) < 0)
|
||||
testsupport, external_support) < 0)
|
||||
return 1;
|
||||
for (int ti = 0; ti < ntesttargets; ti++) {
|
||||
int seen = 0;
|
||||
@@ -634,8 +793,14 @@ main(int argc, char **argv)
|
||||
if (u->kind != N_USE || u->imported || u->usepath == NULL
|
||||
|| strcmp(u->usepath, testtargets[ti]) != 0)
|
||||
continue;
|
||||
u->str = u->usepath;
|
||||
u->strlen = strlen(u->usepath);
|
||||
/* Valid generated target imports use their canonical path as
|
||||
* the compiler-owned qualifier. Preserve an invalid provider's
|
||||
* fake explicit-or-leaf spelling for source-local recovery. */
|
||||
if (u->usepkgname == NULL
|
||||
|| strcmp(u->usepkgname, "_") != 0) {
|
||||
u->str = u->usepath;
|
||||
u->strlen = strlen(u->usepath);
|
||||
}
|
||||
seen++;
|
||||
}
|
||||
if (seen != 1) {
|
||||
@@ -644,6 +809,11 @@ main(int argc, char **argv)
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
free(reached.v);
|
||||
|
||||
Node *head = NULL, *tail = NULL;
|
||||
for (int i = 0; i < nimports; i++)
|
||||
appendnodes(&head, &tail, imports[i].ast->list);
|
||||
if (head != NULL) {
|
||||
tail->next = file->list;
|
||||
file->list = head;
|
||||
|
||||
@@ -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