From 0e9f8ab3a51f3f87243c5f18f041efb44943f939 Mon Sep 17 00:00:00 2001
From: Oleh Prypin <oleh@pryp.in>
Date: Sat, 3 Apr 2021 11:56:13 +0200
Subject: [PATCH 1/3] Add return type restrictions to HTTP-related classes

---
 src/http/client/response.cr     | 14 +++++++-------
 src/http/common.cr              | 12 ++++++------
 src/http/cookie.cr              | 32 ++++++++++++++++----------------
 src/http/formdata/builder.cr    |  2 +-
 src/http/formdata/parser.cr     |  2 +-
 src/http/headers.cr             | 32 ++++++++++++++++----------------
 src/http/request.cr             | 16 ++++++++--------
 src/http/server/response.cr     |  4 ++--
 src/http/web_socket/protocol.cr |  2 +-
 src/mime/media_type.cr          |  6 +++---
 src/mime/multipart.cr           |  2 +-
 src/mime/multipart/builder.cr   |  2 +-
 src/mime/multipart/parser.cr    |  2 +-
 src/socket.cr                   | 20 ++++++++++----------
 src/socket/address.cr           |  8 ++++----
 src/socket/addrinfo.cr          |  2 +-
 src/socket/ip_socket.cr         |  4 ++--
 src/socket/tcp_socket.cr        |  2 +-
 src/socket/udp_socket.cr        |  4 ++--
 src/socket/unix_socket.cr       |  6 +++---
 src/uri.cr                      |  6 +++---
 src/uri/params.cr               | 12 ++++++------
 src/uri/punycode.cr             |  6 +++---
 src/uri/uri_parser.cr           |  4 ++--
 24 files changed, 101 insertions(+), 101 deletions(-)

diff --git a/src/http/client/response.cr b/src/http/client/response.cr
index 3b81b523eb84..6ece2a4211ae 100644
--- a/src/http/client/response.cr
+++ b/src/http/client/response.cr
@@ -25,26 +25,26 @@ class HTTP::Client::Response
     new(HTTP::Status.new(status_code), body, headers, status_message, version, body_io)
   end
 
-  def body
+  def body : String
     @body || ""
   end
 
-  def body?
+  def body? : String?
     @body
   end
 
   # Returns `true` if the response status code is between 200 and 299.
-  def success?
+  def success? : Bool
     @status.success?
   end
 
   # Returns a convenience wrapper around querying and setting cookie related
   # headers, see `HTTP::Cookies`.
-  def cookies
+  def cookies : HTTP::Cookies
     @cookies ||= Cookies.from_server_headers(headers)
   end
 
-  def keep_alive?
+  def keep_alive? : Bool
     HTTP.keep_alive?(self)
   end
 
@@ -53,7 +53,7 @@ class HTTP::Client::Response
   end
 
   # Convenience method to retrieve the HTTP status code.
-  def status_code
+  def status_code : Int32
     status.code
   end
 
@@ -98,7 +98,7 @@ class HTTP::Client::Response
   # Parses an `HTTP::Client::Response` from the given `IO`.
   # Might return `nil` if there's no data in the `IO`,
   # which probably means that the connection was closed.
-  def self.from_io?(io, ignore_body = false, decompress = true)
+  def self.from_io?(io, ignore_body = false, decompress = true) : self?
     from_io?(io, ignore_body: ignore_body, decompress: decompress) do |response|
       if response
         response.consume_body_io
diff --git a/src/http/common.cr b/src/http/common.cr
index ad2288fae67e..33b6a1705e8d 100644
--- a/src/http/common.cr
+++ b/src/http/common.cr
@@ -243,7 +243,7 @@ module HTTP
   )
 
   # :nodoc:
-  def self.header_name(slice : Bytes)
+  def self.header_name(slice : Bytes) : String
     # Check if the header name is a common one.
     # If so we avoid having to allocate a string for it.
     if slice.size < 20
@@ -306,7 +306,7 @@ module HTTP
   end
 
   # :nodoc:
-  def self.content_length(headers)
+  def self.content_length(headers) : UInt64?
     length_headers = headers.get? "Content-Length"
     return nil unless length_headers
     first_header = length_headers[0]
@@ -317,7 +317,7 @@ module HTTP
   end
 
   # :nodoc:
-  def self.keep_alive?(message)
+  def self.keep_alive?(message) : Bool
     case message.headers["Connection"]?.try &.downcase
     when "keep-alive"
       true
@@ -333,7 +333,7 @@ module HTTP
     end
   end
 
