From 949fa3c3be83de437f84c6acf8b9503347083471 Mon Sep 17 00:00:00 2001 From: Fred Snyder Date: Sat, 27 Nov 2021 12:49:11 -0500 Subject: [PATCH] TypeChecker validates aliased namespaces (#497) --- lib/solargraph/type_checker.rb | 11 ++++++++++- spec/type_checker/levels/normal_spec.rb | 17 +++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/solargraph/type_checker.rb b/lib/solargraph/type_checker.rb index 4fd8f71a6..cd2183c64 100644 --- a/lib/solargraph/type_checker.rb +++ b/lib/solargraph/type_checker.rb @@ -89,7 +89,7 @@ def method_return_type_problems_for pin if declared.undefined? if pin.return_type.undefined? && rules.require_type_tags? result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) - elsif pin.return_type.defined? + elsif pin.return_type.defined? && !resolved_constant?(pin) result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin) elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined? result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred") @@ -111,6 +111,15 @@ def method_return_type_problems_for pin result end + # @todo This is not optimal. A better solution would probably be to mix + # namespace alias into types at the ApiMap level. + # + # @param pin [Pin::Base] + # @return [Boolean] + def resolved_constant? pin + api_map.get_constants('', pin.binder.tag).any? { |pin| pin.name == pin.return_type.namespace && ['Class', 'Module'].include?(pin.return_type.name) } + end + def virtual_pin? pin pin.location && source_map.source.comment_at?(pin.location.range.ending) end diff --git a/spec/type_checker/levels/normal_spec.rb b/spec/type_checker/levels/normal_spec.rb index efbeeeaed..9430fe7b0 100644 --- a/spec/type_checker/levels/normal_spec.rb +++ b/spec/type_checker/levels/normal_spec.rb @@ -782,7 +782,7 @@ class Foo def self.make(arg, *args, **kwargs) new([1, 2], *args, **kwargs) end - + def initialize(timeframe, scope = nil, now: Time.now) end end @@ -794,7 +794,7 @@ def initialize(timeframe, scope = nil, now: Time.now) checker = type_checker(%( def foo(arg1:, arg2:, arg3:, arg4: false) end - + foo( arg1: val1, arg2: val2, @@ -803,5 +803,18 @@ def foo(arg1:, arg2:, arg3:, arg4: false) )) expect(checker.problems).to be_empty end + + it 'validates constant aliases for namespaces' do + checker = type_checker(%( + class Foo; end + Bar = Foo + + # @return [Bar] + def foo + Bar + end + )) + expect(checker.problems).to be_empty + end end end