Skip to content

Commit 23f60da

Browse files
hsbtclaude
andcommitted
Address Copilot review feedback on the libfyaml backend
Reject canonical output with NotImplementedError instead of silently ignoring the request, and honor the implicit flag in start_sequence and start_mapping so an implicit tag is not printed as a redundant verbose tag. Relax the unquoted-boolean dump test to allow an optional document end marker. Also correct the version docstrings and a stale comment about how the parser collects diagnostics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 7c8d152 commit 23f60da

5 files changed

Lines changed: 40 additions & 10 deletions

File tree

ext/psych/psych.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
/* call-seq: Psych.libyaml_version
44
*
5-
* Returns the version of libyaml being used
5+
* Returns the version of the underlying YAML library as a three-element
6+
* array. This is libyaml by default. On the experimental libfyaml backend,
7+
* where libyaml is not linked, it reports the libfyaml version instead.
68
*/
79
static VALUE libyaml_version(VALUE module)
810
{
@@ -30,7 +32,9 @@ static VALUE libyaml_version(VALUE module)
3032
#ifdef PSYCH_USE_LIBFYAML
3133
/* call-seq: Psych.libfyaml_version
3234
*
33-
* Returns the libfyaml version string, or nil when not built with libfyaml.
35+
* Returns the libfyaml version string. This method is only defined when
36+
* psych was built with the experimental libfyaml backend
37+
* (+--enable-libfyaml+).
3438
*/
3539
static VALUE libfyaml_version(VALUE module)
3640
{

ext/psych/psych_emitter_fy.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ static VALUE initialize(int argc, VALUE *argv, VALUE self)
122122
if (rb_scan_args(argc, argv, "11", &io, &options) == 2) {
123123
e->width = NUM2INT(rb_funcall(options, id_line_width, 0));
124124
e->indent = NUM2INT(rb_funcall(options, id_indentation, 0));
125-
e->canonical = (Qtrue == rb_funcall(options, id_canonical, 0)) ? 1 : 0;
125+
/* libfyaml has no canonical emit mode, so fail fast instead of
126+
* silently producing non-canonical output. */
127+
if (RTEST(rb_funcall(options, id_canonical, 0))) {
128+
rb_raise(rb_eNotImpError,
129+
"canonical output is not supported by the libfyaml backend");
130+
}
126131
}
127132

128133
rb_ivar_set(self, id_io, io);
@@ -329,10 +334,14 @@ static VALUE start_sequence(VALUE self, VALUE anchor, VALUE tag,
329334
if (!NIL_P(anchor)) { Check_Type(anchor, T_STRING); anchor = rb_str_export_to_enc(anchor, encoding); }
330335
if (!NIL_P(tag)) { Check_Type(tag, T_STRING); tag = rb_str_export_to_enc(tag, encoding); }
331336

337+
/* An implicit tag can be omitted, matching libyaml; emitting it anyway
338+
* would print a redundant (often verbose) tag. */
339+
int emit_tag = !NIL_P(tag) && !RTEST(implicit);
340+
332341
struct fy_event *event = fy_emit_event_create(e->emit, FYET_SEQUENCE_START,
333342
psych_to_fyns(NUM2INT(style)),
334343
NIL_P(anchor) ? NULL : StringValueCStr(anchor),
335-
NIL_P(tag) ? NULL : StringValueCStr(tag));
344+
emit_tag ? StringValueCStr(tag) : NULL);
336345

337346
do_emit(e, event);
338347
RB_GC_GUARD(anchor);
@@ -360,10 +369,14 @@ static VALUE start_mapping(VALUE self, VALUE anchor, VALUE tag,
360369
if (!NIL_P(anchor)) { Check_Type(anchor, T_STRING); anchor = rb_str_export_to_enc(anchor, encoding); }
361370
if (!NIL_P(tag)) { Check_Type(tag, T_STRING); tag = rb_str_export_to_enc(tag, encoding); }
362371

372+
/* An implicit tag can be omitted, matching libyaml; emitting it anyway
373+
* would print a redundant (often verbose) tag. */
374+
int emit_tag = !NIL_P(tag) && !RTEST(implicit);
375+
363376
struct fy_event *event = fy_emit_event_create(e->emit, FYET_MAPPING_START,
364377
psych_to_fyns(NUM2INT(style)),
365378
NIL_P(anchor) ? NULL : StringValueCStr(anchor),
366-
NIL_P(tag) ? NULL : StringValueCStr(tag));
379+
emit_tag ? StringValueCStr(tag) : NULL);
367380

368381
do_emit(e, event);
369382
RB_GC_GUARD(anchor);
@@ -397,8 +410,12 @@ static VALUE set_canonical(VALUE self, VALUE style)
397410
{
398411
psych_fy_emitter_t *e;
399412
TypedData_Get_Struct(self, psych_fy_emitter_t, &psych_emitter_type, e);
400-
e->canonical = (Qtrue == style) ? 1 : 0;
401-
rebuild_emitter(self, e);
413+
/* libfyaml has no canonical emit mode, so reject enabling it rather than
414+
* pretending to honor the request. */
415+
if (RTEST(style)) {
416+
rb_raise(rb_eNotImpError,
417+
"canonical output is not supported by the libfyaml backend");
418+
}
402419
return style;
403420
}
404421

ext/psych/psych_parser_fy.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ static VALUE allocate(VALUE klass)
9898
}
9999

100100
/* Reconstruct a Psych::SyntaxError from libfyaml's collected diagnostics. The
101-
* parser is created with FYPCF_COLLECT_DIAG, so the first collected error gives
102-
* us the message and position. */
101+
* parser's diag was switched to collect mode with fy_diag_set_collect_errors()
102+
* in parse(), so the first collected error gives us the message and position. */
103103
static VALUE make_exception(psych_fy_parser_t *parser, VALUE path)
104104
{
105105
VALUE ePsychSyntaxError = rb_const_get(mPsych, rb_intern("SyntaxError"));

test/psych/test_emitter.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ def test_line_width
1717
end
1818

1919
def test_set_canonical
20+
if libfyaml?
21+
# The libfyaml backend has no canonical mode and rejects enabling it.
22+
assert_raise(NotImplementedError) { @emitter.canonical = true }
23+
@emitter.canonical = false
24+
assert_equal false, @emitter.canonical
25+
return
26+
end
27+
2028
@emitter.canonical = true
2129
assert_equal true, @emitter.canonical
2230

test/psych/test_string.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def test_all_yaml_1_1_booleans_are_quoted
3636
def test_yaml_1_1_booleans_are_not_quoted_on_libfyaml
3737
omit 'YAML 1.1 booleans are plain strings on the libfyaml backend' unless libfyaml?
3838
%w[yes no on off].each do |boolean|
39-
assert_equal "--- #{boolean}\n", Psych.dump(boolean)
39+
# Unquoted plain scalar, allowing an optional document end marker.
40+
assert_match(/\A--- #{boolean}\n(?:\.\.\.\n)?\z/, Psych.dump(boolean))
4041
assert_equal boolean, Psych.load(Psych.dump(boolean))
4142
end
4243
end

0 commit comments

Comments
 (0)