Skip to content

safe navigation operator discarded in arithmetic context  #345

@csabahenk

Description

@csabahenk

Versions:

  • ruby 3.2.2
  • parser 3.2.2.3
  • unparser 0.6.7
$ for s in 'a.foo(b)' 'a&.foo(b)' 'a.+(b)' 'a+b' 'a&.+(b)' ;  do echo "$s"; done | \
ruby -runparser -ne '$_.chomp.then { |s|
  puts [Unparser.unparse(*(Unparser.parse_with_comments s)), "# source", s].join(" ")
}'

Output:

a.foo(b) # source a.foo(b)
a&.foo(b) # source a&.foo(b)
a + b # source a.+(b)
a + b # source a+b
a + b # source a&.+(b)

Expected: unparser renders a.+(b) and a&.+(b) to distinct, pairwise equivalent experssions.
Actual: unparser renders a.+(b) and a&.+(b) to the same expression (equivalent only to the former).

Note that unparser handles the safe navigation operator properly for "normal" method calls which are not in use as infix pseudo-operators.


I also checked if the bug propagates down to parser:

$ for s in 'a.foo(b)' 'a&.foo(b)' 'a.+(b)' 'a+b' 'a&.+(b)' ;  do echo "$s"; done | \
ruby -rparser/current -rdigest/md5 -ryaml -ne 'BEGIN { $acc=[] }
$_.chomp.then { |s|
  Parser::CurrentRuby.new.parse(Parser::Source::Buffer.new("(string)", source: s)).inspect.then { |ani|
    $acc << Hash["expr"=>s, "parsedmd5"=> Digest::MD5.new.tap{|m| m << ani }.to_s, "parsed"=>ani]
  }
}  
END { $acc.to_set.classify { |r| r["parsedmd5"] }.transform_values(&:to_a).tap { |t| YAML.dump t, $> }}'

Output:

---
b37ae580875577098e664154610a21fe:
  - expr: a.foo(b)
    parsedmd5: b37ae580875577098e664154610a21fe
    parsed: |-
      s(:send,
        s(:send, nil, :a), :foo,
        s(:send, nil, :b))
8bc7c3994c8548cead4da4b9f36a8568:
  - expr: a&.foo(b)
    parsedmd5: 8bc7c3994c8548cead4da4b9f36a8568
    parsed: |-
      s(:csend,
        s(:send, nil, :a), :foo,
        s(:send, nil, :b))
cfccbdaedfa73c9e97cc26f7ced79e8b:
  - expr: a.+(b)
    parsedmd5: cfccbdaedfa73c9e97cc26f7ced79e8b
    parsed: |-
      s(:send,
        s(:send, nil, :a), :+,
        s(:send, nil, :b))
  - expr: a+b
    parsedmd5: cfccbdaedfa73c9e97cc26f7ced79e8b
    parsed: |-
      s(:send,
        s(:send, nil, :a), :+,
        s(:send, nil, :b))
10f949be7faf629eb6ea7b892f2a75e2:
  - expr: a&.+(b)
    parsedmd5: 10f949be7faf629eb6ea7b892f2a75e2
    parsed: |-
      s(:csend,
        s(:send, nil, :a), :+,
        s(:send, nil, :b))

Expected: parser parses s a.+(b) and a&.+(b) to distinct ASTs.
Actual: meets the expectation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions