Skip to content

Commit 2b9c370

Browse files
authored
Merge pull request #556 from nobodywasishere/nobody/unused-pseudo-or-self
Add `Lint/UnusedSelfAccess` and `Lint/UnusedPseudoMethodCall`
2 parents 4cea7eb + 18f7e81 commit 2b9c370

File tree

5 files changed

+339
-1
lines changed

5 files changed

+339
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
require "../../../spec_helper"
2+
3+
module Ameba::Rule::Lint
4+
describe UnusedPseudoMethodCall do
5+
subject = UnusedPseudoMethodCall.new
6+
7+
it "passes if typeof is unused" do
8+
expect_no_issues subject, <<-CRYSTAL
9+
typeof(1)
10+
CRYSTAL
11+
end
12+
13+
it "passes if as is unused" do
14+
expect_no_issues subject, <<-CRYSTAL
15+
as(Int32)
16+
CRYSTAL
17+
end
18+
19+
it "fails if pointerof is unused" do
20+
expect_issue subject, <<-CRYSTAL
21+
pointerof(Int32)
22+
# ^^^^^^^^^^^^^^ error: Pseudo-method call is not used
23+
CRYSTAL
24+
end
25+
26+
it "fails if sizeof is unused" do
27+
expect_issue subject, <<-CRYSTAL
28+
sizeof(Int32)
29+
# ^^^^^^^^^^^ error: Pseudo-method call is not used
30+
CRYSTAL
31+
end
32+
33+
it "fails if instance_sizeof is unused" do
34+
expect_issue subject, <<-CRYSTAL
35+
instance_sizeof(Int32)
36+
# ^^^^^^^^^^^^^^^^^^^^ error: Pseudo-method call is not used
37+
CRYSTAL
38+
end
39+
40+
it "fails if alignof is unused" do
41+
expect_issue subject, <<-CRYSTAL
42+
alignof(Int32)
43+
# ^^^^^^^^^^^^ error: Pseudo-method call is not used
44+
CRYSTAL
45+
end
46+
47+
it "fails if instance_alignof is unused" do
48+
expect_issue subject, <<-CRYSTAL
49+
instance_alignof(Int32)
50+
# ^^^^^^^^^^^^^^^^^^^^^ error: Pseudo-method call is not used
51+
CRYSTAL
52+
end
53+
54+
it "fails if offsetof is unused" do
55+
expect_issue subject, <<-CRYSTAL
56+
offsetof(Int32, 1)
57+
# ^^^^^^^^^^^^^^^^ error: Pseudo-method call is not used
58+
CRYSTAL
59+
end
60+
61+
it "fails if is_a? is unused" do
62+
expect_issue subject, <<-CRYSTAL
63+
foo = 1
64+
foo.is_a?(Int32)
65+
# ^^^^^^^^^^^^^^ error: Pseudo-method call is not used
66+
CRYSTAL
67+
end
68+
69+
it "fails if as? is unused" do
70+
expect_issue subject, <<-CRYSTAL
71+
foo = 1
72+
foo.as?(Int32)
73+
# ^^^^^^^^^^^^ error: Pseudo-method call is not used
74+
CRYSTAL
75+
end
76+
77+
it "fails if responds_to? is unused" do
78+
expect_issue subject, <<-CRYSTAL
79+
foo = 1
80+
foo.responds_to?(:bar)
81+
# ^^^^^^^^^^^^^^^^^^^^ error: Pseudo-method call is not used
82+
CRYSTAL
83+
end
84+
85+
it "fails if nil? is unused" do
86+
expect_issue subject, <<-CRYSTAL
87+
foo = 1
88+
foo.nil?
89+
# ^^^^^^ error: Pseudo-method call is not used
90+
CRYSTAL
91+
end
92+
93+
it "fails if prefix not is unused" do
94+
expect_issue subject, <<-CRYSTAL
95+
foo = 1
96+
!foo
97+
# ^^ error: Pseudo-method call is not used
98+
CRYSTAL
99+
end
100+
101+
it "fails if suffix not is unused" do
102+
expect_issue subject, <<-CRYSTAL
103+
foo = 1
104+
foo.!
105+
# ^^^ error: Pseudo-method call is not used
106+
CRYSTAL
107+
end
108+
109+
it "passes if pointerof is used as an assign value" do
110+
expect_no_issues subject, <<-CRYSTAL
111+
var = pointerof(Int32)
112+
CRYSTAL
113+
end
114+
115+
it "passes if sizeof is used as an assign value" do
116+
expect_no_issues subject, <<-CRYSTAL
117+
var = sizeof(Int32)
118+
CRYSTAL
119+
end
120+
121+
it "passes if instance_sizeof is used as an assign value" do
122+
expect_no_issues subject, <<-CRYSTAL
123+
var = instance_sizeof(Int32)
124+
CRYSTAL
125+
end
126+
127+
it "passes if alignof is used as an assign value" do
128+
expect_no_issues subject, <<-CRYSTAL
129+
var = alignof(Int32)
130+
CRYSTAL
131+
end
132+
133+
it "passes if instance_alignof is used as an assign value" do
134+
expect_no_issues subject, <<-CRYSTAL
135+
var = instance_alignof(Int32)
136+
CRYSTAL
137+
end
138+
139+
it "passes if offsetof is used as an assign value" do
140+
expect_no_issues subject, <<-CRYSTAL
141+
var = offsetof(Int32, 1)
142+
CRYSTAL
143+
end
144+
145+
it "passes if is_a? is used as an assign value" do
146+
expect_no_issues subject, <<-CRYSTAL
147+
var = is_a?(Int32)
148+
CRYSTAL
149+
end
150+
151+
it "passes if as? is used as an assign value" do
152+
expect_no_issues subject, <<-CRYSTAL
153+
var = as?(Int32)
154+
CRYSTAL
155+
end
156+
157+
it "passes if responds_to? is used as an assign value" do
158+
expect_no_issues subject, <<-CRYSTAL
159+
var = responds_to?(:foo)
160+
CRYSTAL
161+
end
162+
163+
it "passes if nil? is used as an assign value" do
164+
expect_no_issues subject, <<-CRYSTAL
165+
var = nil?
166+
CRYSTAL
167+
end
168+
169+
it "passes if prefix not is used as an assign value" do
170+
expect_no_issues subject, <<-CRYSTAL
171+
var = !true
172+
CRYSTAL
173+
end
174+
175+
it "passes if suffix not is used as an assign value" do
176+
expect_no_issues subject, <<-CRYSTAL
177+
var = true.!
178+
CRYSTAL
179+
end
180+
end
181+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
require "../../../spec_helper"
2+
3+
module Ameba::Rule::Lint
4+
describe UnusedSelfAccess do
5+
subject = UnusedSelfAccess.new
6+
7+
it "passes if self is used as receiver for a method def" do
8+
expect_no_issues subject, <<-CRYSTAL
9+
def self.foo
10+
end
11+
CRYSTAL
12+
end
13+
14+
it "passes if self is used as object of call" do
15+
expect_no_issues subject, <<-CRYSTAL
16+
self.foo
17+
CRYSTAL
18+
end
19+
20+
it "passes if self is used as method of call" do
21+
expect_no_issues subject, <<-CRYSTAL
22+
foo.self
23+
CRYSTAL
24+
end
25+
26+
it "fails if self is unused in void context of class body" do
27+
expect_issue subject, <<-CRYSTAL
28+
class MyClass
29+
self
30+
# ^^^^ error: `self` is not used
31+
end
32+
CRYSTAL
33+
end
34+
35+
it "fails if self is unused in void context of begin" do
36+
expect_issue subject, <<-CRYSTAL
37+
begin
38+
self
39+
# ^^^^ error: `self` is not used
40+
41+
"foo"
42+
end
43+
CRYSTAL
44+
end
45+
46+
it "fails if self is unused in void context of method def" do
47+
expect_issue subject, <<-CRYSTAL
48+
def foo
49+
self
50+
# ^^^^ error: `self` is not used
51+
"bar"
52+
end
53+
CRYSTAL
54+
end
55+
end
56+
end

