diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d0764aec..6972a3c65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -59,8 +59,8 @@ jobs: - name: RBS Inline run: bundle exec rake rbs_inline - # - name: Sorbet - # run: bundle exec srb tc + - name: Sorbet + run: bundle exec srb tc - name: Run C tests run: ./run_herb_tests diff --git a/lib/herb/ast/helpers.rb b/lib/herb/ast/helpers.rb index a24637261..07f628e39 100644 --- a/lib/herb/ast/helpers.rb +++ b/lib/herb/ast/helpers.rb @@ -4,12 +4,12 @@ module Herb module AST module Helpers - #: (Herb::AST::Node) -> bool + #: (Herb::AST::Node?) -> bool def erb_outputs?(node) return false unless node.is_a?(Herb::AST::ERBContentNode) - opening = node.tag_opening&.value - opening&.include?("=") && !opening&.start_with?("<%#") + opening = node.tag_opening&.value || "" + opening.include?("=") && !opening.start_with?("<%#") end #: (String) -> bool diff --git a/lib/herb/ast/node.rb b/lib/herb/ast/node.rb index e1b5768c6..c39f625d4 100644 --- a/lib/herb/ast/node.rb +++ b/lib/herb/ast/node.rb @@ -2,13 +2,21 @@ # typed: true module Herb + #: type serialized_node = { + #| type: String, + #| location: serialized_location?, + #| errors: Array[serialized_error] + #| } module AST class Node - attr_reader :type #: String - attr_reader :location #: Location - attr_reader :errors #: Array[Herb::Errors::Error] - - #: (String, Location, Array[Herb::Errors::Error]) -> void + #: String + attr_reader :type + #: Location + attr_reader :location + #: Array[Herb::Errors::Error] + attr_reader :errors + + #: (String, Location, ?Array[Herb::Errors::Error]) -> void def initialize(type, location, errors = []) @type = type @location = location @@ -19,7 +27,7 @@ def initialize(type, location, errors = []) def to_hash { type: type, - location: location&.to_hash, + location: location.to_hash, errors: errors.map(&:to_hash), } end diff --git a/lib/herb/engine.rb b/lib/herb/engine.rb index 967fd82f8..55ef226ff 100644 --- a/lib/herb/engine.rb +++ b/lib/herb/engine.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: false require "json" require "time" diff --git a/lib/herb/engine/debug_visitor.rb b/lib/herb/engine/debug_visitor.rb index 568244199..5e3624f04 100644 --- a/lib/herb/engine/debug_visitor.rb +++ b/lib/herb/engine/debug_visitor.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true +# typed: false module Herb class Engine @@ -30,6 +31,7 @@ def initialize(file_path: nil, project_path: nil) @in_html_comment = false @in_html_doctype = false @erb_nodes_to_wrap = [] #: Array[Herb::AST::ERBContentNode] + @top_level_elements = [] #: Array[Herb::AST::HTMLElementNode] end def visit_document_node(node) @@ -142,8 +144,6 @@ def replace_erb_nodes_recursive(node) end def find_top_level_elements(document_node) - @top_level_elements = [] #: Array[Herb::AST::HTMLElementNode] - document_node.children.each do |child| @top_level_elements << child if child.is_a?(Herb::AST::HTMLElementNode) end diff --git a/lib/herb/lex_result.rb b/lib/herb/lex_result.rb index 5c32a76d1..e7b0d15cc 100644 --- a/lib/herb/lex_result.rb +++ b/lib/herb/lex_result.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true +# typed: true module Herb class LexResult < Result - attr_reader :value #: TokenList + #: TokenList + attr_reader :value #: (Array[Herb::Token], String, Array[Herb::Warnings::Warning], Array[Herb::Errors::Error]) -> void def initialize(value, source, warnings, errors) diff --git a/lib/herb/location.rb b/lib/herb/location.rb index 276ce61aa..1474adf57 100644 --- a/lib/herb/location.rb +++ b/lib/herb/location.rb @@ -2,9 +2,15 @@ # typed: true module Herb + #: type serialized_location = { + #| start: serialized_position, + #| end: serialized_position + #| } class Location - attr_reader :start #: Position - attr_reader :end #: Position + #: Position + attr_reader :start + #: Position + attr_reader :end #: (Position, Position) -> void def initialize(start_position, end_position) @@ -28,9 +34,9 @@ def self.[](start_line, start_column, end_line, end_column) #: () -> serialized_location def to_hash { - start: start, - end: self.end, - } #: Herb::serialized_location + start: start.to_hash, + end: self.end.to_hash, + } end #: (?untyped) -> String diff --git a/lib/herb/parse_result.rb b/lib/herb/parse_result.rb index 9b0bc1764..8849cbcfb 100644 --- a/lib/herb/parse_result.rb +++ b/lib/herb/parse_result.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true +# typed: true require "json" module Herb class ParseResult < Result - attr_reader :value #: Herb::AST::DocumentNode + #: Herb::AST::DocumentNode + attr_reader :value #: (Herb::AST::DocumentNode, String, Array[Herb::Warnings::Warning], Array[Herb::Errors::Error]) -> void def initialize(value, source, warnings, errors) diff --git a/lib/herb/position.rb b/lib/herb/position.rb index f7131d151..1213b0355 100644 --- a/lib/herb/position.rb +++ b/lib/herb/position.rb @@ -2,9 +2,12 @@ # typed: true module Herb + #: type serialized_position = { line: Integer, column: Integer } class Position - attr_reader :line #: Integer - attr_reader :column #: Integer + #: Integer + attr_reader :line + #: Integer + attr_reader :column #: (Integer, Integer) -> void def initialize(line, column) diff --git a/lib/herb/range.rb b/lib/herb/range.rb index 7adb16c10..edd15a8e9 100644 --- a/lib/herb/range.rb +++ b/lib/herb/range.rb @@ -2,9 +2,12 @@ # typed: true module Herb + #: type serialized_range = [Integer, Integer] class Range - attr_reader :from #: Integer - attr_reader :to #: Integer + #: Integer + attr_reader :from + #: Integer + attr_reader :to #: (Integer, Integer) -> void def initialize(from, to) diff --git a/lib/herb/result.rb b/lib/herb/result.rb index 9067e2843..1b0356b44 100644 --- a/lib/herb/result.rb +++ b/lib/herb/result.rb @@ -3,9 +3,12 @@ module Herb class Result - attr_reader :source #: String - attr_reader :warnings #: Array[Herb::Warnings::Warning] - attr_reader :errors #: Array[Herb::Errors::Error] + #: String + attr_reader :source + #: Array[Herb::Warnings::Warning] + attr_reader :warnings + #: Array[Herb::Errors::Error] + attr_reader :errors #: (String, Array[Herb::Warnings::Warning], Array[Herb::Errors::Error]) -> void def initialize(source, warnings, errors) diff --git a/lib/herb/token.rb b/lib/herb/token.rb index 33742fe5a..520d3872f 100644 --- a/lib/herb/token.rb +++ b/lib/herb/token.rb @@ -2,13 +2,23 @@ # typed: true module Herb + #: type serialized_token = { + #| value: String, + #| range: serialized_range?, + #| location: serialized_location?, + #| type: String + #| } class Token include Colors - attr_reader :value #: String - attr_reader :range #: Range - attr_reader :location #: Location - attr_reader :type #: String + #: String + attr_reader :value + #: Range? + attr_reader :range + #: Location? + attr_reader :location + #: String + attr_reader :type #: (String, Range, Location, String) -> void def initialize(value, range, location, type) @@ -25,7 +35,7 @@ def to_hash range: range&.to_a, location: location&.to_hash, type: type, - } #: Herb::serialized_token + } end #: (?untyped) -> String diff --git a/lib/herb/warnings.rb b/lib/herb/warnings.rb index 9020a3d7d..950effd54 100644 --- a/lib/herb/warnings.rb +++ b/lib/herb/warnings.rb @@ -3,10 +3,18 @@ module Herb module Warnings + #: type serialized_warning = { + #| type: String, + #| location: serialized_location?, + #| message: String + #| } class Warning - attr_reader :type #: String - attr_reader :location #: Location - attr_reader :message #: String + #: String + attr_reader :type + #: Location? + attr_reader :location + #: String + attr_reader :message #: (String, Location, String) -> void def initialize(type, location, message) diff --git a/sig/herb/ast/helpers.rbs b/sig/herb/ast/helpers.rbs index 0ee286c34..7cef1eb3a 100644 --- a/sig/herb/ast/helpers.rbs +++ b/sig/herb/ast/helpers.rbs @@ -3,7 +3,7 @@ module Herb module AST module Helpers - # : (Herb::AST::Node) -> bool + # : (Herb::AST::Node?) -> bool def erb_outputs?: (Herb::AST::Node) -> bool # : (String) -> bool diff --git a/sig/herb/token.rbs b/sig/herb/token.rbs index ea8d71937..59f580545 100644 --- a/sig/herb/token.rbs +++ b/sig/herb/token.rbs @@ -6,9 +6,9 @@ module Herb attr_reader value: String - attr_reader range: Range + attr_reader range: Range? - attr_reader location: Location + attr_reader location: Location? attr_reader type: String diff --git a/sorbet/config b/sorbet/config index e76047cc3..af3793756 100644 --- a/sorbet/config +++ b/sorbet/config @@ -2,5 +2,4 @@ lib/ --ignore=/tmp/ --ignore=/vendor/bundle ---enable-experimental-rbs-signatures ---enable-experimental-rbs-assertions +--enable-experimental-rbs-comments diff --git a/templates/lib/herb/ast/nodes.rb.erb b/templates/lib/herb/ast/nodes.rb.erb index 3fa363070..ad3f0ed62 100644 --- a/templates/lib/herb/ast/nodes.rb.erb +++ b/templates/lib/herb/ast/nodes.rb.erb @@ -1,11 +1,19 @@ module Herb module AST <%- nodes.each do |node| -%> + #: type serialized_<%= node.human %> = { + <%- node.fields.each do |field| -%> + <%- is_nilable = !%w[Array[Herb::AST::Node] Array[Herb::AST::ERBWhenNode] Array[Herb::AST::ERBInNode] bool nil].include?(field.ruby_type) -%> + #| <%= field.name %>: <%= field.ruby_type %><%= if is_nilable then "?" end %>, + <%- end -%> + #| } class <%= node.name -%> < Node include Colors <%- node.fields.each do |field| -%> - attr_reader :<%= field.name %> #: <%= field.ruby_type %> + <%- is_nilable = !%w[Array[Herb::AST::Node] Array[Herb::AST::ERBWhenNode] Array[Herb::AST::ERBInNode] bool nil].include?(field.ruby_type) -%> + #: <%= field.ruby_type %><%= if is_nilable then "?" end %> + attr_reader :<%= field.name %> <%- end -%> #: (<%= ["String", "Location", "Array[Herb::Errors::Error]", *node.fields.map(&:ruby_type)].join(", ") %>) -> void @@ -26,7 +34,7 @@ module Herb <%- node.fields.each do |field| -%> <%= field.name %>: <%= field.name %>, <%- end -%> - }) #: Herb::serialized_<%= node.human %> + }) end #: (Visitor) -> void diff --git a/templates/lib/herb/errors.rb.erb b/templates/lib/herb/errors.rb.erb index da4a8474a..677cb8648 100644 --- a/templates/lib/herb/errors.rb.erb +++ b/templates/lib/herb/errors.rb.erb @@ -1,9 +1,15 @@ -<%- base_arguments = [["type", "String"], ["location", "Location"], ["message", "String"]] -%> +<%- base_arguments = [["type", "String"], ["location", "Location?"], ["message", "String"]] -%> module Herb + #: type serialized_error = { + #| type: String, + #| location: serialized_location?, + #| message: String + #| } module Errors class Error <%- base_arguments.each do |argument, type| -%> - attr_reader :<%= argument %> #: <%= type %> + #: <%= type %> + attr_reader :<%= argument %> <%- end -%> #: (<%= base_arguments.map { |_argument, type| type }.join(", ") %>) -> void @@ -45,10 +51,18 @@ module Herb <%- errors.each do |error| -%> class <%= error.name -%> < Error + include Colors + #: type serialized_<%= error.human %> = { + <%- error.fields.each do |field| -%> + #| <%= "#{field.name}: #{field.ruby_type}?," %> + <%- end -%> + #| } + <%- error.fields.each do |field| -%> - attr_reader :<%= field.name %> #: <%= field.ruby_type %> + #: <%= field.ruby_type %>? + attr_reader :<%= field.name %> <%- end -%> #: (<%= [*base_arguments.map(&:last), *error.fields.map(&:ruby_type)].join(", ") %>) -> void @@ -71,7 +85,7 @@ module Herb <%- error.fields.each do |field| -%> <%= field.name %>: <%= field.name %>, <%- end -%> - }) #: Herb::serialized_<%= error.human %> + }) end #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String diff --git a/templates/lib/herb/visitor.rb.erb b/templates/lib/herb/visitor.rb.erb index 06bbe335a..e9e5d9ed5 100644 --- a/templates/lib/herb/visitor.rb.erb +++ b/templates/lib/herb/visitor.rb.erb @@ -2,12 +2,12 @@ module Herb class Visitor include AST::Helpers - #: (Herb::AST::Node) -> void + #: (Herb::AST::Node?) -> void def visit(node) node&.accept(self) end - #: (Array[Herb::AST::Node]) -> void + #: (Array[Herb::AST::Node?]) -> void def visit_all(nodes) nodes.each { |node| node&.accept(self) } end