Class: RBS::Validator

Inherits:
Object
  • Object
show all
Defined in:
lib/rbs/validator.rb,
sig/validator.rbs

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env:, resolver: nil) ⇒ Validator

Returns a new instance of Validator.

Parameters:



9
10
11
12
13
# File 'lib/rbs/validator.rb', line 9

def initialize(env:, resolver: nil)
  @env = env
  @resolver = resolver
  @definition_builder = DefinitionBuilder.new(env: env)
end

Instance Attribute Details

#definition_builderDefinitionBuilder (readonly)

Returns the value of attribute definition_builder.

Returns:



7
8
9
# File 'lib/rbs/validator.rb', line 7

def definition_builder
  @definition_builder
end

#envEnvironment (readonly)

Returns the value of attribute env.

Returns:



5
6
7
# File 'lib/rbs/validator.rb', line 5

def env
  @env
end

#resolverResolver::TypeNameResolver? (readonly)

Returns the value of attribute resolver.

Returns:



6
7
8
# File 'lib/rbs/validator.rb', line 6

def resolver
  @resolver
end

#type_alias_dependencyTypeAliasDependency (readonly)

Returns the value of attribute type_alias_dependency.

Returns:



178
179
180
# File 'lib/rbs/validator.rb', line 178

def type_alias_dependency
  @type_alias_dependency ||= TypeAliasDependency.new(env: env)
end

#type_alias_regularityTypeAliasRegularity (readonly)

Returns the value of attribute type_alias_regularity.

Returns:



182
183
184
# File 'lib/rbs/validator.rb', line 182

def type_alias_regularity
  @type_alias_regularity ||= TypeAliasRegularity.validate(env: env)
end

Instance Method Details

#absolute_type(type, context:, &block) ⇒ void

This method returns an undefined value.

Resolves relative type names to absolute type names in given context. Yields the type when the type name resolution using #resolver fails.

Parameters:

  • (Types::t)
  • context: (Resolver::context)


61
62
63
64
65
66
67
# File 'sig/validator.rbs', line 61

def absolute_type(type, context:, &block)
  return type unless resolver

  type.map_type_name do |type_name, _, type|
    resolver.resolve(type_name, context: context) || (block ? yield(type) : type_name)
  end
end

#validate_class_alias(entry:) ⇒ void

This method returns an undefined value.

Validates class alias declaration

  • The right hand side can be normalized
  • No mixing alias declaration between class and modules


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'sig/validator.rbs', line 50

def validate_class_alias(entry:)
  case env.normalize_module_name?(entry.decl.new_name)
  when nil
    raise NoTypeFoundError.new(type_name: entry.decl.old_name, location: entry.decl.location&.[](:old_name))
  when false
    raise CyclicClassAliasDefinitionError.new(entry)
  end

  case entry
  when Environment::ClassAliasEntry
    unless env.class_entry(entry.decl.old_name)
      raise InconsistentClassModuleAliasError.new(entry)
    end
  when Environment::ModuleAliasEntry
    unless env.module_entry(entry.decl.old_name)
      raise InconsistentClassModuleAliasError.new(entry)
    end
  end
end

#validate_method_definition(method_def, type_name:) ⇒ void

This method returns an undefined value.

Validates the type parameters in generic methods.

Parameters:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'sig/validator.rbs', line 32

def validate_method_definition(method_def, type_name:)
  method_def.overloads.each do |overload|
    method_type = overload.method_type
    unless method_type.type_params.empty?
      loc = method_type.location&.aref(:type_params)

      validate_type_params(
        method_type.type_params,
        type_name: type_name,
        method_name: method_def.name,
        location: loc
      )
    end
  end
end

#validate_type(type, context:) ⇒ void

This method returns an undefined value.

Validates the presence of type names and type application arity match.

Parameters:

  • (Types::t)
  • context: (Resolver::context)


24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/rbs/validator.rb', line 24

