driver: ignore package documentation sources
This commit is contained in:
@@ -6,6 +6,7 @@ import strconv;
|
||||
import strings;
|
||||
import temp;
|
||||
import time;
|
||||
import ww.syntax;
|
||||
|
||||
type pkgsource = struct {
|
||||
path: str,
|
||||
@@ -165,6 +166,20 @@ type pkgdiscover = struct {
|
||||
fatal: bool,
|
||||
};
|
||||
|
||||
type pkgrequestselection = struct {
|
||||
requested: str,
|
||||
discoverroot: str,
|
||||
start: i32,
|
||||
end: i32,
|
||||
recurse: bool,
|
||||
haderrors: bool,
|
||||
};
|
||||
|
||||
fn pkgallocrequests(cap: i32) ([]pkgrequestselection | nomem) = {
|
||||
let value: []pkgrequestselection = alloc([], cap: u64)?;
|
||||
return value;
|
||||
};
|
||||
|
||||
fn pkggrowcap(current: i32, need: i32) i32 = {
|
||||
if (need < 0) { return -1; };
|
||||
if (need <= current) { return current; };
|
||||
@@ -765,6 +780,60 @@ fn pkgskipspace(src: str, start: i32) i32 = {
|
||||
return i;
|
||||
};
|
||||
|
||||
fn pkgdocumentationword(src: str, at: *i32, word: str) bool = {
|
||||
let i: i32 = *at;
|
||||
if (i < 0 || i > src.len || src.len - i < word.len) { return false; };
|
||||
let j: i32 = 0;
|
||||
for (j < word.len) {
|
||||
if (src[i + j] != word[j]) { return false; };
|
||||
j += 1;
|
||||
};
|
||||
if (src.len - i > word.len && pkgident(src[i + word.len])) {
|
||||
return false;
|
||||
};
|
||||
*at = i + word.len;
|
||||
return true;
|
||||
};
|
||||
|
||||
// This is deliberately a quiet exact-name recognizer. Only a plausible
|
||||
// reserved package clause enters the narrow syntax header parser, so ordinary
|
||||
// sources retain their existing full-body diagnostic timing.
|
||||
fn pkgdocumentationcandidate(src: str) bool = {
|
||||
let at: i32 = 0;
|
||||
if (src.len >= 3 && src[0] == 0xefu8 && src[1] == 0xbbu8
|
||||
&& src[2] == 0xbfu8) { at = 3; };
|
||||
at = pkgskipspace(src, at);
|
||||
if (!pkgdocumentationword(src, &at, "package")) { return false; };
|
||||
at = pkgskipspace(src, at);
|
||||
if (!pkgdocumentationword(src, &at, "documentation")) { return false; };
|
||||
at = pkgskipspace(src, at);
|
||||
return at < src.len && src[at] == ';';
|
||||
};
|
||||
|
||||
// -1 is a diagnosed package/import-header error, 0 an ordinary source, and
|
||||
// 1 an exact documentation source. A remaining raw 'i' reproduces pinned
|
||||
// readGoInfo's failed-import-keyword branch; an exact import token was already
|
||||
// consumed or diagnosed by parsepackageheader.
|
||||
fn pkgdocumentation(path: str, src: str) i32 = {
|
||||
if (!pkgdocumentationcandidate(src)) { return 0; };
|
||||
let l: syntax.lex;
|
||||
syntax.lexinit(&l, path, src.ptr, src.len: u64);
|
||||
let ps: syntax.parser;
|
||||
syntax.parserinit(&ps, &l);
|
||||
let header: *syntax.node = syntax.parsepackageheader(&ps);
|
||||
if (l.errs > 0 || ps.errs > 0) { return -1; };
|
||||
if (header.nmod.len == 0) {
|
||||
pkgfailsource(path, 1, 1, "invalid or missing package clause");
|
||||
return -1;
|
||||
};
|
||||
if (l.lpos < src.len: u64 && src[l.lpos] == 'i') {
|
||||
pkgfailsource(path, l.line, l.col, "expected top-level decl");
|
||||
return -1;
|
||||
};
|
||||
if (strings.compare(header.nmod, "documentation") == 0) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn pkgclause(src: str, out: *str) bool = {
|
||||
// Match the compiler readers: one UTF-8 BOM is invisible only at the
|
||||
// first raw source position. The full stage driver owns later-BOM errors.
|
||||
@@ -1412,6 +1481,19 @@ fn pkgdedup(ss: []str) []str = {
|
||||
return ss;
|
||||
};
|
||||
|
||||
fn pkgindexselected(ss: []str, value: str) i32 = {
|
||||
let i: i32 = 0;
|
||||
for (i < ss.len) {
|
||||
if (strings.compare(ss[i], value) == 0) { return i; };
|
||||
i += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pkgcontainsselected(ss: []str, value: str) bool = {
|
||||
return pkgindexselected(ss, value) >= 0;
|
||||
};
|
||||
|
||||
fn pkgparsedec(s: str, max: i64) i64 = {
|
||||
if (s.len == 0) { return -1i64; };
|
||||
let i: i32 = 0;
|
||||
@@ -2182,6 +2264,16 @@ export fn packagecommand(args: []str) int = {
|
||||
directroots.paths = emptydirect;
|
||||
directroots.errors = 0;
|
||||
directroots.fatal = false;
|
||||
let requestallocation: ([]pkgrequestselection | nomem) =
|
||||
pkgallocrequests(roots.len);
|
||||
let requests: []pkgrequestselection;
|
||||
match (requestallocation) {
|
||||
case let value: []pkgrequestselection => requests = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: out of memory");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
let anyrecurse: bool = false;
|
||||
i = 0;
|
||||
for (i < roots.len) {
|
||||
@@ -2231,14 +2323,32 @@ export fn packagecommand(args: []str) int = {
|
||||
};
|
||||
pkgfreematcher(&matcher);
|
||||
};
|
||||
if (recurse && ds.paths.len == before && ds.errors == errorsbefore) {
|
||||
pkgput(os.STDERR_FILENO, "ww: warning: ");
|
||||
pkgputquoted(os.STDERR_FILENO, requested);
|
||||
pkgputln(os.STDERR_FILENO, " matched no packages");
|
||||
} else if (!recurse && ds.paths.len == before
|
||||
&& ds.errors == errorsbefore) {
|
||||
pkgfailpath(discoverroot, "directory contains no WW package sources");
|
||||
ds.errors += 1;
|
||||
let request: pkgrequestselection;
|
||||
request.requested = requested;
|
||||
request.discoverroot = discoverroot;
|
||||
request.start = before;
|
||||
request.end = ds.paths.len;
|
||||
request.recurse = recurse;
|
||||
request.haderrors = ds.errors != errorsbefore;
|
||||
append(requests, request);
|
||||
i += 1;
|
||||
};
|
||||
// Preserve established empty-discovery diagnostics before any new source
|
||||
// read. Requests with raw candidates are accounted again only after exact
|
||||
// documentation sources have been removed from the canonical unique set.
|
||||
i = 0;
|
||||
for (i < requests.len) {
|
||||
if (requests[i].start == requests[i].end
|
||||
&& !requests[i].haderrors) {
|
||||
if (requests[i].recurse) {
|
||||
pkgput(os.STDERR_FILENO, "ww: warning: ");
|
||||
pkgputquoted(os.STDERR_FILENO, requests[i].requested);
|
||||
pkgputln(os.STDERR_FILENO, " matched no packages");
|
||||
} else {
|
||||
pkgfailpath(requests[i].discoverroot,
|
||||
"directory contains no WW package sources");
|
||||
ds.errors += 1;
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
@@ -2264,8 +2374,112 @@ export fn packagecommand(args: []str) int = {
|
||||
ds.paths[i] = canonicalsource;
|
||||
i += 1;
|
||||
};
|
||||
pkgsort(ds.paths);
|
||||
ds.paths = pkgdedup(ds.paths);
|
||||
// Keep the canonical occurrence array in discovery order for per-request
|
||||
// membership. Classification reads each canonical path exactly once after
|
||||
// the established directory/source ordering and deduplication, so overlap
|
||||
// cannot duplicate a header diagnostic, pre-existing diagnostic precedence
|
||||
// is retained, and ordinary bytes can feed source validation unchanged.
|
||||
let occurrencepaths: []str = ds.paths;
|
||||
let selectedallocation: ([]str | nomem) = pkgallocstrs(ds.paths.len);
|
||||
let selected: []str;
|
||||
match (selectedallocation) {
|
||||
case let value: []str => selected = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: out of memory");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
i = 0;
|
||||
for (i < ds.paths.len) { append(selected, ds.paths[i]); i += 1; };
|
||||
pkgsort(selected);
|
||||
selected = pkgdedup(selected);
|
||||
let bodyallocation: ([]str | nomem) = pkgallocstrs(selected.len);
|
||||
let bodies: []str;
|
||||
match (bodyallocation) {
|
||||
case let value: []str => bodies = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: out of memory");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
let readallocation: ([]i32 | nomem) = pkgalloci32(selected.len);
|
||||
let readstates: []i32;
|
||||
match (readallocation) {
|
||||
case let value: []i32 => readstates = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: out of memory");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
let retained: i32 = 0;
|
||||
i = 0;
|
||||
for (i < selected.len) {
|
||||
let path: str = selected[i];
|
||||
let body: str = "";
|
||||
let readstate: i32 = 0;
|
||||
let documentation: i32 = 0;
|
||||
if (buildonly && strings.hassuffix(pkgbase(path), "_test.ww")) {
|
||||
readstate = 2;
|
||||
} else if (pkgread(path, &body)) {
|
||||
readstate = 1;
|
||||
documentation = pkgdocumentation(path, body);
|
||||
if (documentation < 0) {
|
||||
return pkgteststatusfail(explicitstatus, compileonly);
|
||||
};
|
||||
};
|
||||
if (documentation == 0) {
|
||||
selected[retained] = path;
|
||||
append(bodies, body);
|
||||
append(readstates, readstate);
|
||||
retained += 1;
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
selected.len = retained;
|
||||
// A raw nonempty request can become empty only through this suppression.
|
||||
// Each recursive spelling owns one warning; a direct empty root is a
|
||||
// request error that prevents every retained sibling from launching.
|
||||
i = 0;
|
||||
for (i < requests.len) {
|
||||
if (requests[i].start != requests[i].end
|
||||
&& !requests[i].haderrors) {
|
||||
let found: bool = false;
|
||||
let oi: i32 = requests[i].start;
|
||||
for (oi < requests[i].end && !found) {
|
||||
found = pkgcontainsselected(selected, occurrencepaths[oi]);
|
||||
oi += 1;
|
||||
};
|
||||
if (!found) {
|
||||
if (requests[i].recurse) {
|
||||
pkgput(os.STDERR_FILENO, "ww: warning: ");
|
||||
pkgputquoted(os.STDERR_FILENO,
|
||||
requests[i].requested);
|
||||
pkgputln(os.STDERR_FILENO, " matched no packages");
|
||||
} else {
|
||||
pkgfailpath(requests[i].discoverroot,
|
||||
"directory contains no WW package sources");
|
||||
ds.errors += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (ds.errors != 0) {
|
||||
return pkgteststatusfail(explicitstatus, compileonly);
|
||||
};
|
||||
let sortedallocation: ([]str | nomem) = pkgallocstrs(selected.len);
|
||||
let sortedpaths: []str;
|
||||
match (sortedallocation) {
|
||||
case let value: []str => sortedpaths = value;
|
||||
case nomem => {
|
||||
pkgputln(os.STDERR_FILENO, "wwtest package: out of memory");
|
||||
return 1;
|
||||
};
|
||||
};
|
||||
i = 0;
|
||||
for (i < selected.len) { append(sortedpaths, selected[i]); i += 1; };
|
||||
pkgsort(sortedpaths);
|
||||
ds.paths = sortedpaths;
|
||||
if (ds.paths.len == 0) {
|
||||
if (buildonly) {
|
||||
if (explicitout && !buildnull && pkgoutputdir(outname)) {
|
||||
@@ -2304,9 +2518,15 @@ export fn packagecommand(args: []str) int = {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
let body: str;
|
||||
let selectedindex: i32 = pkgindexselected(selected, ds.paths[i]);
|
||||
if (selectedindex < 0) {
|
||||
pkgputln(os.STDERR_FILENO,
|
||||
"wwtest package: package graph is inconsistent");
|
||||
return 1;
|
||||
};
|
||||
let body: str = bodies[selectedindex];
|
||||
let pn: str;
|
||||
if (!pkgread(ds.paths[i], &body)) {
|
||||
if (readstates[selectedindex] != 1) {
|
||||
pkgfailpath(ds.paths[i], "invalid or missing package clause");
|
||||
return pkgteststatusfail(explicitstatus, compileonly);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user