class RBS::TypeAliasRegularity
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.
Attributes
diagnostics
[R]
Hash[TypeName, Diagnostic]
Diagnostics of each type aliases. The type names are normalized.
Public Class Methods
(env: Environment) → void
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/lib/rbs/type_alias_regularity.rb, line 16 def initialize(env:) @env = env @builder = DefinitionBuilder.new(env: env) @diagnostics = {} end
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/lib/rbs/type_alias_regularity.rb, line 120 def self.validate(env:) self.new(env: env).tap do |validator| validator.validate() end end
Returns new instance which already run validate.
Public Instance Methods
(TypeName) → Types::Alias?
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/lib/rbs/type_alias_regularity.rb, line 61 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
Returns alias type for given type name, if the alias is generic. Returns nil if the type alias is not generic.
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/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
(Types::t) { (Types::Alias) → void } → void
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/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
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/lib/rbs/type_alias_regularity.rb, line 83 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.expand_alias1(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
Yields set of normalized type names
(TypeName) → Diagnostic?
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/lib/rbs/type_alias_regularity.rb, line 79 def nonregular?(type_name) diagnostics[env.normalize_type_name!(type_name)] end
Returns Diagnostic instance if the alias type is nonregular. Returns nil if the alias type is regular.
Normalizes the given type name automatically.
() → void
Source
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/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
# File vendor/bundle/ruby/4.0.0/gems/rbs-4.0.3/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 expanded = builder.expand_alias2(alias_name, alias_type.args) each_alias_type(expanded) do |at| validate_alias_type(at, names, types) end end end