reek 3.0.4 → 3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG +6 -0
- data/README.md +41 -20
- data/features/configuration_files/directory_specific_directives.feature +280 -0
- data/features/configuration_files/masking_smells.feature +0 -14
- data/features/step_definitions/sample_file_steps.rb +2 -2
- data/lib/reek/ast/sexp_extensions.rb +72 -60
- data/lib/reek/cli/application.rb +2 -5
- data/lib/reek/cli/reek_command.rb +1 -1
- data/lib/reek/configuration/app_configuration.rb +115 -61
- data/lib/reek/configuration/configuration_file_finder.rb +19 -0
- data/lib/reek/context/code_context.rb +102 -29
- data/lib/reek/context/method_context.rb +11 -48
- data/lib/reek/context/module_context.rb +2 -6
- data/lib/reek/context/root_context.rb +7 -14
- data/lib/reek/examiner.rb +15 -10
- data/lib/reek/report/report.rb +5 -4
- data/lib/reek/smells/boolean_parameter.rb +1 -1
- data/lib/reek/smells/duplicate_method_call.rb +1 -5
- data/lib/reek/smells/irresponsible_module.rb +2 -2
- data/lib/reek/smells/smell_repository.rb +30 -16
- data/lib/reek/smells/utility_function.rb +1 -1
- data/lib/reek/source/source_code.rb +10 -6
- data/lib/reek/source/source_locator.rb +27 -26
- data/lib/reek/spec.rb +8 -6
- data/lib/reek/spec/should_reek.rb +6 -2
- data/lib/reek/spec/should_reek_of.rb +5 -2
- data/lib/reek/spec/should_reek_only_of.rb +1 -1
- data/lib/reek/tree_walker.rb +49 -21
- data/lib/reek/version.rb +1 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +4 -12
- data/spec/reek/configuration/app_configuration_spec.rb +80 -52
- data/spec/reek/configuration/configuration_file_finder_spec.rb +27 -15
- data/spec/reek/context/code_context_spec.rb +66 -17
- data/spec/reek/context/method_context_spec.rb +66 -64
- data/spec/reek/context/root_context_spec.rb +3 -1
- data/spec/reek/context/singleton_method_context_spec.rb +1 -2
- data/spec/reek/examiner_spec.rb +5 -8
- data/spec/reek/report/xml_report_spec.rb +3 -2
- data/spec/reek/smells/attribute_spec.rb +12 -17
- data/spec/reek/smells/duplicate_method_call_spec.rb +17 -25
- data/spec/reek/smells/feature_envy_spec.rb +4 -5
- data/spec/reek/smells/irresponsible_module_spec.rb +43 -0
- data/spec/reek/smells/nested_iterators_spec.rb +12 -26
- data/spec/reek/smells/too_many_statements_spec.rb +2 -210
- data/spec/reek/smells/utility_function_spec.rb +49 -5
- data/spec/reek/source/source_locator_spec.rb +39 -43
- data/spec/reek/spec/should_reek_of_spec.rb +3 -2
- data/spec/reek/spec/should_reek_spec.rb +25 -17
- data/spec/reek/tree_walker_spec.rb +214 -23
- data/spec/samples/configuration/full_configuration.reek +9 -0
- data/spec/spec_helper.rb +10 -10
- metadata +4 -3
- data/features/configuration_files/overrides_defaults.feature +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c490c79513c93451a148336881db8ffec7665e7e
|
4
|
+
data.tar.gz: 88059b5d7db2a76be01ca26339e3753f1ef4fa68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a93e6367ce746f4c1fba78e8ca91e035b9f3f9624316f329c1f5640415649816dfcc5608427ec113ac4c66aceb7f3eaed800af0c11a8931ebfc828e0a1931413
|
7
|
+
data.tar.gz: 24b764ba2d1a15fefe32702567f55cdf49270aaf00c78b6cab50420ebad3b96651d450c210934b8e76e50da2048b14e0b496c4a861ebeb98f4a6d6fedfdac21c
|
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -144,42 +144,63 @@ of how many `*.reek` files you might have on your filesystem.
|
|
144
144
|
|
145
145
|
#### Configuration options
|
146
146
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
types offer a configuration that goes beyond that
|
151
|
-
of the basic smell options, for instance
|
152
|
-
[Data Clump](docs/Data-Clump.md).
|
153
|
-
All options that go beyond the [Basic Smell Options](docs/Basic-Smell-Options.md)
|
154
|
-
should be documented in the corresponding smell type wiki page,
|
155
|
-
but if you want to get a quick and full overview over all possible
|
156
|
-
configurations you can always check out [the `config/default.reek`
|
157
|
-
file in this repository](config/defaults.reek).
|
158
|
-
|
159
|
-
Here's an excerpt of a `reek` configuration file from a commercial project:
|
147
|
+
We put a lot of effort into making `reek`'s configuration as self explanatory as possible so the
|
148
|
+
best way to understand it is by looking at a simple
|
149
|
+
example (e.g. `config.reek` in your project directory):
|
160
150
|
|
161
151
|
```yaml
|
162
152
|
---
|
153
|
+
|
154
|
+
### Generic smell configuration
|
155
|
+
|
156
|
+
# You can disable smells completely
|
163
157
|
IrresponsibleModule:
|
164
158
|
enabled: false
|
165
159
|
|
160
|
+
# You can use filters to silence reek warnings.
|
161
|
+
# Either because you simply disagree with reek (we are not the police) or
|
162
|
+
# because you want to fix this at a later point in time.
|
166
163
|
NestedIterators:
|
167
164
|
exclude:
|
168
|
-
- "
|
169
|
-
- "
|
170
|
-
- "BookingRequestsController#vehicle_options" # respond_to block
|
171
|
-
- "Content::Base#self.expose_fields" # unavoidable due to metaprogramming
|
165
|
+
- "MyWorker#self.class_method" # should be refactored
|
166
|
+
- "AnotherWorker#instance_method" # should be refactored as well
|
172
167
|
|
168
|
+
# A lot of smells allow fine tuning their configuration. You can look up all available options
|
169
|
+
# in the corresponding smell documentation in /docs. In most cases you probably can just go
|
170
|
+
# with the defaults we set in config/defaults.reek.
|
173
171
|
DataClump:
|
174
172
|
max_copies: 3
|
175
173
|
min_clump_size: 3
|
176
174
|
|
175
|
+
### Directory specific configuration
|
176
|
+
|
177
|
+
# You can configure smells on a per-directory base.
|
178
|
+
# E.g. the classic Rails case: controllers smell of NestedIterators (see /docs/Nested-Iterators.md) and
|
179
|
+
# helpers smell of UtilityFunction (see docs/Utility-Function.md)
|
180
|
+
"web_app/app/controllers":
|
181
|
+
NestedIterators:
|
182
|
+
enabled: false
|
183
|
+
"web_app/app/helpers":
|
184
|
+
UtilityFunction:
|
185
|
+
enabled: false
|
186
|
+
|
187
|
+
### Excluding directories
|
188
|
+
|
189
|
+
# Directories below will not be scanned at all
|
177
190
|
exclude_paths:
|
178
|
-
-
|
179
|
-
-
|
191
|
+
- lib/legacy
|
192
|
+
- lib/rake/legacy_tasks
|
180
193
|
```
|
181
194
|
|
182
|
-
|
195
|
+
Note you do not need a configuration file at all. If you're fine with all the [defaults](config/defaults.reek) we set you can skip this completely.
|
196
|
+
|
197
|
+
For more details please check out the [Basic Smell Options](docs/Basic-Smell-Options.md)
|
198
|
+
which are supported by every smell type. As you can see above, certain smell
|
199
|
+
types offer a configuration that goes beyond that of the basic smell options, for instance
|
200
|
+
[Data Clump](docs/Data-Clump.md).
|
201
|
+
All options that go beyond the [Basic Smell Options](docs/Basic-Smell-Options.md)
|
202
|
+
are documented in the corresponding smell type /docs page (if you want to get a quick overview over all possible
|
203
|
+
configurations you can also check out [the `config/default.reek` file in this repository](config/defaults.reek).
|
183
204
|
|
184
205
|
### Source code comments
|
185
206
|
|
@@ -0,0 +1,280 @@
|
|
1
|
+
Feature: Directory directives
|
2
|
+
In order to have a more fine-grained control over what reek reports
|
3
|
+
And to enable domain specific modes (like a Ruby on Rails mode)
|
4
|
+
As a user
|
5
|
+
I want to be able to configure reek using directory directives
|
6
|
+
|
7
|
+
Scenario: Configure multiple directories
|
8
|
+
Given a file named "web_app/config.reek" with:
|
9
|
+
"""
|
10
|
+
---
|
11
|
+
"web_app/app/controllers":
|
12
|
+
IrresponsibleModule:
|
13
|
+
enabled: false
|
14
|
+
NestedIterators:
|
15
|
+
enabled: false
|
16
|
+
"web_app/app/helpers":
|
17
|
+
IrresponsibleModule:
|
18
|
+
enabled: false
|
19
|
+
UtilityFunction:
|
20
|
+
enabled: false
|
21
|
+
"""
|
22
|
+
And a file named "web_app/app/controllers/users_controller.rb" with:
|
23
|
+
"""
|
24
|
+
class UsersController < ApplicationController
|
25
|
+
def show
|
26
|
+
respond_with do |format|
|
27
|
+
format.json { @user.to_custom_json }
|
28
|
+
format.xlm { @user.to_fancy_xml }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
"""
|
33
|
+
And a file named "web_app/app/helpers/application_helper.rb" with:
|
34
|
+
"""
|
35
|
+
module ApplicationHelper
|
36
|
+
def current_year
|
37
|
+
Time.zone.now.year
|
38
|
+
end
|
39
|
+
end
|
40
|
+
"""
|
41
|
+
And a file named "web_app/app/models/user.rb" with:
|
42
|
+
"""
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
def logged_in_with_role(r)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
"""
|
49
|
+
When I run `reek -c web_app/config.reek web_app/`
|
50
|
+
Then it reports:
|
51
|
+
"""
|
52
|
+
web_app/app/models/user.rb -- 2 warnings:
|
53
|
+
[1]:User has no descriptive comment (IrresponsibleModule)
|
54
|
+
[2]:User#logged_in_with_role has unused parameter 'r' (UnusedParameters)
|
55
|
+
2 total warnings
|
56
|
+
"""
|
57
|
+
|
58
|
+
Scenario: Ignore trailing slashes
|
59
|
+
Given a file named "web_app/config.reek" with:
|
60
|
+
"""
|
61
|
+
---
|
62
|
+
"controllers/":
|
63
|
+
IrresponsibleModule:
|
64
|
+
enabled: false
|
65
|
+
"""
|
66
|
+
And a file named "controllers/users_controller.rb" with:
|
67
|
+
"""
|
68
|
+
class UsersController
|
69
|
+
def show
|
70
|
+
end
|
71
|
+
end
|
72
|
+
"""
|
73
|
+
When I run `reek -c web_app/config.reek controllers`
|
74
|
+
Then it reports nothing
|
75
|
+
|
76
|
+
Scenario: Partially mask smells in different directories
|
77
|
+
Given a file named "web_app/config.reek" with:
|
78
|
+
"""
|
79
|
+
---
|
80
|
+
"web_app/app/controllers":
|
81
|
+
IrresponsibleModule:
|
82
|
+
enabled: true
|
83
|
+
"web_app/app/helpers":
|
84
|
+
IrresponsibleModule:
|
85
|
+
enabled: false
|
86
|
+
UtilityFunction:
|
87
|
+
enabled: false
|
88
|
+
"web_app/app/models":
|
89
|
+
IrresponsibleModule:
|
90
|
+
enabled: false
|
91
|
+
"""
|
92
|
+
And a file named "web_app/app/controllers/users_controller.rb" with:
|
93
|
+
"""
|
94
|
+
class UsersController < ApplicationController
|
95
|
+
def show
|
96
|
+
respond_with do |format|
|
97
|
+
format.json { @user.to_custom_json }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
"""
|
102
|
+
And a file named "web_app/app/helpers/application_helper.rb" with:
|
103
|
+
"""
|
104
|
+
module ApplicationHelper
|
105
|
+
def current_year
|
106
|
+
Time.zone.now.year
|
107
|
+
end
|
108
|
+
end
|
109
|
+
"""
|
110
|
+
And a file named "web_app/app/models/user.rb" with:
|
111
|
+
"""
|
112
|
+
class User < ActiveRecord::Base
|
113
|
+
def logged_in_with_role(r)
|
114
|
+
true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
"""
|
118
|
+
When I run `reek -c web_app/config.reek web_app/`
|
119
|
+
Then it reports:
|
120
|
+
"""
|
121
|
+
web_app/app/controllers/users_controller.rb -- 2 warnings:
|
122
|
+
[1]:UsersController has no descriptive comment (IrresponsibleModule)
|
123
|
+
[4]:UsersController#show contains iterators nested 2 deep (NestedIterators)
|
124
|
+
web_app/app/models/user.rb -- 1 warning:
|
125
|
+
[2]:User#logged_in_with_role has unused parameter 'r' (UnusedParameters)
|
126
|
+
3 total warnings
|
127
|
+
"""
|
128
|
+
|
129
|
+
Scenario: Use the default directive if there is no directory directive
|
130
|
+
Given a file named "config.reek" with:
|
131
|
+
"""
|
132
|
+
---
|
133
|
+
"web_app/app/controllers":
|
134
|
+
IrresponsibleModule:
|
135
|
+
enabled: true
|
136
|
+
NestedIterators:
|
137
|
+
enabled: false
|
138
|
+
IrresponsibleModule:
|
139
|
+
enabled: false
|
140
|
+
NestedIterators:
|
141
|
+
enabled: true
|
142
|
+
"""
|
143
|
+
And a file named "web_app/app/controllers/users_controller.rb" with:
|
144
|
+
"""
|
145
|
+
class UsersController < ApplicationController
|
146
|
+
def show
|
147
|
+
respond_with do |format|
|
148
|
+
format.json { @user.to_custom_json }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
"""
|
153
|
+
And a file named "other/projects_controller.rb" with:
|
154
|
+
"""
|
155
|
+
class ProjectController < ApplicationController
|
156
|
+
def show
|
157
|
+
respond_with do |format|
|
158
|
+
format.json { @project.to_custom_json }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
"""
|
163
|
+
When I run `reek -c config.reek other/ web_app/`
|
164
|
+
Then it reports:
|
165
|
+
"""
|
166
|
+
other/projects_controller.rb -- 1 warning:
|
167
|
+
[4]:ProjectController#show contains iterators nested 2 deep (NestedIterators)
|
168
|
+
web_app/app/controllers/users_controller.rb -- 1 warning:
|
169
|
+
[1]:UsersController has no descriptive comment (IrresponsibleModule)
|
170
|
+
2 total warnings
|
171
|
+
"""
|
172
|
+
|
173
|
+
Scenario: Abort on non-existent directory
|
174
|
+
Given a file named "config.reek" with:
|
175
|
+
"""
|
176
|
+
---
|
177
|
+
"does/not/exist":
|
178
|
+
NestedIterators:
|
179
|
+
enabled: false
|
180
|
+
"""
|
181
|
+
When I run `reek -c config.reek lib/`
|
182
|
+
Then the exit status indicates an error
|
183
|
+
And stderr reports:
|
184
|
+
"""
|
185
|
+
Configuration error: Directory `does/not/exist` does not exist
|
186
|
+
|
187
|
+
"""
|
188
|
+
|
189
|
+
Scenario: Abort on non-existent smell type in directory directive
|
190
|
+
Given a file named "config.reek" with:
|
191
|
+
"""
|
192
|
+
---
|
193
|
+
"dummy_directory":
|
194
|
+
# Typo: Should be NestedIterators
|
195
|
+
IteratorsNested:
|
196
|
+
enabled: false
|
197
|
+
"""
|
198
|
+
And a file named "dummy_directory/dummy.rb" with:
|
199
|
+
"""
|
200
|
+
class Dummy
|
201
|
+
end
|
202
|
+
"""
|
203
|
+
|
204
|
+
When I run `reek -c config.reek dummy_directory/`
|
205
|
+
Then the exit status indicates an error
|
206
|
+
And stderr reports:
|
207
|
+
"""
|
208
|
+
You are trying to configure smell type IteratorsNested but we can't find one with that name.
|
209
|
+
Please make sure you spelled it right (see 'config/defaults.reek' in the reek
|
210
|
+
repository for a list of all available smell types.
|
211
|
+
|
212
|
+
"""
|
213
|
+
|
214
|
+
Scenario: Abort on file as directory directive
|
215
|
+
Given a file named "config.reek" with:
|
216
|
+
"""
|
217
|
+
---
|
218
|
+
"dummy_directory/dummy.rb":
|
219
|
+
NestedIterators:
|
220
|
+
enabled: false
|
221
|
+
"""
|
222
|
+
And a file named "dummy_directory/dummy.rb" with:
|
223
|
+
"""
|
224
|
+
class Dummy
|
225
|
+
end
|
226
|
+
"""
|
227
|
+
When I run `reek -c config.reek dummy_directory/`
|
228
|
+
Then the exit status indicates an error
|
229
|
+
And stderr reports:
|
230
|
+
"""
|
231
|
+
Configuration error: `dummy_directory/dummy.rb` is supposed to be a directory but is a file
|
232
|
+
|
233
|
+
"""
|
234
|
+
|
235
|
+
Scenario: In case of overlapping configurations, pick the most appropriate one
|
236
|
+
Given a file named "config.reek" with:
|
237
|
+
"""
|
238
|
+
---
|
239
|
+
"foo/bar/baz":
|
240
|
+
IrresponsibleModule:
|
241
|
+
enabled: false
|
242
|
+
NestedIterators:
|
243
|
+
enabled: true
|
244
|
+
"foo/bar":
|
245
|
+
IrresponsibleModule:
|
246
|
+
enabled: true
|
247
|
+
NestedIterators:
|
248
|
+
enabled: false
|
249
|
+
"""
|
250
|
+
And a file named "foo/bar/baz/klass.rb" with:
|
251
|
+
"""
|
252
|
+
class Klass
|
253
|
+
def meth
|
254
|
+
respond_to do
|
255
|
+
answer_to do
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
"""
|
261
|
+
And a file named "foo/bar/klazz.rb" with:
|
262
|
+
"""
|
263
|
+
class Klazz
|
264
|
+
def meth
|
265
|
+
respond_to do
|
266
|
+
answer_to do
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
"""
|
272
|
+
When I run `reek -c config.reek foo/`
|
273
|
+
Then it reports:
|
274
|
+
"""
|
275
|
+
foo/bar/baz/klass.rb -- 1 warning:
|
276
|
+
[4]:Klass#meth contains iterators nested 2 deep (NestedIterators)
|
277
|
+
foo/bar/klazz.rb -- 1 warning:
|
278
|
+
[1]:Klazz has no descriptive comment (IrresponsibleModule)
|
279
|
+
2 total warnings
|
280
|
+
"""
|
@@ -3,20 +3,6 @@ Feature: Masking smells using config files
|
|
3
3
|
As a developer
|
4
4
|
I want to mask some smells using config files
|
5
5
|
|
6
|
-
Scenario: empty config file is ignored
|
7
|
-
Given a smelly file called 'smelly.rb'
|
8
|
-
And an empty configuration file called 'empty.reek'
|
9
|
-
When I run reek -c empty.reek smelly.rb
|
10
|
-
Then it reports the error 'Warning: Invalid configuration file "empty.reek" -- Empty file'
|
11
|
-
And the exit status indicates smells
|
12
|
-
And it reports:
|
13
|
-
"""
|
14
|
-
smelly.rb -- 3 warnings:
|
15
|
-
[4, 5]:Smelly#m calls @foo.bar 2 times (DuplicateMethodCall)
|
16
|
-
[4, 5]:Smelly#m calls puts(@foo.bar) 2 times (DuplicateMethodCall)
|
17
|
-
[3]:Smelly#m has the name 'm' (UncommunicativeMethodName)
|
18
|
-
"""
|
19
|
-
|
20
6
|
Scenario: corrupt config file prevents normal output
|
21
7
|
Given a smelly file called 'smelly.rb'
|
22
8
|
And a corrupt configuration file called 'corrupt.reek'
|
@@ -26,7 +26,7 @@ Given(/^a smelly file with inline masking called 'inline.rb'$/) do
|
|
26
26
|
end
|
27
27
|
|
28
28
|
Given(/^the "(.*?)" sample file exists$/) do |file_name|
|
29
|
-
full_path =
|
29
|
+
full_path = Pathname.new("#{__dir__}/../../spec/samples/#{file_name}")
|
30
30
|
in_current_directory { FileUtils.cp full_path, file_name }
|
31
31
|
end
|
32
32
|
|
@@ -135,7 +135,7 @@ When(/^I run "reek (.*?)" in the subdirectory$/) do |args|
|
|
135
135
|
end
|
136
136
|
|
137
137
|
Given(/^a masking configuration file in the HOME directory$/) do
|
138
|
-
set_env
|
138
|
+
set_env 'HOME', Pathname.new("#{current_directory}/home").expand_path.to_s
|
139
139
|
write_file('home/config.reek', <<-EOS.strip_heredoc)
|
140
140
|
---
|
141
141
|
DuplicateMethodCall:
|
@@ -169,9 +169,9 @@ module Reek
|
|
169
169
|
|
170
170
|
# Utility methods for :send nodes.
|
171
171
|
module SendNode
|
172
|
-
def receiver
|
173
|
-
def method_name()
|
174
|
-
def args()
|
172
|
+
def receiver; children.first; end
|
173
|
+
def method_name() children[1]; end
|
174
|
+
def args() children[2..-1] end
|
175
175
|
|
176
176
|
def participants
|
177
177
|
([receiver] + args).compact
|
@@ -180,8 +180,14 @@ module Reek
|
|
180
180
|
def arg_names
|
181
181
|
args.map { |arg| arg[1] }
|
182
182
|
end
|
183
|
+
|
184
|
+
def object_creation_call?
|
185
|
+
method_name == :new
|
186
|
+
end
|
183
187
|
end
|
184
188
|
|
189
|
+
Op_AsgnNode = SendNode
|
190
|
+
|
185
191
|
# Base module for utility methods for nodes representing variables.
|
186
192
|
module VariableBase
|
187
193
|
def name() self[1] end
|
@@ -252,43 +258,7 @@ module Reek
|
|
252
258
|
# Checking if a method is a singleton method.
|
253
259
|
module SingletonMethod
|
254
260
|
def singleton_method?
|
255
|
-
singleton_method_via_class_self_notation?
|
256
|
-
singleton_method_via_module_function?
|
257
|
-
end
|
258
|
-
|
259
|
-
# Ruby allows us to make a method a singleton_method after having defined the
|
260
|
-
# method via `module_function`.
|
261
|
-
#
|
262
|
-
# To check if we used "module_function" for a method we need to check on the parent
|
263
|
-
# level of the method in question if there is a call to "module_function".
|
264
|
-
# Given someting like
|
265
|
-
# class C
|
266
|
-
# def m; 3 + 7; end
|
267
|
-
# module_function :m
|
268
|
-
# end
|
269
|
-
# the AST (truncated, without the class definition) would look like this:
|
270
|
-
# (def :m
|
271
|
-
# (args)
|
272
|
-
# (send
|
273
|
-
# (int 3) :+
|
274
|
-
# (int 7)))
|
275
|
-
# (send nil :module_function
|
276
|
-
# (sym :m))))
|
277
|
-
# With multiple arguments to module_function this gets more complicated:
|
278
|
-
# (send nil :module_function
|
279
|
-
# (sym :m1)
|
280
|
-
# (send nil :m2))
|
281
|
-
#
|
282
|
-
# @return [Boolean]
|
283
|
-
def singleton_method_via_module_function?
|
284
|
-
return unless parent
|
285
|
-
module_function_calls(parent).any? do |module_function_call|
|
286
|
-
method_name_nodes = module_function_call.children[2..-1]
|
287
|
-
method_names = method_name_nodes.map do |node|
|
288
|
-
node.children.last
|
289
|
-
end
|
290
|
-
method_names.include?(name)
|
291
|
-
end
|
261
|
+
singleton_method_via_class_self_notation?
|
292
262
|
end
|
293
263
|
|
294
264
|
# Ruby allows us to make a method a singleton_method using the
|
@@ -301,16 +271,6 @@ module Reek
|
|
301
271
|
return unless parent
|
302
272
|
parent.type == :sclass
|
303
273
|
end
|
304
|
-
|
305
|
-
private
|
306
|
-
|
307
|
-
def module_function_calls(parent)
|
308
|
-
parent.children.select do |elem|
|
309
|
-
elem.is_a?(::Parser::AST::Node) &&
|
310
|
-
elem.type == :send &&
|
311
|
-
elem.children[1] == :module_function
|
312
|
-
end
|
313
|
-
end
|
314
274
|
end
|
315
275
|
|
316
276
|
# Utility methods for :def nodes.
|
@@ -346,6 +306,7 @@ module Reek
|
|
346
306
|
end
|
347
307
|
|
348
308
|
include MethodNodeBase
|
309
|
+
|
349
310
|
def full_name(outer)
|
350
311
|
prefix = outer == '' ? '' : "#{outer}#"
|
351
312
|
"#{prefix}#{SexpFormatter.format(receiver)}.#{name}"
|
@@ -389,21 +350,45 @@ module Reek
|
|
389
350
|
end
|
390
351
|
end
|
391
352
|
|
392
|
-
#
|
393
|
-
module
|
394
|
-
|
353
|
+
# Base module for utility methods for module nodes.
|
354
|
+
module ModuleNodeBase
|
355
|
+
# The full name of the module or class, including the name of any
|
356
|
+
# module or class it is nested inside of.
|
357
|
+
#
|
358
|
+
# For example, given code like this:
|
359
|
+
#
|
360
|
+
# module Foo
|
361
|
+
# class Bar::Baz
|
362
|
+
# end
|
363
|
+
# end
|
364
|
+
#
|
365
|
+
# The full name for the inner class will be 'Foo::Bar::Baz'. To return
|
366
|
+
# the correct name, the name of the outer context has to be passed into this method.
|
367
|
+
#
|
368
|
+
# @param outer [String] full name of the wrapping module or class
|
369
|
+
# @return the module's full name
|
370
|
+
def full_name(outer)
|
371
|
+
prefix = outer == '' ? '' : "#{outer}::"
|
372
|
+
"#{prefix}#{name}"
|
373
|
+
end
|
395
374
|
|
375
|
+
# The final section of the module or class name. For example, for a
|
376
|
+
# module with name 'Foo::Bar' this will return 'Bar'; for a module with
|
377
|
+
# name 'Foo' this will return 'Foo'.
|
378
|
+
#
|
379
|
+
# @return [String] the final section of the name
|
396
380
|
def simple_name
|
397
|
-
name.
|
381
|
+
name.split('::').last
|
398
382
|
end
|
383
|
+
end
|
399
384
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
end
|
385
|
+
# Utility methods for :module nodes.
|
386
|
+
module ModuleNode
|
387
|
+
include ModuleNodeBase
|
404
388
|
|
405
|
-
|
406
|
-
|
389
|
+
# @return [String] name as given in the module statement
|
390
|
+
def name
|
391
|
+
SexpFormatter.format(children.first)
|
407
392
|
end
|
408
393
|
end
|
409
394
|
|
@@ -413,6 +398,33 @@ module Reek
|
|
413
398
|
def superclass() self[2] end
|
414
399
|
end
|
415
400
|
|
401
|
+
# Utility methods for :casgn nodes.
|
402
|
+
module CasgnNode
|
403
|
+
include ModuleNodeBase
|
404
|
+
|
405
|
+
MODULE_DEFINERS = [:Class, :Struct]
|
406
|
+
|
407
|
+
def defines_module?
|
408
|
+
call = case value.type
|
409
|
+
when :block
|
410
|
+
value.call
|
411
|
+
when :send
|
412
|
+
value
|
413
|
+
end
|
414
|
+
call &&
|
415
|
+
call.object_creation_call? &&
|
416
|
+
MODULE_DEFINERS.include?(call.receiver.simple_name)
|
417
|
+
end
|
418
|
+
|
419
|
+
def name
|
420
|
+
SexpFormatter.format(children[1])
|
421
|
+
end
|
422
|
+
|
423
|
+
def value
|
424
|
+
children.last
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
416
428
|
# Utility methods for :yield nodes.
|
417
429
|
module YieldNode
|
418
430
|
def args() self[1..-1] end
|