-  def self.expect_continue?(headers)
+  def self.expect_continue?(headers) : Bool
     headers["Expect"]?.try(&.downcase) == "100-continue"
   end
 
@@ -379,7 +379,7 @@ module HTTP
   # quoted = %q(\"foo\\bar\")
   # HTTP.dequote_string(quoted) # => %q("foo\bar")
   # ```
-  def self.dequote_string(str)
+  def self.dequote_string(str) : String
     data = str.to_slice
     quoted_pair_index = data.index('\\'.ord)
     return str unless quoted_pair_index
@@ -434,7 +434,7 @@ module HTTP
   # string = %q("foo\ bar")
   # HTTP.quote_string(string) # => %q(\"foo\\\ bar\")
   # ```
-  def self.quote_string(string)
+  def self.quote_string(string) : String
     String.build do |io|
       quote_string(string, io)
     end
diff --git a/src/http/cookie.cr b/src/http/cookie.cr
index c86a16bdf7e7..77ef34230eeb 100644
--- a/src/http/cookie.cr
+++ b/src/http/cookie.cr
@@ -78,7 +78,7 @@ module HTTP
       end
     end
 
-    def to_set_cookie_header
+    def to_set_cookie_header : String
       path = @path
       expires = @expires
       domain = @domain
@@ -95,7 +95,7 @@ module HTTP
       end
     end
 
-    def to_cookie_header
+    def to_cookie_header : String
       String.build do |io|
         to_cookie_header(io)
       end
@@ -107,7 +107,7 @@ module HTTP
       io << @value
     end
 
-    def expired?
+    def expired? : Bool
       if e = expires
         e <= Time.utc
       else
@@ -156,13 +156,13 @@ module HTTP
         end
       end
 
-      def parse_cookies(header)
+      def parse_cookies(header) : Array(HTTP::Cookie)
         cookies = [] of Cookie
         parse_cookies(header) { |cookie| cookies << cookie }
         cookies
       end
 
-      def parse_set_cookie(header)
+      def parse_set_cookie(header) : HTTP::Cookie?
         match = header.match(SetCookieString)
         return unless match
 
@@ -224,7 +224,7 @@ module HTTP
     end
 
     # Filling cookies by parsing the `Cookie` headers in the given `HTTP::Headers`.
-    def fill_from_client_headers(headers)
+    def fill_from_client_headers(headers) : self
       if values = headers.get?("Cookie")
         values.each do |header|
           Cookie::Parser.parse_cookies(header) { |cookie| self << cookie }
@@ -241,7 +241,7 @@ module HTTP
     end
 
     # Filling cookies by parsing the `Set-Cookie` headers in the given `HTTP::Headers`.
-    def fill_from_server_headers(headers)
+    def fill_from_server_headers(headers) : self
       if values = headers.get?("Set-Cookie")
         values.each do |header|
           Cookie::Parser.parse_set_cookie(header).try { |cookie| self << cookie }
@@ -292,7 +292,7 @@ module HTTP
     # ```
     # request.cookies["foo"].value # => "bar"
     # ```
-    def [](key)
+    def [](key) : HTTP::Cookie
       @cookies[key]
     end
 
@@ -306,7 +306,7 @@ module HTTP
     # request.cookies["foo"] = "bar"
     # request.cookies["foo"]?.try &.value # > "bar"
     # ```
-    def []?(key)
+    def []?(key) : HTTP::Cookie?
       @cookies[key]?
     end
 
@@ -315,7 +315,7 @@ module HTTP
     # ```
     # request.cookies.has_key?("foo") # => true
     # ```
-    def has_key?(key)
+    def has_key?(key) : Bool
       @cookies.has_key?(key)
     end
 
@@ -325,19 +325,19 @@ module HTTP
     # ```
     # response.cookies << HTTP::Cookie.new("foo", "bar", http_only: true)
     # ```
-    def <<(cookie : Cookie)
+    def <<(cookie : Cookie) : HTTP::Cookie
       self[cookie.name] = cookie
     end
 
     # Clears the collection, removing all cookies.
-    def clear
+    def clear : Hash(String, HTTP::Cookie)
       @cookies.clear
     end
 
     # Deletes and returns the `HTTP::Cookie` for the specified *key*, or
     # returns `nil` if *key* cannot be found in the collection. Note that
     # *key* should match the name attribute of the desired `HTTP::Cookie`.
-    def delete(key)
+    def delete(key) : HTTP::Cookie?
       @cookies.delete(key)
     end
 
