{"atom":"regression/dot-method-call-and-formatter","family":"regression","slug":"dot-method-call-and-formatter","header":{"atom":"regression/dot-method-call-and-formatter","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/dot-method-call-and-formatter.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/dot-method-call-and-formatter.grid"],"records":[["0001","","# Regression: the four fixes that took DateTime::Format to PASS, 2026-08-04.\n# The first is the general one and by far the widest: `$.name(ARGS)` was not a\n# method call. Every expectation was checked against Rakudo.\n\nmy $ok = True;\nsub ck($got, $want, $l) { unless $got eqv $want { say \"FAIL: $l — {$got.raku} vs {$want.raku}\"; $ok = False } }\n\n# 1. `$.name(ARGS)` is `self.name(ARGS)` — a call that TAKES those arguments.\n#    It used to parse as the no-argument accessor `$.name` followed by a postfix\n#    call on whatever that returned, so the arguments never arrived and the\n#    result was invoked as if it were a Callable.\n{\n    my class P {\n        method greet($n) { \"hi-$n\" }\n        method viaDot($n)  { $.greet($n) }\n        method viaSelf($n) { self.greet($n) }\n        method twoArgs($a, $b) { $.greet(\"$a$b\") }\n        method noArgs() { $.greet('none') }\n    }\n    ck(P.new.viaDot(1),      'hi-1',    '$.name($x) passes its argument');\n    ck(P.new.viaSelf(1),     'hi-1',    'self.name($x) is unchanged');\n    ck(P.new.twoArgs(1, 2),  'hi-12',   'two arguments');\n    ck(P.new.noArgs,         'hi-none', 'a literal argument');\n\n    # the bare accessor, with no parentheses, still reads the attribute\n    my class Q { has $.v = 7; method read() { $.v } }\n    ck(Q.new.read, 7, 'bare $.attr is still the accessor');\n}\n\n# 2. A formatter may be an OBJECT doing Callable, not only a bare Code — which\n#    is how DateTime::Format ships one. Requiring Code dropped it silently and\n#    the DateTime stringified as ISO-8601.\n{\n    my class F does Callable { method CALL-ME($d) { \"F-{$d.year}\" } }\n    my $d = DateTime.new(:2020year, :1month, :2day, :formatter(F.new));\n    ck(~$d, 'F-2020', 'an object formatter is invoked');\n\n    my $c = DateTime.new(:2020year, :1month, :2day, :formatter(-> $x { \"C-{$x.year}\" }));\n    ck(~$c, 'C-2020', 'a plain Callable still works');\n}\n\n# 3. Every DateTime CONVERSION keeps the formatter.\n{\n    my $f = -> $d { \"F-{$d.hour}\" };\n    my $d = DateTime.new(:2020year, :1month, :2day, :3hour, :formatter($f), :timezone(3600));\n    ck((~$d.utc, ~$d.clone, ~$d.in-timezone(0), ~$d.later(:1day), ~$d.earlier(:1hour)),\n       ('F-2', 'F-3', 'F-2', 'F-3', 'F-2'),\n       'utc / clone / in-timezone / later / earlier all stay formatted');\n}\n\n# 4. `|$obj` passes the object as ONE positional. Only a real Hash or Map slips\n#    as named arguments — plenty of ordinary values are hash-BACKED here, and\n#    slipping their internals meant the value never arrived at all.\n{\n    my sub takes-dt(DateTime $d, *%o) { $d.defined ?? \"got-{$d.year}\" !! 'TYPE OBJECT' }\n    my $dt = DateTime.new(:2020year, :1month, :2day);\n    ck(takes-dt(|$dt), 'got-2020', 'a slipped DateTime arrives as itself');\n\n    my sub takes-named(:$a, :$b) { \"$a-$b\" }\n    my %h = a => 1, b => 2;\n    ck(takes-named(|%h), '1-2', 'a real Hash still slips as nameds');\n}\n\nsay $ok ?? 'PASS' !! 'FAIL';","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}