Skip to content

Commit

Permalink
Add max_allowed calls to FeatureEnvy smell detector
Browse files Browse the repository at this point in the history
This is adding a configuration `max_calls` key for the FeatureEnvy smell detector

Fixes troessner#1535
  • Loading branch information
JuanVqz committed Oct 16, 2023
1 parent d3c2968 commit da50b56
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 3 deletions.
47 changes: 46 additions & 1 deletion docs/Feature-Envy.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Reek cannot reliably detect that each call's receiver is a different arg and wil
```
[4, 5, 6]:FeatureEnvy: Foo#initialize refers to 'arg' more than self (maybe move it to another class?)
```

If you're running into this problem you can disable this smell detector for this method either via
configuration:

Expand Down Expand Up @@ -91,3 +91,48 @@ _Feature Envy_ is only triggered if there are some references to self and _[Util
## Configuration

_Feature Envy_ supports the [Basic Smell Options](Basic-Smell-Options.md).

Option | Value | Effect
-------|-------|-------
`max_calls` | integer | The maximum number of duplicate calls allowed within a method. Defaults to 3.


## Example configuration

### Adjusting `max_calls`

Imagine code like this:

```ruby
class Alfa
def bravo(charlie)
(charlie.delta - charlie.echo) * foxtrot
end
end
```

This would report:

>>
src.rb -- 1 warnings:
[3, 3]:FeatureEnvy: Alfa#bravo refers to 'charlie' more than self (maybe move it to another class?)

If you want to allow those double calls here you can disable it in 2 different ways:

1.) Via source code comment:

```ruby
class Alfa
# :reek:FeatureEnvy { max_calls: 3 }
def bravo(charlie)
(charlie.delta - charlie.echo) * foxtrot
end
end
```

2.) Via configuration file:

```yaml
DuplicateMethodCall:
max_calls: 3
```
1 change: 1 addition & 0 deletions docs/defaults.reek.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ detectors:
FeatureEnvy:
enabled: true
exclude: []
max_calls: 2
InstanceVariableAssumption:
enabled: true
exclude: []
Expand Down
4 changes: 3 additions & 1 deletion lib/reek/configuration/schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ mapping:
type: map
mapping:
<<: *detector_base
max_calls:
type: number
InstanceVariableAssumption:
type: map
mapping:
Expand Down Expand Up @@ -203,7 +205,7 @@ mapping:
=:
type: map
mapping: *all_detectors

"exclude_paths":
type: seq
sequence:
Expand Down
17 changes: 16 additions & 1 deletion lib/reek/smell_detectors/feature_envy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ module SmellDetectors
#
# See {file:docs/Feature-Envy.md} for details.
class FeatureEnvy < BaseDetector
# The name of the config field that sets the maximum number of
# identical calls to be permitted within any single method.
MAX_ALLOWED_CALLS_KEY = 'max_calls'
DEFAULT_MAX_CALLS = 2

def self.default_config
super.merge(MAX_ALLOWED_CALLS_KEY => DEFAULT_MAX_CALLS)
end

#
# Checks whether the given +context+ includes any code fragment that
# might "belong" on another class.
Expand All @@ -46,7 +55,9 @@ def sniff
return [] if context.singleton_method? || context.module_function?
return [] unless context.references_self?

envious_receivers.map do |name, lines|
envious_receivers.select do |_key, lines|
lines.length >= max_allowed_calls
end.map do |name, lines|
smell_warning(
lines: lines,
message: "refers to '#{name}' more than self (maybe move it to another class?)",
Expand All @@ -65,6 +76,10 @@ def envious_receivers

refs.most_popular
end

def max_allowed_calls
@max_allowed_calls ||= value(MAX_ALLOWED_CALLS_KEY, context)
end
end
end
end

0 comments on commit da50b56

Please sign in to comment.