Skip to content

Commit c9cd5f0

Browse files
authored
Merge pull request #1173 from dleavitt/rbs-2-standalone
Support rbs 2.x under ruby 3.1
2 parents 9fca64f + edd094a commit c9cd5f0

File tree

7 files changed

+180
-21
lines changed

7 files changed

+180
-21
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
- "2.6"
1717
- "2.7"
1818
- "3.0"
19+
- "3.1"
1920
runs-on: ${{ matrix.platform }}
2021
env:
2122
PLUGIN_RUBY_CI: true

src/rbs/parser.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,47 @@ def to_json(*a)
9898
end
9999
end
100100

101+
if defined?(RBS::AST::Declarations::ModuleTypeParams)
102+
# This class was removed in rbs 2.0. Monkeypatch < 2.0 to give the same json
103+
# output
104+
class RBS::AST::Declarations::ModuleTypeParams
105+
def to_json(*a)
106+
params.to_json(*a)
107+
end
108+
end
109+
110+
# make this look like the new AST::TypeParam json
111+
class RBS::AST::Declarations::ModuleTypeParams::TypeParam
112+
def to_json(*a)
113+
{ name: name, variance: variance, unchecked: skip_validation }.to_json(*a)
114+
end
115+
end
116+
117+
# https://github.com/ruby/rbs/commit/3ccdcb1f3ac5dcb866280f745866a852658195e6
118+
class RBS::MethodType
119+
# promote the array of symbols into TypeParam looking-things
120+
def to_json(*a)
121+
type_param_objects =
122+
type_params.map do |name|
123+
{
124+
name: name,
125+
variance: 'invariant',
126+
unchecked: 'false',
127+
upper_bound: nil
128+
# location - harder to get this but not needed
129+
}
130+
end
131+
132+
{
133+
type_params: type_param_objects,
134+
type: type,
135+
block: block,
136+
location: location
137+
}.to_json(*a)
138+
end
139+
end
140+
end
141+
101142
# The main parser interface.
102143
module Prettier
103144
class RBSParser

