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

[Fix #3666] Adds Uncommunicative MethodArg & BlockParam Naming cops #5211

Merged
merged 5 commits into from
Dec 29, 2017

Conversation

garettarrowood
Copy link
Contributor

@garettarrowood garettarrowood commented Dec 11, 2017

Adds new Naming/UncommunicativeMethodArgName & Naming/UncommunicativeBlockParamName cops. This enhancement was discussed in detail on issue #3666. One detail left off the discussion was that there is now a Naming namespace, and it makes sense to add these there.

These cops are disabled by default and should be easily extendable. I'd be happy to add more configuration, but it may be best to flush out what exactly comes next in an issue.


  • Wrote good commit messages.
  • Commit message starts with [Fix #issue-number] (if the related issue exists).
  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Added an entry to the Changelog if the new code introduces user-observable changes. See changelog entry format.
  • The PR relates to only one subject with a clear title
    and description in grammatically correct, complete sentences.
  • Run rake default or rake parallel. It executes all tests and RuboCop for itself, and generates the documentation.

@bbatsov
Copy link
Collaborator

bbatsov commented Dec 11, 2017

These cops are disabled by default and should be easily extendable. I'd be happy to add more configuration, but it may be best to flush out what exactly comes next in an issue.

Out of curiosity - how many offences does the RuboCop codebase produce?

@garettarrowood
Copy link
Contributor Author

garettarrowood commented Dec 11, 2017

1076 files inspected, 101 offenses detected

And 2 understandable cli_spec failures. However, doing this has highlighted a bug in this PR. I have to account for default args (I guess only operate on the lhs when the arg assigns). So this is WIP until I fix it. E.g.

lib/rubocop/target_finder.rb:107:29: C: Naming/UncommunicativeMethodArgName: Only use lowercase characters for method argument.
    def target_files_in_dir(base_dir = Dir.pwd)
                            ^^^^^^^^^^^^^^^^^^

The vast majority of those 101 offenses are legitimate based on standards these new cops present. Only a small handful are default argument issues. Would you like me to fix the offenses and default these to enabled?

@bbatsov
Copy link
Collaborator

bbatsov commented Dec 12, 2017

The vast majority of those 101 offenses are legitimate based on standards these new cops present. Only a small handful are default argument issues. Would you like me to fix the offenses and default these to enabled?

That would be my preference, yes.


def min_length
cop_config['MinParamNameLength'] ||
cop_config['MinArgNameLength']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is confusing. We will be able to set MinParamNameLength to Naming/UncommunicativeMethodArgName:. I think it is a bug. Naming/UncommunicativeMethodArgName should only use MinArgNameLength, do not use MinParamNameLength.

For example:

# in the mixin
def min_length
  cop_config[MIN_LENGTH_KEY]
end

# in the cops
class UncommunicativeMethodArgName
  MIN_LENGTH_KEY = 'MinArgNameLength'
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point! Thanks!

Copy link
Contributor Author

@garettarrowood garettarrowood Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't able to do exactly this. Since the mixin is a Module, it didn't have access to constants from the Class it extends. But this issue is resolved.

end

def uppercase?(name)
name =~ /[A-Z]/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably it should be [[:upper:]].
See #5017

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent! Thank you!

def on_def(node)
return unless node.arguments?
check(node, node.arguments)
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cop should check defs node also (For def self.foo; end).

@@ -685,6 +685,15 @@ Naming/PredicateName:
Exclude:
- 'spec/**/*'

Naming/UncommunicativeBlockParamName:
# Parameter names may be equal to or greater than this value
MinParamNameLength: 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, with such a default will this ever flag anything? :-)

Copy link
Contributor Author

@garettarrowood garettarrowood Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will only flag BlockParams that end in numbers or contain capital letters. It will never flag length. But, should users want to enforce a 2 or 3 char min length, they have the ability to.


If we raise it, I think we'd have to add more config to whitelist individual letter exceptions. As we discussed some on the issue, single letter block params are more common. Perhaps we can hash it out in a new issue after this becomes deliverable?

@garettarrowood garettarrowood force-pushed the uncommunicative_names branch 2 times, most recently from 70dedf0 to 4c249c3 Compare December 12, 2017 20:26
@@ -70,7 +70,7 @@ def no_surrounding_space?(arg, equals)
!arg.space_after? && !equals.space_after?
end

def message(_)
def message(_msg)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument to #message is not a message. It is the offending node. 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll change it here and everywhere else this PR corrects a #message.

@@ -10,7 +10,7 @@ def checkable_layout?(_node)
true
end

def deltas_for_first_pair(*)
def deltas_for_first_pair(*_args)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't any more communicative than just *. 🙂

@Drenmi
Copy link
Collaborator

Drenmi commented Dec 14, 2017

I think some of the fixes made here highlight the limitations of the method. The sets "short" and "uncommunicative" are not perfectly overlapping. For example, x and y are communicative for a Point class, and args (several "fixes" in the PR use this) is uncommunicative for any method 🙂

Not sure how to approach this though. Perhaps a whitelist might be a good start?

For blocks, I know there have been many discussions in the Reek repo, because people tend to use single-letter arguments for single-line blocks.

@garettarrowood
Copy link
Contributor Author

I agree that these Cops have there limitations, particularly when it comes to length. To clarify:

  • The UncommunicativeBlockParamName sets a default length to 1. In effect, it does not enforce any length requirement. But having the config allows users to enforce a higher one should they desire.

  • Total agreement that arg doesn't add anything. But I'd argue it doesn't subtract anything either. And overall this did point out at least a few naming improvements. operator for op. buffer for sb. index for ix. string for s.

I'm going to give this another full pass and see if I can improve some of these names more. About half of our offenders are either t1/t2 or n1/n2. These were difficult to rename because they are often named via a each_cons(2) and thus truly seem like a first and second. I've renamed some of these token_one and token_two, and this seems horrible. I'm contemplating naming all these left_token/right_token and left_node/right_node. WDYT?

That leaves adding a whitelist. I will go ahead and add this configurable option in my next pass. Based on the corrections you see in this PR, what exactly would you like to see on rubocop's whitelist? What should be set as universal/default whitelisted names? Would t1/t2/n1/n2 be something we want to whitelist because we know they represent tokens and nodes?

Opinions & suggestions are very welcome here.

@bbatsov
Copy link
Collaborator

bbatsov commented Dec 27, 2017

That leaves adding a whitelist. I will go ahead and add this configurable option in my next pass. Based on the corrections you see in this PR, what exactly would you like to see on rubocop's whitelist? What should be set as universal/default whitelisted names? Would t1/t2/n1/n2 be something we want to whitelist because we know they represent tokens and nodes?

I guess this would become apparent just by analysing the offences produced by the cop with a whitelist present. :-)