def validate_type(type, context:)
  case type
  when Types::ClassInstance, Types::Interface, Types::Alias
    type = absolute_type(type, context: context) #: Types::ClassInstance | Types::Interface | Types::Alias

    definition_builder.validate_type_name(type.name, type.location)

    normalized_type_name = env.normalize_type_name?(type.name)

    if normalized_type_name
      type_params =
        case type
        when Types::ClassInstance
          entry = env.class_decls[normalized_type_name] or raise
          entry.type_params
        when Types::Interface
          env.interface_decls[normalized_type_name].decl.type_params
        when Types::Alias
          env.type_alias_decls[normalized_type_name].decl.type_params
        end

      InvalidTypeApplicationError.check!(
        type_name: type.name,
        args: type.args,
        params: type_params,
        location: type.location
      )
    end

  when Types::ClassSingleton
    type = absolute_type(type, context: context) #: Types::ClassSingleton
    definition_builder.validate_type_presence(type)
  end

  type.each_type do |type|
    validate_type(type, context: context)
  end
end

#validate_type_alias(entry:) ⇒ void

This method returns an undefined value.

Validates type alias definition:

  • There is no circular definition between aliases
  • The type alias is regular
  • The generics type parameter variance annotation is consistent with respect to their usage
  • There is no circular dependencies between the generics type parameter bounds

It yields the rhs type if block is given, so that you can validate the rhs type.

Parameters:



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'sig/validator.rbs', line 28

def validate_type_alias(entry:)
  type_name = entry.decl.name

  if type_alias_dependency.circular_definition?(type_name)
    location = entry.decl.location or raise
    raise RecursiveTypeAliasError.new(alias_names: [type_name], location: location)
  end

  if diagnostic = type_alias_regularity.nonregular?(type_name)
    location = entry.decl.location or raise
    raise NonregularTypeAliasError.new(diagnostic: diagnostic, location: location)
  end

  unless entry.decl.type_params.empty?
    calculator = VarianceCalculator.new(builder: definition_builder)
    result = calculator.in_type_alias(name: type_name)
    if set = result.incompatible?(entry.decl.type_params)
      set.each do |param_name|
        param = entry.decl.type_params.find {|param| param.name == param_name } or raise
        next if param.unchecked?

        raise InvalidVarianceAnnotationError.new(
          type_name: type_name,
          param: param,
          location: entry.decl.type.location
        )
      end
    end

    validate_type_params(
      entry.decl.type_params,
      type_name: type_name,
      location: entry.decl.location&.aref(:type_params)
    )
  end

  if block_given?
    yield entry.decl.type
  end
end

#validate_type_params(params, type_name:, method_name: nil, location:) ⇒ void

This method returns an undefined value.

Validates the type parameters if there is no circular dependencies between the bounds.

[X, Y]                      # OK
[X, Y < _Foo[X]]            # OK
[X < _Foo[Y], Y]            # OK
[X < _Foo[Y], Y < _Foo[X]]  # Error

Parameters:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'sig/validator.rbs', line 43

def validate_type_params(params, type_name: , method_name: nil, location:)
  # @type var each_node: ^() { (Symbol) -> void } -> void
  each_node = -> (&block) do
    params.each do |param|
      block[param.name]
    end
  end
  # @type var each_child: ^(Symbol) { (Symbol) -> void } -> void
  each_child = -> (name, &block) do
    if param = params.find {|p| p.name == name }
      [param.upper_bound_type, param.lower_bound_type].compact.each do |bound|
        bound.free_variables.each do |tv|
          block[tv]
        end
      end
    end
  end

  TSort.each_strongly_connected_component(each_node, each_child) do |names|
    if names.size > 1
      params = names.map do |name|
        params.find {|param| param.name == name} or raise
      end

      raise CyclicTypeParameterBound.new(
        type_name: type_name,
        method_name: method_name,
        params: params,
        location: location
      )
    end
  end
end

#validate_variable(var) ⇒ void

This method returns an undefined value.

Validates instance/class-instance/class variables.

Parameters:



54
55
56
# File 'sig/validator.rbs', line 54

def validate_variable(var)
  validate_type(var.type, context: nil)
end