Skip to content

Commit ba5a932

Browse files
committed
revamp exception handling
1 parent 8b18bb0 commit ba5a932

File tree

10 files changed

+982
-445
lines changed

10 files changed

+982
-445
lines changed

source/dyaml/composer.d

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,6 @@ import dyaml.resolver;
2929

3030

3131
package:
32-
/**
33-
* Exception thrown at composer errors.
34-
*
35-
* See_Also: MarkedYAMLException
36-
*/
37-
class ComposerException : MarkedYAMLException
38-
{
39-
mixin MarkedExceptionCtors;
40-
}
4132

4233
///Composes YAML documents from events provided by a Parser.
4334
struct Composer
@@ -70,7 +61,7 @@ struct Composer
7061
* Params: parser = Parser to provide YAML events.
7162
* resolver = Resolver to resolve tags (data types).
7263
*/
73-
this(Parser parser, Resolver resolver) @safe
64+
this(Parser parser, Resolver resolver) @safe nothrow
7465
{
7566
parser_ = parser;
7667
resolver_ = resolver;
@@ -102,9 +93,14 @@ struct Composer
10293
}
10394

10495
/// Set file name.
105-
void name(string name) @safe pure nothrow @nogc
96+
ref inout(string) name() inout @safe return pure nothrow @nogc
97+
{
98+
return parser_.name;
99+
}
100+
/// Get a mark from the current reader position
101+
Mark mark() const @safe pure nothrow @nogc
106102
{
107-
parser_.name = name;
103+
return parser_.mark;
108104
}
109105

110106
/// Get resolver
@@ -169,8 +165,8 @@ struct Composer
169165
//it's not finished, i.e. we're currently composing it
170166
//and trying to use it recursively here.
171167
enforce(anchors_[anchor] != Node(),
172-
new ComposerException("Found recursive alias: " ~ anchor,
173-
event.startMark));
168+
new ComposerException(text("Found recursive alias: ", anchor),
169+
event.startMark, "defined here", anchors_[anchor].startMark));
174170

175171
return anchors_[anchor];
176172
}
@@ -179,16 +175,18 @@ struct Composer
179175
const anchor = event.anchor;
180176
if((anchor !is null) && (anchor in anchors_) !is null)
181177
{
182-
throw new ComposerException("Found duplicate anchor: " ~ anchor,
183-
event.startMark);
178+
throw new ComposerException(text("Found duplicate anchor: ", anchor),
179+
event.startMark, "defined here", anchors_[anchor].startMark);
184180
}
185181

186182
Node result;
187183
//Associate the anchor, if any, with an uninitialized node.
188184
//used to detect duplicate and recursive anchors.
189185
if(anchor !is null)
190186
{
191-
anchors_[anchor] = Node();
187+
Node tempNode;
188+
tempNode.startMark_ = event.startMark;
189+
anchors_[anchor] = tempNode;
192190
}
193191

194192
switch (parser_.front.id)
@@ -276,12 +274,10 @@ struct Composer
276274
{
277275
//this is Composer, but the code is related to Constructor.
278276
throw new ConstructorException("While constructing a mapping, " ~
279-
"expected a mapping or a list of " ~
280-
"mappings for merging, but found: " ~
281-
text(node.type) ~
282-
" NOTE: line/column shows topmost parent " ~
283-
"to which the content is being merged",
284-
startMark, endMark);
277+
"expected a mapping or a list of " ~
278+
"mappings for merging, but found: " ~
279+
text(node.type),
280+
endMark, "mapping started here", startMark);
285281
}
286282

287283
ensureAppendersExist(pairAppenderLevel, nodeAppenderLevel);
@@ -371,14 +367,14 @@ struct Composer
371367
}
372368

373369
auto sorted = pairAppender.data.dup.sort!((x,y) => x.key > y.key);
374-
if (sorted.length) {
370+
if (sorted.length)
371+
{
375372
foreach (index, const ref value; sorted[0 .. $ - 1].enumerate)
376-
if (value.key == sorted[index + 1].key) {
377-
const message = () @trusted {
378-
return format("Key '%s' appears multiple times in mapping (first: %s)",
379-
value.key.get!string, value.key.startMark);
380-
}();
381-
throw new ComposerException(message, sorted[index + 1].key.startMark);
373+
if (value.key == sorted[index + 1].key)
374+
{
375+
throw new ComposerException(
376+
text("Key '", value.key.get!string, "' appears multiple times in mapping"),
377+
sorted[index + 1].key.startMark, "defined here", value.key.startMark);
382378
}
383379
}
384380

@@ -403,10 +399,73 @@ struct Composer
403399
"comment": "To write down comments pre-JSON5"
404400
}`;
405401

406-
try
407-
auto node = Loader.fromString(str).load();
408-
catch (ComposerException exc)
409-
assert(exc.message() ==
410-
"Key 'comment' appears multiple times in mapping " ~
411-
"(first: file <unknown>,line 2,column 5)\nfile <unknown>,line 4,column 5");
402+
const exc = collectException!LoaderException(Loader.fromString(str).load());
403+
assert(exc);
404+
assert(exc.message() ==
405+
"Unable to load <unknown>: Key 'comment' appears multiple times in mapping\n" ~
406+
"<unknown>:4,5\ndefined here: <unknown>:2,5");
407+
}
408+
409+
// Provide good error message on duplicate anchors
410+
@safe unittest
411+
{
412+
import dyaml.loader : Loader;
413+
414+
const str = `{
415+
a: &anchor b,
416+
b: &anchor c,
417+
}`;
418+
419+
const exc = collectException!LoaderException(Loader.fromString(str).load());
420+
assert(exc);
421+
assert(exc.message() ==
422+
"Unable to load <unknown>: Found duplicate anchor: anchor\n" ~
423+
"<unknown>:3,8\ndefined here: <unknown>:2,8");
424+
}
425+
426+
// Provide good error message on missing alias
427+
@safe unittest
428+
{
429+
import dyaml.loader : Loader;
430+
431+
const str = `{
432+
a: *anchor,
433+
}`;
434+
435+
const exc = collectException!LoaderException(Loader.fromString(str).load());
436+
assert(exc);
437+
assert(exc.message() ==
438+
"Unable to load <unknown>: Found undefined alias: anchor\n" ~
439+
"<unknown>:2,8");
440+
}
441+
442+
// Provide good error message on recursive alias
443+
@safe unittest
444+
{
445+
import dyaml.loader : Loader;
446+
447+
const str = `a: &anchor {
448+
b: *anchor
449+
}`;
450+
451+
const exc = collectException!LoaderException(Loader.fromString(str).load());
452+
assert(exc);
453+
assert(exc.message() ==
454+
"Unable to load <unknown>: Found recursive alias: anchor\n" ~
455+
"<unknown>:2,8\ndefined here: <unknown>:1,4");
456+
}
457+
458+
// Provide good error message on failed merges
459+
@safe unittest
460+
{
461+
import dyaml.loader : Loader;
462+
463+
const str = `a: &anchor 3
464+
b: { <<: *anchor }`;
465+
466+
const exc = collectException!LoaderException(Loader.fromString(str).load());
467+
assert(exc);
468+
assert(exc.message() ==
469+
"Unable to load <unknown>: While constructing a mapping, expected a mapping or a list of mappings for merging, but found: integer\n" ~
470+
"<unknown>:2,19\nmapping started here: <unknown>:2,4");
412471
}

0 commit comments

Comments
 (0)