Skip to content

Commit

Permalink
[Ruby 3.0] Add support for endless method definitions (sorbet#4359)
Browse files Browse the repository at this point in the history
* Import endless method definition tests

Co-authored-by: Alexandre Terrasa <583144+Morriar@users.noreply.github.com>

* [Ruby 3.0] Add support for endless method definitions

Co-authored-by: Alexandre Terrasa <583144+Morriar@users.noreply.github.com>

* Remove overload parse error that no longer occures with endless method support

Co-authored-by: Alexandre Terrasa <583144+Morriar@users.noreply.github.com>

Co-authored-by: Alexandre Terrasa <583144+Morriar@users.noreply.github.com>
  • Loading branch information
vinistock and Morriar committed Jul 14, 2021
1 parent f7d5423 commit 2953c69
Show file tree
Hide file tree
Showing 44 changed files with 311 additions and 43 deletions.
80 changes: 68 additions & 12 deletions parser/Builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,29 @@ class Builder::Impl {
return make_unique<Class>(loc, declLoc, std::move(name), std::move(superclass), std::move(body));
}

unique_ptr<Node> defEndlessMethod(const token *def, const token *tname, unique_ptr<Node> args, const token *equal,
unique_ptr<Node> body) {
core::LocOffsets declLoc = tokLoc(def, tname).join(maybe_loc(args));
core::LocOffsets loc = tokLoc(def).join(body->loc);
std::string name = tname->string();

checkEndlessSetter(name, declLoc);

return make_unique<DefMethod>(loc, declLoc, gs_.enterNameUTF8(name), std::move(args), std::move(body));
}

unique_ptr<Node> defEndlessSingleton(unique_ptr<Node> defHead, unique_ptr<Node> args, const token *equal,
unique_ptr<Node> body) {
auto *head = parser::cast_node<DefsHead>(defHead.get());
core::LocOffsets declLoc = head->loc.join(maybe_loc(args));
core::LocOffsets loc = head->loc.join(body->loc);
std::string name = head->name.toString(gs_);

checkEndlessSetter(name, declLoc);

return make_unique<DefS>(loc, declLoc, std::move(head->definee), head->name, std::move(args), std::move(body));
}

unique_ptr<Node> defMethod(const token *def, const token *name, unique_ptr<Node> args, unique_ptr<Node> body,
const token *end) {
core::LocOffsets declLoc = tokLoc(def, name).join(maybe_loc(args));
Expand All @@ -725,17 +748,23 @@ class Builder::Impl {
return make_unique<SClass>(loc, declLoc, std::move(expr), std::move(body));
}

unique_ptr<Node> defSingleton(const token *def, unique_ptr<Node> definee, const token *dot, const token *name,
unique_ptr<Node> args, unique_ptr<Node> body, const token *end) {
core::LocOffsets declLoc = tokLoc(def, name).join(maybe_loc(args));
core::LocOffsets loc = tokLoc(def, end);
unique_ptr<Node> defsHead(const token *def, unique_ptr<Node> definee, const token *dot, const token *name) {
core::LocOffsets declLoc = tokLoc(def, name);

if (isLiteralNode(*(definee.get()))) {
error(ruby_parser::dclass::SingletonLiteral, definee->loc);
return make_unique<DefsHead>(declLoc, std::move(definee), gs_.enterNameUTF8(name->string()));
}

unique_ptr<Node> defSingleton(unique_ptr<Node> defHead, unique_ptr<Node> args, unique_ptr<Node> body,
const token *end) {
auto *head = parser::cast_node<DefsHead>(defHead.get());
core::LocOffsets declLoc = head->loc.join(maybe_loc(args));
core::LocOffsets loc = head->loc.join(tokLoc(end));

if (isLiteralNode(*(head->definee.get()))) {
error(ruby_parser::dclass::SingletonLiteral, head->definee->loc);
}

return make_unique<DefS>(loc, declLoc, std::move(definee), gs_.enterNameUTF8(name->string()), std::move(args),
std::move(body));
return make_unique<DefS>(loc, declLoc, std::move(head->definee), head->name, std::move(args), std::move(body));
}

unique_ptr<Node> empty_else(const token *tok) {
Expand Down Expand Up @@ -1513,6 +1542,13 @@ class Builder::Impl {
driver_->pattern_hash_keys.declare(name);
}

void checkEndlessSetter(std::string name, core::LocOffsets loc) {
if (name != "===" && name != "==" && name != "!=" && name != "<=" && name != ">=" &&
name[name.length() - 1] == '=') {
error(ruby_parser::dclass::EndlessSetter, loc);
}
}

void checkLVarName(std::string name, core::LocOffsets loc) {
std::regex lvar_regex("^[a-z_][a-zA-Z0-9_]*$");
if (!std::regex_match(name, lvar_regex)) {
Expand Down Expand Up @@ -1776,11 +1812,28 @@ ForeignPtr def_sclass(SelfPtr builder, const token *class_, const token *lshft_,
return build->toForeign(build->def_sclass(class_, lshft_, build->cast_node(expr), build->cast_node(body), end_));
}

ForeignPtr defSingleton(SelfPtr builder, const token *def, ForeignPtr definee, const token *dot, const token *name,
ForeignPtr args, ForeignPtr body, const token *end) {
ForeignPtr defsHead(SelfPtr builder, const token *def, ForeignPtr definee, const token *dot, const token *name) {
auto build = cast_builder(builder);
return build->toForeign(build->defSingleton(def, build->cast_node(definee), dot, name, build->cast_node(args),
build->cast_node(body), end));
return build->toForeign(build->defsHead(def, build->cast_node(definee), dot, name));
}

ForeignPtr defEndlessMethod(SelfPtr builder, const token *def, const token *name, ForeignPtr args, const token *equal,
ForeignPtr body) {
auto build = cast_builder(builder);
return build->toForeign(build->defEndlessMethod(def, name, build->cast_node(args), equal, build->cast_node(body)));
}

ForeignPtr defEndlessSingleton(SelfPtr builder, ForeignPtr defHead, ForeignPtr args, const token *equal,
ForeignPtr body) {
auto build = cast_builder(builder);
return build->toForeign(
build->defEndlessSingleton(build->cast_node(defHead), build->cast_node(args), equal, build->cast_node(body)));
}

ForeignPtr defSingleton(SelfPtr builder, ForeignPtr defHead, ForeignPtr args, ForeignPtr body, const token *end) {
auto build = cast_builder(builder);
return build->toForeign(
build->defSingleton(build->cast_node(defHead), build->cast_node(args), build->cast_node(body), end));
}

ForeignPtr encodingLiteral(SelfPtr builder, const token *tok) {
Expand Down Expand Up @@ -2325,9 +2378,12 @@ struct ruby_parser::builder Builder::interface = {
cvar,
dedentString,
def_class,
defEndlessMethod,
defEndlessSingleton,
defMethod,
defModule,
def_sclass,
defsHead,
defSingleton,
encodingLiteral,
false_,
Expand Down
9 changes: 9 additions & 0 deletions parser/tools/generate_ast.cc
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ NodeDef nodes[] = {
{"args", FieldType::Node},
{"body", FieldType::Node}}),
},
// def <expr>.name singleton-class name method def
{
"DefsHead",
"defshead",
vector<FieldDef>({
{"definee", FieldType::Node},
{"name", FieldType::Name},
}),
},
// string with an interpolation, all nodes are concatenated in a single string
{
"DString",
Expand Down
5 changes: 0 additions & 5 deletions test/testdata/parser/compare_overload_parse_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,3 @@ class B # error: class definition in method body
def less_equal<=(foo) # error: unexpected token "<="
end
end

class C # error: class definition in method body
def not_equal!=(foo) # error: unexpected token "="
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:def, :===,
s(:args,
s(:arg, :other)),
s(:send, nil, :do_something))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_comparison_method_0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def ===(other) = do_something
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:def, :==,
s(:args,
s(:arg, :other)),
s(:send, nil, :do_something))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_comparison_method_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def ==(other) = do_something
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:def, :!=,
s(:args,
s(:arg, :other)),
s(:send, nil, :do_something))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_comparison_method_2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def !=(other) = do_something
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:def, :<=,
s(:args,
s(:arg, :other)),
s(:send, nil, :do_something))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_comparison_method_3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def <=(other) = do_something
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:def, :>=,
s(:args,
s(:arg, :other)),
s(:send, nil, :do_something))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_comparison_method_4.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def >=(other) = do_something
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:def, :!=,
s(:args,
s(:arg, :other)),
s(:send, nil, :do_something))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_comparison_method_5.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def !=(other) = do_something
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
s(:def, :foo,
s(:args),
s(:int, "42"))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def foo() = 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
s(:def, :inc,
s(:args,
s(:arg, :x)),
s(:send,
s(:lvar, :x), :+,
s(:int, "1")))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def inc(x) = x + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s(:defs,
s(:send, nil, :obj), :foo,
s(:args),
s(:int, "42"))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def obj.foo() = 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
s(:defs,
s(:send, nil, :obj), :inc,
s(:args,
s(:arg, :x)),
s(:send,
s(:lvar, :x), :+,
s(:int, "1")))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def obj.inc(x) = x + 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
s(:def, :foo,
s(:args,
s(:forward_arg)),
s(:send, nil, :bar,
s(:forwarded_args)))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def foo(...) = bar(...)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
s(:def, :m,
s(:args),
s(:rescue,
s(:int, "1"),
s(:resbody, nil, nil,
s(:int, "2")), nil))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_with_rescue_mod_0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def m() = 1 rescue 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
s(:defs,
s(:self), :m,
s(:args),
s(:rescue,
s(:int, "1"),
s(:resbody, nil, nil,
s(:int, "2")), nil))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_with_rescue_mod_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def self.m() = 1 rescue 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
s(:def, :foo, nil,
s(:int, "42"))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_without_args_0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def foo = 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
s(:def, :foo, nil,
s(:rescue,
s(:int, "42"),
s(:resbody, nil, nil,
s(:nil)), nil))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_without_args_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def foo = 42 rescue nil
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
s(:defs,
s(:self), :foo, nil,
s(:int, "42"))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_without_args_2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def self.foo = 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
s(:defs,
s(:self), :foo, nil,
s(:rescue,
s(:int, "42"),
s(:resbody, nil, nil,
s(:nil)), nil))
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_method_without_args_3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def self.foo = 42 rescue nil
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_setter_0.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def foo=() = 42 # error: setter method cannot be defined in an endless method definition
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_setter_1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def obj.foo=() = 42 # error: setter method cannot be defined in an endless method definition
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_setter_2.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def foo=() = 42 rescue nil # error: setter method cannot be defined in an endless method definition
3 changes: 3 additions & 0 deletions test/whitequark/test_endless_setter_3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# typed: true

def obj.foo=() = 42 rescue nil # error: setter method cannot be defined in an endless method definition

0 comments on commit 2953c69

Please sign in to comment.