diff --git a/.rubocop_todo/graphql/descriptions.yml b/.rubocop_todo/graphql/descriptions.yml index 7a19a0cd1263807cac817d2911110787654c196b..db7e899cf05f6971fc789300252b6933fcc601f6 100644 --- a/.rubocop_todo/graphql/descriptions.yml +++ b/.rubocop_todo/graphql/descriptions.yml @@ -1,6 +1,7 @@ --- # Cop supports --autocorrect. Graphql/Descriptions: + Safe: false Details: grace period Exclude: - 'app/graphql/mutations/boards/lists/base_update.rb' diff --git a/rubocop/cop/graphql/descriptions.rb b/rubocop/cop/graphql/descriptions.rb index 0234ad995210c39bd8f4001d9fb664f7ad07f2c0..239f5b966a4b8c7b046f909c951939a397a216aa 100644 --- a/rubocop/cop/graphql/descriptions.rb +++ b/rubocop/cop/graphql/descriptions.rb @@ -3,6 +3,11 @@ # This cop checks for missing GraphQL descriptions and enforces the description style guide: # https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#description-style-guide # +# @safety +# This cop is unsafe because not all cases of "this" can be substituted with +# "the". This will require a technical writer to assist with the alternative, +# proper grammar that can be used for that particular GraphQL descriptions. +# # @examples # # # bad @@ -94,6 +99,7 @@ def on_send(node) next unless description corrector.insert_after(before_end_quote(description), '.') if no_period?(description) + corrector.replace(locate_this(description), 'the') if contains_demonstrative_this?(description) end end @@ -134,6 +140,14 @@ def before_end_quote(string) adjust = heredoc_source.index(/\s+\Z/) - heredoc_source.length string.location.heredoc_body.adjust(end_pos: adjust) end + + # Returns a `Parser::Source::Range` of the first `this` encountered + def locate_this(string) + target = 'this' + range = string.heredoc? ? string.location.heredoc_body : string.location.expression + index = range.source.index(target) + range.adjust(begin_pos: index, end_pos: (index + target.length) - range.length) + end end end end diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb index 6aee4b544f6c5d6357c6c845b1607838c581a42e..0ff2812d6f646d0b600870d308903ea404f948b2 100644 --- a/spec/rubocop/cop/graphql/descriptions_spec.rb +++ b/spec/rubocop/cop/graphql/descriptions_spec.rb @@ -79,10 +79,19 @@ class FakeType < BaseObject end TYPE - expect_no_corrections + expect_correction(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + GraphQL::Types::String, + null: false, + description: 'Description of the thing.' + end + end + TYPE end - it 'does not add an offense when a word contains the substring "this"' do + it 'does not add an offense when a word does not contain the substring "this"' do expect_no_offenses(<<~TYPE) module Types class FakeType < BaseObject @@ -194,10 +203,19 @@ class FakeType < BaseObject end TYPE - expect_no_corrections + expect_correction(<<~TYPE) + module Types + class FakeType < BaseObject + argument :a_thing, + GraphQL::Types::String, + null: false, + description: 'Description of the thing.' + end + end + TYPE end - it 'does not add an offense when a word contains the substring "this"' do + it 'does not add an offense when a word does not contain the substring "this"' do expect_no_offenses(<<~TYPE) module Types class FakeType < BaseObject @@ -285,10 +303,16 @@ class FakeEnum < BaseEnum end TYPE - expect_no_corrections + expect_correction(<<~TYPE.strip) + module Types + class FakeEnum < BaseEnum + value 'FOO', value: 'foo', description: 'Description of the issue.' + end + end + TYPE end - it 'does not add an offense when a word contains the substring "this"' do + it 'does not add an offense when a word does not contain the substring "this"' do expect_no_offenses(<<~TYPE.strip) module Types class FakeEnum < BaseEnum @@ -402,4 +426,88 @@ class FakeType < BaseObject TYPE end end + + describe 'autocorrecting "this" to "the"' do + it 'autocorrects if "this" is found' do + expect_offense(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + ^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS} + GraphQL::Types::String, + null: false, + description: 'Description of this thing.' + end + end + TYPE + + expect_correction(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + GraphQL::Types::String, + null: false, + description: 'Description of the thing.' + end + end + TYPE + end + + it 'does not autocorrect if "this" is not found' do + expect_no_offenses(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + GraphQL::Types::String, + null: false, + description: 'Description of the thing.' + end + end + TYPE + end + + it 'autocorrects a heredoc if "this" is found' do + expect_offense(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + ^^^^^^^^^^^^^^^ #{described_class::MSG_CONTAINS_THIS} + GraphQL::Types::String, + null: false, + description: <<~DESC + Description of this thing. + DESC + end + end + TYPE + + expect_correction(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + GraphQL::Types::String, + null: false, + description: <<~DESC + Description of the thing. + DESC + end + end + TYPE + end + + it 'does not autocorrect a heredoc if "this" is not found' do + expect_no_offenses(<<~TYPE) + module Types + class FakeType < BaseObject + field :a_thing, + GraphQL::Types::String, + null: false, + description: <<~DESC + Description of the thing. + DESC + end + end + TYPE + end + end end