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 Typing/MacroCallArgumentTypeRestriction #521

Merged
Show file tree
Hide file tree
Changes from 15 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
1 change: 1 addition & 0 deletions spec/ameba/base_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module Ameba::Rule
Naming
Performance
Style
Typing
]
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "../../../spec_helper"

module Ameba::Rule::Typing
describe MacroCallArgumentTypeRestriction do
subject = MacroCallArgumentTypeRestriction.new

it "passes if macro call args have type restrictions" do
expect_no_issues subject, <<-CRYSTAL
class Greeter
getter name : String?
class_getter age : Int32 = 0
setter tasks : Array(String) = [] of String
class_setter queue : Array(Int32)?
property task_mutex : Mutex = Mutex.new
class_property asdf : String
end

record Task,
cmd : String,
args : Array(String) = %w[]
CRYSTAL
end

it "fails if a macro call arg doesn't have a type restriction" do
expect_issue subject, <<-CRYSTAL
class Greeter
getter name
# ^^^^ error: Argument should have a type restriction
end
CRYSTAL
end

it "fails if a record call arg doesn't have a type restriction" do
expect_issue subject, <<-CRYSTAL
record Task,
cmd : String,
args = %[]
# ^^^^ error: Argument should have a type restriction
CRYSTAL
end

context "properties" do
context "#default_value" do
rule = MacroCallArgumentTypeRestriction.new
rule.default_value = false

it "passes if a macro call arg with a default value doesn't have a type restriction" do
expect_no_issues rule, <<-CRYSTAL
class Greeter
getter name = "Kenobi"
end
CRYSTAL
end

it "passes if a record call arg with default value doesn't have a type restriction" do
expect_no_issues rule, <<-CRYSTAL
record Task,
cmd : String,
args = %[]
CRYSTAL
end
end
end
end
end
92 changes: 92 additions & 0 deletions src/ameba/rule/typing/macro_call_argument_type_restriction.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
module Ameba::Rule::Typing
# A rule that enforces variable arguments to specific macros have a type restriction.
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
# By default these macros are: `(class_)getter/setter/property(?/!)` and `record`.
#
# For example, these are considered invalid:
#
# ```
# class Greeter
# getter name
# getter age = 0.days
# end
#
# record Task,
# cmd = "",
# args = %w[]
# ```
#
# And these are considered valid:
#
# ```
# class Greeter
# getter name : String?
# getter age : Time::Span = 0.days
# end
#
# record Task,
# cmd : String = "",
# args : Array(String) = %w[]
# ```
#
# The `DefaultValue` configuration option controls whether this rule applies to
# call arguments that have a default value.
#
# YAML configuration example:
#
# ```
# Typing/MacroCallArgumentTypeRestriction:
# Enabled: false
# DefaultValue: true
# MacroNames:
# - getter
# - getter?
# - getter!
# - class_getter
# - class_getter?
# - class_getter!
# - setter
# - setter?
# - setter!
# - class_setter
# - class_setter?
# - class_setter!
# - property
# - property?
# - property!
# - class_property
# - class_property?
# - class_property!
# - record
# ```
class MacroCallArgumentTypeRestriction < Base
properties do
since_version "1.7.0"
description "Recommends that call arguments to certain macros have type restrictions"
enabled false
default_value true
macro_names %w[
getter getter? getter! class_getter class_getter? class_getter!
setter setter? setter! class_setter class_setter? class_setter!
property property? property! class_property class_property? class_property!
record
]
end

MSG = "Argument should have a type restriction"

def test(source, node : Crystal::Call)
return unless node.name.in?(macro_names)

node.args.each do |arg|
case arg
when Crystal::Assign
next unless default_value?

issue_for arg.target, MSG, prefer_name_location: true
when Crystal::Var, Crystal::Call
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
issue_for arg, MSG, prefer_name_location: true
end
end
end
end
end
Loading