@@ -354,12 +354,12 @@ module HTTP
     end
 
     # Returns the number of cookies contained in this collection.
-    def size
+    def size : Int32
       @cookies.size
     end
 
     # Whether the collection contains any cookies.
-    def empty?
+    def empty? : Bool
       @cookies.empty?
     end
 
@@ -393,7 +393,7 @@ module HTTP
     end
 
     # Returns this collection as a plain `Hash`.
-    def to_h
+    def to_h : Hash(String, HTTP::Cookie)
       @cookies.dup
     end
   end
diff --git a/src/http/formdata/builder.cr b/src/http/formdata/builder.cr
index 5947dc06724b..983451f25865 100644
--- a/src/http/formdata/builder.cr
+++ b/src/http/formdata/builder.cr
@@ -29,7 +29,7 @@ module HTTP::FormData
     # builder = HTTP::FormData::Builder.new(io, "a4VF")
     # builder.content_type # => "multipart/form-data; boundary=\"a4VF\""
     # ```
-    def content_type
+    def content_type : String
       String.build do |str|
         str << "multipart/form-data; boundary=\""
         HTTP.quote_string(@boundary, str)
diff --git a/src/http/formdata/parser.cr b/src/http/formdata/parser.cr
index 49b935b4ec80..e93f2b029e6c 100644
--- a/src/http/formdata/parser.cr
+++ b/src/http/formdata/parser.cr
@@ -38,7 +38,7 @@ module HTTP::FormData
     end
 
     # True if `#next` can be called legally.
-    def has_next?
+    def has_next? : Bool
       @multipart.has_next?
     end
   end
diff --git a/src/http/headers.cr b/src/http/headers.cr
index 6bbc8063897c..d8823a9aa0b2 100644
--- a/src/http/headers.cr
+++ b/src/http/headers.cr
@@ -67,12 +67,12 @@ struct HTTP::Headers
     @hash[wrap(key)] = value
   end
 
-  def [](key)
+  def [](key) : String
     values = @hash[wrap(key)]
     concat values
   end
 
-  def []?(key)
+  def []?(key) : String?
     fetch(key, nil)
   end
 
@@ -85,7 +85,7 @@ struct HTTP::Headers
   # headers = HTTP::Headers{"Connection" => "keep-alive, Upgrade"}
   # headers.includes_word?("Connection", "Upgrade") # => true
   # ```
-  def includes_word?(key, word)
+  def includes_word?(key, word) : Bool
     return false if word.empty?
 
     values = @hash[wrap(key)]?
@@ -118,31 +118,31 @@ struct HTTP::Headers
     false
   end
 
-  def add(key, value : String)
+  def add(key, value : String) : self
     check_invalid_header_content value
     unsafe_add(key, value)
     self
   end
 
-  def add(key, value : Array(String))
+  def add(key, value : Array(String)) : self
     value.each { |val| check_invalid_header_content val }
     unsafe_add(key, value)
     self
   end
 
-  def add?(key, value : String)
+  def add?(key, value : String) : Bool
     return false unless valid_value?(value)
     unsafe_add(key, value)
     true
   end
 
-  def add?(key, value : Array(String))
+  def add?(key, value : Array(String)) : Bool
     value.each { |val| return false unless valid_value?(val) }
     unsafe_add(key, value)
     true
   end
 
-  def fetch(key, default)
+  def fetch(key, default) : String?
     fetch(wrap(key)) { default }
   end
 
@@ -151,20 +151,20 @@ struct HTTP::Headers
     values ? concat(values) : yield key
   end
 
-  def has_key?(key)
+  def has_key?(key) : Bool
     @hash.has_key? wrap(key)
   end
 
-  def empty?
+  def empty? : Bool
     @hash.empty?
   end
 
-  def delete(key)
+  def delete(key) : String?
     values = @hash.delete wrap(key)
     values ? concat(values) : nil
   end
 
-  def merge!(other)
+  def merge!(other) : self
     other.each do |key, value|
       self[wrap(key)] = value
     end
@@ -233,11 +233,11 @@ struct HTTP::Headers
     end
   end
 
-  def get(key)
+  def get(key) : Array(String)
     cast @hash[wrap(key)]
   end
 
-  def get?(key)
+  def get?(key) : Array(String)?
     @hash[wrap(key)]?.try { |value| cast(value) }
   end
 
@@ -253,7 +253,7 @@ struct HTTP::Headers
     dup
   end
 
-  def same?(other : HTTP::Headers)
+  def same?(other : HTTP::Headers) : Bool
     object_id == other.object_id
   end
 