src/ameba/ast/visitors/implicit_return_visitor.cr

+15-1
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,22 @@ module Ameba::AST
177177
false
178178
end
179179

180-
def visit(node : Crystal::Cast | Crystal::NilableCast) : Bool
180+
def visit(node : Crystal::Cast | Crystal::NilableCast | Crystal::IsA | Crystal::RespondsTo) : Bool
181181
report_implicit_return(node)
182182

183183
incr_stack { node.obj.accept(self) }
184184

185185
false
186186
end
187187

188+
def visit(node : Crystal::UnaryExpression) : Bool
189+
report_implicit_return(node)
190+
191+
incr_stack { node.exp.accept(self) }
192+
193+
false
194+
end
195+
188196
def visit(node : Crystal::Annotation) : Bool
189197
report_implicit_return(node)
190198

@@ -393,6 +401,12 @@ module Ameba::AST
393401
false
394402
end
395403

404+
def visit(node : Crystal::OffsetOf) : Bool
405+
report_implicit_return(node)
406+
407+
false
408+
end
409+
396410
def visit(node : Crystal::LibDef) : Bool
397411
report_implicit_return(node)
398412

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
module Ameba::Rule::Lint
2+
# A rule that disallows unused pseudo method calls (is_a?, sizeof, etc).
3+
#
4+
# For example, these are considered invalid:
5+
#
6+
# ```
7+
# pointerof(foo)
8+
# sizeof(Bar)
9+
#
10+
# def method
11+
# !!valid? if guard?
12+
# nil
13+
# end
14+
# ```
15+
#
16+
# YAML configuration example:
17+
#
18+
# ```
19+
# Lint/UnusedPseudoMethodCall:
20+
# Enabled: true
21+
# ```
22+
class UnusedPseudoMethodCall < Base
23+
properties do
24+
since_version "1.7.0"
25+
description "Disallows unused pseudo-method calls"
26+
end
27+
28+
MSG = "Pseudo-method call is not used"
29+
30+
def test(source : Source)
31+
AST::ImplicitReturnVisitor.new(self, source)
32+
end
33+
34+
def test(
35+
source,
36+
node : Crystal::PointerOf | Crystal::SizeOf | Crystal::InstanceSizeOf |
37+
Crystal::AlignOf | Crystal::InstanceAlignOf | Crystal::OffsetOf |
38+
Crystal::IsA | Crystal::NilableCast | Crystal::RespondsTo | Crystal::Not,
39+
in_macro : Bool,
40+
)
41+
issue_for node, MSG
42+
end
43+
end
44+
end
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module Ameba::Rule::Lint
2+
# A rule that disallows unused accesses of `self`.
3+
#
4+
# For example, this is considered invalid:
5+
#
6+
# ```
7+
# class MyClass
8+
# self
9+
#
10+
# def self.foo
11+
# self
12+
# puts "Hello, world!"
13+
# end
14+
# end
15+
# ```
16+
#
17+
# YAML configuration example:
18+
#
19+
# ```
20+
# Lint/UnusedSelfAccess:
21+
# Enabled: true
22+
# ```
23+
class UnusedSelfAccess < Base
24+
properties do
25+
since_version "1.7.0"
26+
description "Disallows unused self"
27+
end
28+
29+
MSG = "`self` is not used"
30+
31+
def test(source : Source)
32+
AST::ImplicitReturnVisitor.new(self, source)
33+
end
34+
35+
def test(source, node : Crystal::Self, in_macro : Bool)
36+
issue_for node, MSG
37+
end
38+
39+
def test(source, node : Crystal::Var, in_macro : Bool)
40+
issue_for node, MSG if node.name == "self"
41+
end
42+
end
43+
end

0 commit comments

Comments
 (0)