From 9d6b29cdde949d762aa438367326312595de8aad Mon Sep 17 00:00:00 2001 From: Soutaro Matsumoto Date: Wed, 22 Jun 2022 22:04:54 +0900 Subject: [PATCH] Revert "Revert "Add class variable validation"" --- lib/steep/diagnostic/signature.rb | 18 ++++++++++++++++ lib/steep/signature/validator.rb | 19 ++++++++++++++++ test/validation_test.rb | 36 +++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/lib/steep/diagnostic/signature.rb b/lib/steep/diagnostic/signature.rb index 02070adba..c61c2a4db 100644 --- a/lib/steep/diagnostic/signature.rb +++ b/lib/steep/diagnostic/signature.rb @@ -264,6 +264,24 @@ def header_line end end + class ClassVariableDuplicationError < Base + attr_reader :class_name + attr_reader :other_class_name + attr_reader :variable_name + + def initialize(class_name:, other_class_name:, variable_name:, location:) + super(location: location) + + @class_name = class_name + @other_class_name = other_class_name + @variable_name = variable_name + end + + def header_line + "Class variable definition `#{variable_name}` in `#{class_name}` may be overtaken by `#{other_class_name}`" + end + end + class InstanceVariableTypeError < Base attr_reader :name attr_reader :variable diff --git a/lib/steep/signature/validator.rb b/lib/steep/signature/validator.rb index 0ee2f2bbc..bdcac8193 100644 --- a/lib/steep/signature/validator.rb +++ b/lib/steep/signature/validator.rb @@ -319,6 +319,25 @@ def validate_one_class(name) end end + definition.class_variables.each do |name, var| + if var.declared_in == definition.type_name + if (parent = var.parent_variable) && var.declared_in != parent.declared_in + class_var = definition.entry.decls.flat_map {|decl| decl.decl.members }.find do |member| + member.is_a?(RBS::AST::Members::ClassVariable) && member.name == name + end + + if class_var + @errors << Diagnostic::Signature::ClassVariableDuplicationError.new( + class_name: definition.type_name, + other_class_name: parent.declared_in, + variable_name: name, + location: class_var.location[:name] + ) + end + end + end + end + ancestors = builder.ancestor_builder.one_singleton_ancestors(name) mixin_constraints(definition, ancestors.extended_modules, immediate_self_types: ancestors.self_types).each do |relation, ancestor| checker.check( diff --git a/test/validation_test.rb b/test/validation_test.rb index 369640be0..570c717ba 100644 --- a/test/validation_test.rb +++ b/test/validation_test.rb @@ -597,6 +597,42 @@ class D end end + def test_validate_class_variables + with_checker <<-EOF do |checker| +class A + @@foo: Integer +end + +class B < A + @@foo: Integer? +end + +class C < B +end + EOF + Validator.new(checker: checker).tap do |validator| + validator.validate_one_class(TypeName("::A")) + + assert_predicate validator, :no_error? + end + + Validator.new(checker: checker).tap do |validator| + validator.validate_one_class(TypeName("::C")) + + assert_predicate validator, :no_error? + end + + Validator.new(checker: checker).tap do |validator| + validator.validate_one_class(TypeName("::B")) + + assert_predicate validator, :has_error? + assert_any!(validator.each_error, size: 1) do |error| + assert_instance_of Diagnostic::Signature::ClassVariableDuplicationError, error + end + end + end + end + def test_validate_type_application with_checker <<-EOF do |checker| class Foo[X < Numeric]