@@ -299,7 +299,7 @@ struct HTTP::Headers
     end
   end
 
-  def valid_value?(value)
+  def valid_value?(value) : Bool
     return invalid_value_char(value).nil?
   end
 
diff --git a/src/http/request.cr b/src/http/request.cr
index c7547bd4ad3c..324e05c23542 100644
--- a/src/http/request.cr
+++ b/src/http/request.cr
@@ -57,26 +57,26 @@ class HTTP::Request
 
   # Returns a convenience wrapper around querying and setting cookie related
   # headers, see `HTTP::Cookies`.
-  def cookies
+  def cookies : HTTP::Cookies
     @cookies ||= Cookies.from_client_headers(headers)
   end
 
   # Returns a convenience wrapper around querying and setting query params,
   # see `URI::Params`.
-  def query_params
+  def query_params : URI::Params
     @query_params ||= uri.query_params
   end
 
-  def resource
+  def resource : String
     update_uri
     @uri.try(&.request_target) || @resource
   end
 
-  def keep_alive?
+  def keep_alive? : Bool
     HTTP.keep_alive?(self)
   end
 
-  def ignore_body?
+  def ignore_body? : Bool
     @method == "HEAD"
   end
 
@@ -237,7 +237,7 @@ class HTTP::Request
   end
 
   # Returns the request's path component.
-  def path
+  def path : String
     uri.path.presence || "/"
   end
 
@@ -247,7 +247,7 @@ class HTTP::Request
   end
 
   # Lazily parses and returns the request's query component.
-  def query
+  def query : String?
     update_uri
     uri.query
   end
@@ -287,7 +287,7 @@ class HTTP::Request
 
   # Returns request host with port from headers.
   @[Deprecated(%q(Use `headers["Host"]?` instead.))]
-  def host_with_port
+  def host_with_port : String?
     @headers["Host"]?
   end
 
diff --git a/src/http/server/response.cr b/src/http/server/response.cr
index 6d0eef046126..f68046a702aa 100644
--- a/src/http/server/response.cr
+++ b/src/http/server/response.cr
@@ -69,7 +69,7 @@ class HTTP::Server
     end
 
     # Convenience method to retrieve the HTTP status code.
-    def status_code
+    def status_code : Int32
       status.code
     end
 
@@ -87,7 +87,7 @@ class HTTP::Server
     end
 
     # Convenience method to set cookies, see `HTTP::Cookies`.
-    def cookies
+    def cookies : HTTP::Cookies
       @cookies ||= HTTP::Cookies.new
     end
 
diff --git a/src/http/web_socket/protocol.cr b/src/http/web_socket/protocol.cr
index 6075c67453cf..b190ebb25d03 100644
--- a/src/http/web_socket/protocol.cr
+++ b/src/http/web_socket/protocol.cr
@@ -106,7 +106,7 @@ class HTTP::WebSocket::Protocol
     @io.flush if flush
   end
 
-  def receive(buffer : Bytes)
+  def receive(buffer : Bytes) : HTTP::WebSocket::Protocol::PacketInfo
     if @remaining == 0
       opcode = read_header
     else
diff --git a/src/mime/media_type.cr b/src/mime/media_type.cr
index 8d96b782fb98..3d692d3ae421 100644
--- a/src/mime/media_type.cr
+++ b/src/mime/media_type.cr
@@ -77,7 +77,7 @@ module MIME
     # MIME::MediaType.parse("x-application/example").fetch("foo", "baz")          # => "baz"
     # MIME::MediaType.parse("x-application/example; foo=bar").fetch("foo", "baz") # => "bar"
     # ```
-    def fetch(key : String, default)
+    def fetch(key : String, default : T) : String | T forall T
       @params.fetch(key, default)
     end
 
@@ -483,12 +483,12 @@ module MIME
     end
 
     # :nodoc:
-    def self.token?(char : Char)
+    def self.token?(char : Char) : Bool
       !TSPECIAL_CHARACTERS.includes?(char) && 0x20 <= char.ord < 0x7F
     end
 
     # :nodoc:
-    def self.token?(string)
+    def self.token?(string) : Bool
       string.each_char.all? { |char| token? char }
     end
 
diff --git a/src/mime/multipart.cr b/src/mime/multipart.cr
index f5f86da3508a..179a7b92790e 100644
--- a/src/mime/multipart.cr
+++ b/src/mime/multipart.cr
@@ -39,7 +39,7 @@ module MIME::Multipart
   #
   # MIME::Multipart.parse_boundary("multipart/mixed; boundary=\"abcde\"") # => "abcde"
   # ```
