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 #9144] Add aggressive and conservative enforced styles for Style/StringConcatenation cop #9153

Merged
merged 1 commit into from Jun 28, 2021

Conversation

tejasbubane
Copy link
Contributor

@tejasbubane tejasbubane commented Dec 2, 2020

Closes #9144


Before submitting the PR make sure the following are checked:

  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • 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.
  • Ran bundle exec rake default. It executes all tests and runs RuboCop on its own code.
  • Added an entry (file) to the changelog folder named {change_type}_{change_description}.md if the new code introduces user-observable changes. See changelog entry format for details.

@ianfixes
Copy link
Contributor

ianfixes commented Dec 3, 2020

This is a small step forward, but I want to be clear that my aim in opening #9144 was simply to show that the + operator could not be trusted to indicate a concatenation operation. What's being addressed in this PR is only my very contrived counterexample.

A more common counterexample would be in a form like this:

def for_example(a_pathname)
    a_pathname + "a_string"
end

RuboCop really has no business pretending that string concatenation is the only thing that + "a_string" could be accomplishing.

@bbatsov
Copy link
Collaborator

bbatsov commented Dec 3, 2020

I also don't like the proposed change as I don't think the problem is solvable by ignoring some cases. Fundamentally, the problem is that with operator overloading we can never know what happens in certain cases and we can only guess. The cop is already marked as unsafe because of this. The only thing I can think of is to add a mode that ignores cases where the first operand is not a string literal.

@tejasbubane
Copy link
Contributor Author

This is a small step forward, but I want to be clear that my aim in opening #9144 was simply to show that the + operator could not be trusted to indicate a concatenation operation. What's being addressed in this PR is only my very contrived counterexample.

Yes I had the same thing in mind, but thought Pathname being stdlib made sense to add an exception.

The only thing I can think of is to add a mode that ignores cases where the first operand is not a string literal.

That sounds a lot better. I will change this PR accordingly.

@ianfixes
Copy link
Contributor

ianfixes commented Dec 3, 2020

The only thing I can think of is to add a mode that ignores cases where the first operand is not a string literal.

Cross posting my latest comment from #9144, I think this could be expanded to say "only consider cases where the first and last operands in a chain of + operators are string lterals"

def some_fn(var_of_unknown_type)
  puts "hello" + "world"                       # string concatenation 
  puts var_of_unknown_type + "world"           # insufficient info to decide
  puts "hello" + var_of_unknown_type           # string concatenation (I think? Is there a case where that's not true?)
  puts "hello" + var_of_unknown_type + "world" # string concatenation 
end

@tejasbubane tejasbubane changed the title [Fix #9144] Add IgnoredPatterns config to Style/StringConcatenation [Fix #9144] Add literal_first_operand enforced-style for Style/StringConcatenation cop Dec 3, 2020
@tejasbubane
Copy link
Contributor Author

tejasbubane commented Dec 3, 2020

@bbatsov Changed this PR to use a config option.

@bbatsov
Copy link
Collaborator

bbatsov commented Dec 5, 2020

I'm also thinking we can have some more generic names for the enforced style (e.g. conservative and aggressive) or perhaps it'd be even better if this was some boolean flag like SafeMode. We're not really supporting different styles here, we're rather limitting the scope of the cop.

@bbatsov
Copy link
Collaborator

bbatsov commented Jan 10, 2021

Ping :-)

@tejasbubane
Copy link
Contributor Author

I forgot about this PR. I will fix this in a day or two.

@bbatsov
Copy link
Collaborator

bbatsov commented Feb 15, 2021

@tejasbubane another friendly reminder about this PR :-)

@tejasbubane
Copy link
Contributor Author

@bbatsov Changes done. Sorry for the delay.

@@ -15,7 +15,13 @@ module Style
# lines, this cop does not register an offense; instead,
# `Style/LineEndConcatenation` will pick up the offense if enabled.
#
# @example
# Two modes are supported:
Copy link
Collaborator

Choose a reason for hiding this comment

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

You should also mention how this ties to the cop safety - e.g. with the Pathname example.

@bbatsov
Copy link
Collaborator

bbatsov commented Mar 17, 2021

