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 Naming/AsciiIdentifiers rule #414

Merged
merged 1 commit into from
Nov 10, 2023
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
94 changes: 94 additions & 0 deletions spec/ameba/rule/naming/ascii_identifiers_spec.cr
Original file line number Diff line number Diff line change
@@ -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 łódź
# ^^^^ 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
80 changes: 80 additions & 0 deletions src/ameba/rule/naming/ascii_identifiers.cr
Original file line number Diff line number Diff line change
@@ -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