-  def self.parse_boundary(content_type)
+  def self.parse_boundary(content_type) : String?
     type = MIME::MediaType.parse?(content_type)
 
     if type && type.type == "multipart"
diff --git a/src/mime/multipart/builder.cr b/src/mime/multipart/builder.cr
index 202406880103..e96d6fe121b1 100644
--- a/src/mime/multipart/builder.cr
+++ b/src/mime/multipart/builder.cr
@@ -33,7 +33,7 @@ module MIME::Multipart
     # builder = MIME::Multipart::Builder.new(io, "a4VF")
     # builder.content_type("mixed") # => "multipart/mixed; boundary=a4VF"
     # ```
-    def content_type(subtype = "mixed")
+    def content_type(subtype = "mixed") : String
       MIME::MediaType.new("multipart/#{subtype}", {"boundary" => @boundary}).to_s
     end
 
diff --git a/src/mime/multipart/parser.cr b/src/mime/multipart/parser.cr
index aebe99db4129..97e4be756d27 100644
--- a/src/mime/multipart/parser.cr
+++ b/src/mime/multipart/parser.cr
@@ -79,7 +79,7 @@ module MIME::Multipart
     end
 
     # True if `#next` can be called legally.
-    def has_next?
+    def has_next? : Bool
       @state != :FINISHED && @state != :ERRORED
     end
 
diff --git a/src/socket.cr b/src/socket.cr
index 7781515f7e6e..faf1d089a679 100644
--- a/src/socket.cr
+++ b/src/socket.cr
@@ -68,7 +68,7 @@ class Socket < IO
 
   # Creates a TCP socket. Consider using `TCPSocket` or `TCPServer` unless you
   # need full control over the socket.
-  def self.tcp(family : Family, blocking = false)
+  def self.tcp(family : Family, blocking = false) : self
     new(family, Type::STREAM, Protocol::TCP, blocking)
   end
 
@@ -80,7 +80,7 @@ class Socket < IO
 
   # Creates an UNIX socket. Consider using `UNIXSocket` or `UNIXServer` unless
   # you need full control over the socket.
-  def self.unix(type : Type = Type::STREAM, blocking = false)
+  def self.unix(type : Type = Type::STREAM, blocking = false) : self
     new(Family::UNIX, type, blocking: blocking)
   end
 
@@ -256,7 +256,7 @@ class Socket < IO
   #   socket.close
   # end
   # ```
-  def accept?
+  def accept? : Socket?
     if client_fd = accept_impl
       sock = Socket.new(client_fd, family, type, protocol, blocking)
       sock.sync = sync?
@@ -397,7 +397,7 @@ class Socket < IO
     io << "#<#{self.class}:fd #{fd}>"
   end
 
-  def send_buffer_size
+  def send_buffer_size : Int32
     getsockopt LibC::SO_SNDBUF, 0
   end
 
@@ -406,7 +406,7 @@ class Socket < IO
     val
   end
 
-  def recv_buffer_size
+  def recv_buffer_size : Int32
     getsockopt LibC::SO_RCVBUF, 0
   end
 
@@ -415,7 +415,7 @@ class Socket < IO
     val
   end
 
-  def reuse_address?
+  def reuse_address? : Bool
     getsockopt_bool LibC::SO_REUSEADDR
   end
 
@@ -423,7 +423,7 @@ class Socket < IO
     setsockopt_bool LibC::SO_REUSEADDR, val
   end
 
-  def reuse_port?
+  def reuse_port? : Bool
     getsockopt(LibC::SO_REUSEPORT, 0) do |value|
       return value != 0
     end
@@ -439,7 +439,7 @@ class Socket < IO
     setsockopt_bool LibC::SO_REUSEPORT, val
   end
 
-  def broadcast?
+  def broadcast? : Bool
     getsockopt_bool LibC::SO_BROADCAST
   end
 
@@ -517,13 +517,13 @@ class Socket < IO
   end
 
   # Returns `true` if the string represents a valid IPv4 or IPv6 address.
-  def self.ip?(string : String)
+  def self.ip?(string : String) : Bool
     addr = LibC::In6Addr.new
     ptr = pointerof(addr).as(Void*)
     LibC.inet_pton(LibC::AF_INET, string, ptr) > 0 || LibC.inet_pton(LibC::AF_INET6, string, ptr) > 0
   end
 