Seems I totally forgot about this myself. After some further thought let's not use EnforcedStyle, but rather some other configuration option name (e.g. Mode/Detection), as that's not really a different style just an instruction how hard to try to find offenses. If someone from @rubocop/rubocop-core can suggest a better config setup - be my guest.

Copy link
Collaborator

@bbatsov bbatsov left a comment

Choose a reason for hiding this comment

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

Overall things look good. We just need to add some safety documentation and adopt some dedicated configuration option for the two modes of operation.

lib/rubocop/cop/style/string_concatenation.rb Outdated Show resolved Hide resolved
it 'does not register offense' do
expect_no_offenses(<<~RUBY)
user.name + "!!"
user.name + "<" + "user.email" + ">"
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, I would expect an offense here. Not on user.name + "<", but on "<" + "user.email" (and "user.email" + ">").

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good catch!

Copy link
Contributor

Choose a reason for hiding this comment

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

I disagree.

# yes, I know that a variable called user.name is unlikely to be a Pathname
user.name = Pathname.new("user.name")
result = user.name + "<" + "user.email" + ">"
puts "result is #{result}" # result is user.name/</user.email/>
                           # which is in no way equivalent to "user.name<user.email>"

Copy link
Contributor

Choose a reason for hiding this comment

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

@tejasbubane -- I think the fix that was called for here was in error. See my above comment

@tejasbubane tejasbubane force-pushed the fix-9144 branch 2 times, most recently from 3871cec to e381cbf Compare April 20, 2021 19:09
@tejasbubane tejasbubane changed the title [Fix #9144] Add literal_first_operand enforced-style for Style/StringConcatenation cop [Fix #9144] Add aggressive and conservative enforced styles for Style/StringConcatenation cop Apr 20, 2021
@tejasbubane tejasbubane force-pushed the fix-9144 branch 2 times, most recently from cc708b1 to 7932e00 Compare April 20, 2021 19:15
@tejasbubane
Copy link
Contributor Author

Review comments fixed.

  • Changed config name to Mode.
  • Fixed the issue @bquorning pointed out.
  • Added Pathname example to cop documentation to show how the config ties to cop safety.

# either the left or right side of `+` is a string literal.
# 2. `conservative` style on the other hand, checks and corrects only if
# left side (receiver of `+` method call) is a string literal.
# This is useful when the receiver is some expression that returns string like `Pathname`
Copy link
Contributor

Choose a reason for hiding this comment

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

Pathname.new returns a Pathname, not a string. Pathname.new("x") + "y" also returns a Pathname.

I think this comment should read

This is useful when the receiver is an object whose + operator accepts a string, but does not return a string -- such as Pathname

@bbatsov bbatsov assigned dvandersluis and unassigned bbatsov May 19, 2021
@@ -0,0 +1 @@
* [#9144](https://github.com/rubocop/rubocop/issues/9144): Add `aggressive` and `conservative` enforced styles for `Style/StringConcatenation` cop. ([@tejasbubane][])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Those are not enforced styles, but "modes of operation".

@@ -203,4 +203,36 @@
RUBY
end
end

context 'EnforcedStyle = conservative' do
Copy link
Collaborator

Choose a reason for hiding this comment

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

Mode

@bbatsov bbatsov requested a review from dvandersluis May 19, 2021 05:31
@bbatsov
Copy link
Collaborator

bbatsov commented Jun 10, 2021

@tejasbubane ping :-)

@tejasbubane
Copy link
Contributor Author

@bbatsov Minor wording changes done.

@bbatsov bbatsov merged commit 9b5402d into rubocop:master Jun 28, 2021
@tejasbubane tejasbubane deleted the fix-9144 branch June 28, 2021 08:51
koic added a commit to koic/rubocop that referenced this pull request Jun 2, 2022
Fixes rubocop#10685.

This PR fixes a false positive for `Style/StringConcatenation`
when `Mode: conservative` and first operand is not string literal.

This also resolves the following feedback:
rubocop#9153 (comment)

`Mode: conservative` should be allowed considering the possibility that
receiver is a `Pathname` object.
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.

Style/StringConcatenation mistakenly assumes that the + operator means concatenation, e.g. with Pathname
5 participants