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 9 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,74 @@
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

record Task,
var1 : String,
var2 : String = "asdf"
end
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
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
class Greeter
record Task,
var1 : String,
var2 = "asdf"
# ^^^^ error: Argument should have a type restriction
end
CRYSTAL
end

nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
it "fails if a top-level record call arg doesn't have a type restriction" do
expect_issue subject, <<-CRYSTAL
record Task,
var1 : String,
var2 = "asdf"
# ^^^^ error: Argument should have a type restriction
CRYSTAL
end
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved

context "#default_value" do
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
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
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
end

it "passes if a top-level record call arg with default value doesn't have a type restriction" do
expect_no_issues rule, <<-CRYSTAL
record Task,
var1 : String,
var2 = "asdf"
CRYSTAL
end
end
end
end
94 changes: 94 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,94 @@
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
#
# For example, these are considered invalid:
#
# ```
# class Greeter
# getter name
#
# record Task,
# var1 : String,
# var2 = "asdf"
# end
# ```
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
#
# And these are considered valid:
#
# ```
# 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

# record Task,
# var1 : String,
# var2 : String = "asdf"
# end
# ```
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
#
# The `DefaultValue` configuration option controls whether this rule applies to
# call arguments that have a default value.
#
# YAML configuration example:
#
# ```
# Typing/MethodParamTypeRestriction:
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
# 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 variable args to certain macros have type restrictions"
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
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 % node.name, prefer_name_location: true
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
when Crystal::Var, Crystal::Call
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
issue_for arg, MSG % node.name, prefer_name_location: true
nobodywasishere marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
end
end
Loading