From 53bf6bd0766736a2492c40a61683109345f0e418 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Wed, 16 Aug 2017 23:45:41 +0900 Subject: [PATCH] Fix case-when to pass exhaustiveness check --- src/callstack.cr | 2 ++ src/compiler/crystal/semantic/call.cr | 4 +++ src/compiler/crystal/semantic/call_error.cr | 2 ++ .../crystal/semantic/literal_expander.cr | 11 ++---- src/compiler/crystal/semantic/main_visitor.cr | 18 +++++----- .../crystal/semantic/semantic_visitor.cr | 3 +- .../crystal/semantic/top_level_visitor.cr | 4 +-- src/compiler/crystal/semantic/type_lookup.cr | 2 ++ src/compiler/crystal/syntax/exception.cr | 7 ++-- src/compiler/crystal/syntax/parser.cr | 36 +++++++++++++------ src/compiler/crystal/tools/doc/method.cr | 2 ++ src/compiler/crystal/tools/doc/type.cr | 12 +++---- src/compiler/crystal/tools/formatter.cr | 4 +++ src/compiler/crystal/tools/print_hierarchy.cr | 2 ++ src/compiler/crystal/types.cr | 13 +++++++ src/http/client.cr | 8 ++--- src/http/common.cr | 11 +++--- src/http/web_socket.cr | 2 ++ src/json/builder.cr | 6 ++++ src/process.cr | 8 ++--- src/socket/addrinfo.cr | 2 ++ src/spec/expectations.cr | 2 ++ src/unicode/unicode.cr | 1 + src/xml/node.cr | 8 ++--- src/yaml/pull_parser.cr | 2 ++ 25 files changed, 111 insertions(+), 61 deletions(-) diff --git a/src/callstack.cr b/src/callstack.cr index 28dd04cd7783..fe92ddea9e2d 100644 --- a/src/callstack.cr +++ b/src/callstack.cr @@ -214,6 +214,8 @@ struct CallStack elsif value.responds_to?(:to_i) high_pc = low_pc.as(LibC::SizeT) + value.to_i end + else + # nothing end end diff --git a/src/compiler/crystal/semantic/call.cr b/src/compiler/crystal/semantic/call.cr index dc0cf5bfe8d5..cefa022f34e7 100644 --- a/src/compiler/crystal/semantic/call.cr +++ b/src/compiler/crystal/semantic/call.cr @@ -40,6 +40,8 @@ class Crystal::Call when LibType # `LibFoo.call` has a separate logic return recalculate_lib_call obj_type + else + # nothing end # Check if it's call inside LibFoo @@ -652,6 +654,8 @@ class Crystal::Call return result when Type::DefInMacroLookup return nil + else + # nothing end end end diff --git a/src/compiler/crystal/semantic/call_error.cr b/src/compiler/crystal/semantic/call_error.cr index 0a6d63d50649..06ba98b8ad02 100644 --- a/src/compiler/crystal/semantic/call_error.cr +++ b/src/compiler/crystal/semantic/call_error.cr @@ -566,6 +566,8 @@ class Crystal::Call def check_visibility(match) case match.def.visibility + when .public? + # nothing when .private? if obj = @obj if obj.is_a?(Var) && obj.name == "self" && match.def.name.ends_with?('=') diff --git a/src/compiler/crystal/semantic/literal_expander.cr b/src/compiler/crystal/semantic/literal_expander.cr index 71c78c87b757..216acddc3b71 100644 --- a/src/compiler/crystal/semantic/literal_expander.cr +++ b/src/compiler/crystal/semantic/literal_expander.cr @@ -616,15 +616,8 @@ module Crystal return IsA.new(right_side, cond) when Call obj = cond.obj - case obj - when Path - if cond.name == "class" - return IsA.new(right_side, Metaclass.new(obj.clone).at(obj)) - end - when Generic - if cond.name == "class" - return IsA.new(right_side, Metaclass.new(obj.clone).at(obj)) - end + if (obj.is_a?(Path) || obj.is_a?(Generic)) && cond.name == "class" + return IsA.new(right_side, Metaclass.new(obj.clone).at(obj)) end end diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index e2436547b379..13148b8606d5 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -596,12 +596,12 @@ module Crystal def lookup_similar_instance_variable_name(node, owner) case owner when NonGenericModuleType, GenericClassType, GenericModuleType - return nil - end - - Levenshtein.find(node.name) do |finder| - owner.all_instance_vars.each_key do |name| - finder.test(name) + nil + else + Levenshtein.find(node.name) do |finder| + owner.all_instance_vars.each_key do |name| + finder.test(name) + end end end end @@ -1724,6 +1724,8 @@ module Crystal node.raise "can't cast to Reference yet" when @program.class_type node.raise "can't cast to Class yet" + else + # nothing end obj_type = node.obj.type? @@ -2106,9 +2108,9 @@ module Crystal end when Call return get_while_cond_assign_target(node.obj) + else + nil end - - nil end # If we have: diff --git a/src/compiler/crystal/semantic/semantic_visitor.cr b/src/compiler/crystal/semantic/semantic_visitor.cr index a7979b979096..2aaeca5ea991 100644 --- a/src/compiler/crystal/semantic/semantic_visitor.cr +++ b/src/compiler/crystal/semantic/semantic_visitor.cr @@ -447,8 +447,7 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor def class_var_owner(node) scope = (@scope || current_type).class_var_owner - case scope - when Program + if scope.is_a?(Program) node.raise "can't use class variables at the top level" end diff --git a/src/compiler/crystal/semantic/top_level_visitor.cr b/src/compiler/crystal/semantic/top_level_visitor.cr index a763a4801e6f..0932bfece7b9 100644 --- a/src/compiler/crystal/semantic/top_level_visitor.cr +++ b/src/compiler/crystal/semantic/top_level_visitor.cr @@ -595,13 +595,13 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor method_name = is_flags ? "includes?" : "==" body = Call.new(Var.new("self").at(member), method_name, Path.new(member.name).at(member)).at(member) a_def = Def.new("#{member.name.underscore}?", body: body).at(member) - enum_type.add_def a_def + enum_type.add_def a_def, question_method: true end def define_enum_none_question_method(enum_type, node) body = Call.new(Call.new(nil, "value").at(node), "==", NumberLiteral.new(0)).at(node) a_def = Def.new("none?", body: body).at(node) - enum_type.add_def a_def + enum_type.add_def a_def, question_method: true end def visit(node : Expressions) diff --git a/src/compiler/crystal/semantic/type_lookup.cr b/src/compiler/crystal/semantic/type_lookup.cr index 1fe8627da5af..9deb05eec4f2 100644 --- a/src/compiler/crystal/semantic/type_lookup.cr +++ b/src/compiler/crystal/semantic/type_lookup.cr @@ -90,6 +90,8 @@ class Crystal::Type return type_var when Self return lookup(type_var) + else + # nothing end if @raise diff --git a/src/compiler/crystal/syntax/exception.cr b/src/compiler/crystal/syntax/exception.cr index e6991defe64a..3e4e28476dc9 100644 --- a/src/compiler/crystal/syntax/exception.cr +++ b/src/compiler/crystal/syntax/exception.cr @@ -64,11 +64,12 @@ module Crystal def fetch_source(source) case filename = @filename when String - source = File.read(filename) if File.file?(filename) + File.read(filename) if File.file?(filename) when VirtualFile - source = filename.source + filename.source + when Nil + nil end - source end def deepest_error_message diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index f3a2e5d95eb6..d8711fb6148b 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -2933,6 +2933,8 @@ module Crystal return MacroIf.new(BoolLiteral.new(true), body).at_end(token_end_location) when :else, :elsif, :end return nil + else + # nothing end end @@ -3548,6 +3550,8 @@ module Crystal a_else = parse_expressions when :elsif a_else = parse_if check_end: false + else + # nothing end end @@ -3610,6 +3614,8 @@ module Crystal when :nil? obj = Var.new("self").at(location) return parse_nil?(obj) + else + # nothing end name = @token.value.to_s @@ -3639,6 +3645,8 @@ module Crystal @calls_initialize = true when "previous_def" @calls_previous_def = true + else + # nothing end call_args = preserve_stop_on_do(@stop_on_do) { parse_call_args stop_on_do_after_space: @stop_on_do } @@ -3984,6 +3992,8 @@ module Crystal return nil unless next_comes_colon_space? when :yield return nil if @stop_on_yield > 0 && !next_comes_colon_space? + else + # nothing end args = [] of ASTNode @@ -4126,6 +4136,8 @@ module Crystal arg = Splat.new(arg).at(arg.location) when :double arg = DoubleSplat.new(arg).at(arg.location) + else + # nothing end arg @@ -4576,12 +4588,16 @@ module Crystal case @token.value when :typeof, :self, :sizeof, :instance_sizeof return true + else + # nothing end when :"::" next_token_skip_space if @token.type == :CONST return true end + else + # nothing end false @@ -5233,6 +5249,8 @@ module Crystal when :protected visibility = Visibility::Protected next_token_skip_space + else + # nothing end def_location = @token.location @@ -5277,21 +5295,16 @@ module Crystal node end + END_TOKEN = {:do, :end, :else, :elsif, :when, :rescue, :ensure, :then} + def end_token? case @token.type when :"}", :"]", :"%}", :EOF return true end - if @token.type == :IDENT - case @token.value - when :do, :end, :else, :elsif, :when, :rescue, :ensure, :then - if next_comes_colon_space? - return false - end - - return true - end + if @token.type == :IDENT && END_TOKEN.includes?(@token.value) + return !next_comes_colon_space? end false @@ -5374,13 +5387,14 @@ module Crystal end def check_void_expression_keyword - case @token.type - when :IDENT + if @token.type == :IDENT case @token.value when :break, :next, :return unless next_comes_colon_space? raise "void value expression", @token, @token.value.to_s.size end + else + # nothing end end end diff --git a/src/compiler/crystal/tools/doc/method.cr b/src/compiler/crystal/tools/doc/method.cr index 70b5bf7a238a..b5061e6db2c6 100644 --- a/src/compiler/crystal/tools/doc/method.cr +++ b/src/compiler/crystal/tools/doc/method.cr @@ -160,6 +160,8 @@ class Crystal::Doc::Method when Crystal::Type io << " : " @type.type_to_html return_type, io, links: links + when Nil + # nothing end if free_vars = @def.free_vars diff --git a/src/compiler/crystal/tools/doc/type.cr b/src/compiler/crystal/tools/doc/type.cr index 608160f30261..5926afd28af1 100644 --- a/src/compiler/crystal/tools/doc/type.cr +++ b/src/compiler/crystal/tools/doc/type.cr @@ -159,10 +159,8 @@ class Crystal::Doc::Type defs = [] of Method @type.defs.try &.each do |def_name, defs_with_metadata| defs_with_metadata.each do |def_with_metadata| - case def_with_metadata.def.visibility - when .private?, .protected? - next - end + visibility = def_with_metadata.def.visibility + next if visibility.private? || visibility.protected? if @generator.must_include? def_with_metadata.def defs << method(def_with_metadata.def, false) @@ -182,10 +180,8 @@ class Crystal::Doc::Type @type.metaclass.defs.try &.each_value do |defs_with_metadata| defs_with_metadata.each do |def_with_metadata| a_def = def_with_metadata.def - case a_def.visibility - when .private?, .protected? - next - end + visibility = a_def.visibility + next if visibility.private? || visibility.protected? body = a_def.body diff --git a/src/compiler/crystal/tools/formatter.cr b/src/compiler/crystal/tools/formatter.cr index 3215f29db0df..d76d97c43ff3 100644 --- a/src/compiler/crystal/tools/formatter.cr +++ b/src/compiler/crystal/tools/formatter.cr @@ -2722,6 +2722,8 @@ module Crystal else clear_object(node.obj) end + else + # nothing end end @@ -3025,6 +3027,8 @@ module Crystal def visit(node : VisibilityModifier) case node.modifier + when .public? + # nothing to do because 'public' is default modifier when .private? write_keyword :private, " " when .protected? diff --git a/src/compiler/crystal/tools/print_hierarchy.cr b/src/compiler/crystal/tools/print_hierarchy.cr index 15d94f183d11..1050e88aef33 100644 --- a/src/compiler/crystal/tools/print_hierarchy.cr +++ b/src/compiler/crystal/tools/print_hierarchy.cr @@ -9,6 +9,8 @@ module Crystal HierarchyPrinter.new(program, exp).execute when "json" JSONHierarchyPrinter.new(program, exp).execute + else + raise "BUG: invalid format: #{format}" end end diff --git a/src/compiler/crystal/types.cr b/src/compiler/crystal/types.cr index 24154e02ddd5..2fd1de6b26cf 100644 --- a/src/compiler/crystal/types.cr +++ b/src/compiler/crystal/types.cr @@ -2403,10 +2403,13 @@ module Crystal getter base_type : IntegerType getter? flags : Bool + getter question_methods + def initialize(program, namespace, name, @base_type, flags) super(program, namespace, name) @flags = !!flags + @question_methods = Set(String).new add_def Def.new("value", [] of Arg, Primitive.new("enum_value", @base_type)) metaclass.as(ModuleType).add_def Def.new("new", [Arg.new("value", type: @base_type)], Primitive.new("enum_new", self)) @@ -2422,6 +2425,16 @@ module Crystal const end + def add_def(a_def, question_method = false) + if question_method + question_methods.add(a_def.name) + else + question_methods.delete(a_def.name) + end + + super(a_def) + end + def has_attribute?(name) return true if flags? && name == "Flags" false diff --git a/src/http/client.cr b/src/http/client.cr index abb178956fe8..b18425cac6f9 100644 --- a/src/http/client.cr +++ b/src/http/client.cr @@ -117,12 +117,12 @@ class HTTP::Client {% else %} def initialize(@host : String, port = nil, tls : Bool | OpenSSL::SSL::Context::Client = false) @tls = case tls - when true - OpenSSL::SSL::Context::Client.new + when Bool + if tls + OpenSSL::SSL::Context::Client.new + end when OpenSSL::SSL::Context::Client tls - when false - nil end @port = (port || (@tls ? 443 : 80)).to_i diff --git a/src/http/common.cr b/src/http/common.cr index c369fb65be85..0edb21f1c106 100644 --- a/src/http/common.cr +++ b/src/http/common.cr @@ -48,6 +48,8 @@ module HTTP body = Gzip::Reader.new(body, sync_close: true) when "deflate" body = Flate::Reader.new(body, sync_close: true) + else + # ignore unsupported encoding end {% end %} end @@ -176,14 +178,11 @@ module HTTP return true when "close", "upgrade" return false - end - - case message.version - when "HTTP/1.0" - false else - true + # nothing end + + message.version != "HTTP/1.0" end record ComputedContentTypeHeader, diff --git a/src/http/web_socket.cr b/src/http/web_socket.cr index 3e1d95d4fd6f..1c164af73753 100644 --- a/src/http/web_socket.cr +++ b/src/http/web_socket.cr @@ -141,6 +141,8 @@ class HTTP::WebSocket @current_message.clear break end + when Protocol::Opcode::CONTINUATION + raise "BUG: CONTINUATION does not appear here" end end end diff --git a/src/json/builder.cr b/src/json/builder.cr index 64618a37a237..432db1547694 100644 --- a/src/json/builder.cr +++ b/src/json/builder.cr @@ -41,6 +41,8 @@ class JSON::Builder raise JSON::Error.new("Empty JSON") when DocumentStartState raise JSON::Error.new("Empty JSON") + when DocumentEndState + # nothing when ArrayState raise JSON::Error.new("Unterminated JSON array") when ObjectState @@ -267,6 +269,8 @@ class JSON::Builder case state = @state.last when StartState raise JSON::Error.new("Write before start_document") + when DocumentStartState + # nothing when DocumentEndState raise JSON::Error.new("Write past end_document and before start_document") when ArrayState @@ -290,6 +294,8 @@ class JSON::Builder when ObjectState colon if state.name @state[-1] = ObjectState.new(empty: false, name: !state.name) + else + # nothing end end diff --git a/src/process.cr b/src/process.cr index ebe70c5536c6..e118a3ba4930 100644 --- a/src/process.cr +++ b/src/process.cr @@ -118,15 +118,15 @@ class Process # *run_hooks* should ALWAYS be `true` unless `exec` is used immediately after fork. # Channels, `IO` and other will not work reliably if *run_hooks* is `false`. protected def self.fork_internal(run_hooks : Bool = true) - pid = LibC.fork - case pid + case pid = LibC.fork when 0 - pid = nil Process.after_fork_child_callbacks.each(&.call) if run_hooks + nil when -1 raise Errno.new("fork") + else + pid end - pid end # The standard `IO` configuration of a process: diff --git a/src/socket/addrinfo.cr b/src/socket/addrinfo.cr index 9b4327515b22..3c36d359cbc9 100644 --- a/src/socket/addrinfo.cr +++ b/src/socket/addrinfo.cr @@ -159,6 +159,8 @@ class Socket addrinfo.value.ai_addr.as(LibC::SockaddrIn6*).copy_to(pointerof(@addr).as(LibC::SockaddrIn6*), 1) when Family::INET addrinfo.value.ai_addr.as(LibC::SockaddrIn*).copy_to(pointerof(@addr).as(LibC::SockaddrIn*), 1) + else + # nothing end end diff --git a/src/spec/expectations.cr b/src/spec/expectations.cr index 2d785b71bcb0..ad5cede20b66 100644 --- a/src/spec/expectations.cr +++ b/src/spec/expectations.cr @@ -272,6 +272,8 @@ module Spec backtrace = %ex.backtrace.map { |f| " # #{f}" }.join "\n" fail "Expected {{klass.id}} with #{ %msg.inspect }, got #<#{ %ex.class }: #{ %ex_to_s }> with backtrace:\n#{backtrace}", {{file}}, {{line}} end + else + # skip end %ex diff --git a/src/unicode/unicode.cr b/src/unicode/unicode.cr index ab8c7fd52f70..2a40424904e1 100644 --- a/src/unicode/unicode.cr +++ b/src/unicode/unicode.cr @@ -148,6 +148,7 @@ module Unicode case char when 'I'; return 'ı' when 'İ'; return 'i' + else # nothing end end nil diff --git a/src/xml/node.cr b/src/xml/node.cr index 3c23a238036f..219f1f2d8420 100644 --- a/src/xml/node.cr +++ b/src/xml/node.cr @@ -282,11 +282,11 @@ struct XML::Node def namespace case type when Type::DOCUMENT_NODE, Type::ATTRIBUTE_DECL, Type::DTD_NODE, Type::ELEMENT_DECL - return nil + nil + else + ns = @node.value.ns + ns ? Namespace.new(document, ns) : nil end - - ns = @node.value.ns - ns ? Namespace.new(document, ns) : nil end # Returns namespaces in scope for self – those defined on self element diff --git a/src/yaml/pull_parser.cr b/src/yaml/pull_parser.cr index 8f0a7fb94500..02fba3046a56 100644 --- a/src/yaml/pull_parser.cr +++ b/src/yaml/pull_parser.cr @@ -239,6 +239,8 @@ class YAML::PullParser skip end read_next + else + # not skip this event end end