src/rbs/printer.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -447,30 +447,32 @@ const printer: Plugin.PrinterConfig<RBS.AnyNode> = {
447447
path: Plugin.Path<RBS.NameAndTypeParams>,
448448
node: RBS.NameAndTypeParams
449449
) {
450-
if (node.type_params.params.length === 0) {
450+
if (node.type_params.length === 0) {
451451
return node.name;
452452
}
453453

454-
const docs = path.map(
455-
(paramPath) => {
456-
const node = paramPath.getValue();
457-
const parts = [];
454+
const docs = path.map((paramPath) => {
455+
const node = paramPath.getValue();
456+
const parts = [];
458457

459-
if (node.skip_validation) {
460-
parts.push("unchecked");
461-
}
458+
if (node.unchecked) {
459+
parts.push("unchecked");
460+
}
462461

463-
if (node.variance === "covariant") {
464-
parts.push("out");
465-
} else if (node.variance === "contravariant") {
466-
parts.push("in");
467-
}
462+
if (node.variance === "covariant") {
463+
parts.push("out");
464+
} else if (node.variance === "contravariant") {
465+
parts.push("in");
466+
}
468467

468+
if (node.upper_bound) {
469+
const path = paramPath as Plugin.Path<{ upper_bound: RBS.Type }>;
470+
const upperBound = path.call(printType, "upper_bound");
471+
return join(" ", [...parts, node.name, "<", upperBound]);
472+
} else {
469473
return join(" ", [...parts, node.name]);
470-
},
471-
"type_params",
472-
"params"
473-
);
474+
}
475+
}, "type_params");
474476

475477
return [node.name, "[", join(", ", docs), "]"];
476478
}
@@ -568,7 +570,9 @@ const printer: Plugin.PrinterConfig<RBS.AnyNode> = {
568570

569571
// We won't have a type_params key if we're printing a block
570572
if (node.type_params && node.type_params.length > 0) {
571-
parts.push("[", join(", ", node.type_params), "] ");
573+
const typeParamNames = node.type_params.map((tp) => tp.name);
574+
575+
parts.push("[", join(", ", typeParamNames), "] ");
572576
}
573577

574578
const params = path.call(printMethodParams, "type");

src/types/rbs.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export type MethodParams = {
1818
};
1919

2020
export type MethodSignature = {
21-
type_params: string[],
21+
type_params: Param[],
2222
type: MethodParams & { return_type: Type },
2323
block: { required: boolean } & MethodSignature
2424
};
@@ -74,13 +74,14 @@ export type MethodDefinition = Member & { member: "method_definition" };
7474

7575
export type Param = {
7676
name: string,
77-
skip_validation: boolean,
77+
unchecked: boolean,
7878
variance: "invariant" | "covariant" | "contravariant"
79+
upper_bound?: Type,
7980
};
8081

8182
export type NameAndTypeParams = {
8283
name: string,
83-
type_params: { params: Param[] }
84+
type_params: Param[]
8485
};
8586

8687
export type Class = { declaration: "class", super_class?: NameAndArgs, members: Member[] } & NameAndTypeParams;

test/js/rbs/rbs.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,26 @@ describe("rbs", () => {
4747
expect(content).toMatchFormat();
4848
});
4949

50+
if (atLeastVersion("3.1")) {
51+
test("interface with bounded type param", () => {
52+
const content = rbs(`
53+
interface _Foo[A < B]
54+
end
55+
`);
56+
57+
expect(content).toMatchFormat();
58+
});
59+
60+
test("interface with fancy bounded type params", () => {
61+
const content = rbs(`
62+
interface _Foo[U < singleton(::Hash), V < W[X, Y]]
63+
end
64+
`);
65+
66+
expect(content).toMatchFormat();
67+
});
68+
}
69+
5070
test("class", () => {
5171
const content = rbs(`
5272
class Foo
@@ -74,6 +94,26 @@ describe("rbs", () => {
7494
expect(content).toMatchFormat();
7595
});
7696

97+
if (atLeastVersion("3.1")) {
98+
test("class with bounded type param", () => {
99+
const content = rbs(`
100+
class Foo[A < B]
101+
end
102+
`);
103+
104+
expect(content).toMatchFormat();
105+
});
106+
107+
test("class with fancy bounded type params", () => {
108+
const content = rbs(`
109+
class Foo[U < singleton(::Hash), V < W[X, Y]]
110+
end
111+
`);
112+
113+
expect(content).toMatchFormat();
114+
});
115+
}
116+
77117
test("class with annotations", () => {
78118
const content = rbs(`
79119
%a{This is an annotation.}

test/rb/rbs_test.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# frozen_string_literal: true
2+
3+
require 'test_helper'
4+
5+
class RBSTest < Minitest::Test
6+
# https://github.com/ruby/rbs/commit/c5da467adacc75fe9294ec1630f1b7931d9d6871
7+
def test_module_type_params_empty
8+
actual = declaration_json("interface _Foo\nend")['type_params']
9+
assert_instance_of Array, actual
10+
assert_empty actual
11+
end
12+
13+
def test_module_type_params_fancy
14+
source = <<~SOURCE
15+
class Foo[unchecked in A, B]
16+
end
17+
SOURCE
18+
19+
actual = declaration_json(source)['type_params']
20+
assert_instance_of Array, actual
21+
assert_equal 2, actual.length
22+
23+
type_a, type_b = actual
24+
25+
assert_equal 'A', type_a['name']
26+
assert_equal 'contravariant', type_a['variance']
27+
assert_equal true, type_a['unchecked']
28+
29+
assert_equal 'B', type_b['name']
30+
assert_equal 'invariant', type_b['variance']
31+
assert_equal false, type_b['unchecked']
32+
end
33+
34+
# https://github.com/ruby/rbs/commit/3ccdcb1f3ac5dcb866280f745866a852658195e6
35+
def test_generic_method
36+
source = <<~SOURCE
37+
class T
38+
def t: [A, B] (A) -> B
39+
end
40+
SOURCE
41+
42+
actual = member_json(source)['types'].first['type_params']
43+
assert_equal 2, actual.length
44+
45+
type_a, type_b = actual
46+
assert_equal 'A', type_a['name']
47+
assert_equal 'B', type_b['name']
48+
end
49+
50+
private
51+
52+
def declaration_json(source)
53+
JSON.parse(first_declaration(source).to_json)
54+
end
55+
56+
def member_json(source)
57+
JSON.parse(first_member(source).to_json)
58+
end
59+
60+
def parse(source)
61+
Prettier::RBSParser.parse(source)
62+
end
63+
64+
def first_declaration(source)
65+
parse(source)[:declarations].first
66+
end
67+
68+
def first_member(source)
69+
first_declaration(source).members.first
70+
end
71+
end

test/rb/test_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44

55
require 'prettier'
66
require_relative '../../src/ruby/parser'
7+
require_relative '../../src/rbs/parser'
78

89
require 'minitest/autorun'

0 commit comments

Comments
 (0)