-  def blocking
+  def blocking : Bool
     fcntl(LibC::F_GETFL) & LibC::O_NONBLOCK == 0
   end
 
diff --git a/src/socket/address.cr b/src/socket/address.cr
index 298ca77e89bb..7a4f4c493c45 100644
--- a/src/socket/address.cr
+++ b/src/socket/address.cr
@@ -30,7 +30,7 @@ class Socket
     # * `unix://<path>`
     #
     # See `IPAddress.parse` and `UNIXAddress.parse` for details.
-    def self.parse(uri : URI)
+    def self.parse(uri : URI) : self
       case uri.scheme
       when "ip", "tcp", "udp"
         IPAddress.parse uri
@@ -42,7 +42,7 @@ class Socket
     end
 
     # :ditto:
-    def self.parse(uri : String)
+    def self.parse(uri : String) : self
       parse URI.parse(uri)
     end
 
@@ -138,7 +138,7 @@ class Socket
     end
 
     # :ditto:
-    def self.parse(uri : String)
+    def self.parse(uri : String) : self
       parse URI.parse(uri)
     end
 
@@ -377,7 +377,7 @@ class Socket
     end
 
     # :ditto:
-    def self.parse(uri : String)
+    def self.parse(uri : String) : self
       parse URI.parse(uri)
     end
 
diff --git a/src/socket/addrinfo.cr b/src/socket/addrinfo.cr
index 9885a9b48f3d..a52984eca272 100644
--- a/src/socket/addrinfo.cr
+++ b/src/socket/addrinfo.cr
@@ -197,7 +197,7 @@ class Socket
     @ip_address : IPAddress?
 
     # Returns an `IPAddress` matching this addrinfo.
-    def ip_address
+    def ip_address : Socket::IPAddress
       @ip_address ||= IPAddress.from(to_unsafe, size)
     end
 
diff --git a/src/socket/ip_socket.cr b/src/socket/ip_socket.cr
index ea59b9fcc6c3..d406389ae67f 100644
--- a/src/socket/ip_socket.cr
+++ b/src/socket/ip_socket.cr
@@ -1,6 +1,6 @@
 class IPSocket < Socket
   # Returns the `IPAddress` for the local end of the IP socket.
-  def local_address
+  def local_address : Socket::IPAddress
     sockaddr6 = uninitialized LibC::SockaddrIn6
     sockaddr = pointerof(sockaddr6).as(LibC::Sockaddr*)
     addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrIn6))
@@ -13,7 +13,7 @@ class IPSocket < Socket
   end
 
   # Returns the `IPAddress` for the remote end of the IP socket.
-  def remote_address
+  def remote_address : Socket::IPAddress
     sockaddr6 = uninitialized LibC::SockaddrIn6
     sockaddr = pointerof(sockaddr6).as(LibC::Sockaddr*)
     addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrIn6))
diff --git a/src/socket/tcp_socket.cr b/src/socket/tcp_socket.cr
index cbf105c80b7b..68ec58226eae 100644
--- a/src/socket/tcp_socket.cr
+++ b/src/socket/tcp_socket.cr
@@ -61,7 +61,7 @@ class TCPSocket < IPSocket
   end
 
   # Returns `true` if the Nagle algorithm is disabled.
-  def tcp_nodelay?
+  def tcp_nodelay? : Bool
     getsockopt_bool LibC::TCP_NODELAY, level: Protocol::TCP
   end
 
diff --git a/src/socket/udp_socket.cr b/src/socket/udp_socket.cr
index 5f25c6b1ce30..bb76391529a0 100644
--- a/src/socket/udp_socket.cr
+++ b/src/socket/udp_socket.cr
@@ -93,7 +93,7 @@ class UDPSocket < IPSocket
 
   # Reports whether transmitted multicast packets should be copied and sent
   # back to the originator.
-  def multicast_loopback?
+  def multicast_loopback? : Bool
     case @family
     when Family::INET
       getsockopt_bool LibC::IP_MULTICAST_LOOP, LibC::IPPROTO_IP
@@ -122,7 +122,7 @@ class UDPSocket < IPSocket
   # Multicast datagrams with a `hoplimit` of `0` will not be transmitted on any
   # network, but may be delivered locally if the sending host belongs to the
   # destination group and multicast loopback is enabled.
-  def multicast_hops
+  def multicast_hops : Int32
     case @family
     when Family::INET
       getsockopt LibC::IP_MULTICAST_TTL, 0, LibC::IPPROTO_IP
