-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
Typing/MacroCallArgumentTypeRestriction
(#521)
- Loading branch information
1 parent
bde34f4
commit 65f7db0
Showing
2 changed files
with
164 additions
and
0 deletions.
There are no files selected for viewing
70 changes: 70 additions & 0 deletions
70
spec/ameba/rule/typing/macro_call_argument_type_restriction_spec.cr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
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 | ||
getter :age | ||
# ^^^^ error: Argument should have a type restriction | ||
getter "height" | ||
# ^^^^^^^^ error: Argument should have a type restriction | ||
end | ||
CRYSTAL | ||
end | ||
|
||
it "passes if a record call arg with a default value doesn't have a type restriction" do | ||
expect_no_issues subject, <<-CRYSTAL | ||
record Task, | ||
cmd : String, | ||
args = %[] | ||
CRYSTAL | ||
end | ||
|
||
context "properties" do | ||
context "#default_value" do | ||
rule = MacroCallArgumentTypeRestriction.new | ||
rule.default_value = true | ||
|
||
it "fails if a macro call arg with a default value doesn't have a type restriction" do | ||
expect_issue rule, <<-CRYSTAL | ||
class Greeter | ||
getter name = "Kenobi" | ||
# ^^^^ error: Argument should have a type restriction | ||
end | ||
CRYSTAL | ||
end | ||
|
||
it "fails if a record call arg with default value doesn't have a type restriction" do | ||
expect_issue rule, <<-CRYSTAL | ||
record Task, | ||
cmd : String, | ||
args = %[] | ||
# ^^^^ error: Argument should have a type restriction | ||
CRYSTAL | ||
end | ||
end | ||
end | ||
end | ||
end |
94 changes: 94 additions & 0 deletions
94
src/ameba/rule/typing/macro_call_argument_type_restriction.cr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
module Ameba::Rule::Typing | ||
# A rule that enforces call arguments to specific macros have a type restriction. | ||
# 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 | ||
# getter :height | ||
# end | ||
# | ||
# record Task, | ||
# cmd = "", | ||
# args = %w[] | ||
# ``` | ||
# | ||
# And these are considered valid: | ||
# | ||
# ``` | ||
# class Greeter | ||
# getter name : String? | ||
# getter age : Time::Span = 0.days | ||
# getter height : Float64? | ||
# 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: false | ||
# 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 false | ||
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, Crystal::StringLiteral, Crystal::SymbolLiteral | ||
issue_for arg, MSG, prefer_name_location: true | ||
end | ||
end | ||
end | ||
end | ||
end |