Skip to content

Commit

Permalink
Merge pull request #279 from issyl0/lint-erb-comment-syntax
Browse files Browse the repository at this point in the history
lib/erb_lint/linters: Add a new CommentSyntax linter
  • Loading branch information
etiennebarrie authored Oct 26, 2022
2 parents 7f0b494 + af809c6 commit cc14978
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 0 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ linters:
| ------------------------------------------------ |:--------:|-------------|
| [AllowedScriptType](#AllowedScriptType) | Yes | prevents the addition of `<script>` tags that have `type` attributes that are not in a white-list of allowed values |
| ClosingErbTagIndent | Yes | |
| [CommentSyntax](#CommentSyntax) | Yes | detects bad ERB comment syntax |
| ExtraNewline | Yes | |
| [FinalNewline](#FinalNewline) | Yes | warns about missing newline at the end of a ERB template |
| [NoJavascriptTagHelper](#NoJavascriptTagHelper) | Yes | prevents the usage of Rails' `javascript_tag` |
Expand Down Expand Up @@ -487,6 +488,27 @@ Linter-Specific Option | Description
`allow_blank` | True or false, depending on whether or not the `type` attribute may be omitted entirely from a `<script>` tag. Defaults to `true`.
`disallow_inline_scripts` | Do not allow inline `<script>` tags anywhere in ERB templates. Defaults to `false`.

## CommentSyntax

This linter enforces the use of the correct ERB comment syntax, since Ruby comments (`<% # comment %>` with a space) are not technically valid ERB comments.

```erb
Bad ❌
<% # This is a Ruby comment %>
Good ✅
<%# This is an ERB comment %>
Bad ❌
<% # This is a Ruby comment; it can fail to parse. %>
Good ✅
<%# This is an ERB comment; it is parsed correctly. %>
Good ✅
<%
# This is a multi-line ERB comment.
%>
```

## Custom Linters

`erb-lint` allows you to create custom linters specific to your project. It will load linters from the `.erb-linters` directory in the root of your
Expand Down
52 changes: 52 additions & 0 deletions lib/erb_lint/linters/comment_syntax.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module ERBLint
module Linters
# Detects comment syntax that isn't valid ERB.
class CommentSyntax < Linter
include LinterRegistry

def initialize(file_loader, config)
super
end

def run(processed_source)
file_content = processed_source.file_content
return if file_content.empty?

processed_source.ast.descendants(:erb).each do |erb_node|
indicator_node, _, code_node, _ = *erb_node
next if code_node.nil?

indicator_node_str = indicator_node&.deconstruct&.last
next if indicator_node_str == "#"

code_node_str = code_node.deconstruct.last
next unless code_node_str.start_with?(" #")

range = find_range(erb_node, code_node_str)
source_range = processed_source.to_source_range(range)

correct_erb_tag = indicator_node_str == "=" ? "<%#=" : "<%#"

add_offense(
source_range,
<<~EOF.chomp
Bad ERB comment syntax. Should be #{correct_erb_tag} without a space between.
Leaving a space between ERB tags and the Ruby comment character can cause parser errors.
EOF
)
end
end

def find_range(node, str)
match = node.loc.source.match(Regexp.new(Regexp.quote(str.strip)))
return unless match

range_begin = match.begin(0) + node.loc.begin_pos
range_end = match.end(0) + node.loc.begin_pos
(range_begin...range_end)
end
end
end
end
1 change: 1 addition & 0 deletions lib/erb_lint/runner_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def default(default_enabled: nil)
SpaceInHtmlTag: { enabled: default_enabled },
TrailingWhitespace: { enabled: default_enabled },
RequireInputAutocomplete: { enabled: default_enabled },
CommentSyntax: { enabled: default_enabled },
},
)
end
Expand Down
66 changes: 66 additions & 0 deletions spec/erb_lint/linters/comment_syntax_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

require "spec_helper"

describe ERBLint::Linters::CommentSyntax do
let(:linter_config) { described_class.config_schema.new }

let(:file_loader) { ERBLint::FileLoader.new(".") }
let(:linter) { described_class.new(file_loader, linter_config) }
let(:processed_source) { ERBLint::ProcessedSource.new("file.rb", file) }

subject { linter.offenses }
before { linter.run(processed_source) }

context "when the ERB comment syntax is correct" do
let(:file) { <<~FILE }
<%# good comment %>
FILE

it "does not report any offenses" do
expect(subject.size).to(eq(0))
end
end

context "when the ERB multi-line comment syntax is correct" do
let(:file) { <<~FILE }
<%
# good comment
%>
FILE

it "does not report any offenses" do
expect(subject.size).to(eq(0))
end
end

context "when the ERB comment syntax is incorrect" do
let(:file) { <<~FILE }
<% # bad comment %>
FILE

it "reports one offense" do
expect(subject.size).to(eq(1))
end

it "reports the suggested fix" do
expect(subject.first.message).to(include("Bad ERB comment syntax. Should be <%# without a space between."))
end
end

context "when the ERB comment syntax is incorrect multiple times in one file" do
let(:file) { <<~FILE }
<% # first bad comment %>
<%= # second bad comment %>
FILE

it "reports two offenses" do
expect(subject.size).to(eq(2))
end

it "reports the suggested fixes" do
expect(subject.first.message).to(include("Bad ERB comment syntax. Should be <%# without a space between."))
expect(subject.last.message).to(include("Bad ERB comment syntax. Should be <%#= without a space between."))
end
end
end

0 comments on commit cc14978

Please sign in to comment.