Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Style/HeredocIndent #554

Merged
merged 2 commits into from
Feb 4, 2025
Merged
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
16 changes: 8 additions & 8 deletions spec/ameba/rule/documentation/documentation_spec.cr
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ module Ameba::Rule::Documentation

private macro bag
end
CRYSTAL
CRYSTAL
end

it "passes for documented public types" do
@@ -59,31 +59,31 @@ module Ameba::Rule::Documentation
# bag
macro bag
end
CRYSTAL
CRYSTAL
end

it "fails if there is an undocumented public type" do
expect_issue subject, <<-CRYSTAL
class Foo
# ^^^^^^^^^ error: Missing documentation
# ^^^^^^^ error: Missing documentation
end

module Bar
# ^^^^^^^^^^ error: Missing documentation
# ^^^^^^^^ error: Missing documentation
end

enum Baz
# ^^^^^^^^ error: Missing documentation
# ^^^^^^ error: Missing documentation
end

def bat
# ^^^^^^^ error: Missing documentation
# ^^^^^ error: Missing documentation
end

macro bag
# ^^^^^^^^^ error: Missing documentation
# ^^^^^^^ error: Missing documentation
end
CRYSTAL
CRYSTAL
end

context "properties" do
10 changes: 5 additions & 5 deletions spec/ameba/rule/lint/empty_ensure_spec.cr
Original file line number Diff line number Diff line change
@@ -23,17 +23,17 @@ module Ameba::Rule::Lint
ensure
nil
end
CRYSTAL
CRYSTAL
end

it "fails if there is an empty ensure in method" do
expect_issue subject, <<-CRYSTAL
def method
do_some_stuff
ensure
# ^^^^^^ error: Empty `ensure` block detected
# ^^^^ error: Empty `ensure` block detected
end
CRYSTAL
CRYSTAL
end

it "fails if there is an empty ensure in a block" do
@@ -43,10 +43,10 @@ module Ameba::Rule::Lint
rescue
do_some_other_stuff
ensure
# ^^^^^^ error: Empty `ensure` block detected
# ^^^^ error: Empty `ensure` block detected
# nothing here
end
CRYSTAL
CRYSTAL
end
end
end
24 changes: 12 additions & 12 deletions spec/ameba/rule/lint/shadowed_exception_spec.cr
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ module Ameba::Rule::Lint
rescue e : Exception
handle_exception
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed exception" do
@@ -38,7 +38,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^^^^ error: Shadowed exception found: `ArgumentError`
handle_argument_error_exception
end
CRYSTAL
CRYSTAL
end

it "fails if there is a custom shadowed exceptions" do
@@ -51,7 +51,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^^^^^^^ error: Shadowed exception found: `MySuperException`
3
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed exception in a type list" do
@@ -60,7 +60,7 @@ module Ameba::Rule::Lint
rescue Exception | IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there is a first shadowed exception in a type list" do
@@ -72,7 +72,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^ error: Shadowed exception found: `Exception`
rescue
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed duplicated exception" do
@@ -83,7 +83,7 @@ module Ameba::Rule::Lint
rescue IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there is a shadowed duplicated exception in a type list" do
@@ -93,7 +93,7 @@ module Ameba::Rule::Lint
rescue ArgumentError | IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there is only shadowed duplicated exceptions" do
@@ -104,7 +104,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
rescue Exception
end
CRYSTAL
CRYSTAL
end

it "fails if there is only shadowed duplicated exceptions in a type list" do
@@ -113,7 +113,7 @@ module Ameba::Rule::Lint
rescue IndexError | IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if all rescues are shadowed and there is a catch-all rescue" do
@@ -129,7 +129,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^ error: Shadowed exception found: `KeyError`
rescue
end
CRYSTAL
CRYSTAL
end

it "fails if there are shadowed exception with args" do
@@ -140,7 +140,7 @@ module Ameba::Rule::Lint
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
rescue
end
CRYSTAL
CRYSTAL
end