diff --git a/src/socket/unix_socket.cr b/src/socket/unix_socket.cr
index 940b2cc3f589..2bc9caaa2ee8 100644
--- a/src/socket/unix_socket.cr
+++ b/src/socket/unix_socket.cr
@@ -62,7 +62,7 @@ class UNIXSocket < Socket
   # left.puts "message"
   # left.gets # => "message"
   # ```
-  def self.pair(type : Type = Type::STREAM)
+  def self.pair(type : Type = Type::STREAM) : {UNIXSocket, UNIXSocket}
     fds = uninitialized Int32[2]
 
     socktype = type.value
@@ -91,11 +91,11 @@ class UNIXSocket < Socket
     end
   end
 
-  def local_address
+  def local_address : Socket::UNIXAddress
     UNIXAddress.new(path.to_s)
   end
 
-  def remote_address
+  def remote_address : Socket::UNIXAddress
     UNIXAddress.new(path.to_s)
   end
 
diff --git a/src/uri.cr b/src/uri.cr
index d68789bf0b03..a09ad83974ed 100644
--- a/src/uri.cr
+++ b/src/uri.cr
@@ -183,7 +183,7 @@ class URI
   # URI.parse("http://[::1]/bar").hostname # => "::1"
   # URI.parse("http://[::1]/bar").host     # => "[::1]"
   # ```
-  def hostname
+  def hostname : String?
     host.try { |host| self.class.unwrap_ipv6(host) }
   end
 
@@ -196,7 +196,7 @@ class URI
   # URI.unwrap_ipv6("127.0.0.1")   # => "127.0.0.1"
   # URI.unwrap_ipv6("example.com") # => "example.com"
   # ```
-  def self.unwrap_ipv6(host)
+  def self.unwrap_ipv6(host) : String
     if host.starts_with?('[') && host.ends_with?(']')
       host.byte_slice(1, host.bytesize - 2)
     else
@@ -557,7 +557,7 @@ class URI
   # ```
   #
   # The return value is URL encoded (see `#encode_www_form`).
-  def userinfo
+  def userinfo : String?
     if user = @user
       String.build { |io| userinfo(user, io) }
     end
diff --git a/src/uri/params.cr b/src/uri/params.cr
index 224dcb6dd759..9964429133e4 100644
--- a/src/uri/params.cr
+++ b/src/uri/params.cr
@@ -87,7 +87,7 @@ class URI
     #
     # URI::Params.encode({"foo" => "bar", "baz" => ["quux", "quuz"]}) # => "foo=bar&baz=quux&baz=quuz"
     # ```
-    def self.encode(hash : Hash(String, String | Array(String)))
+    def self.encode(hash : Hash(String, String | Array(String))) : String
       build do |builder|
         hash.each do |key, value|
           builder.add key, value
@@ -190,7 +190,7 @@ class URI
     # params["email"]              # => "john@example.org"
     # params["non_existent_param"] # KeyError
     # ```
-    def [](name)
+    def [](name) : String
       fetch(name) { raise KeyError.new "Missing param name: #{name.inspect}" }
     end
 
@@ -200,7 +200,7 @@ class URI
     # params["email"]?              # => "john@example.org"
     # params["non_existent_param"]? # nil
     # ```
-    def []?(name)
+    def []?(name) : String?
       fetch(name, nil)
     end
 
@@ -248,7 +248,7 @@ class URI
     # params.set_all("item", ["pencil", "book", "workbook"])
     # params.fetch_all("item") # => ["pencil", "book", "workbook"]
     # ```
-    def fetch_all(name)
+    def fetch_all(name) : Array(String)
       raw_params.fetch(name) { [] of String }
     end
 
@@ -332,7 +332,7 @@ class URI
     #
     # params.delete("non_existent_param") # KeyError
     # ```
-    def delete(name)
+    def delete(name) : String
       value = raw_params[name].shift
       raw_params.delete(name) if raw_params[name].size == 0
       value
@@ -346,7 +346,7 @@ class URI
     # params.delete_all("comments") # => ["hello, world!", ":+1:"]
     # params.has_key?("comments")   # => false
     # ```
-    def delete_all(name)
+    def delete_all(name) : Array(String)?
       raw_params.delete(name)
     end
 
