Merge pull request #414 from crystal-ameba/add-ascii-identifiers-rule

Add `Naming/AsciiIdentifiers` rule
This commit is contained in:
Sijawusz Pur Rahnama 2023-11-10 01:59:04 +01:00 committed by GitHub
commit 0abb73f0b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 174 additions and 0 deletions

View File

@ -0,0 +1,94 @@
require "../../../spec_helper"
module Ameba::Rule::Naming
subject = AsciiIdentifiers.new
describe AsciiIdentifiers do
it "reports classes with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
class BigAwesome🐺
# ^^^^^^^^^^^ error: Identifier contains non-ascii characters
@🐺_name : String
# ^^^^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports modules with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
module Bąk
# ^^^ error: Identifier contains non-ascii characters
@@bąk_name : String
# ^^^^^^^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports enums with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
enum TypeOf🔥
# ^^^^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports defs with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
def łó
# ^^^^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports defs with parameter names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
def forest_adventure(include_🐺 = true, include_🐿 = true)
# ^ error: Identifier contains non-ascii characters
# ^ error: Identifier contains non-ascii characters
end
CRYSTAL
end
it "reports argument names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
%w[wensleydale cheddar brie].each { |🧀| nil }
# ^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports aliases with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
alias JSON🧀 = JSON::Any
# ^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports constants with names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
I_LOVE_🍣 = true
# ^^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports assignments with variable names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
space_👾 = true
# ^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "reports multiple assignments with variable names containing non-ascii characters" do
expect_issue subject, <<-CRYSTAL
foo, space_👾 = true, true
# ^^^^^^^ error: Identifier contains non-ascii characters
CRYSTAL
end
it "passes for strings with non-ascii characters" do
expect_no_issues subject, <<-CRYSTAL
space = "👾"
space = :invader # 👾
CRYSTAL
end
end
end

View File

@ -0,0 +1,80 @@
module Ameba::Rule::Naming
# A rule that reports non-ascii characters in identifiers.
#
# Favour this:
#
# ```
# class BigAwesomeWolf
# end
# ```
#
# Over this:
#
# ```
# class BigAwesome🐺
# end
# ```
#
# YAML configuration example:
#
# ```
# Naming/AsciiIdentifiers:
# Enabled: true
# ```
class AsciiIdentifiers < Base
include AST::Util
properties do
description "Disallows non-ascii characters in identifiers"
end
MSG = "Identifier contains non-ascii characters"
def test(source, node : Crystal::Assign)
if (target = node.target).is_a?(Crystal::Path)
check_issue(source, target, target)
end
end
def test(source, node : Crystal::MultiAssign)
node.targets.each do |target|
check_issue(source, target, target)
end
end
def test(source, node : Crystal::Def)
check_issue_with_location(source, node)
node.args.each do |arg|
check_issue_with_location(source, arg)
end
end
def test(source, node : Crystal::ClassVar | Crystal::InstanceVar | Crystal::Var | Crystal::Alias)
check_issue_with_location(source, node)
end
def test(source, node : Crystal::ClassDef | Crystal::ModuleDef | Crystal::EnumDef | Crystal::LibDef)
check_issue(source, node.name, node.name)
end
private def check_issue_with_location(source, node)
location = name_location(node)
end_location = name_end_location(node)
if location && end_location
check_issue(source, location, end_location, node.name)
else
check_issue(source, node, node.name)
end
end
private def check_issue(source, location, end_location, name)
issue_for location, end_location, MSG unless name.to_s.ascii_only?
end
private def check_issue(source, node, name)
issue_for node, MSG unless name.to_s.ascii_only?
end
end
end