reek 3.5.0 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +6 -0
- data/CHANGELOG.md +19 -12
- data/CONTRIBUTING.md +7 -7
- data/README.md +91 -28
- data/ataru_setup.rb +13 -0
- data/{config/defaults.reek → defaults.reek} +0 -0
- data/docs/API.md +32 -31
- data/docs/Attribute.md +1 -1
- data/docs/Basic-Smell-Options.md +2 -1
- data/docs/Boolean-Parameter.md +1 -1
- data/docs/Class-Variable.md +2 -2
- data/docs/Command-Line-Options.md +2 -2
- data/docs/Control-Couple.md +3 -3
- data/docs/Control-Parameter.md +2 -2
- data/docs/Data-Clump.md +2 -2
- data/docs/Duplicate-Method-Call.md +4 -4
- data/docs/Feature-Envy.md +2 -2
- data/docs/How-reek-works-internally.md +2 -2
- data/docs/Irresponsible-Module.md +2 -2
- data/docs/Large-Class.md +2 -2
- data/docs/Long-Parameter-List.md +1 -1
- data/docs/Long-Yield-List.md +2 -2
- data/docs/Module-Initialize.md +3 -3
- data/docs/Nested-Iterators.md +1 -1
- data/docs/Nil-Check.md +2 -2
- data/docs/Prima-Donna-Method.md +4 -4
- data/docs/RSpec-matchers.md +7 -7
- data/docs/Rake-Task.md +2 -2
- data/docs/Reek-Driven-Development.md +4 -4
- data/docs/Repeated-Conditional.md +2 -2
- data/docs/Simulated-Polymorphism.md +2 -2
- data/docs/Smell-Suppression.md +3 -3
- data/docs/Too-Many-Instance-Variables.md +4 -4
- data/docs/Too-Many-Methods.md +5 -5
- data/docs/Too-Many-Statements.md +2 -2
- data/docs/Uncommunicative-Method-Name.md +4 -4
- data/docs/Uncommunicative-Module-Name.md +4 -4
- data/docs/Uncommunicative-Name.md +2 -2
- data/docs/Uncommunicative-Parameter-Name.md +4 -4
- data/docs/Uncommunicative-Variable-Name.md +3 -3
- data/docs/Unused-Parameters.md +2 -2
- data/docs/Utility-Function.md +4 -4
- data/docs/Versioning-Policy.md +2 -2
- data/features/command_line_interface/options.feature +1 -1
- data/features/configuration_files/directory_specific_directives.feature +4 -4
- data/features/configuration_loading.feature +10 -24
- data/features/programmatic_access.feature +3 -3
- data/features/reports/json.feature +1 -1
- data/features/reports/reports.feature +2 -2
- data/features/reports/yaml.feature +1 -1
- data/lib/reek/ast/sexp_extensions.rb +17 -498
- data/lib/reek/ast/sexp_extensions/arguments.rb +101 -0
- data/lib/reek/ast/sexp_extensions/attribute_assignments.rb +12 -0
- data/lib/reek/ast/sexp_extensions/block.rb +36 -0
- data/lib/reek/ast/sexp_extensions/case.rb +20 -0
- data/lib/reek/ast/sexp_extensions/constant.rb +12 -0
- data/lib/reek/ast/sexp_extensions/if.rb +16 -0
- data/lib/reek/ast/sexp_extensions/literal.rb +12 -0
- data/lib/reek/ast/sexp_extensions/logical_operators.rb +26 -0
- data/lib/reek/ast/sexp_extensions/methods.rb +114 -0
- data/lib/reek/ast/sexp_extensions/module.rb +85 -0
- data/lib/reek/ast/sexp_extensions/nested_assignables.rb +23 -0
- data/lib/reek/ast/sexp_extensions/send.rb +60 -0
- data/lib/reek/ast/sexp_extensions/super.rb +14 -0
- data/lib/reek/ast/sexp_extensions/symbols.rb +16 -0
- data/lib/reek/ast/sexp_extensions/variables.rb +38 -0
- data/lib/reek/ast/sexp_extensions/when.rb +16 -0
- data/lib/reek/ast/sexp_extensions/yield.rb +16 -0
- data/lib/reek/cli/application.rb +0 -4
- data/lib/reek/cli/options.rb +2 -4
- data/lib/reek/configuration/app_configuration.rb +37 -9
- data/lib/reek/configuration/configuration_file_finder.rb +8 -5
- data/lib/reek/configuration/directory_directives.rb +2 -2
- data/lib/reek/context/attribute_context.rb +21 -0
- data/lib/reek/context/code_context.rb +5 -9
- data/lib/reek/rake/task.rb +5 -5
- data/lib/reek/smells/nested_iterators.rb +73 -26
- data/lib/reek/smells/smell_warning.rb +1 -38
- data/lib/reek/source/source_code.rb +1 -1
- data/lib/reek/spec.rb +2 -2
- data/lib/reek/spec/should_reek_of.rb +8 -3
- data/lib/reek/spec/should_reek_only_of.rb +2 -1
- data/lib/reek/spec/smell_matcher.rb +59 -0
- data/lib/reek/tree_walker.rb +4 -3
- data/lib/reek/version.rb +1 -1
- data/logo/reek.bw.png +0 -0
- data/logo/reek.bw.svg +77 -0
- data/logo/reek.png +0 -0
- data/logo/reek.svg +621 -0
- data/logo/reek.text.png +0 -0
- data/logo/reek.text.svg +628 -0
- data/reek.gemspec +1 -1
- data/spec/factories/factories.rb +0 -1
- data/spec/reek/ast/sexp_extensions_spec.rb +0 -7
- data/spec/reek/cli/options_spec.rb +1 -2
- data/spec/reek/configuration/app_configuration_spec.rb +30 -14
- data/spec/reek/configuration/configuration_file_finder_spec.rb +23 -5
- data/spec/reek/smells/attribute_spec.rb +11 -2
- data/spec/reek/smells/boolean_parameter_spec.rb +14 -12
- data/spec/reek/smells/class_variable_spec.rb +18 -15
- data/spec/reek/smells/control_parameter_spec.rb +1 -2
- data/spec/reek/smells/duplicate_method_call_spec.rb +1 -2
- data/spec/reek/smells/feature_envy_spec.rb +8 -29
- data/spec/reek/smells/irresponsible_module_spec.rb +1 -2
- data/spec/reek/smells/long_parameter_list_spec.rb +1 -2
- data/spec/reek/smells/long_yield_list_spec.rb +1 -2
- data/spec/reek/smells/nested_iterators_spec.rb +1 -2
- data/spec/reek/smells/nil_check_spec.rb +1 -1
- data/spec/reek/smells/prima_donna_method_spec.rb +1 -1
- data/spec/reek/smells/repeated_conditional_spec.rb +1 -2
- data/spec/reek/smells/smell_detector_shared.rb +1 -1
- data/spec/reek/smells/smell_warning_spec.rb +2 -4
- data/spec/reek/smells/too_many_instance_variables_spec.rb +20 -19
- data/spec/reek/smells/too_many_statements_spec.rb +1 -1
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -4
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +1 -4
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +1 -4
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
- data/spec/reek/smells/utility_function_spec.rb +1 -3
- data/spec/reek/spec/should_reek_of_spec.rb +5 -5
- data/spec/reek/spec/smell_matcher_spec.rb +92 -0
- data/tasks/configuration.rake +15 -0
- metadata +37 -5
- data/config/cucumber.yml +0 -3
- data/tasks/develop.rake +0 -21
@@ -24,19 +24,19 @@ class TooManyInstanceVariables
|
|
24
24
|
end
|
25
25
|
```
|
26
26
|
|
27
|
-
|
27
|
+
Reek would emit the following warning:
|
28
28
|
|
29
29
|
```
|
30
30
|
test.rb -- 5 warnings:
|
31
31
|
[1]:TooManyInstanceVariables has at least 4 instance variables (TooManyInstanceVariables)
|
32
32
|
```
|
33
|
-
## Current Support in
|
33
|
+
## Current Support in Reek
|
34
34
|
|
35
|
-
|
35
|
+
Reek only counts the instance variables you use explicitly like in the example above. Class macros like `attr_accessor` are disregarded.
|
36
36
|
|
37
37
|
## Configuration
|
38
38
|
|
39
|
-
|
39
|
+
Reek's `Too Many Instance Variables` detector offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
40
40
|
|
41
41
|
| Option | Value | Effect |
|
42
42
|
| ---------------|-------------|---------|
|
data/docs/Too-Many-Methods.md
CHANGED
@@ -22,15 +22,15 @@ class TooManyMethods
|
|
22
22
|
end
|
23
23
|
```
|
24
24
|
|
25
|
-
|
25
|
+
Reek would emit the following warning:
|
26
26
|
|
27
27
|
```
|
28
28
|
test.rb -- 1 warning:
|
29
29
|
[1]:TooManyMethods has at least 4 methods (TooManyMethods)
|
30
30
|
```
|
31
|
-
## Current Support in
|
31
|
+
## Current Support in Reek
|
32
32
|
|
33
|
-
|
33
|
+
Reek counts all the methods it can find in a `class` - instance *and* class methods. So given `max_methods` from above is 4, this:
|
34
34
|
|
35
35
|
```Ruby
|
36
36
|
class TooManyMethods
|
@@ -44,11 +44,11 @@ class TooManyMethods
|
|
44
44
|
end
|
45
45
|
```
|
46
46
|
|
47
|
-
would cause
|
47
|
+
would cause Reek to emit the same warning as in the example above.
|
48
48
|
|
49
49
|
## Configuration
|
50
50
|
|
51
|
-
|
51
|
+
Reek's `Too Many Methods` detector offers the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
52
52
|
|
53
53
|
| Option | Value | Effect |
|
54
54
|
| ---------------|-------------|---------|
|
data/docs/Too-Many-Statements.md
CHANGED
@@ -6,7 +6,7 @@ A method with `Too Many Statements` is any method that has a large number of lin
|
|
6
6
|
|
7
7
|
## Current Support in Reek
|
8
8
|
|
9
|
-
`Too Many Statements` warns about any method that has more than 5 statements.
|
9
|
+
`Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.
|
10
10
|
|
11
11
|
So the following method would score +6 in Reek's statement-counting algorithm:
|
12
12
|
|
@@ -30,7 +30,7 @@ end
|
|
30
30
|
|
31
31
|
## Configuration
|
32
32
|
|
33
|
-
|
33
|
+
Reek's `Too Many Statements` detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
34
34
|
|
35
35
|
| Option | Value | Effect |
|
36
36
|
| ---------------|-------------|---------|
|
@@ -6,7 +6,7 @@ An `Uncommunicative Method Name` is a method name that doesn't communicate its i
|
|
6
6
|
|
7
7
|
Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
|
8
8
|
|
9
|
-
## Current Support in
|
9
|
+
## Current Support in Reek
|
10
10
|
|
11
11
|
`Uncommunicative Method Name` checks for:
|
12
12
|
|
@@ -16,9 +16,9 @@ Poor names make it hard for the reader to build a mental picture of what's going
|
|
16
16
|
|
17
17
|
## Configuration
|
18
18
|
|
19
|
-
|
19
|
+
Reek's Uncommunicative Method Name detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
20
20
|
|
21
21
|
| Option | Value | Effect |
|
22
22
|
| ---------------|-------------|---------|
|
23
|
-
| `reject` | array of regular expressions | The set of regular expressions that
|
24
|
-
| `accept` | array of strings or regular expressions | Name that will be accepted (not reported) even if they match one of the `reject` expressions. |
|
23
|
+
| `reject` | array of regular expressions | The set of regular expressions that Reek uses to check for bad names. Defaults to `[/^[a-z]$/, /[0-9]$/, /[A-Z]/]`. |
|
24
|
+
| `accept` | array of strings or regular expressions | Name that will be accepted (not reported) even if they match one of the `reject` expressions. |
|
@@ -6,7 +6,7 @@ An `Uncommunicative Module Name` is a module name that doesn't communicate its i
|
|
6
6
|
|
7
7
|
Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
|
8
8
|
|
9
|
-
## Current Support in
|
9
|
+
## Current Support in Reek
|
10
10
|
|
11
11
|
`Uncommunicative Module Name` checks for:
|
12
12
|
|
@@ -15,9 +15,9 @@ Poor names make it hard for the reader to build a mental picture of what's going
|
|
15
15
|
|
16
16
|
## Configuration
|
17
17
|
|
18
|
-
|
18
|
+
Reek's `Uncommunicative Module Name` detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
19
19
|
|
20
20
|
| Option | Value | Effect |
|
21
21
|
| ---------------|-------------|---------|
|
22
|
-
| `reject` | array of regular expressions | The set of regular expressions that
|
23
|
-
| `accept` | array of names as strings | List of names that will be accepted (not reported) even if they match one of the `reject` expressions. Empty by default.|
|
22
|
+
| `reject` | array of regular expressions | The set of regular expressions that Reek uses to check for bad names. Defaults to `[/^.$/, /[0-9]$/]`. |
|
23
|
+
| `accept` | array of names as strings | List of names that will be accepted (not reported) even if they match one of the `reject` expressions. Empty by default.|
|
@@ -8,9 +8,9 @@ Poor names make it hard for the reader to build a mental picture of what's going
|
|
8
8
|
|
9
9
|
## Current Support in Reek
|
10
10
|
|
11
|
-
|
11
|
+
Reek offers four related checks:
|
12
12
|
|
13
13
|
* [Uncommunicative Method Name](Uncommunicative-Method-Name.md)
|
14
14
|
* [Uncommunicative Module Name](Uncommunicative-Module-Name.md)
|
15
15
|
* [Uncommunicative Parameter Name](Uncommunicative-Parameter-Name.md)
|
16
|
-
* [Uncommunicative Variable Name](Uncommunicative-Variable-Name.md)
|
16
|
+
* [Uncommunicative Variable Name](Uncommunicative-Variable-Name.md)
|
@@ -6,7 +6,7 @@ An `Uncommunicative Parameter Name` is a parameter name that doesn't communicate
|
|
6
6
|
|
7
7
|
Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
|
8
8
|
|
9
|
-
## Current Support in
|
9
|
+
## Current Support in Reek
|
10
10
|
|
11
11
|
`Uncommunicative Parameter Name` checks for:
|
12
12
|
|
@@ -16,9 +16,9 @@ Poor names make it hard for the reader to build a mental picture of what's going
|
|
16
16
|
|
17
17
|
## Configuration
|
18
18
|
|
19
|
-
|
19
|
+
Reek's Uncommunicative Parameter Name detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
20
20
|
|
21
21
|
| Option | Value | Effect |
|
22
22
|
| ---------------|-------------|---------|
|
23
|
-
| `reject` | array of regular expressions | The set of regular expressions that
|
24
|
-
| `accept` | array of strings or regular expressions | Name that will be accepted (not reported) even if they match one of the `reject` expressions. |
|
23
|
+
| `reject` | array of regular expressions | The set of regular expressions that Reek uses to check for bad names. Defaults to `[/^.$/, /[0-9]$/, /[A-Z]/]@. |
|
24
|
+
| `accept` | array of strings or regular expressions | Name that will be accepted (not reported) even if they match one of the `reject` expressions. |
|
@@ -16,9 +16,9 @@ Poor names make it hard for the reader to build a mental picture of what's going
|
|
16
16
|
|
17
17
|
## Configuration
|
18
18
|
|
19
|
-
|
19
|
+
Reek's `Uncommunicative Variable Name` detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
20
20
|
|
21
21
|
| Option | Value | Effect |
|
22
22
|
| ---------------|-------------|---------|
|
23
|
-
| `reject` | array of regular expressions | The set of regular expressions that
|
24
|
-
| `accept` | array of strings or regular expressions | Name that will be accepted (not reported) even if they match one of the `reject` expressions. Defaults to @['_']@.|
|
23
|
+
| `reject` | array of regular expressions | The set of regular expressions that Reek uses to check for bad names. Defaults to `[/^.$/, /[0-9]$/, /[A-Z]/]`. |
|
24
|
+
| `accept` | array of strings or regular expressions | Name that will be accepted (not reported) even if they match one of the `reject` expressions. Defaults to @['_']@.|
|
data/docs/Unused-Parameters.md
CHANGED
@@ -16,7 +16,7 @@ class Klass
|
|
16
16
|
end
|
17
17
|
```
|
18
18
|
|
19
|
-
|
19
|
+
Reek would emit the following warning:
|
20
20
|
|
21
21
|
```
|
22
22
|
[2]:Klass#unused_parameters has unused parameter 'z' (UnusedParameters)
|
@@ -24,4 +24,4 @@ end
|
|
24
24
|
|
25
25
|
## Configuration
|
26
26
|
|
27
|
-
`Unused Parameter` offers the [Basic Smell Options](Basic-Smell-Options.md).
|
27
|
+
`Unused Parameter` offers the [Basic Smell Options](Basic-Smell-Options.md).
|
data/docs/Utility-Function.md
CHANGED
@@ -18,14 +18,14 @@ class UtilityFunction
|
|
18
18
|
end
|
19
19
|
```
|
20
20
|
|
21
|
-
|
21
|
+
Reek would report:
|
22
22
|
|
23
23
|
```
|
24
24
|
test.rb -- 2 warnings:
|
25
25
|
[2]:UtilityFunction#showcase doesn't depend on instance state (UtilityFunction)
|
26
26
|
```
|
27
27
|
|
28
|
-
## Current Support in
|
28
|
+
## Current Support in Reek
|
29
29
|
|
30
30
|
_Utility Function_ will warn about any method that:
|
31
31
|
|
@@ -41,7 +41,7 @@ _[Feature Envy](Feature-Envy.md)_ is only triggered if there are some references
|
|
41
41
|
|
42
42
|
## Configuration
|
43
43
|
|
44
|
-
|
44
|
+
Reek's _Utility Function_ detector supports the [Basic Smell Options](Basic-Smell-Options.md), plus:
|
45
45
|
|
46
46
|
| Option | Value | Effect |
|
47
47
|
| ---------------|-------------|---------|
|
@@ -53,4 +53,4 @@ A sample configuration file would look like this:
|
|
53
53
|
---
|
54
54
|
UtilityFunction:
|
55
55
|
public_methods_only: true
|
56
|
-
|
56
|
+
```
|
data/docs/Versioning-Policy.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Versioning Policy
|
2
2
|
|
3
|
-
* CLI interface: Adding options is a non-breaking change, and would warrant an update of the minor version. Removing options is a breaking change and requires a major version update (we did this going to
|
4
|
-
* API: We haven't really defined a 'public' API for using Reek programmatically, and we've only just started testing it. So, this is basically a blank slate at the moment. We will work on this as a part of the
|
3
|
+
* CLI interface: Adding options is a non-breaking change, and would warrant an update of the minor version. Removing options is a breaking change and requires a major version update (we did this going to Reek 2). Adding a report format probably also warrents a minor version upgrade.
|
4
|
+
* API: We haven't really defined a 'public' API for using Reek programmatically, and we've only just started testing it. So, this is basically a blank slate at the moment. We will work on this as a part of the Reek 3 release.
|
5
5
|
* List of detected smells: Adding a smell warrants a minor release, removing a smell is a breaking change. This makes sense if you consider that the CLI allows running a single smell detector.
|
6
6
|
* Consistency of detected smells: This is very hard to guarantee. If we fix a bug in one of the detectors, some fragrant code may become smelly, or vice versa. Right now we don't bother with this.
|
7
7
|
* Smell configuration: The detectors are quite tolerant regarding configuration options that they don't recognize, so we regard any change here as only requiring a minor release.
|
@@ -1,8 +1,8 @@
|
|
1
1
|
Feature: Directory directives
|
2
|
-
In order to have a more fine-grained control over what
|
2
|
+
In order to have a more fine-grained control over what Reek reports
|
3
3
|
And to enable domain specific modes (like a Ruby on Rails mode)
|
4
4
|
As a user
|
5
|
-
I want to be able to configure
|
5
|
+
I want to be able to configure Reek using directory directives
|
6
6
|
|
7
7
|
Scenario: Configure multiple directories
|
8
8
|
Given a file named "web_app/config.reek" with:
|
@@ -206,8 +206,8 @@ Feature: Directory directives
|
|
206
206
|
And stderr reports:
|
207
207
|
"""
|
208
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 (
|
210
|
-
repository for a list of all available smell types.
|
209
|
+
Please make sure you spelled it right. (See 'defaults.reek' in the Reek
|
210
|
+
repository for a list of all available smell types.)
|
211
211
|
|
212
212
|
"""
|
213
213
|
|
@@ -1,16 +1,16 @@
|
|
1
1
|
Feature: Offer different ways how to load configuration
|
2
2
|
|
3
|
-
|
4
|
-
- Using the cli "-c" switch
|
5
|
-
- Having a
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
Reek can be configured in two ways:
|
4
|
+
- Using the cli "-c" switch to pass a configuration file on the command line.
|
5
|
+
- Having a Reek configuration file that is automatically found. Reek will
|
6
|
+
look for a file ending in .reek in the following places, in order:
|
7
|
+
- The current working directory
|
8
|
+
- The working directory's ancestor directories, traversing all the way up
|
9
|
+
to the root.
|
10
|
+
- Your HOME directory
|
11
|
+
Reek will check these in order and stop after the first file found.
|
12
12
|
|
13
|
-
Scenario:
|
13
|
+
Scenario: Default configuration
|
14
14
|
Given a smelly file called 'smelly.rb'
|
15
15
|
When I run reek smelly.rb
|
16
16
|
Then the exit status indicates smells
|
@@ -36,20 +36,6 @@ Feature: Offer different ways how to load configuration
|
|
36
36
|
Then it reports no errors
|
37
37
|
And it succeeds
|
38
38
|
|
39
|
-
Scenario: Configuration file in the parent directory of the working directory
|
40
|
-
Given a smelly file called 'smelly.rb' in a subdirectory
|
41
|
-
And a masking configuration file called 'config.reek'
|
42
|
-
When I run "reek smelly.rb" in the subdirectory
|
43
|
-
Then it reports no errors
|
44
|
-
And it succeeds
|
45
|
-
|
46
|
-
Scenario: Configuration file in the HOME directory
|
47
|
-
Given a smelly file called 'smelly.rb'
|
48
|
-
And a masking configuration file in the HOME directory
|
49
|
-
When I run reek smelly.rb
|
50
|
-
Then it reports no errors
|
51
|
-
And it succeeds
|
52
|
-
|
53
39
|
Scenario: Two opposing configuration files and we stop after the first one
|
54
40
|
Given a smelly file called 'smelly.rb' in a subdirectory
|
55
41
|
And an enabling configuration file in the subdirectory
|
@@ -1,5 +1,5 @@
|
|
1
|
-
Feature: Using
|
2
|
-
In order to use
|
1
|
+
Feature: Using Reek programmatically
|
2
|
+
In order to use Reek from inside my program
|
3
3
|
As a developer
|
4
4
|
I want to be able to use its classes
|
5
5
|
|
@@ -22,7 +22,7 @@ Feature: Using reek programmatically
|
|
22
22
|
has the name 'm'
|
23
23
|
"""
|
24
24
|
|
25
|
-
Scenario: Using
|
25
|
+
Scenario: Using Reek's built-in report classes
|
26
26
|
Given a smelly file called 'smelly.rb'
|
27
27
|
And a file named "examine.rb" with:
|
28
28
|
"""
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Feature: Correctly formatted reports
|
2
|
-
In order to get the most out of
|
2
|
+
In order to get the most out of Reek
|
3
3
|
As a developer
|
4
|
-
I want to be able to parse
|
4
|
+
I want to be able to parse Reek's output simply and consistently
|
5
5
|
|
6
6
|
Scenario Outline: two reports run together with indented smells
|
7
7
|
Given a directory called 'smelly' containing two smelly files
|
@@ -1,500 +1,19 @@
|
|
1
1
|
require_relative 'reference_collector'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def marked_unused?
|
22
|
-
plain_name.start_with?('_')
|
23
|
-
end
|
24
|
-
|
25
|
-
def plain_name
|
26
|
-
name.to_s
|
27
|
-
end
|
28
|
-
|
29
|
-
def block?
|
30
|
-
false
|
31
|
-
end
|
32
|
-
|
33
|
-
def optional_argument?
|
34
|
-
false
|
35
|
-
end
|
36
|
-
|
37
|
-
def anonymous_splat?
|
38
|
-
false
|
39
|
-
end
|
40
|
-
|
41
|
-
def components
|
42
|
-
[self]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Utility methods for :arg nodes.
|
47
|
-
module ArgNode
|
48
|
-
include ArgNodeBase
|
49
|
-
end
|
50
|
-
|
51
|
-
# Utility methods for :kwarg nodes.
|
52
|
-
module KwargNode
|
53
|
-
include ArgNodeBase
|
54
|
-
end
|
55
|
-
|
56
|
-
# Utility methods for :optarg nodes.
|
57
|
-
module OptargNode
|
58
|
-
include ArgNodeBase
|
59
|
-
|
60
|
-
def optional_argument?
|
61
|
-
true
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Utility methods for :kwoptarg nodes.
|
66
|
-
module KwoptargNode
|
67
|
-
include ArgNodeBase
|
68
|
-
|
69
|
-
def optional_argument?
|
70
|
-
true
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Utility methods for :blockarg nodes.
|
75
|
-
module BlockargNode
|
76
|
-
include ArgNodeBase
|
77
|
-
def block?
|
78
|
-
true
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Utility methods for :restarg nodes.
|
83
|
-
module RestargNode
|
84
|
-
include ArgNodeBase
|
85
|
-
def anonymous_splat?
|
86
|
-
!name
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Utility methods for :kwrestarg nodes.
|
91
|
-
module KwrestargNode
|
92
|
-
include ArgNodeBase
|
93
|
-
def anonymous_splat?
|
94
|
-
!name
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Utility methods for :shadowarg nodes.
|
99
|
-
module ShadowargNode
|
100
|
-
include ArgNodeBase
|
101
|
-
end
|
102
|
-
|
103
|
-
# Base module for utility methods for nodes that can contain argument
|
104
|
-
# nodes nested through :mlhs nodes.
|
105
|
-
module NestedAssignables
|
106
|
-
def components
|
107
|
-
children.flat_map(&:components)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Utility methods for :args nodes.
|
112
|
-
module ArgsNode
|
113
|
-
include NestedAssignables
|
114
|
-
end
|
115
|
-
|
116
|
-
# Utility methods for :mlhs nodes.
|
117
|
-
module MlhsNode
|
118
|
-
include NestedAssignables
|
119
|
-
end
|
120
|
-
|
121
|
-
# Base module for utility methods for :and and :or nodes.
|
122
|
-
module LogicOperatorBase
|
123
|
-
def condition() children.first end
|
124
|
-
|
125
|
-
def body_nodes(type, ignoring = [])
|
126
|
-
children[1].find_nodes type, ignoring
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# Utility methods for :and nodes.
|
131
|
-
module AndNode
|
132
|
-
include LogicOperatorBase
|
133
|
-
end
|
134
|
-
|
135
|
-
# Utility methods for :or nodes.
|
136
|
-
module OrNode
|
137
|
-
include LogicOperatorBase
|
138
|
-
end
|
139
|
-
|
140
|
-
# Utility methods for :attrasgn nodes.
|
141
|
-
module AttrasgnNode
|
142
|
-
def args() children[2] end
|
143
|
-
end
|
144
|
-
|
145
|
-
# Utility methods for :case nodes.
|
146
|
-
module CaseNode
|
147
|
-
def condition() children.first end
|
148
|
-
|
149
|
-
def body_nodes(type, ignoring = [])
|
150
|
-
children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
|
151
|
-
end
|
152
|
-
|
153
|
-
def else_body
|
154
|
-
children.last
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# Utility methods for :when nodes.
|
159
|
-
module WhenNode
|
160
|
-
def condition_list
|
161
|
-
children[0..-2]
|
162
|
-
end
|
163
|
-
|
164
|
-
def body
|
165
|
-
children.last
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# Utility methods for :send nodes.
|
170
|
-
module SendNode
|
171
|
-
def receiver; children.first; end
|
172
|
-
def method_name() children[1]; end
|
173
|
-
def args() children[2..-1] end
|
174
|
-
|
175
|
-
def participants
|
176
|
-
([receiver] + args).compact
|
177
|
-
end
|
178
|
-
|
179
|
-
def arg_names
|
180
|
-
args.map { |arg| arg.children.first }
|
181
|
-
end
|
182
|
-
|
183
|
-
def module_creation_call?
|
184
|
-
object_creation_call? && module_creation_receiver?
|
185
|
-
end
|
186
|
-
|
187
|
-
def module_creation_receiver?
|
188
|
-
receiver && [:Class, :Struct].include?(receiver.simple_name)
|
189
|
-
end
|
190
|
-
|
191
|
-
def object_creation_call?
|
192
|
-
method_name == :new
|
193
|
-
end
|
194
|
-
|
195
|
-
def visibility_modifier?
|
196
|
-
VISIBILITY_MODIFIERS.include?(method_name)
|
197
|
-
end
|
198
|
-
|
199
|
-
def attribute_writer?
|
200
|
-
ATTR_DEFN_METHODS.include?(method_name) ||
|
201
|
-
attr_with_writable_flag?
|
202
|
-
end
|
203
|
-
|
204
|
-
# Handles the case where we create an attribute writer via:
|
205
|
-
# attr :foo, true
|
206
|
-
def attr_with_writable_flag?
|
207
|
-
method_name == :attr && args.any? && args.last.type == :true
|
208
|
-
end
|
209
|
-
|
210
|
-
VISIBILITY_MODIFIERS = [:private, :public, :protected, :module_function]
|
211
|
-
ATTR_DEFN_METHODS = [:attr_writer, :attr_accessor]
|
212
|
-
end
|
213
|
-
|
214
|
-
Op_AsgnNode = SendNode
|
215
|
-
|
216
|
-
# Base module for utility methods for nodes representing variables.
|
217
|
-
module VariableBase
|
218
|
-
def name() children.first end
|
219
|
-
end
|
220
|
-
|
221
|
-
# Utility methods for :cvar nodes.
|
222
|
-
module CvarNode
|
223
|
-
include VariableBase
|
224
|
-
end
|
225
|
-
|
226
|
-
CvasgnNode = CvarNode
|
227
|
-
CvdeclNode = CvarNode
|
228
|
-
|
229
|
-
# Utility methods for :ivar nodes.
|
230
|
-
module IvarNode
|
231
|
-
include VariableBase
|
232
|
-
end
|
233
|
-
|
234
|
-
# Utility methods for :ivasgn nodes.
|
235
|
-
module IvasgnNode
|
236
|
-
include VariableBase
|
237
|
-
end
|
238
|
-
|
239
|
-
# Utility methods for :lvar nodes.
|
240
|
-
module LvarNode
|
241
|
-
include VariableBase
|
242
|
-
|
243
|
-
alias_method :simple_name, :name
|
244
|
-
alias_method :var_name, :name
|
245
|
-
end
|
246
|
-
|
247
|
-
LvasgnNode = LvarNode
|
248
|
-
|
249
|
-
# Base module for utility methods for :def and :defs nodes.
|
250
|
-
module MethodNodeBase
|
251
|
-
def arguments
|
252
|
-
parameters.reject(&:block?)
|
253
|
-
end
|
254
|
-
|
255
|
-
def arg_names
|
256
|
-
arguments.map(&:name)
|
257
|
-
end
|
258
|
-
|
259
|
-
def parameters
|
260
|
-
argslist.components
|
261
|
-
end
|
262
|
-
|
263
|
-
def parameter_names
|
264
|
-
parameters.map(&:name)
|
265
|
-
end
|
266
|
-
|
267
|
-
def name_without_bang
|
268
|
-
name.to_s.chop
|
269
|
-
end
|
270
|
-
|
271
|
-
def ends_with_bang?
|
272
|
-
name[-1] == '!'
|
273
|
-
end
|
274
|
-
|
275
|
-
def body_nodes(types, ignoring = [])
|
276
|
-
if body
|
277
|
-
body.find_nodes(types, ignoring)
|
278
|
-
else
|
279
|
-
[]
|
280
|
-
end
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
# Checking if a method is a singleton method.
|
285
|
-
module SingletonMethod
|
286
|
-
def singleton_method?
|
287
|
-
singleton_method_via_class_self_notation?
|
288
|
-
end
|
289
|
-
|
290
|
-
# Ruby allows us to make a method a singleton_method using the
|
291
|
-
# class << self syntax.
|
292
|
-
#
|
293
|
-
# To check for this we check if the parent node is of type :sclass.
|
294
|
-
#
|
295
|
-
# @return [Boolean]
|
296
|
-
def singleton_method_via_class_self_notation?
|
297
|
-
return unless parent
|
298
|
-
parent.type == :sclass
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
# Utility methods for :def nodes.
|
303
|
-
module DefNode
|
304
|
-
def name() children.first end
|
305
|
-
def argslist() children[1] end
|
306
|
-
|
307
|
-
def body
|
308
|
-
children[2]
|
309
|
-
end
|
310
|
-
|
311
|
-
def full_name(outer)
|
312
|
-
prefix = outer == '' ? '' : "#{outer}#"
|
313
|
-
"#{prefix}#{name}"
|
314
|
-
end
|
315
|
-
|
316
|
-
def depends_on_instance?
|
317
|
-
ReferenceCollector.new(self).num_refs_to_self > 0
|
318
|
-
end
|
319
|
-
|
320
|
-
include MethodNodeBase
|
321
|
-
include SingletonMethod
|
322
|
-
end
|
323
|
-
|
324
|
-
# Utility methods for :defs nodes.
|
325
|
-
module DefsNode
|
326
|
-
def receiver() children.first end
|
327
|
-
def name() children[1] end
|
328
|
-
def argslist() children[2] end
|
329
|
-
|
330
|
-
def body
|
331
|
-
children[3]
|
332
|
-
end
|
333
|
-
|
334
|
-
include MethodNodeBase
|
335
|
-
|
336
|
-
def full_name(outer)
|
337
|
-
prefix = outer == '' ? '' : "#{outer}#"
|
338
|
-
"#{prefix}#{SexpFormatter.format(receiver)}.#{name}"
|
339
|
-
end
|
340
|
-
|
341
|
-
def depends_on_instance?
|
342
|
-
false
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
# Utility methods for :if nodes.
|
347
|
-
module IfNode
|
348
|
-
def condition() children.first end
|
349
|
-
|
350
|
-
def body_nodes(type, ignoring = [])
|
351
|
-
children[1..-1].compact.flat_map { |child| child.find_nodes(type, ignoring) }
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
# Utility methods for :block nodes.
|
356
|
-
module BlockNode
|
357
|
-
def call() children.first end
|
358
|
-
def args() children[1] end
|
359
|
-
def block() children[2] end
|
360
|
-
def parameters() children[1] || [] end
|
361
|
-
|
362
|
-
def parameter_names
|
363
|
-
parameters.children
|
364
|
-
end
|
365
|
-
|
366
|
-
def simple_name
|
367
|
-
:block
|
368
|
-
end
|
369
|
-
|
370
|
-
def without_block_arguments?
|
371
|
-
args.components.empty?
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
# Utility methods for :lit nodes.
|
376
|
-
module LitNode
|
377
|
-
def value() children.first end
|
378
|
-
end
|
379
|
-
|
380
|
-
# Utility methods for :const nodes.
|
381
|
-
module ConstNode
|
382
|
-
def simple_name
|
383
|
-
children.last
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
# Base module for utility methods for module nodes.
|
388
|
-
module ModuleNodeBase
|
389
|
-
# The full name of the module or class, including the name of any
|
390
|
-
# module or class it is nested inside of.
|
391
|
-
#
|
392
|
-
# For example, given code like this:
|
393
|
-
#
|
394
|
-
# module Foo
|
395
|
-
# class Bar::Baz
|
396
|
-
# end
|
397
|
-
# end
|
398
|
-
#
|
399
|
-
# The full name for the inner class will be 'Foo::Bar::Baz'. To return
|
400
|
-
# the correct name, the name of the outer context has to be passed into this method.
|
401
|
-
#
|
402
|
-
# @param outer [String] full name of the wrapping module or class
|
403
|
-
# @return the module's full name
|
404
|
-
def full_name(outer)
|
405
|
-
prefix = outer == '' ? '' : "#{outer}::"
|
406
|
-
"#{prefix}#{name}"
|
407
|
-
end
|
408
|
-
|
409
|
-
# The final section of the module or class name. For example, for a
|
410
|
-
# module with name 'Foo::Bar' this will return 'Bar'; for a module with
|
411
|
-
# name 'Foo' this will return 'Foo'.
|
412
|
-
#
|
413
|
-
# @return [String] the final section of the name
|
414
|
-
def simple_name
|
415
|
-
name.split('::').last
|
416
|
-
end
|
417
|
-
end
|
418
|
-
|
419
|
-
# Utility methods for :module nodes.
|
420
|
-
module ModuleNode
|
421
|
-
include ModuleNodeBase
|
422
|
-
|
423
|
-
# @return [String] name as given in the module statement
|
424
|
-
def name
|
425
|
-
SexpFormatter.format(children.first)
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
# Utility methods for :class nodes.
|
430
|
-
module ClassNode
|
431
|
-
include ModuleNode
|
432
|
-
def superclass() children[1] end
|
433
|
-
end
|
434
|
-
|
435
|
-
# Utility methods for :casgn nodes.
|
436
|
-
module CasgnNode
|
437
|
-
include ModuleNodeBase
|
438
|
-
|
439
|
-
def defines_module?
|
440
|
-
return false unless value
|
441
|
-
call = case value.type
|
442
|
-
when :block
|
443
|
-
value.call
|
444
|
-
when :send
|
445
|
-
value
|
446
|
-
end
|
447
|
-
call && call.module_creation_call?
|
448
|
-
end
|
449
|
-
|
450
|
-
def name
|
451
|
-
SexpFormatter.format(children[1])
|
452
|
-
end
|
453
|
-
|
454
|
-
# there are two valid forms of the casgn sexp
|
455
|
-
# (casgn <namespace> <name> <value>) and
|
456
|
-
# (casgn <namespace> <name>) used in or-asgn and mlhs
|
457
|
-
#
|
458
|
-
# source = "class Hi; THIS ||= 3; end"
|
459
|
-
# (class
|
460
|
-
# (const nil :Hi) nil
|
461
|
-
# (or-asgn
|
462
|
-
# (casgn nil :THIS)
|
463
|
-
# (int 3)))
|
464
|
-
def value
|
465
|
-
children[2]
|
466
|
-
end
|
467
|
-
end
|
468
|
-
|
469
|
-
# Utility methods for :yield nodes.
|
470
|
-
module YieldNode
|
471
|
-
def args() children end
|
472
|
-
|
473
|
-
def arg_names
|
474
|
-
args.map { |arg| arg[1] }
|
475
|
-
end
|
476
|
-
end
|
477
|
-
|
478
|
-
# Utility methods for :super nodes.
|
479
|
-
module SuperNode
|
480
|
-
def method_name
|
481
|
-
:super
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
ZsuperNode = SuperNode
|
486
|
-
|
487
|
-
# Utility methods for :sym nodes.
|
488
|
-
module SymNode
|
489
|
-
def name
|
490
|
-
children.first
|
491
|
-
end
|
492
|
-
|
493
|
-
def full_name(outer)
|
494
|
-
prefix = outer == '' ? '' : "#{outer}#"
|
495
|
-
"#{prefix}#{name}"
|
496
|
-
end
|
497
|
-
end
|
498
|
-
end
|
499
|
-
end
|
500
|
-
end
|
3
|
+
require_relative 'sexp_extensions/arguments'
|
4
|
+
require_relative 'sexp_extensions/attribute_assignments'
|
5
|
+
require_relative 'sexp_extensions/block'
|
6
|
+
require_relative 'sexp_extensions/case'
|
7
|
+
require_relative 'sexp_extensions/constant'
|
8
|
+
require_relative 'sexp_extensions/if'
|
9
|
+
require_relative 'sexp_extensions/literal'
|
10
|
+
require_relative 'sexp_extensions/logical_operators'
|
11
|
+
require_relative 'sexp_extensions/methods'
|
12
|
+
require_relative 'sexp_extensions/module'
|
13
|
+
require_relative 'sexp_extensions/nested_assignables'
|
14
|
+
require_relative 'sexp_extensions/send'
|
15
|
+
require_relative 'sexp_extensions/super'
|
16
|
+
require_relative 'sexp_extensions/symbols'
|
17
|
+
require_relative 'sexp_extensions/variables'
|
18
|
+
require_relative 'sexp_extensions/when'
|
19
|
+
require_relative 'sexp_extensions/yield'
|