parlour 4.0.1 → 5.0.0.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +208 -20
- data/exe/parlour +45 -6
- data/lib/parlour.rb +27 -1
- data/lib/parlour/conversion/converter.rb +34 -0
- data/lib/parlour/conversion/rbi_to_rbs.rb +223 -0
- data/lib/parlour/detached_rbs_generator.rb +25 -0
- data/lib/parlour/generator.rb +34 -0
- data/lib/parlour/options.rb +71 -0
- data/lib/parlour/rbi_generator.rb +24 -37
- data/lib/parlour/rbi_generator/arbitrary.rb +5 -2
- data/lib/parlour/rbi_generator/attribute.rb +14 -5
- data/lib/parlour/rbi_generator/class_namespace.rb +8 -3
- data/lib/parlour/rbi_generator/constant.rb +17 -6
- data/lib/parlour/rbi_generator/enum_class_namespace.rb +8 -3
- data/lib/parlour/rbi_generator/extend.rb +5 -2
- data/lib/parlour/rbi_generator/include.rb +5 -2
- data/lib/parlour/rbi_generator/method.rb +15 -10
- data/lib/parlour/rbi_generator/module_namespace.rb +7 -2
- data/lib/parlour/rbi_generator/namespace.rb +39 -12
- data/lib/parlour/rbi_generator/parameter.rb +11 -5
- data/lib/parlour/rbi_generator/rbi_object.rb +19 -78
- data/lib/parlour/rbi_generator/struct_class_namespace.rb +9 -2
- data/lib/parlour/rbi_generator/struct_prop.rb +12 -9
- data/lib/parlour/rbi_generator/type_alias.rb +101 -0
- data/lib/parlour/rbs_generator.rb +24 -0
- data/lib/parlour/rbs_generator/arbitrary.rb +92 -0
- data/lib/parlour/rbs_generator/attribute.rb +82 -0
- data/lib/parlour/rbs_generator/block.rb +49 -0
- data/lib/parlour/rbs_generator/class_namespace.rb +106 -0
- data/lib/parlour/rbs_generator/constant.rb +95 -0
- data/lib/parlour/rbs_generator/extend.rb +92 -0
- data/lib/parlour/rbs_generator/include.rb +92 -0
- data/lib/parlour/rbs_generator/interface_namespace.rb +34 -0
- data/lib/parlour/rbs_generator/method.rb +146 -0
- data/lib/parlour/rbs_generator/method_signature.rb +104 -0
- data/lib/parlour/rbs_generator/module_namespace.rb +35 -0
- data/lib/parlour/rbs_generator/namespace.rb +627 -0
- data/lib/parlour/rbs_generator/parameter.rb +145 -0
- data/lib/parlour/rbs_generator/rbs_object.rb +78 -0
- data/lib/parlour/rbs_generator/type_alias.rb +96 -0
- data/lib/parlour/type_parser.rb +152 -0
- data/lib/parlour/typed_object.rb +87 -0
- data/lib/parlour/types.rb +445 -0
- data/lib/parlour/version.rb +1 -1
- data/parlour.gemspec +1 -1
- data/rbi/parlour.rbi +982 -76
- metadata +30 -7
- data/lib/parlour/rbi_generator/options.rb +0 -74
@@ -0,0 +1,145 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
class RbsGenerator < Generator
|
4
|
+
# Represents a method parameter with a Sorbet type signature.
|
5
|
+
class Parameter
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig do
|
9
|
+
params(
|
10
|
+
name: String,
|
11
|
+
type: T.nilable(Types::TypeLike),
|
12
|
+
required: T::Boolean,
|
13
|
+
).void
|
14
|
+
end
|
15
|
+
# Create a new method parameter.
|
16
|
+
# Note that, in RBS, blocks are not parameters. Use a {Block} instead.
|
17
|
+
#
|
18
|
+
# @example Create a simple Integer parameter named +num+.
|
19
|
+
# Parlour::RbsGenerator::Parameter.new('num', type: 'Integer')
|
20
|
+
# @example Create a nilable array parameter.
|
21
|
+
# Parlour::RbsGenerator::Parameter.new('array_of_strings_or_symbols', type:
|
22
|
+
# Parlour::Types::Nilable.new(
|
23
|
+
# Parlour::Types::Array.new(
|
24
|
+
# Parlour::Types::Union.new('String', 'Symbol')
|
25
|
+
# )
|
26
|
+
# )
|
27
|
+
# )
|
28
|
+
# @example Create an optional parameter.
|
29
|
+
# Parlour::RbsGenerator::Parameter.new('name', type: 'String', default: 'Parlour')
|
30
|
+
#
|
31
|
+
# @param name [String] The name of this parameter. This may start with +*+ or +**+,
|
32
|
+
# ,or end with +:+, which will infer the {kind} of this
|
33
|
+
# parameter. (If it contains none of those, {kind} will be +:normal+.)
|
34
|
+
# @param type [Types::TypeLike, nil] This type of this parameter.
|
35
|
+
# @param required [Boolean] Whether this parameter is required.
|
36
|
+
# @return [void]
|
37
|
+
def initialize(name, type: nil, required: true)
|
38
|
+
name = T.must(name)
|
39
|
+
@name = name
|
40
|
+
|
41
|
+
prefix = /^(\*\*|\*|\&)?/.match(name)&.captures&.first || ''
|
42
|
+
@kind = PREFIXES.rassoc(prefix).first
|
43
|
+
|
44
|
+
@kind = :keyword if kind == :normal && name.end_with?(':')
|
45
|
+
|
46
|
+
@type = type || Parlour::Types::Untyped.new
|
47
|
+
@required = required
|
48
|
+
end
|
49
|
+
|
50
|
+
sig { params(other: Object).returns(T::Boolean) }
|
51
|
+
# Returns true if this instance is equal to another method.
|
52
|
+
#
|
53
|
+
# @param other [Object] The other instance. If this is not a {Parameter} (or a
|
54
|
+
# subclass of it), this will always return false.
|
55
|
+
# @return [Boolean]
|
56
|
+
def ==(other)
|
57
|
+
Parameter === other &&
|
58
|
+
name == other.name &&
|
59
|
+
kind == other.kind &&
|
60
|
+
type == other.type &&
|
61
|
+
required == other.required
|
62
|
+
end
|
63
|
+
|
64
|
+
sig { returns(String) }
|
65
|
+
# The name of this parameter, including any prefixes or suffixes such as
|
66
|
+
# +*+.
|
67
|
+
# @return [String]
|
68
|
+
attr_reader :name
|
69
|
+
|
70
|
+
sig { returns(String) }
|
71
|
+
# The name of this parameter, stripped of any prefixes or suffixes. For
|
72
|
+
# example, +*rest+ would become +rest+, or +foo:+ would become +foo+.
|
73
|
+
#
|
74
|
+
# @return [String]
|
75
|
+
def name_without_kind
|
76
|
+
return T.must(name[0..-2]) if kind == :keyword
|
77
|
+
|
78
|
+
prefix_match = /^(\*\*|\*|\&)?[a-zA-Z_]/.match(name)
|
79
|
+
raise 'unknown prefix' unless prefix_match
|
80
|
+
prefix = prefix_match.captures.first || ''
|
81
|
+
T.must(name[prefix.length..-1])
|
82
|
+
end
|
83
|
+
|
84
|
+
sig { returns(Types::TypeLike) }
|
85
|
+
# This parameter's type.
|
86
|
+
# @return [String]
|
87
|
+
attr_reader :type
|
88
|
+
|
89
|
+
sig { returns(T::Boolean) }
|
90
|
+
# Whether this parameter is required.
|
91
|
+
# @return [Boolean]
|
92
|
+
attr_reader :required
|
93
|
+
|
94
|
+
sig { returns(Symbol) }
|
95
|
+
# The kind of parameter that this is. This will be one of +:normal+,
|
96
|
+
# +:splat+, +:double_splat+, or +:keyword+.
|
97
|
+
# @return [Symbol]
|
98
|
+
attr_reader :kind
|
99
|
+
|
100
|
+
# An array of reserved keywords in RBS which may be used as parameter
|
101
|
+
# names in standard Ruby.
|
102
|
+
# TODO: probably incomplete
|
103
|
+
RBS_KEYWORDS = [
|
104
|
+
'type', 'interface', 'out', 'in', 'instance'
|
105
|
+
]
|
106
|
+
|
107
|
+
# A mapping of {kind} values to the characteristic prefixes each kind has.
|
108
|
+
PREFIXES = {
|
109
|
+
normal: '',
|
110
|
+
splat: '*',
|
111
|
+
double_splat: '**',
|
112
|
+
}.freeze
|
113
|
+
|
114
|
+
sig { returns(String) }
|
115
|
+
# A string of how this parameter should be defined in an RBS signature.
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
def to_rbs_param
|
119
|
+
raise 'blocks are not parameters in RBS' if kind == :block
|
120
|
+
|
121
|
+
t = String === @type ? @type : @type.generate_rbs
|
122
|
+
t = "^#{t}" if Types::Proc === @type
|
123
|
+
|
124
|
+
if RBS_KEYWORDS.include? name_without_kind
|
125
|
+
unless $VERBOSE.nil?
|
126
|
+
print Rainbow("Parlour warning: ").yellow.dark.bold
|
127
|
+
print Rainbow("Type generalization: ").magenta.bright.bold
|
128
|
+
puts "'#{name_without_kind}' is a keyword in RBS, renaming method parameter to '_#{name_without_kind}'"
|
129
|
+
end
|
130
|
+
|
131
|
+
n = "_#{name_without_kind}"
|
132
|
+
else
|
133
|
+
n = name_without_kind
|
134
|
+
end
|
135
|
+
|
136
|
+
# Extra check because "?*something" is invalid
|
137
|
+
((required || (kind != :normal && kind != :keyword)) ? '' : '?') + if kind == :keyword
|
138
|
+
"#{n}: #{t}"
|
139
|
+
else
|
140
|
+
"#{PREFIXES[kind]}#{t} #{n}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
class RbsGenerator < Generator
|
4
|
+
# An abstract class which is subclassed by any classes which can generate
|
5
|
+
# entire lines of an RBS, such as {Namespace} and {Method}. (As an example,
|
6
|
+
# {Parameter} is _not_ a subclass because it does not generate lines, only
|
7
|
+
# segments of definition lines.)
|
8
|
+
# @abstract
|
9
|
+
class RbsObject < TypedObject
|
10
|
+
abstract!
|
11
|
+
|
12
|
+
sig { params(generator: Generator, name: String).void }
|
13
|
+
# Creates a new RBS object.
|
14
|
+
# @note Don't call this directly.
|
15
|
+
#
|
16
|
+
# @param generator [RbsGenerator] The current RbsGenerator.
|
17
|
+
# @param name [String] The name of this module.
|
18
|
+
# @return [void]
|
19
|
+
def initialize(generator, name)
|
20
|
+
super(name)
|
21
|
+
@generator = generator
|
22
|
+
@generated_by = RbsGenerator === generator ? generator.current_plugin : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { returns(Generator) }
|
26
|
+
# The generator which this object belongs to.
|
27
|
+
# @return [Generator]
|
28
|
+
attr_reader :generator
|
29
|
+
|
30
|
+
sig do
|
31
|
+
abstract.params(
|
32
|
+
indent_level: Integer,
|
33
|
+
options: Options
|
34
|
+
).returns(T::Array[String])
|
35
|
+
end
|
36
|
+
# Generates the RBS lines for this object.
|
37
|
+
#
|
38
|
+
# @abstract
|
39
|
+
# @param indent_level [Integer] The indentation level to generate the lines at.
|
40
|
+
# @param options [Options] The formatting options to use.
|
41
|
+
# @return [Array<String>] The RBS lines, formatted as specified.
|
42
|
+
def generate_rbs(indent_level, options); end
|
43
|
+
|
44
|
+
sig do
|
45
|
+
abstract.params(
|
46
|
+
others: T::Array[RbsGenerator::RbsObject]
|
47
|
+
).returns(T::Boolean)
|
48
|
+
end
|
49
|
+
# Given an array of other objects, returns true if they may be merged
|
50
|
+
# into this instance using {merge_into_self}. Each subclass will have its
|
51
|
+
# own criteria on what allows objects to be mergeable.
|
52
|
+
#
|
53
|
+
# @abstract
|
54
|
+
# @param others [Array<RbsGenerator::RbsObject>] An array of other {RbsObject} instances.
|
55
|
+
# @return [Boolean] Whether this instance may be merged with them.
|
56
|
+
def mergeable?(others); end
|
57
|
+
|
58
|
+
sig do
|
59
|
+
abstract.params(
|
60
|
+
others: T::Array[RbsGenerator::RbsObject]
|
61
|
+
).void
|
62
|
+
end
|
63
|
+
# Given an array of other objects, merges them into this one. Each
|
64
|
+
# subclass will do this differently.
|
65
|
+
# You MUST ensure that {mergeable?} is true for those instances.
|
66
|
+
#
|
67
|
+
# @abstract
|
68
|
+
# @param others [Array<RbsGenerator::RbsObject>] An array of other {RbsObject} instances.
|
69
|
+
# @return [void]
|
70
|
+
def merge_into_self(others); end
|
71
|
+
|
72
|
+
sig { overridable.override.returns(String) }
|
73
|
+
def describe
|
74
|
+
'RBS object'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
class RbsGenerator < Generator
|
4
|
+
# Represents a type alias.
|
5
|
+
class TypeAlias < RbsObject
|
6
|
+
sig do
|
7
|
+
params(
|
8
|
+
generator: Generator,
|
9
|
+
name: String,
|
10
|
+
type: Types::TypeLike,
|
11
|
+
block: T.nilable(T.proc.params(x: TypeAlias).void)
|
12
|
+
).void
|
13
|
+
end
|
14
|
+
# Creates a new type alias.
|
15
|
+
#
|
16
|
+
# @param name [String] The name of the alias.
|
17
|
+
# @param value [String] The type to alias to.
|
18
|
+
def initialize(generator, name:, type:, &block)
|
19
|
+
super(generator, name)
|
20
|
+
@type = type
|
21
|
+
yield_self(&block) if block
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String] The type to alias to.
|
25
|
+
sig { returns(Types::TypeLike) }
|
26
|
+
attr_reader :type
|
27
|
+
|
28
|
+
sig { params(other: Object).returns(T::Boolean) }
|
29
|
+
# Returns true if this instance is equal to another type alias.
|
30
|
+
#
|
31
|
+
# @param other [Object] The other instance. If this is not a {TypeAlias} (or a
|
32
|
+
# subclass of it), this will always return false.
|
33
|
+
# @return [Boolean]
|
34
|
+
def ==(other)
|
35
|
+
TypeAlias === other && name == other.name && type == other.type
|
36
|
+
end
|
37
|
+
|
38
|
+
sig do
|
39
|
+
override.params(
|
40
|
+
indent_level: Integer,
|
41
|
+
options: Options
|
42
|
+
).returns(T::Array[String])
|
43
|
+
end
|
44
|
+
# Generates the RBS lines for this type alias.
|
45
|
+
#
|
46
|
+
# @param indent_level [Integer] The indentation level to generate the lines at.
|
47
|
+
# @param options [Options] The formatting options to use.
|
48
|
+
# @return [Array<String>] The RBS lines, formatted as specified.
|
49
|
+
def generate_rbs(indent_level, options)
|
50
|
+
[options.indented(indent_level,
|
51
|
+
"type #{name} = #{String === @type ? @type : @type.generate_rbs}"
|
52
|
+
)]
|
53
|
+
end
|
54
|
+
|
55
|
+
sig do
|
56
|
+
override.params(
|
57
|
+
others: T::Array[RbsGenerator::RbsObject]
|
58
|
+
).returns(T::Boolean)
|
59
|
+
end
|
60
|
+
# Given an array of {TypeAlias} instances, returns true if they may be
|
61
|
+
# merged into this instance using {merge_into_self}. This is always false.
|
62
|
+
#
|
63
|
+
# @param others [Array<RbiGenerator::RbsObject>] An array of other
|
64
|
+
# {TypeAlias} instances.
|
65
|
+
# @return [Boolean] Whether this instance may be merged with them.
|
66
|
+
def mergeable?(others)
|
67
|
+
others.all? { |other| self == other }
|
68
|
+
end
|
69
|
+
|
70
|
+
sig do
|
71
|
+
override.params(
|
72
|
+
others: T::Array[RbsGenerator::RbsObject]
|
73
|
+
).void
|
74
|
+
end
|
75
|
+
# Given an array of {TypeAlias} instances, merges them into this one.
|
76
|
+
# This particular implementation will simply do nothing, as instances
|
77
|
+
# are only mergeable if they are indentical.
|
78
|
+
# You MUST ensure that {mergeable?} is true for those instances.
|
79
|
+
#
|
80
|
+
# @param others [Array<RbiGenerator::RbsObject>] An array of other
|
81
|
+
# {TypeAlias} instances.
|
82
|
+
# @return [void]
|
83
|
+
def merge_into_self(others)
|
84
|
+
# We don't need to change anything! We only merge identical type alias
|
85
|
+
end
|
86
|
+
|
87
|
+
sig { override.returns(String) }
|
88
|
+
# Returns a human-readable brief string description of this code.
|
89
|
+
#
|
90
|
+
# @return [String]
|
91
|
+
def describe
|
92
|
+
"Type Alias (#{name} = #{type})"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/parlour/type_parser.rb
CHANGED
@@ -696,8 +696,160 @@ module Parlour
|
|
696
696
|
end
|
697
697
|
end
|
698
698
|
|
699
|
+
sig { params(str: String).returns(Types::Type) }
|
700
|
+
# TODO doc
|
701
|
+
def self.parse_single_type(str)
|
702
|
+
i = TypeParser.from_source('(none)', str)
|
703
|
+
i.parse_node_to_type(i.ast)
|
704
|
+
end
|
705
|
+
|
706
|
+
sig { params(node: Parser::AST::Node).returns(Types::Type) }
|
707
|
+
# Given an AST node representing an RBI type (such as 'T::Array[String]'),
|
708
|
+
# parses it into a generic type.
|
709
|
+
#
|
710
|
+
# @param [Parser::AST::Node] node
|
711
|
+
# @return [Parlour::Types::Type]
|
712
|
+
def parse_node_to_type(node)
|
713
|
+
case node.type
|
714
|
+
when :send
|
715
|
+
target, message, *args = *node
|
716
|
+
|
717
|
+
# Special case: is this a generic type instantiation?
|
718
|
+
if message == :[]
|
719
|
+
names = constant_names(target)
|
720
|
+
known_single_element_collections = [:Array, :Set, :Range, :Enumerator, :Enumerable]
|
721
|
+
|
722
|
+
if names.length == 2 && names[0] == :T &&
|
723
|
+
known_single_element_collections.include?(names[1])
|
724
|
+
|
725
|
+
parse_err "no type in T::#{names[1]}[...]", node if args.nil? || args.empty?
|
726
|
+
parse_err "too many types in T::#{names[1]}[...]", node unless args.length == 1
|
727
|
+
return T.must(Types.const_get(T.must(names[1]))).new(parse_node_to_type(T.must(args.first)))
|
728
|
+
elsif names.length == 2 && names == [:T, :Hash]
|
729
|
+
parse_err "not enough types in T::Hash[...]", node if args.nil? || args.length < 2
|
730
|
+
parse_err "too many types in T::Hash[...]", node unless args.length == 2
|
731
|
+
return Types::Hash.new(
|
732
|
+
parse_node_to_type(args[0]), parse_node_to_type(args[1])
|
733
|
+
)
|
734
|
+
else
|
735
|
+
# TODO
|
736
|
+
warning "user-defined generic types not implemented, treating #{names.last} as untyped", node
|
737
|
+
return Types::Untyped.new
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
# Special case: is this a proc?
|
742
|
+
# This parsing is pretty simplified, but you'd also have to be doing
|
743
|
+
# something pretty cursed with procs to break this
|
744
|
+
# This checks for (send (send (send (const nil :T) :proc) ...) ...)
|
745
|
+
# That's the right amount of nesting for T.proc.params(...).returns(...)
|
746
|
+
if node.to_a[0].type == :send &&
|
747
|
+
node.to_a[0].to_a[0].type == :send &&
|
748
|
+
node.to_a[0].to_a[0].to_a[1] == :proc &&
|
749
|
+
node.to_a[0].to_a[0].to_a[0].type == :const &&
|
750
|
+
node.to_a[0].to_a[0].to_a[0].to_a == [nil, :T] # yuck
|
751
|
+
|
752
|
+
# Get parameters
|
753
|
+
params_send = node.to_a[0]
|
754
|
+
parse_err "expected 'params' to follow 'T.proc'", node unless params_send.to_a[1] == :params
|
755
|
+
parse_err "expected 'params' to have kwargs", node unless params_send.to_a[2].type == :hash
|
756
|
+
|
757
|
+
parameters = params_send.to_a[2].to_a.map do |pair|
|
758
|
+
name, value = *pair
|
759
|
+
parse_err "expected 'params' name to be symbol", node unless name.type == :sym
|
760
|
+
name = name.to_a[0].to_s
|
761
|
+
value = parse_node_to_type(value)
|
762
|
+
|
763
|
+
RbiGenerator::Parameter.new(name, type: value)
|
764
|
+
end
|
765
|
+
|
766
|
+
# Get return value
|
767
|
+
if node.to_a[1] == :void
|
768
|
+
return_type = nil
|
769
|
+
else
|
770
|
+
_, call, *args = *node
|
771
|
+
parse_err 'expected .returns or .void', node unless call == :returns
|
772
|
+
parse_err 'no argument to .returns', node if args.nil? || args.empty?
|
773
|
+
parse_err 'too many arguments to .returns', node unless args.length == 1
|
774
|
+
return_type = parse_node_to_type(T.must(args.first))
|
775
|
+
end
|
776
|
+
|
777
|
+
return Types::Proc.new(parameters, return_type)
|
778
|
+
end
|
779
|
+
|
780
|
+
# The other options for a valid call are all "T.something" methods
|
781
|
+
parse_err "unexpected call #{node_to_s(node).inspect} in type", node \
|
782
|
+
unless target.type == :const && target.to_a == [nil, :T]
|
783
|
+
|
784
|
+
case message
|
785
|
+
when :nilable
|
786
|
+
parse_err 'no argument to T.nilable', node if args.nil? || args.empty?
|
787
|
+
parse_err 'too many arguments to T.nilable', node unless args.length == 1
|
788
|
+
Types::Nilable.new(parse_node_to_type(T.must(args.first)))
|
789
|
+
when :any
|
790
|
+
Types::Union.new((args || []).map { |x| parse_node_to_type(T.must(x)) })
|
791
|
+
when :all
|
792
|
+
Types::Intersection.new((args || []).map { |x| parse_node_to_type(T.must(x)) })
|
793
|
+
when :let
|
794
|
+
# Not really allowed in a type signature, but handy for generalizing
|
795
|
+
# constant types
|
796
|
+
parse_err 'not enough argument to T.let', node if args.nil? || args.length < 2
|
797
|
+
parse_err 'too many arguments to T.nilable', node unless args.length == 2
|
798
|
+
parse_node_to_type(args[1])
|
799
|
+
when :type_parameter
|
800
|
+
parse_err 'no argument to T.type_parameter', node if args.nil? || args.empty?
|
801
|
+
parse_err 'too many arguments to T.type_parameter', node unless args.length == 1
|
802
|
+
parse_err 'expected T.type_parameter to be passed a symbol', node unless T.must(args.first).type == :sym
|
803
|
+
Types::Raw.new(T.must(args.first.to_a[0].to_s))
|
804
|
+
when :class_of
|
805
|
+
parse_err 'no argument to T.class_of', node if args.nil? || args.empty?
|
806
|
+
parse_err 'too many arguments to T.class_of', node unless args.length == 1
|
807
|
+
Types::Class.new(parse_node_to_type(args[0]))
|
808
|
+
when :untyped
|
809
|
+
parse_err 'T.untyped does not accept arguments', node if !args.nil? && !args.empty?
|
810
|
+
Types::Untyped.new
|
811
|
+
else
|
812
|
+
warning "unknown method T.#{message}, treating as untyped", node
|
813
|
+
Types::Untyped.new
|
814
|
+
end
|
815
|
+
when :const
|
816
|
+
# Special case: T::Boolean
|
817
|
+
if constant_names(node) == [:T, :Boolean]
|
818
|
+
return Types::Boolean.new
|
819
|
+
end
|
820
|
+
|
821
|
+
# Otherwise, just a plain old constant
|
822
|
+
Types::Raw.new(constant_names(node).join('::'))
|
823
|
+
when :array
|
824
|
+
# Tuple
|
825
|
+
Types::Tuple.new(node.to_a.map { |x| parse_node_to_type(T.must(x)) })
|
826
|
+
when :hash
|
827
|
+
# Shape/record
|
828
|
+
keys_to_types = node.to_a.map do |pair|
|
829
|
+
key, value = *pair
|
830
|
+
parse_err "all shape keys must be symbols", node unless key.type == :sym
|
831
|
+
[key.to_a[0], parse_node_to_type(value)]
|
832
|
+
end.to_h
|
833
|
+
|
834
|
+
Types::Record.new(keys_to_types)
|
835
|
+
else
|
836
|
+
parse_err "unable to parse type #{node_to_s(node).inspect}", node
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
699
840
|
protected
|
700
841
|
|
842
|
+
sig { params(msg: String, node: Parser::AST::Node).void }
|
843
|
+
def warning(msg, node)
|
844
|
+
return if $VERBOSE.nil?
|
845
|
+
|
846
|
+
print Rainbow("Parlour warning: ").yellow.dark.bold
|
847
|
+
print Rainbow("Type generalization: ").magenta.bright.bold
|
848
|
+
puts msg
|
849
|
+
print Rainbow(" └ at code: ").blue.bright.bold
|
850
|
+
puts node_to_s(node)
|
851
|
+
end
|
852
|
+
|
701
853
|
sig { params(node: T.nilable(Parser::AST::Node)).returns(T::Array[Symbol]) }
|
702
854
|
# Given a node representing a simple chain of constants (such as A or
|
703
855
|
# A::B::C), converts that node into an array of the constant names which
|