Class: RBS::TypeAliasRegularity
- Inherits:
-
Object
- Object
- RBS::TypeAliasRegularity
- Defined in:
- sig/type_alias_regularity.rbs,
lib/rbs/type_alias_regularity.rb
Overview
TypeAliasRegularity validates if a type alias is regular or not.
Generic and recursive type alias cannot be polymorphic in their definitions.
type foo[T] = Integer
| foo[T]? # Allowed. The type argument of `foo` doesn't change.
type bar[T] = Integer
| foo[T]
| foo[Array[T]] # Allowed. There are two type arguments `T` and `Array[T]` of `foo`, but it's not definition of `foo`.
type baz[T] = Integer
| baz[Array[T]] # Error. Recursive definition of `baz` has different type argument from the definition.
The #nonregular? method can be used to test if given type name is regular or not.
validator = RBS::TypeAliasRegularity.validate(env: env)
validator.nonregular?(TypeName.parse("::foo")) # => nil
validator.nonregular?(TypeName.parse("::bar")) # => nil
validator.nonregular?(TypeName.parse("::baz")) # => TypeAliasRegularity::Diagnostic
A special case is when the type argument is untyped.
type foo[T] = Integer | foo[untyped] # This is allowed.
Defined Under Namespace
Classes: Diagnostic
Instance Attribute Summary collapse
-
#builder ⇒ DefinitionBuilder
readonly
Returns the value of attribute builder.
-
#diagnostics ⇒ Hash[TypeName, Diagnostic]
readonly
Diagnostics of each type aliases.
-
#env ⇒ Environment
readonly
Returns the value of attribute env.
Class Method Summary collapse
-
.validate(env:) ⇒ TypeAliasRegularity
Returns new instance which already run
#validate.
Instance Method Summary collapse
-
#build_alias_type(name) ⇒ Types::Alias?
Returns alias type for given type name, if the alias is generic.
- #compatible_args?(args1, args2) ⇒ Boolean
- #each_alias_type(type) {|arg0| ... } ⇒ void
-
#each_mutual_alias_defs {|arg0| ... } ⇒ void
Yields set of normalized type names.
-
#initialize(env:) ⇒ TypeAliasRegularity
constructor
A new instance of TypeAliasRegularity.
-
#nonregular?(type_name) ⇒ Diagnostic?
Returns
Diagnosticinstance if the alias type is nonregular. - #validate ⇒ void
- #validate_alias_type(alias_type, names, types) ⇒ void
Constructor Details
#initialize(env:) ⇒ TypeAliasRegularity
Returns a new instance of TypeAliasRegularity.
16 17 18 19 20 |
# File 'lib/rbs/type_alias_regularity.rb', line 16 def initialize(env:) @env = env @builder = DefinitionBuilder.new(env: env) @diagnostics = {} end |
Instance Attribute Details
#builder ⇒ DefinitionBuilder (readonly)
Returns the value of attribute builder.
14 15 16 |
# File 'lib/rbs/type_alias_regularity.rb', line 14 def builder @builder end |
#diagnostics ⇒ Hash[TypeName, Diagnostic] (readonly)
Diagnostics of each type aliases. The type names are normalized.
42 43 44 |
# File 'sig/type_alias_regularity.rbs', line 42 def diagnostics @diagnostics end |
#env ⇒ Environment (readonly)
Returns the value of attribute env.
14 15 16 |
# File 'lib/rbs/type_alias_regularity.rb', line 14 def env @env end |
Class Method Details
.validate(env:) ⇒ TypeAliasRegularity
Returns new instance which already run #validate.
69 70 71 72 73 |
# File 'sig/type_alias_regularity.rbs', line 69 def self.validate(env:) self.new(env: env).tap do |validator| validator.validate() end end |
Instance Method Details
#build_alias_type(name) ⇒ Types::Alias?
Returns alias type for given type name, if the alias is generic. Returns nil if the type alias is not generic.
89 90 91 92 93 94 95 |
# File 'sig/type_alias_regularity.rbs', line 89 def build_alias_type(name) entry = env.type_alias_decls[name] or return unless entry.decl.type_params.empty? as = entry.decl.type_params.each.map {|param| Types::Variable.new(name: param.name, location: nil) } Types::Alias.new(name: name, args: as, location: nil) end end |
#compatible_args?(args1, args2) ⇒ Boolean
69 70 71 72 73 74 75 76 77 |
# File 'lib/rbs/type_alias_regularity.rb', line 69 def compatible_args?(args1, args2) if args1.size == args2.size args1.zip(args2).all? do |t1, t2| t1.is_a?(Types::Bases::Any) || t2.is_a?(Types::Bases::Any) || t1 == t2 end end end |
#each_alias_type(type) {|arg0| ... } ⇒ void
This method returns an undefined value.
110 111 112 113 114 115 116 117 118 |
# File 'lib/rbs/type_alias_regularity.rb', line 110 def each_alias_type(type, &block) if type.is_a?(RBS::Types::Alias) yield type end type.each_type do |ty| each_alias_type(ty, &block) end end |
#each_mutual_alias_defs {|arg0| ... } ⇒ void
This method returns an undefined value.
Yields set of normalized type names
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'sig/type_alias_regularity.rbs', line 96 def each_mutual_alias_defs(&block) # @type var each_node: ^() { (TypeName) -> void } -> void each_node = -> (&block) do env.type_alias_decls.each_value do |decl| if normalized = env.normalize_type_name?(decl.name) block[normalized] end end end # @type var each_child: ^(TypeName) { (TypeName) -> void } -> void each_child = -> (name, &block) do if env.type_alias_decls.key?(name) type = builder.(name) each_alias_type(type) do |ty| if normalized = env.normalize_type_name?(ty.name) block[normalized] end end end end TSort.each_strongly_connected_component(each_node, each_child) do |names| yield Set.new(names) end end |
#nonregular?(type_name) ⇒ Diagnostic?
Returns Diagnostic instance if the alias type is nonregular.
Returns nil if the alias type is regular.
Normalizes the given type name automatically.
78 79 80 |
# File 'sig/type_alias_regularity.rbs', line 78 def nonregular?(type_name) diagnostics[env.normalize_type_name!(type_name)] end |
#validate ⇒ void
This method returns an undefined value.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/rbs/type_alias_regularity.rb', line 22 def validate diagnostics.clear each_mutual_alias_defs do |names| # Find the first generic type alias in strongly connected component. # This is to skip the regularity check when the alias is not generic. names.each do |name| # @type break: nil if type = build_alias_type(name) # Running validation only once from the first generic type is enough, because they are mutual recursive definition. validate_alias_type(type, names, {}) break end end end end |
#validate_alias_type(alias_type, names, types) ⇒ void
This method returns an undefined value.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/rbs/type_alias_regularity.rb', line 39 def validate_alias_type(alias_type, names, types) alias_name = env.normalize_type_name?(alias_type.name) or return if names.include?(alias_name) if ex_type = types[alias_name] unless compatible_args?(ex_type.args, alias_type.args) diagnostics[alias_name] ||= Diagnostic.new(type_name: alias_type.name, nonregular_type: alias_type) end return else types[alias_type.name] = alias_type end = builder.(alias_name, alias_type.args) each_alias_type() do |at| validate_alias_type(at, names, types) end end end |