Improve preprocessor error reporting

This commit is contained in:
Tony Garnock-Jones 2021-12-11 16:49:12 +01:00
parent 8888ac3fe9
commit 3edb680c19
7 changed files with 54 additions and 22 deletions

View File

@ -6,7 +6,7 @@ import {
laxRead, itemText, laxRead, itemText,
Items, Pattern, Templates, Substitution, TokenType, Items, Pattern, Templates, Substitution, TokenType,
SourceMap, CodeWriter, TemplateFunction, Token, SpanIndex, SourceMap, CodeWriter, TemplateFunction, Token, SpanIndex, match, TokenBase, getRange, Pos,
} from '../syntax/index.js'; } from '../syntax/index.js';
import { import {
SyndicateParser, SyndicateTypedParser, SyndicateParser, SyndicateTypedParser,
@ -29,7 +29,7 @@ export function stripShebang(items: Items): Items {
export type ModuleType ='es6' | 'require' | 'global'; export type ModuleType ='es6' | 'require' | 'global';
export type ErrorSink = (message: string) => void; export type ErrorSink = (message: string, start: Pos | undefined, end: Pos | undefined) => void;
export interface CompileOptions { export interface CompileOptions {
source: string, source: string,
@ -52,17 +52,17 @@ export class ExpansionContext {
readonly parser: SyndicateParser; readonly parser: SyndicateParser;
readonly moduleType: ModuleType; readonly moduleType: ModuleType;
readonly typescript: boolean; readonly typescript: boolean;
readonly emitError: (message: string) => void; readonly errorEmitter: ErrorSink;
nextIdNumber = 0; nextIdNumber = 0;
constructor(moduleType: ModuleType, constructor(moduleType: ModuleType,
typescript: boolean, typescript: boolean,
emitError: ErrorSink) errorEmitter: ErrorSink)
{ {
this.parser = typescript ? new SyndicateTypedParser : new SyndicateParser(); this.parser = typescript ? new SyndicateTypedParser : new SyndicateParser();
this.moduleType = moduleType; this.moduleType = moduleType;
this.typescript = typescript; this.typescript = typescript;
this.emitError = emitError; this.errorEmitter = errorEmitter;
} }
quasiRandomId(): string { quasiRandomId(): string {
@ -72,6 +72,10 @@ export class ExpansionContext {
argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Items { argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Items {
return (this.typescript) ? t`${name}: ${type}` : t`${name}`; return (this.typescript) ? t`${name}: ${type}` : t`${name}`;
} }
emitError(m: string, loc: TokenBase) {
this.errorEmitter(m, loc.start, loc.end);
}
} }
function stringifyId(i: Identifier): Items { function stringifyId(i: Identifier): Items {
@ -99,7 +103,8 @@ function binderTypeGuard(ctx: ExpansionContext, t: TemplateFunction): (binder: B
case 'any': case 'any':
return bind; return bind;
default: default:
ctx.emitError(`Cannot emit guard for binding of type: ${JSON.stringify(typeText)}`); ctx.emitError(`Cannot emit guard for binding of type: ${JSON.stringify(typeText)}`,
getRange(binder.type));
return bind; /* act as if "any", for now */ return bind; /* act as if "any", for now */
} }
} }

View File

@ -2,13 +2,13 @@
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { import {
Token, Item, Items, Token, Items,
Pattern, Pattern,
foldItems, match, anonymousTemplate as template, commaJoin, foldItems, match, anonymousTemplate as template, commaJoin,
scope, bind, seq, alt, upTo, atom, atomString, group, scope, bind, seq, alt, upTo, atom, atomString, group,
repeat, option, withoutSpace, map, mapm, rest, discard, repeat, option, withoutSpace, map, mapm, rest, discard,
value, succeed, fail, separatedOrTerminatedBy, anything, not value, succeed, fail, separatedOrTerminatedBy, not, TokenBase
} from '../syntax/index.js'; } from '../syntax/index.js';
import * as Matcher from '../syntax/matcher.js'; import * as Matcher from '../syntax/matcher.js';

View File

@ -9,6 +9,10 @@ export interface Pos {
fixed?: boolean; fixed?: boolean;
} }
export function formatPos(p?: Pos): string {
return p ? `${p.name ?? '?'}:${p.line}:${p.column}` : '?';
}
export function startPos(name: string | null): Pos { export function startPos(name: string | null): Pos {
return { line: 1, column: 0, pos: 0, name }; return { line: 1, column: 0, pos: 0, name };
} }

View File

@ -34,6 +34,14 @@ export type Items = Array<Item>;
export type GroupInProgress = Omit<Group, 'end'>; export type GroupInProgress = Omit<Group, 'end'>;
export function getRange(t: Item | Items): TokenBase {
if (Array.isArray(t)) {
return { start: t[0].start, end: t[t.length - 1].end, synthetic: true };
} else {
return t;
}
}
export function finishGroup(g: GroupInProgress, end: Pos): Group { export function finishGroup(g: GroupInProgress, end: Pos): Group {
return { ... g, end }; return { ... g, end };
} }

View File

@ -7,7 +7,7 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import { glob } from 'glob'; import { glob } from 'glob';
import { compile } from '@syndicate-lang/compiler'; import { compile, Syntax } from '@syndicate-lang/compiler';
import { dataURL, sourceMappingComment} from './util.js'; import { dataURL, sourceMappingComment} from './util.js';
export type ModuleChoice = 'es6' | 'require' | 'global'; export type ModuleChoice = 'es6' | 'require' | 'global';
@ -117,7 +117,7 @@ export function main(argv: string[]) {
argv => argv) argv => argv)
.argv); .argv);
const collectedErrors: Array<string> = []; let errorCount = 0;
const rename = makeRenamer(options.outputDirectory ?? '', const rename = makeRenamer(options.outputDirectory ?? '',
options.rootDirectory ?? '.', options.rootDirectory ?? '.',
@ -146,9 +146,9 @@ export function main(argv: string[]) {
runtime: options.runtime, runtime: options.runtime,
module: options.module, module: options.module,
typescript: options.typed, typescript: options.typed,
emitError: m => { emitError: (m, start, end) => {
console.error(m); console.error(`${Syntax.formatPos(start)}-${Syntax.formatPos(end)}: ${m}`);
collectedErrors.push(m); errorCount++;
} }
}); });
map.sourcesContent = [source]; map.sourcesContent = [source];
@ -168,7 +168,7 @@ export function main(argv: string[]) {
} }
} }
if (collectedErrors.length > 0) { if (errorCount > 0) {
throw new Error(`Compilation failed with ${collectedErrors.length} error(s).`); throw new Error(`Compilation failed with ${errorCount} error(s).`);
} }
} }

