diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bd041ae..c85b0bfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ with 1.8-style hash rockets spanning multiple lines * Fix `RuboCop` linter to not report `Style/EmptyElse` warnings for HAML code containing `if`/`else` blocks containing only HAML filters +* Add `MultilineScript` linter to report scripts with trailing operators that + should be merged with the following line ## 0.11.0 diff --git a/config/default.yml b/config/default.yml index 35db69bc..b0880e4e 100644 --- a/config/default.yml +++ b/config/default.yml @@ -40,6 +40,9 @@ linters: MultilinePipe: enabled: true + MultilineScript: + enabled: true + ObjectReferenceAttributes: enabled: true diff --git a/lib/haml_lint/linter/README.md b/lib/haml_lint/linter/README.md index 60a68816..6393bca5 100644 --- a/lib/haml_lint/linter/README.md +++ b/lib/haml_lint/linter/README.md @@ -12,6 +12,7 @@ Below is a list of linters supported by `haml-lint`, ordered alphabetically. * [LeadingCommentSpace](#leadingcommentspace) * [LineLength](#linelength) * [MultilinePipe](#multilinepipe) +* [MultilineScript](#multilinescript) * [ObjectReferenceAttributes](#objectreferenceattributes) * [RuboCop](#rubocop) * [RubyComments](#rubycomments) @@ -242,6 +243,45 @@ The multiline bar was almost always suggests an unnecessarily complicated template that should have its logic extracted into a helper. +## MultilineScript + +Don't span Ruby script over multiple lines using operators. + +**Bad** +```haml +- if condition || +- other_condition + Display something! +``` + +**Good** +```haml +- if condition || other_condition + Display something! +``` + +While writing code this way may sometimes work, it is actually a result of a +quirk in how HAML generates code from a template. While the following code +will compile and run: + +```haml +- if condition || +- other_condition + Display something! +``` + +...this code will fail with a parse error: + +```haml +- if condition || +- other_condition + Display something! +- else + Otherwise display this! +``` + +Thus it's best to stay away from writing code this way. + ## ObjectReferenceAttributes Don't use the diff --git a/lib/haml_lint/linter/multiline_script.rb b/lib/haml_lint/linter/multiline_script.rb new file mode 100644 index 00000000..4c3b6282 --- /dev/null +++ b/lib/haml_lint/linter/multiline_script.rb @@ -0,0 +1,43 @@ +module HamlLint + # Checks scripts spread over multiple lines. + class Linter::MultilineScript < Linter + include LinterRegistry + + # List of operators that can split a script into two lines that we want to + # alert on. + SPLIT_OPERATORS = %w[ + || or && and + ||= &&= + ^ << >> | & + <<= >>= |= &= + + - * / ** % + += -= *= /= **= %= + < <= <=> >= > + = == === != =~ !~ + .. ... + ? : + not + if unless while until + begin + ].to_set + + def visit_script(node) + check(node) + end + + def visit_silent_script(node) + check(node) + end + + private + + def check(node) + operator = node.script[/\s+(\S+)\z/, 1] + if SPLIT_OPERATORS.include?(operator) + add_lint(node, + "Script with trailing operator `#{operator}` should be " \ + 'merged with the script on the following line') + end + end + end +end diff --git a/spec/haml_lint/linter/multiline_script_spec.rb b/spec/haml_lint/linter/multiline_script_spec.rb new file mode 100644 index 00000000..8f355e60 --- /dev/null +++ b/spec/haml_lint/linter/multiline_script_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe HamlLint::Linter::MultilineScript do + include_context 'linter' + + context 'when silent script is split with a Boolean operator' do + let(:haml) { <<-HAML } + - if condition || + - true + Result + HAML + + it { should report_lint line: 1 } + end + + context 'when silent script is split with an equality operator' do + let(:haml) { <<-HAML } + - if condition == + - something + Result + HAML + + it { should report_lint line: 1 } + end + + context 'when script is split with a binary operator' do + let(:haml) { <<-HAML } + = 1 + + = 2 + HAML + + it { should report_lint line: 1 } + end +end