From 93a7e0da5c4bf4058df72a7a6525b4cea3265f01 Mon Sep 17 00:00:00 2001 From: uplus Date: Mon, 11 Jan 2021 17:28:54 +0900 Subject: [PATCH] Add new cop `Lint/OrAssignmentToConstant` This cop checks for unintended or-assignment to a constant. Constants should always be assigned in the same location. And its value should always be the same. If constants are assigned in multiple locations, the result may vary depending on the order of `require`. Also, if you already have such an implementation, auto-correction may change the result. ```ruby CONST ||= 1 CONST = 1 ``` --- CHANGELOG.md | 1 + ..._add_new_cop_lintorassignmenttoconstant.md | 1 + config/default.yml | 6 +++ lib/rubocop.rb | 1 + .../cop/lint/or_assignment_to_constant.rb | 39 +++++++++++++++++++ .../lint/or_assignment_to_constant_spec.rb | 36 +++++++++++++++++ 6 files changed, 84 insertions(+) create mode 100644 changelog/new_add_new_cop_lintorassignmenttoconstant.md create mode 100644 lib/rubocop/cop/lint/or_assignment_to_constant.rb create mode 100644 spec/rubocop/cop/lint/or_assignment_to_constant_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 46db90c86522..75e5f8f227e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5366,3 +5366,4 @@ [@ohbarye]: https://github.com/ohbarye [@magneland]: https://github.com/magneland [@k-karen]: https://github.com/k-karen +[@uplus]: https://github.com/uplus diff --git a/changelog/new_add_new_cop_lintorassignmenttoconstant.md b/changelog/new_add_new_cop_lintorassignmenttoconstant.md new file mode 100644 index 000000000000..15cd930c6d9c --- /dev/null +++ b/changelog/new_add_new_cop_lintorassignmenttoconstant.md @@ -0,0 +1 @@ +* [#9363](https://github.com/rubocop-hq/rubocop/pull/9363): Add new cop `Lint/OrAssignmentToConstant`. ([@uplus][]) diff --git a/config/default.yml b/config/default.yml index 5f56c66b55ae..f54a327725b8 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1811,6 +1811,12 @@ Lint/NumberedParameterAssignment: Enabled: pending VersionAdded: '<>' +Lint/OrAssignmentToConstant: + Description: 'Checks unintended or-assignment to constant.' + Enabled: pending + Safe: false + VersionAdded: '<>' + Lint/OrderedMagicComments: Description: 'Checks the proper ordering of magic comments and whether a magic comment is not placed before a shebang.' Enabled: true diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 75ef2e0476a2..407f37b4e58a 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -313,6 +313,7 @@ require_relative 'rubocop/cop/lint/non_local_exit_from_iterator' require_relative 'rubocop/cop/lint/number_conversion' require_relative 'rubocop/cop/lint/numbered_parameter_assignment' +require_relative 'rubocop/cop/lint/or_assignment_to_constant' require_relative 'rubocop/cop/lint/ordered_magic_comments' require_relative 'rubocop/cop/lint/out_of_range_regexp_ref' require_relative 'rubocop/cop/lint/parentheses_as_grouped_expression' diff --git a/lib/rubocop/cop/lint/or_assignment_to_constant.rb b/lib/rubocop/cop/lint/or_assignment_to_constant.rb new file mode 100644 index 000000000000..3681ae867062 --- /dev/null +++ b/lib/rubocop/cop/lint/or_assignment_to_constant.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Lint + # This cop checks for unintended or-assignment to a constant. + # + # Constants should always be assigned in the same location. And its value + # should always be the same. If constants are assigned in multiple + # locations, the result may vary depending on the order of `require`. + # + # Also, if you already have such an implementation, auto-correction may + # change the result. + # + # @example + # + # # bad + # CONST ||= 1 + # + # # good + # CONST = 1 + # + class OrAssignmentToConstant < Base + extend AutoCorrector + + MSG = 'Avoid using or-assignment with constants.' + + def on_or_asgn(node) + lhs, _rhs = *node + return unless lhs&.casgn_type? + + add_offense(node.loc.operator) do |corrector| + corrector.replace(node.loc.operator, '=') + end + end + end + end + end +end diff --git a/spec/rubocop/cop/lint/or_assignment_to_constant_spec.rb b/spec/rubocop/cop/lint/or_assignment_to_constant_spec.rb new file mode 100644 index 000000000000..cd15d6a10ef1 --- /dev/null +++ b/spec/rubocop/cop/lint/or_assignment_to_constant_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Lint::OrAssignmentToConstant, :config do + subject(:cop) { described_class.new(config) } + + it 'registers an offense with or-assignment to a constant' do + expect_offense(<<~RUBY) + CONST ||= 1 + ^^^ Avoid using or-assignment with constants. + RUBY + + expect_correction(<<~RUBY) + CONST = 1 + RUBY + end + + it 'does not register an offense with plain assignment to a constant' do + expect_no_offenses(<<~RUBY) + CONST = 1 + RUBY + end + + [ + ['a local variable', 'var'], + ['an instance variable', '@var'], + ['a class variable', '@@var'], + ['a global variable', '$var'], + ['an attribute', 'self.var'] + ].each do |type, var| + it "does not register an offense with or-assignment to #{type}" do + expect_no_offenses(<<~RUBY) + #{var} ||= 1 + RUBY + end + end +end