View File

@ -11,6 +11,7 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => {
interface SyndicateInfo { interface SyndicateInfo {
sourceFile: ts.SourceFile; sourceFile: ts.SourceFile;
originalSource: string; originalSource: string;
diagnostics: ts.DiagnosticWithLocation[];
targetToSourceMap: Syntax.SpanIndex<Syntax.Token>; targetToSourceMap: Syntax.SpanIndex<Syntax.Token>;
sourceToTargetMap: Syntax.SpanIndex<number>; sourceToTargetMap: Syntax.SpanIndex<number>;
} }
@ -159,20 +160,30 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => {
onError?.(`Could not read input file ${fileName}`); onError?.(`Could not read input file ${fileName}`);
return undefined; return undefined;
} }
const diagnostics:
Array<{ message: string, start?: Syntax.Pos, end?: Syntax.Pos }> = [];
console.log('Syndicate compiling', fileName); console.log('Syndicate compiling', fileName);
const { text: expandedText, targetToSourceMap, sourceToTargetMap } = compile({ const { text: expandedText, targetToSourceMap, sourceToTargetMap } = compile({
source: inputText, source: inputText,
name: fileName, name: fileName,
typescript: true, typescript: true,
emitError: m => { emitError: (message, start, end) => {
console.error(m); console.error(`${Syntax.formatPos(start)}-${Syntax.formatPos(end)}: ${message}`);
onError?.(m); diagnostics.push({ message, start, end });
}, },
}); });
const sf = ts.createSourceFile(fileName, expandedText, languageVersion, true); const sf = ts.createSourceFile(fileName, expandedText, languageVersion, true);
syndicateInfo.set(fileName, { syndicateInfo.set(fileName, {
sourceFile: sf, sourceFile: sf,
originalSource: inputText, originalSource: inputText,
diagnostics: diagnostics.map(({ message, start, end }) => ({
category: ts.DiagnosticCategory.Error,
code: 99999,
file: sf,
start: start?.pos ?? 0,
length: Math.max(0, (end?.pos ?? 0) - (start?.pos ?? 0)),
messageText: message,
})),
targetToSourceMap, targetToSourceMap,
sourceToTargetMap, sourceToTargetMap,
}); });
@ -243,7 +254,11 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => {
getSyntacticDiagnostics(fileName: string): ts.DiagnosticWithLocation[] { getSyntacticDiagnostics(fileName: string): ts.DiagnosticWithLocation[] {
const ds = this.inner.getSyntacticDiagnostics(fileName); const ds = this.inner.getSyntacticDiagnostics(fileName);
return withFileName(fileName, () => ds, (fixup) => fixup.diagnostics(ds)); return withFileName(fileName, () => ds, (fixup) => {
const fixedUp = fixup.diagnostics(ds);
fixedUp.push(... fixup.info.diagnostics);
return fixedUp;
});
} }
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] { getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {

View File

@ -8,7 +8,7 @@ import crypto from 'crypto';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { compile } from '@syndicate-lang/compiler'; import { compile, Syntax } from '@syndicate-lang/compiler';
import { SpanIndex, Token } from '@syndicate-lang/compiler/lib/syntax'; import { SpanIndex, Token } from '@syndicate-lang/compiler/lib/syntax';
export type CommandLineArguments = { export type CommandLineArguments = {
@ -150,8 +150,8 @@ function runBuildOnce(options: CommandLineArguments, toWatch = new ToWatch()) {
source: inputText, source: inputText,
name: fileName, name: fileName,
typescript: true, typescript: true,
emitError: m => { emitError: (m, start, end) => {
console.error(m); console.error(`${Syntax.formatPos(start)}-${Syntax.formatPos(end)}: ${m}`);
onError?.(m); onError?.(m);
}, },
}); });