Class: RuboCop::Cop::Naming::InclusiveLanguage
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
::RuboCop::Cop::AutoCorrector,
::RuboCop::Cop::Base,
::RuboCop::ExcludeLimit,
NodePattern::Macros,
RuboCop::AST::Sexp
|
|
|
Instance Chain:
self,
::RuboCop::Cop::RangeHelp,
::RuboCop::Cop::Base,
::RuboCop::Cop::AutocorrectLogic,
::RuboCop::Cop::IgnoredNode,
::RuboCop::Util,
RuboCop::AST::Sexp
|
|
| Inherits: |
RuboCop::Cop::Base
|
| Defined in: | lib/rubocop/cop/naming/inclusive_language.rb |
Overview
Recommends the use of inclusive language instead of problematic terms. The cop can check the following locations for offenses:
-
identifiers
-
constants
-
variables
-
strings
-
symbols
-
comments
-
file paths
Each of these locations can be individually enabled/disabled via configuration, for example CheckIdentifiers = true/false.
Flagged terms are configurable for the cop. For each flagged term an optional
Regex can be specified to identify offenses. Suggestions for replacing a flagged term can
be configured and will be displayed as part of the offense message.
An AllowedRegex can be specified for a flagged term to exempt allowed uses of the term.
WholeWord: true can be set on a flagged term to indicate the cop should only match when
a term matches the whole word (partial matches will not be offenses).
The cop supports autocorrection when there is only one suggestion. When there are multiple suggestions, the best suggestion cannot be identified and will not be autocorrected.
Constant Summary
-
EMPTY_ARRAY =
# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 78[].freeze -
MSG =
# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 79"Consider replacing '%<term>s'%<suffix>s." -
MSG_FOR_FILE_PATH =
# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 80"Consider replacing '%<term>s' in file path%<suffix>s."
::RuboCop::Cop::Base - Inherited
EMPTY_OFFENSES, RESTRICT_ON_SEND
::RuboCop::Cop::RangeHelp - Included
Class Attribute Summary
::RuboCop::Cop::AutoCorrector - Extended
::RuboCop::Cop::Base - Inherited
| .gem_requirements, .lint?, | |
| .support_autocorrect? | Returns if class supports autocorrect. |
| .support_multiple_source? | Override if your cop should be called repeatedly for multiple investigations Between calls to #on_new_investigation and |
Class Method Summary
::RuboCop::Cop::Base - Inherited
| .autocorrect_incompatible_with | List of cops that should not try to autocorrect at the same time as this cop. |
| .badge | Naming. |
| .callbacks_needed, .cop_name, .department, | |
| .documentation_url | Returns a url to view this cops documentation online. |
| .exclude_from_registry | Call for abstract Cop classes. |
| .inherited, | |
| .joining_forces | Override and return the Force class(es) you need to join. |
| .match? | Returns true if the cop name or the cop namespace matches any of the given names. |
| .new, | |
| .requires_gem | Register a version requirement for the given gem name. |
| .restrict_on_send | |
::RuboCop::ExcludeLimit - Extended
| exclude_limit | Sets up a configuration option to have an exclude limit tracked. |
| transform | |
Instance Attribute Summary
::RuboCop::Cop::Base - Inherited
::RuboCop::Cop::AutocorrectLogic - Included
Instance Method Summary
- #on_new_investigation
- #add_offenses_for_token(token, word_locations) private
- #add_to_flagged_term_hash(regex_string, term, term_definition) private
- #array_to_ignorecase_regex(strings) private
- #check_token?(type) ⇒ Boolean private
- #create_message(word, message = MSG) private
- #create_multiple_word_message_for_file(words) private
- #create_single_word_message_for_file(word) private
- #ensure_regex_string(regex) private
- #extract_regexp(term, term_definition) private
- #find_flagged_term(word) private
- #format_suggestions(suggestions) private
- #investigate_filepath private
- #investigate_tokens private
- #mask_input(str) private
- #offense_range(token, word) private
- #preferred_sole_term(suggestions) private
- #preprocess_check_config private
- #preprocess_flagged_terms private
- #preprocess_suggestions(suggestions) private
- #process_allowed_regex(allowed) private
- #scan_for_words(input) private
- #set_regexes(flagged_term_strings, allowed_strings) private
::RuboCop::Cop::RangeHelp - Included
| #add_range, | |
| #arguments_range | A range containing the first to the last argument of a method call or method definition. |
| #column_offset_between, | |
| #contents_range | A range containing only the contents of a literal with delimiters (e.g. |
| #directions, | |
| #effective_column | Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value. |
| #final_pos, #move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range | |
::RuboCop::Cop::Base - Inherited
| #add_global_offense | Adds an offense that has no particular location. |
| #add_offense | Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense. |
| #begin_investigation | Called before any investigation. |
| #callbacks_needed, | |
| #cop_config | Configuration Helpers. |
| #cop_name, #excluded_file?, | |
| #external_dependency_checksum | This method should be overridden when a cop’s behavior depends on state that lives outside of these locations: |
| #inspect, | |
| #message | Gets called if no message is specified when calling |
| #name | Alias for Base#cop_name. |
| #offenses, | |
| #on_investigation_end | Called after all on_… |
| #on_new_investigation | Called before all on_… |
| #on_other_file | Called instead of all on_… |
| #parse | There should be very limited reasons for a Cop to do it’s own parsing. |
| #parser_engine, | |
| #ready | Called between investigations. |
| #relevant_file?, | |
| #target_gem_version | Returns a gems locked versions (i.e. |
| #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction, | |
| #callback_argument | Reserved for Cop::Cop. |
| #complete_investigation | Called to complete an investigation. |
| #correct, #current_corrector, | |
| #current_offense_locations | Reserved for Commissioner: |
| #current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #matches_absolute_include_pattern?, #range_for_original, #range_from_node_or_range, | |
| #reset_investigation | Actually private methods. |
| #use_corrector | |
::RuboCop::Cop::AutocorrectLogic - Included
::RuboCop::Cop::IgnoredNode - Included
Constructor Details
.new(config = nil, options = nil) ⇒ InclusiveLanguage
# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 84
def initialize(config = nil, = nil) super @flagged_term_hash = {} @flagged_terms_regex = nil @allowed_regex = nil @check_token = preprocess_check_config preprocess_flagged_terms end
Instance Method Details
#add_offenses_for_token(token, word_locations) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 113
def add_offenses_for_token(token, word_locations) word_locations.each do |word_location| word = word_location.word range = offense_range(token, word) add_offense(range, message: (word)) do |corrector| suggestions = find_flagged_term(word)['Suggestions'] if (preferred_term = preferred_sole_term(suggestions)) corrector.replace(range, preferred_term) end end end end
#add_to_flagged_term_hash(regex_string, term, term_definition) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 178
def add_to_flagged_term_hash(regex_string, term, term_definition) @flagged_term_hash[Regexp.new(regex_string, Regexp::IGNORECASE)] = term_definition.merge('Term' => term, 'SuggestionString' => preprocess_suggestions(term_definition['Suggestions'])) end
#array_to_ignorecase_regex(strings) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 208
def array_to_ignorecase_regex(strings) Regexp.new(strings.join('|'), Regexp::IGNORECASE) end
#check_token?(type) ⇒ Boolean (private)
# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 128
def check_token?(type) !!@check_token[type] end
#create_message(word, message = MSG) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 258
def (word, = MSG) flagged_term = find_flagged_term(word) suggestions = flagged_term['SuggestionString'] suggestions = ' with another term' if suggestions.blank? format(, term: word, suffix: suggestions) end
#create_multiple_word_message_for_file(words) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 232
def (words) format(MSG_FOR_FILE_PATH, term: words.join("', '"), suffix: ' with other terms') end
#create_single_word_message_for_file(word) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 228
def (word) (word, MSG_FOR_FILE_PATH) end
#ensure_regex_string(regex) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 204
def ensure_regex_string(regex) regex.is_a?(Regexp) ? regex.source : regex end
#extract_regexp(term, term_definition) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 171
def extract_regexp(term, term_definition) return term_definition['Regex'] if term_definition['Regex'] return /(?:\b|(?<=[\W_]))#{term}(?:\b|(?=[\W_]))/ if term_definition['WholeWord'] term end
#find_flagged_term(word) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 266
def find_flagged_term(word) _regexp, flagged_term = @flagged_term_hash.find do |key, _term| key.match?(word) end flagged_term end
#format_suggestions(suggestions) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 280
def format_suggestions(suggestions) quoted_suggestions = Array(suggestions).map { |word| "'#{word}'" } suggestion_str = case quoted_suggestions.size when 1 quoted_suggestions.first when 2 quoted_suggestions.join(' or ') else last_quoted = quoted_suggestions.pop quoted_suggestions << "or #{last_quoted}" quoted_suggestions.join(', ') end " with #{suggestion_str}" end
#investigate_filepath (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 212
def investigate_filepath word_locations = scan_for_words(processed_source.file_path) case word_locations.length when 0 return when 1 = (word_locations.first.word) else words = word_locations.map(&:word) = (words) end add_global_offense() end
#investigate_tokens (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 102
def investigate_tokens processed_source.tokens.each do |token| next unless check_token?(token.type) word_locations = scan_for_words(token.text) next if word_locations.empty? add_offenses_for_token(token, word_locations) end end
#mask_input(str) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 246
def mask_input(str) safe_str = if str.valid_encoding? str else str.encode('UTF-8', invalid: :replace, undef: :replace) end return safe_str if @allowed_regex.nil? safe_str.gsub(@allowed_regex) { |match| '*' * match.size } end
#offense_range(token, word) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 295
def offense_range(token, word) start_position = token.pos.begin_pos + token.pos.source.index(word) range_between(start_position, start_position + word.length) end
#on_new_investigation
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 93
def on_new_investigation return unless @flagged_terms_regex investigate_filepath if cop_config['CheckFilepaths'] investigate_tokens end
#preferred_sole_term(suggestions) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 162
def preferred_sole_term(suggestions) case suggestions when Array suggestions.one? && preferred_sole_term(suggestions.first) when String suggestions end end
#preprocess_check_config (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 132
def preprocess_check_config # rubocop:disable Metrics/AbcSize { tIDENTIFIER: cop_config['CheckIdentifiers'], tCONSTANT: cop_config['CheckConstants'], tIVAR: cop_config['CheckVariables'], tCVAR: cop_config['CheckVariables'], tGVAR: cop_config['CheckVariables'], tSYMBOL: cop_config['CheckSymbols'], tSTRING: cop_config['CheckStrings'], tSTRING_CONTENT: cop_config['CheckStrings'], tCOMMENT: cop_config['CheckComments'] }.freeze end
#preprocess_flagged_terms (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 146
def preprocess_flagged_terms allowed_strings = [] flagged_term_strings = [] (cop_config['FlaggedTerms'] || {}).each do |term, term_definition| next if term_definition.nil? allowed_strings.concat(process_allowed_regex(term_definition['AllowedRegex'])) regex_string = ensure_regex_string(extract_regexp(term, term_definition)) flagged_term_strings << regex_string add_to_flagged_term_hash(regex_string, term, term_definition) end set_regexes(flagged_term_strings, allowed_strings) end
#preprocess_suggestions(suggestions) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 273
def preprocess_suggestions(suggestions) return '' if suggestions.nil? || (suggestions.is_a?(String) && suggestions.strip.empty?) || suggestions.empty? format_suggestions(suggestions) end
#process_allowed_regex(allowed) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 194
def process_allowed_regex(allowed) return EMPTY_ARRAY if allowed.nil? Array(allowed).map do |allowed_term| next if allowed_term.is_a?(String) && allowed_term.strip.empty? ensure_regex_string(allowed_term) end end
#scan_for_words(input) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 236
def scan_for_words(input) masked_input = mask_input(input) return EMPTY_ARRAY unless masked_input.match?(@flagged_terms_regex) masked_input.enum_for(:scan, @flagged_terms_regex).map do match = Regexp.last_match WordLocation.new(match.to_s, match.offset(0).first) end end
#set_regexes(flagged_term_strings, allowed_strings) (private)
[ GitHub ]# File 'lib/rubocop/cop/naming/inclusive_language.rb', line 185
def set_regexes(flagged_term_strings, allowed_strings) # With no flagged terms an empty regex would match everything, so leave the # regex nil and let `on_new_investigation` treat the cop as a no-op. unless flagged_term_strings.empty? @flagged_terms_regex = array_to_ignorecase_regex(flagged_term_strings) end @allowed_regex = array_to_ignorecase_regex(allowed_strings) unless allowed_strings.empty? end