it "fails if there are multiple shadowed exceptions" do
@@ -152,7 +152,7 @@ module Ameba::Rule::Lint
rescue IndexError
# ^^^^^^^^^^ error: Shadowed exception found: `IndexError`
end
CRYSTAL
CRYSTAL
end

it "fails if there are multiple shadowed exceptions in a type list" do
81 changes: 81 additions & 0 deletions spec/ameba/rule/style/heredoc_indent_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
require "../../../spec_helper"

module Ameba::Rule::Style
describe HeredocIndent do
subject = HeredocIndent.new

it "passes if heredoc body indented one level" do
expect_no_issues subject, <<-CRYSTAL
<<-HEREDOC
hello world
HEREDOC

<<-HEREDOC
hello world
HEREDOC
CRYSTAL
end

it "fails if the heredoc body is indented incorrectly" do
expect_issue subject, <<-CRYSTAL
<<-ONE
# ^^^^ error: Heredoc body should be indented by 2 spaces
hello world
ONE

<<-TWO
# ^^^^^^ error: Heredoc body should be indented by 2 spaces
hello world
TWO

<<-THREE
# ^^^^^^^^ error: Heredoc body should be indented by 2 spaces
hello world
THREE

<<-FOUR
# ^^^^^^^ error: Heredoc body should be indented by 2 spaces
hello world
FOUR
CRYSTAL
end

context "properties" do
context "#indent_by" do
rule = HeredocIndent.new
rule.indent_by = 0

it "passes if heredoc body has the same indent level" do
expect_no_issues rule, <<-CRYSTAL
<<-HEREDOC
hello world
HEREDOC

<<-HEREDOC
hello world
HEREDOC
CRYSTAL
end

it "fails if the heredoc body is indented incorrectly" do
expect_issue rule, <<-CRYSTAL
<<-ONE
# ^^^^ error: Heredoc body should be indented by 0 spaces
hello world
ONE

<<-TWO
# ^^^^^^ error: Heredoc body should be indented by 0 spaces
hello world
TWO

<<-FOUR
# ^^^^^^^ error: Heredoc body should be indented by 0 spaces
hello world
FOUR
CRYSTAL
end
end
end
end
end
66 changes: 66 additions & 0 deletions src/ameba/rule/style/heredoc_indent.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module Ameba::Rule::Style
# A rule that enforces _Heredoc_ bodies be indented one level above the indentation of the
# line they're used on.
#
# For example, this is considered invalid:
#
# ```
# <<-HERERDOC
# hello world
# HEREDOC
#
# <<-HERERDOC
# hello world
# HEREDOC
# ```
#
# And should be written as:
#
# ```
# <<-HERERDOC
# hello world
# HEREDOC
#
# <<-HERERDOC
# hello world
# HEREDOC
# ```
#
# The `IndentBy` configuration option changes the enforced indentation level of the _heredoc_.
#
# ```
# Style/HeredocIndent:
# Enabled: true
# IndentBy: 2
# ```
class HeredocIndent < Base
properties do
since_version "1.7.0"
description "Recommends heredoc bodies are indented consistently"
indent_by 2
end

MSG = "Heredoc body should be indented by %s spaces"

def test(source, node : Crystal::StringInterpolation)
return unless start_location = node.location

start_location_pos = source.pos(start_location)
return unless source.code[start_location_pos..(start_location_pos + 2)]? == "<<-"

correct_indent = line_indent(source, start_location) + indent_by

unless node.heredoc_indent == correct_indent
issue_for node, MSG % indent_by
end
end

private def line_indent(source, start_location) : Int32
line_location = Crystal::Location.new(nil, start_location.line_number, 1)
line_location_pos = source.pos(line_location)
line = source.code[line_location_pos..(line_location_pos + start_location.column_number)]

line.size - line.lstrip.size
end
end
end