Sorry for dropping the ball on the cop for a bit, but my focus was getting 0.52.1 out.

I'm going to give this another full pass and see if I can improve some of these names more. About half of our offenders are either t1/t2 or n1/n2. These were difficult to rename because they are often named via a each_cons(2) and thus truly seem like a first and second. I've renamed some of these token_one and token_two, and this seems horrible. I'm contemplating naming all these left_token/right_token and left_node/right_node. WDYT?

token1/2 and node1/2 don't seem like bad names to me. I'm definitely open to better ideas, though. left/right convey a different meaning IMO - I immediately start thinking of lhs and rhs.

nil_or_empty?(node) do |variable1, variable2|
return unless variable1 == variable2
nil_or_empty?(node) do |var1, var2|
return unless var1 == var2
Copy link
Contributor Author

@garettarrowood garettarrowood Dec 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really didn't want to do this. But. For the Rails blank? and present? cops, "variable" or "var" are the best descriptions of what these values contain. (I don't find variable anymore descriptive than var.)

.rubocop.yml Outdated
- 'token1'
- 'token2'
- 'var1'
- 'var2'
Copy link
Contributor Author

@garettarrowood garettarrowood Dec 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this list informative. Seems like these cops could also use a config option to turn off NamesEndingInNumbers offenses.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just about to suggest this. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add this and disable it by default as well.

@bbatsov bbatsov merged commit 5519478 into rubocop:master Dec 29, 2017
@bbatsov
Copy link
Collaborator

bbatsov commented Dec 29, 2017

👍

@garettarrowood garettarrowood deleted the uncommunicative_names branch December 29, 2017 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants