Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## HEAD (unreleased)

- Support naked braces/brackets/parens, invert labels on banner (https://github.com/zombocom/dead_end/pull/89)
- Handle mismatched end when using rescue without begin (https://github.com/zombocom/dead_end/pull/83)
- CLI returns non-zero exit code when syntax error is found (https://github.com/zombocom/dead_end/pull/86)
- Let -v respond with gem version instead of 'unknown' (https://github.com/zombocom/dead_end/pull/82)
Expand Down
58 changes: 58 additions & 0 deletions lib/dead_end/banner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

module DeadEnd
class Banner
attr_reader :invalid_obj

def initialize(invalid_obj:)
@invalid_obj = invalid_obj
end

def call
case invalid_obj.error_symbol
when :missing_end
<<~EOM
DeadEnd: Missing `end` detected

This code has a missing `end`. Ensure that all
syntax keywords (`def`, `do`, etc.) have a matching `end`.
EOM
when :unmatched_syntax
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm realizing that unmatched_syntax and unexpected_syntax are very similar. I'm thinking that we should make them more unique at a glance maybe missing_symbol and unexpected_syntax ? What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually scratch that, If we normalize and reverse characters in "who dis" instead then we won't need to add this extra state. I would prefer it that way instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! There's still one reversal I do here, in missing_character to show which character might be missing, see the following example:

On 2.0 release:

➜  dead_end git:(mauro-oto/support-naked-braces-brackets-and-invert-labels) ✗ dead_end test1.rb

DeadEnd: Unmatched `]` detected

file: /Users/motonelli/Projects/dead_end/test1.rb
simplified:

       1  module Hey
       2    class Foo
       3      def initialize
    ❯  4        [1,2,3
       5      end
       9    end
      10  end

On this branch:

➜  dead_end git:(mauro-oto/support-naked-braces-brackets-and-invert-labels) ✗ exe/dead_end test1.rb

DeadEnd: Unmatched `[` character detected

It appears a `]` is missing.

file: /Users/motonelli/Projects/dead_end/test1.rb
simplified:

       1  module Hey
       2    class Foo
       3      def initialize
    ❯  4        [1,2,3
       5      end
       9    end
      10  end

(For the It appears a ']' is missing. message, which is part of the banner)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, works for me

case unmatched_symbol
when :end
<<~EOM
DeadEnd: Unmatched `end` detected

This code has an unmatched `end`. Ensure that all `end` lines
in your code have a matching syntax keyword (`def`, `do`, etc.)
and that you don't have any extra `end` lines.
EOM
when :|
<<~EOM
DeadEnd: Unmatched `|` character detected

Example:

`do |x` should be `do |x|`
EOM
when *WhoDisSyntaxError::CHARACTERS.keys
<<~EOM
DeadEnd: Unmatched `#{unmatched_symbol}` character detected

It appears a `#{missing_character}` might be missing.
EOM
else
"DeadEnd: Unmatched `#{unmatched_symbol}` detected"
end
end
end

private def unmatched_symbol
invalid_obj.unmatched_symbol
end

private def missing_character
WhoDisSyntaxError::CHARACTERS[unmatched_symbol]
end
end
end
39 changes: 2 additions & 37 deletions lib/dead_end/display_invalid_blocks.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require_relative "banner"
require_relative "capture_code_context"
require_relative "display_code_with_line_numbers"

Expand Down Expand Up @@ -54,43 +55,7 @@ def call
end

def banner
case @invalid_obj.error_symbol
when :missing_end
<<~EOM
DeadEnd: Missing `end` detected

This code has a missing `end`. Ensure that all
syntax keywords (`def`, `do`, etc.) have a matching `end`.
EOM
when :unmatched_syntax
case @invalid_obj.unmatched_symbol
when :end
<<~EOM
DeadEnd: Unmatched `end` detected

This code has an unmatched `end`. Ensure that all `end` lines
in your code have a matching syntax keyword (`def`, `do`, etc.)
and that you don't have any extra `end` lines.
EOM
when :|
<<~EOM
DeadEnd: Unmatched `|` character detected

Example:

`do |x` should be `do |x|`
EOM
when :"}"
<<~EOM
DeadEnd: Unmatched `}` character detected

This code has an unmatched `}`. Ensure that opening curly braces are
closed: `{ }`.
EOM
else
"DeadEnd: Unmatched `#{@invalid_obj.unmatched_symbol}` detected"
end
end
Banner.new(invalid_obj: @invalid_obj).call
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this. Extracting this logic to it's own class makes so much more sense.

end

def indent(string, with: " ")
Expand Down
7 changes: 7 additions & 0 deletions lib/dead_end/who_dis_syntax_error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module DeadEnd
# puts WhoDisSyntaxError.new("def foo;").call.error_symbol
# # => :missing_end
class WhoDisSyntaxError < Ripper
CHARACTERS = {"{": :"}", "}": :"{", "[": :"]", "]": :"[", "(": :")", ")": :"("}
class Null
def error_symbol
:missing_end
Expand Down Expand Up @@ -59,6 +60,12 @@ def on_parse_error(msg)
@unmatched_symbol = :end
@error_symbol = :unmatched_syntax
when /unexpected .* expecting ['`]?(?<unmatched_symbol>[^']*)/
if $1
character = $1.to_sym
@unmatched_symbol = CHARACTERS[character] || character
end
@error_symbol = :unmatched_syntax
when /unexpected '(?<unmatched_symbol>.*)'/
@unmatched_symbol = $1.to_sym if $1
@error_symbol = :unmatched_syntax
when /unexpected `end'/, # Ruby 2.7 and 3.0
Expand Down
122 changes: 122 additions & 0 deletions spec/unit/banner_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# frozen_string_literal: true

require_relative "../spec_helper"

module DeadEnd
RSpec.describe Banner do
it "Unmatched | banner" do
source = <<~EOM
Foo.call do |
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("Unmatched `|` character detected")
end

it "Unmatched { banner" do
source = <<~EOM
class Cat
lol = {
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("Unmatched `{` character detected")
end

it "Unmatched } banner" do
skip("Unsupported ruby version") unless Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7")

source = <<~EOM
def foo
lol = }
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("Unmatched `}` character detected")
end

it "Unmatched [ banner" do
source = <<~EOM
class Cat
lol = [
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("Unmatched `[` character detected")
end

it "Unmatched ] banner" do
source = <<~EOM
def foo
lol = ]
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("Unmatched `]` character detected")
end

it "Unmatched end banner" do
source = <<~EOM
class Cat
end
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("DeadEnd: Unmatched `end` detected")
end

it "Unmatched unknown banner" do
source = <<~EOM
class Cat
def meow
1 *
end
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("DeadEnd: Unmatched `unknown` detected")
end

it "missing end banner" do
source = <<~EOM
class Cat
def meow
end
EOM

invalid_obj = WhoDisSyntaxError.new(source)
banner = Banner.new(invalid_obj: invalid_obj)
expect(banner.call).to include("DeadEnd: Missing `end` detected")
end

it "naked (closing) parenthesis" do
invalid_obj = WhoDisSyntaxError.new("def initialize; ); end").call

expect(
Banner.new(invalid_obj: invalid_obj).call
).to include("Unmatched `)` character detected")
end

it "naked (opening) parenthesis" do
invalid_obj = WhoDisSyntaxError.new("def initialize; (; end").call

expect(
Banner.new(invalid_obj: invalid_obj).call
).to include("Unmatched `(` character detected")
end
end
end
81 changes: 0 additions & 81 deletions spec/unit/display_invalid_blocks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,87 +4,6 @@

module DeadEnd
RSpec.describe DisplayInvalidBlocks do
it "Unmatched | banner" do
source = <<~EOM
Foo.call do |
end
EOM
code_lines = code_line_array(source)

display = DisplayInvalidBlocks.new(
code_lines: code_lines,
blocks: CodeBlock.new(lines: code_lines),
invalid_obj: WhoDisSyntaxError.new(source)
)
expect(display.banner).to include("Unmatched `|` character detected")
end

it "Unmatched } banner" do
source = <<~EOM
class Cat
lol = {
end
EOM
code_lines = CleanDocument.new(source: source).call.lines

display = DisplayInvalidBlocks.new(
code_lines: code_lines,
blocks: CodeBlock.new(lines: code_lines),
invalid_obj: WhoDisSyntaxError.new(source)
)
expect(display.banner).to include("Unmatched `}` character detected")
end

it "Unmatched end banner" do
source = <<~EOM
class Cat
end
end
EOM
code_lines = code_line_array(source)

display = DisplayInvalidBlocks.new(
code_lines: code_lines,
blocks: CodeBlock.new(lines: code_lines),
invalid_obj: WhoDisSyntaxError.new(source)
)
expect(display.banner).to include("DeadEnd: Unmatched `end` detected")
end

it "Unmatched unknown banner" do
source = <<~EOM
class Cat
def meow
1 *
end
end
EOM
code_lines = code_line_array(source)

display = DisplayInvalidBlocks.new(
code_lines: code_lines,
blocks: CodeBlock.new(lines: code_lines),
invalid_obj: WhoDisSyntaxError.new(source)
)
expect(display.banner).to include("DeadEnd: Unmatched `unknown` detected")
end

it "missing end banner" do
source = <<~EOM
class Cat
def meow
end
EOM
code_lines = code_line_array(source)

display = DisplayInvalidBlocks.new(
code_lines: code_lines,
blocks: CodeBlock.new(lines: code_lines),
invalid_obj: WhoDisSyntaxError.new(source)
)
expect(display.banner).to include("DeadEnd: Missing `end` detected")
end

it "works with valid code" do
syntax_string = <<~EOM
class OH
Expand Down
Loading