Skip to content

Commit

Permalink
Revised predicate-type-checking warning.
Browse files Browse the repository at this point in the history
A method foo? must be observed to return an instance of a truthy class at least once, and an instance of a falsy class at least once.
  • Loading branch information
Michael Edgar committed Aug 27, 2011
1 parent 9e79c5f commit a81adbf
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 6 deletions.
4 changes: 2 additions & 2 deletions lib/laser/analysis/bootstrap/laser_method.rb
Expand Up @@ -69,8 +69,8 @@ def argument_annotations

# Combines all known return types for this method into one union type.
def combined_return_type
Types::UnionType.new(@type_instantiations.keys.map do |argtypes|
return_type_for_types(*argtypes)
Types::UnionType.new(@type_instantiations.keys.map do |self_type, *argtypes, blk_type|
return_type_for_types(self_type, argtypes, blk_type)
end)
end

Expand Down
@@ -0,0 +1,19 @@
module Laser
module Analysis
module IncorrectPredicateMethodDetection
def incorrect_predicate_methods
each_user_method.select do |method|
method.name.end_with?('?')
end.select do |method|
!correct_predicate_type?(method.combined_return_type)
end
end

def correct_predicate_type?(return_type)
(Types.subtype?(Types::FALSECLASS, return_type) ||
Types.subtype?(Types::NILCLASS, return_type )) &&
!Types.subtype?(return_type, Types::FALSY)
end
end
end
end
2 changes: 2 additions & 0 deletions lib/laser/analysis/method_analysis/method_analysis.rb
@@ -1,9 +1,11 @@
require_relative 'incorrect_predicate_method_detection'
require_relative 'unused_methods'
module Laser
module Analysis
# General-purpose method analysis functions.
module MethodAnalysis
extend UnusedMethodDetection
extend IncorrectPredicateMethodDetection
def self.each_user_method
return enum_for(__method__) unless block_given?
classes = Set[]
Expand Down
Expand Up @@ -172,8 +172,8 @@ def silly?(x, y)
ClassRegistry["OverI4"].instance_method(:silly?).
return_type_for_types(
ClassRegistry["OverI4"].as_type, [Types::STRING, Types::FIXNUM])
ClassRegistry["OverI4"].instance_method(:silly?).proc.ast_node.should(
have_error(ImproperOverrideTypeError))
MethodAnalysis.incorrect_predicate_methods.should include(
ClassRegistry["OverI4"].instance_method(:silly?))
end

it 'should not warn when a method whose name ends in ? does return a bool | nil' do
Expand All @@ -187,8 +187,8 @@ def silly?(x, y)
ClassRegistry["OverI5"].instance_method(:silly?).
return_type_for_types(
ClassRegistry["OverI5"].as_type, [Types::FIXNUM, Types::FIXNUM])
ClassRegistry["OverI5"].instance_method(:silly?).proc.ast_node.should_not(
have_error(ImproperOverrideTypeError))
MethodAnalysis.incorrect_predicate_methods.should_not include(
ClassRegistry["OverI5"].instance_method(:silly?))
end

%w(public private protected module_function).each do |method|
Expand Down

0 comments on commit a81adbf

Please sign in to comment.