Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Writer] Escape method names that are invalid with def syntax #537

Conversation

pocke
Copy link
Member

@pocke pocke commented Dec 20, 2020

This pull request fixes the escaping strategy of RBS::Writer to escape invalid method names for def syntax correctly.

Problem

Currently RBS::Writer doesn't escape invalid method names for def syntax. For example

require 'rbs'
require 'stringio'

rbs = RBS::Parser.parse_signature(<<~RBS)
  class C
    def `foo!=`: () -> untyped
  end
RBS

io = StringIO.new

RBS::Writer.new(out: io).write(rbs)

puts io.string
# => class C
#      def foo!=: () -> untyped
#    end

RBS::Parser.parse_signature(io.string)
# => RBS::Parser::SyntaxError

I expect def `foo!=`: () -> untyped as the output, but it displays without backtick escaping. So it generates invalid RBS.

By the way, we can't define foo!= method with def syntax even in Ruby. But we can use define_method for this method name.

class C
  define_method(:'foo!=') {}
end

p C.instance_methods(false) # => [:"foo!="]

Solution

Escape method names correctly.

"`#{s}`"
else
if [:tOPERATOR, :kAMP, :kHAT, :kSTAR, :kLT, :kEXCLAMATION, :kSTAR2, :kBAR].include?(Parser::PUNCTS[s]) ||
(/\A[a-zA-Z_]\w*[?!=]?\z/.match?(s) && !/\A#{Parser::KEYWORDS_RE}\z/.match?(s))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RBS::Writer escapes keywords, for example: ``def def: () -> untyped`. But this escaping is not necessary because it is allowed.

I keep this escaping because of compatibility, and I'm not sure why this escaping needs to keep.

def `def`: () -> Symbol

If this escaping is not necessary, I can remove the escaping easily by removing && !/\A#{Parser::KEYWORDS_RE}\z/.match?(s) condition.

What do you think? @soutaro

Copy link
Member

@soutaro soutaro Dec 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense. I don't think we need to keep the quoting behavior.

Let's make it:

  1. Unquoted even if it is a keyword (I'm proposing this change)
  2. Unquoted if it is safe /\A[a-zA-Z_]\w*[?!=]?\z/
  3. Quoted otherwise

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your comment. I applied this charge with 2b03212.

But I noticed we still need to quote self and self? as a method name because it causes syntax error without quote.
So I replace Parser::KEYWORDS_RE with /\Aself\??\z/.

Except `self` and `self?` because they need quote.
Copy link
Member

@soutaro soutaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

@pocke pocke merged commit e23e31d into ruby:master Dec 21, 2020
@pocke pocke deleted the _Writer__Escape_method_names_that_are_invalid_with__def__syntax branch December 21, 2020 03:25
pocke added a commit to pocke/gem_rbs_collection that referenced this pull request Dec 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants