Skip to content

Commit

Permalink
Improve error messages. (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfontein authored Apr 2, 2023
1 parent c1f166b commit 479b59c
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 44 deletions.
4 changes: 4 additions & 0 deletions changelogs/fragments/38-helpful-errors.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
minor_changes:
- "Can switch between error messages containing a shortened version of the faulty markup or the full faulty markup command (https://github.com/ansible-community/antsibull-docs-ts/pull/38)."
breaking_changes:
- "By default, the error messages now contain the full faulty markup command (https://github.com/ansible-community/antsibull-docs-ts/pull/38)."
5 changes: 4 additions & 1 deletion src/opts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ export interface ParsingOptions extends ErrorHandlingOptions {
/** If set to 'true', only 'classic' Ansible docs markup is accepted. */
onlyClassicMarkup?: boolean;

/** If set to 'true', add source information to every part ('source' attribute). */
/** If set to 'true' (default is 'false'), add source information to every part ('source' attribute). */
addSource?: boolean;

/** If set to 'true' (default is 'true'), include the faulty markup in error messages. */
helpfulErrors?: boolean;
}

export interface CommonExportOptions extends ErrorHandlingOptions {
Expand Down
86 changes: 45 additions & 41 deletions src/parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,106 +361,106 @@ describe('parser', (): void => {
]);
});
it('bad parameter parsing (no escaping, throw error)', (): void => {
expect(async () => parse('M(', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('M(', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing M() at index 1: Cannot find closing ")" after last parameter',
);
expect(async () => parse('M(foo', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('M(foo', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing M() at index 1: Cannot find closing ")" after last parameter',
);
expect(async () => parse('L(foo)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('L(foo)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing L() at index 1: Cannot find comma separating parameter 1 from the next one',
);
expect(async () => parse('L(foo,bar', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('L(foo,bar', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing L() at index 1: Cannot find closing ")" after last parameter',
);
expect(async () => parse('L(foo), bar', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('L(foo), bar', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing L() at index 1: Cannot find closing ")" after last parameter',
);
expect(async () => parse('P(', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('P(', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing P() at index 1: Cannot find closing ")" after last parameter',
);
expect(async () => parse('P(foo', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('P(foo', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing P() at index 1: Cannot find closing ")" after last parameter',
);
});
it('bad module ref (throw error)', (): void => {
expect(async () => parse('M(foo)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('M(foo)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing M() at index 1: Module name "foo" is not a FQCN',
);
expect(async () => parse(' M(foo.bar)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse(' M(foo.bar)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing M() at index 2: Module name "foo.bar" is not a FQCN',
);
expect(async () => parse(' M(foo. bar.baz)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse(' M(foo. bar.baz)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing M() at index 3: Module name "foo. bar.baz" is not a FQCN',
);
expect(async () => parse(' M(foo)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse(' M(foo)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing M() at index 4: Module name "foo" is not a FQCN',
);
});
it('bad plugin ref (throw error)', (): void => {
expect(async () => parse('P(foo)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('P(foo)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing P() at index 1: Parameter "foo" is not of the form FQCN#type',
);
expect(async () => parse('P(f o.b r.b z#bar)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('P(f o.b r.b z#bar)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing P() at index 1: Plugin name "f o.b r.b z" is not a FQCN',
);
expect(async () => parse('P(foo.bar.baz#b m)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('P(foo.bar.baz#b m)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing P() at index 1: Plugin type "b m" is not valid',
);
});
it('bad option name/return value (throw error)', (): void => {
expect(async () => parse('O(f o.b r.b z#bam:foobar)', { errors: 'exception' })).rejects.toThrow(
'While parsing O() at index 1: Plugin name "f o.b r.b z" is not a FQCN',
);
expect(async () => parse('O(foo.bar.baz#b m:foobar)', { errors: 'exception' })).rejects.toThrow(
'While parsing O() at index 1: Plugin type "b m" is not valid',
);
expect(async () => parse('O(foo:bar:baz)', { errors: 'exception' })).rejects.toThrow(
expect(async () =>
parse('O(f o.b r.b z#bam:foobar)', { errors: 'exception', helpfulErrors: false }),
).rejects.toThrow('While parsing O() at index 1: Plugin name "f o.b r.b z" is not a FQCN');
expect(async () =>
parse('O(foo.bar.baz#b m:foobar)', { errors: 'exception', helpfulErrors: false }),
).rejects.toThrow('While parsing O() at index 1: Plugin type "b m" is not valid');
expect(async () => parse('O(foo:bar:baz)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing O() at index 1: Invalid option/return value name "foo:bar:baz"',
);
expect(async () => parse('O(foo.bar.baz#role:bam)', { errors: 'exception' })).rejects.toThrow(
expect(async () => parse('O(foo.bar.baz#role:bam)', { errors: 'exception', helpfulErrors: false })).rejects.toThrow(
'While parsing O() at index 1: Role reference is missing entrypoint',
);
});
it('bad parameter parsing (no escaping, error message)', (): void => {
expect(parse('M(')).toEqual([
expect(parse('M(', { helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing M() at index 1: Cannot find closing ")" after last parameter' }],
]);
expect(parse('M(foo', { errors: 'message' })).toEqual([
expect(parse('M(foo', { errors: 'message', helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing M() at index 1: Cannot find closing ")" after last parameter' }],
]);
expect(parse('L(foo)', { errors: 'message' })).toEqual([
expect(parse('L(foo)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
message: 'While parsing L() at index 1: Cannot find comma separating parameter 1 from the next one',
},
],
]);
expect(parse('L(foo,bar', { errors: 'message' })).toEqual([
expect(parse('L(foo,bar', { errors: 'message', helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing L() at index 1: Cannot find closing ")" after last parameter' }],
]);
expect(parse('L(foo), bar', { errors: 'message' })).toEqual([
expect(parse('L(foo), bar', { errors: 'message', helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing L() at index 1: Cannot find closing ")" after last parameter' }],
]);
expect(parse('P(')).toEqual([
expect(parse('P(', { helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing P() at index 1: Cannot find closing ")" after last parameter' }],
]);
expect(parse('P(foo', { errors: 'message' })).toEqual([
expect(parse('P(foo', { errors: 'message', helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing P() at index 1: Cannot find closing ")" after last parameter' }],
]);
});
it('bad module ref (error message)', (): void => {
expect(parse('M(foo)')).toEqual([
expect(parse('M(foo)', { helpfulErrors: false })).toEqual([
[{ type: PartType.ERROR, message: 'While parsing M() at index 1: Module name "foo" is not a FQCN' }],
]);
expect(parse(' M(foo.bar)', { errors: 'message' })).toEqual([
expect(parse(' M(foo.bar)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{ type: PartType.TEXT, text: ' ' },
{ type: PartType.ERROR, message: 'While parsing M() at index 2: Module name "foo.bar" is not a FQCN' },
],
]);
expect(parse(' M(foo. bar.baz)', { errors: 'message' })).toEqual([
expect(parse(' M(foo. bar.baz)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{ type: PartType.TEXT, text: ' ' },
{
Expand All @@ -469,7 +469,7 @@ describe('parser', (): void => {
},
],
]);
expect(parse(' M(foo) baz', { errors: 'message' })).toEqual([
expect(parse(' M(foo) baz', { errors: 'message', helpfulErrors: false })).toEqual([
[
{ type: PartType.TEXT, text: ' ' },
{ type: PartType.ERROR, message: 'While parsing M() at index 4: Module name "foo" is not a FQCN' },
Expand All @@ -478,23 +478,23 @@ describe('parser', (): void => {
]);
});
it('bad plugin ref (error message)', (): void => {
expect(parse('P(foo)')).toEqual([
expect(parse('P(foo)', { helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
message: 'While parsing P() at index 1: Parameter "foo" is not of the form FQCN#type',
},
],
]);
expect(parse('P(f o.b r.b z#bar)', { errors: 'message' })).toEqual([
expect(parse('P(f o.b r.b z#bar)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
message: 'While parsing P() at index 1: Plugin name "f o.b r.b z" is not a FQCN',
},
],
]);
expect(parse('P(foo.bar.baz#b m)', { errors: 'message' })).toEqual([
expect(parse('P(foo.bar.baz#b m)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
Expand All @@ -504,31 +504,31 @@ describe('parser', (): void => {
]);
});
it('bad option name/return value (error message)', (): void => {
expect(parse('O(f o.b r.b z#bam:foobar)')).toEqual([
expect(parse('O(f o.b r.b z#bam:foobar)', { helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
message: 'While parsing O() at index 1: Plugin name "f o.b r.b z" is not a FQCN',
},
],
]);
expect(parse('O(foo.bar.baz#b m:foobar)', { errors: 'message' })).toEqual([
expect(parse('O(foo.bar.baz#b m:foobar)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
message: 'While parsing O() at index 1: Plugin type "b m" is not valid',
},
],
]);
expect(parse('O(foo:bar:baz)', { errors: 'message' })).toEqual([
expect(parse('O(foo:bar:baz)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
message: 'While parsing O() at index 1: Invalid option/return value name "foo:bar:baz"',
},
],
]);
expect(parse('O(foo.bar.baz#role:bam)', { errors: 'message' })).toEqual([
expect(parse('O(foo.bar.baz#role:bam)', { errors: 'message', helpfulErrors: false })).toEqual([
[
{
type: PartType.ERROR,
Expand Down Expand Up @@ -603,10 +603,14 @@ describe('parser engine', (): void => {
);
});
it('combine wrong regexp with command map', (): void => {
expect(parseString('A B()', commandsReA, commandsMapA, {}, '')).toEqual([
expect(parseString('A B()', commandsReA, commandsMapA, { helpfulErrors: false }, '')).toEqual([
{ message: 'While parsing A at index 1: boo!', type: PartType.ERROR },
{ text: ' B()', type: PartType.TEXT },
]);
expect(parseString('A B()', commandsReA, commandsMapA, {}, '')).toEqual([
{ message: 'While parsing "A" at index 1: boo!', type: PartType.ERROR },
{ text: ' B()', type: PartType.TEXT },
]);
});
it('combine wrong regexp with command map', (): void => {
expect(parseString('A B(a, b, c)', commandsReB, commandsMapB, {}, '')).toEqual([
Expand Down
6 changes: 5 additions & 1 deletion src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,11 @@ export function parseString(
}
}
if (error !== undefined) {
error = `While parsing ${cmd}${command.parameters > 0 ? '()' : ''} at index ${match.index + 1}${where}: ${error}`;
const errorSource =
opts.helpfulErrors ?? true
? `"${input.slice(index, endIndex)}"`
: `${cmd}${command.parameters > 0 ? '()' : ''}`;
error = `While parsing ${errorSource} at index ${match.index + 1}${where}: ${error}`;
switch (opts.errors || 'message') {
case 'ignore':
break;
Expand Down
31 changes: 30 additions & 1 deletion test-vectors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1203,11 +1203,13 @@ test_vectors:
`foo=' `bar.baz[123].bam[len(x) - 1]='
`foo=bar' `bar.baz[123].bam[len(x) - 1]=bar'
errors:
unhelpful_errors:
source:
- P(foo)
- C(foo
- R(foo,bar
parse_opts:
helpfulErrors: false
html: |-
<p><span class="error">ERROR while parsing: While parsing P() at index 1 of paragraph 1: Parameter "foo" is not of the form FQCN#type</span></p><p><span class="error">ERROR while parsing: While parsing C() at index 1 of paragraph 2: Cannot find closing ")" after last parameter</span></p><p><span class="error">ERROR while parsing: While parsing R() at index 1 of paragraph 3: Cannot find closing ")" after last parameter</span></p>
html_plain: |-
Expand All @@ -1230,3 +1232,30 @@ test_vectors:
[[ERROR while parsing: While parsing C() at index 1 of paragraph 2: Cannot find closing ")" after last parameter]]
[[ERROR while parsing: While parsing R() at index 1 of paragraph 3: Cannot find closing ")" after last parameter]]
helpful_errors:
source:
- P(foo)
- C(foo
- R(foo,bar
html: |-
<p><span class="error">ERROR while parsing: While parsing "P(foo)" at index 1 of paragraph 1: Parameter "foo" is not of the form FQCN#type</span></p><p><span class="error">ERROR while parsing: While parsing "C(foo" at index 1 of paragraph 2: Cannot find closing ")" after last parameter</span></p><p><span class="error">ERROR while parsing: While parsing "R(foo,bar" at index 1 of paragraph 3: Cannot find closing ")" after last parameter</span></p>
html_plain: |-
<p><span class="error">ERROR while parsing: While parsing "P(foo)" at index 1 of paragraph 1: Parameter "foo" is not of the form FQCN#type</span></p><p><span class="error">ERROR while parsing: While parsing "C(foo" at index 1 of paragraph 2: Cannot find closing ")" after last parameter</span></p><p><span class="error">ERROR while parsing: While parsing "R(foo,bar" at index 1 of paragraph 3: Cannot find closing ")" after last parameter</span></p>
md: |-
<b>ERROR while parsing</b>: While parsing \"P\(foo\)\" at index 1 of paragraph 1\: Parameter \"foo\" is not of the form FQCN\#type
<b>ERROR while parsing</b>: While parsing \"C\(foo\" at index 1 of paragraph 2\: Cannot find closing \"\)\" after last parameter
<b>ERROR while parsing</b>: While parsing \"R\(foo\,bar\" at index 1 of paragraph 3\: Cannot find closing \"\)\" after last parameter
rst: |-
\ :strong:`ERROR while parsing`\ : While parsing "P(foo)" at index 1 of paragraph 1: Parameter "foo" is not of the form FQCN#type\
\ :strong:`ERROR while parsing`\ : While parsing "C(foo" at index 1 of paragraph 2: Cannot find closing ")" after last parameter\
\ :strong:`ERROR while parsing`\ : While parsing "R(foo,bar" at index 1 of paragraph 3: Cannot find closing ")" after last parameter\
ansible_doc_text: |-
[[ERROR while parsing: While parsing "P(foo)" at index 1 of paragraph 1: Parameter "foo" is not of the form FQCN#type]]
[[ERROR while parsing: While parsing "C(foo" at index 1 of paragraph 2: Cannot find closing ")" after last parameter]]
[[ERROR while parsing: While parsing "R(foo,bar" at index 1 of paragraph 3: Cannot find closing ")" after last parameter]]

0 comments on commit 479b59c

Please sign in to comment.