diff --git a/src/uri/punycode.cr b/src/uri/punycode.cr
index 1c77610270de..7fa26ebd874d 100644
--- a/src/uri/punycode.cr
+++ b/src/uri/punycode.cr
@@ -26,7 +26,7 @@ class URI
       k + (((BASE - TMIN + 1) * delta) // (delta + SKEW))
     end
 
-    def self.encode(string)
+    def self.encode(string) : String
       String.build { |io| encode string, io }
     end
 
@@ -87,7 +87,7 @@ class URI
       end
     end
 
-    def self.decode(string)
+    def self.decode(string) : String
       output, _, rest = string.rpartition(DELIMITER)
       output = output.chars
 
@@ -138,7 +138,7 @@ class URI
       output.join
     end
 
-    def self.to_ascii(string)
+    def self.to_ascii(string) : String
       return string if string.ascii_only?
 
       String.build do |io|
diff --git a/src/uri/uri_parser.cr b/src/uri/uri_parser.cr
index 52b0aeec566c..fd28b8584d71 100644
--- a/src/uri/uri_parser.cr
+++ b/src/uri/uri_parser.cr
@@ -18,11 +18,11 @@ class URI
       @ptr = 0
     end
 
-    def c
+    def c : UInt8
       @input[@ptr]
     end
 
-    def run
+    def run : self
       parse_scheme_start
       self
     end

From 6a182f559d7ea862c4aaaeb9b843a57c3e6d2b4b Mon Sep 17 00:00:00 2001
From: Oleh Prypin <oleh@pryp.in>
Date: Mon, 5 Apr 2021 15:47:49 +0200
Subject: [PATCH 2/3] Shorten a type

---
 src/http/web_socket/protocol.cr | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/http/web_socket/protocol.cr b/src/http/web_socket/protocol.cr
index b190ebb25d03..94f95a53a5e9 100644
--- a/src/http/web_socket/protocol.cr
+++ b/src/http/web_socket/protocol.cr
@@ -106,7 +106,7 @@ class HTTP::WebSocket::Protocol
     @io.flush if flush
   end
 
-  def receive(buffer : Bytes) : HTTP::WebSocket::Protocol::PacketInfo
+  def receive(buffer : Bytes) : PacketInfo
     if @remaining == 0
       opcode = read_header
     else

From a9ffbf1c016d1e8f1cef6c2df81261bbbac12bd4 Mon Sep 17 00:00:00 2001
From: Oleh Prypin <oleh@pryp.in>
Date: Thu, 8 Apr 2021 21:40:10 +0200
Subject: [PATCH 3/3] Shorten a type

---
 src/http/cookie.cr | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/http/cookie.cr b/src/http/cookie.cr
index 77ef34230eeb..7efde93224c2 100644
--- a/src/http/cookie.cr
+++ b/src/http/cookie.cr
@@ -156,13 +156,13 @@ module HTTP
         end
       end
 
-      def parse_cookies(header) : Array(HTTP::Cookie)
+      def parse_cookies(header) : Array(Cookie)
         cookies = [] of Cookie
         parse_cookies(header) { |cookie| cookies << cookie }
         cookies
       end
 
-      def parse_set_cookie(header) : HTTP::Cookie?
+      def parse_set_cookie(header) : Cookie?
         match = header.match(SetCookieString)
         return unless match
 
@@ -292,7 +292,7 @@ module HTTP
     # ```
     # request.cookies["foo"].value # => "bar"
     # ```
-    def [](key) : HTTP::Cookie
+    def [](key) : Cookie
       @cookies[key]
     end
 
@@ -306,7 +306,7 @@ module HTTP
     # request.cookies["foo"] = "bar"
     # request.cookies["foo"]?.try &.value # > "bar"
     # ```
-    def []?(key) : HTTP::Cookie?
+    def []?(key) : Cookie?
       @cookies[key]?
     end
 
@@ -325,7 +325,7 @@ module HTTP
     # ```
     # response.cookies << HTTP::Cookie.new("foo", "bar", http_only: true)
     # ```
-    def <<(cookie : Cookie) : HTTP::Cookie
+    def <<(cookie : Cookie)
       self[cookie.name] = cookie
     end
 
@@ -337,7 +337,7 @@ module HTTP
     # Deletes and returns the `HTTP::Cookie` for the specified *key*, or
     # returns `nil` if *key* cannot be found in the collection. Note that
     # *key* should match the name attribute of the desired `HTTP::Cookie`.
-    def delete(key) : HTTP::Cookie?
+    def delete(key) : Cookie?
       @cookies.delete(key)
     end
 
@@ -393,7 +393,7 @@ module HTTP
     end
 
     # Returns this collection as a plain `Hash`.
-    def to_h : Hash(String, HTTP::Cookie)
+    def to_h : Hash(String, Cookie)
       @cookies.dup
     end
   end