Class: RuboCop::Cop::Style::RbsInline::MethodCommentSpacing

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Includes:
RangeHelp, CommentParser, FileFilter, SourceCodeHelper
Defined in:
lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb,
sig/rubocop/cop/style/rbs_inline/method_comment_spacing.rbs

Overview

Checks that method-related @rbs annotations are placed immediately before method definitions.

Method-related annotations (@rbs param, @rbs return, @rbs &block, @rbs override, @rbs skip, @rbs %a{...}, # @rbs (...) -> Type, #: (...) -> Type) must be placed directly before the method definition they describe, without any blank lines in between. These annotations should not appear in standalone locations without an immediately following method definition.

Examples:

# bad
# @rbs param x: Integer
# @rbs return: String

def method(x)
end

# bad
#: (Integer) -> String

def method(x)
end

# bad
# @rbs param x: Integer
# @rbs return: String
puts "something"

# good
# @rbs param x: Integer
# @rbs return: String
def method(x)
end

# good
#: (Integer) -> String
def method(x)
end

# good
# @rbs x: Integer
private_class_method def self.method(x)
end

# good
# @rbs x: Integer
private def method(x)
end

Constant Summary collapse

MSG =

Returns:

  • (::String)
"Method-related `@rbs` annotation must be immediately before a method definition."
MSG_WITH_BLANK_LINE =

Returns:

  • (::String)
"Remove blank line between method annotation and method definition."
METHOD_DEFINITION_PATTERN =

Returns:

  • (Regexp)
/\A(?:(?:private|protected|public|private_class_method|module_function)\s+)?def\s/

Constants included from FileFilter

FileFilter::MAGIC_COMMENT_DISABLED, FileFilter::MAGIC_COMMENT_ENABLED, FileFilter::SUPPORTED_MODES

Instance Attribute Summary collapse

Attributes included from CommentParser

#parsed_comments

Instance Method Summary collapse

Methods included from CommentParser

#find_doc_style_param_annotations, #find_doc_style_return_annotation, #find_last_consecutive_comment, #find_leading_annotation, #find_method_type_signature_comments, #find_trailing_comment, #overload_type_signatures?, #parse_comments, #rbs_inline_file_skipped?

Methods included from SourceCodeHelper

#annotation_range, #blank_line?, #char_at, #character_offset, #comment_at, #comment_range, #line_range, #location_to_range, #source_code_at

Methods included from FileFilter

#add_offense, #configured_mode, #rbs_inline_disabled?, #rbs_inline_enabled?, #rbs_inline_file_skipped?, #skip_by_mode?, warn_invalid_mode

Instance Attribute Details

#processed_commentsArray[RBS::Inline::AnnotationParser::ParsingResult] (readonly)

: Array

Returns:

  • (Array[RBS::Inline::AnnotationParser::ParsingResult])


70
71
72
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 70

def processed_comments
  @processed_comments
end

Instance Method Details

#check_blank_line_case(comment, blank_line_number) ⇒ void

This method returns an undefined value.

Parameters:

  • comment (Prism::Comment)
  • blank_line_number (Integer)


149
150
151
152
153
154
155
156
157
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 149

def check_blank_line_case(comment, blank_line_number) #: void
  line_after_blank = blank_line_number + 1
  if method_definition_line?(line_after_blank)
    register_blank_line_offense(blank_line_number)
  else
    range = comment_range(comment)
    add_offense(range, message: MSG)
  end
end

#check_method_annotation(comment) ⇒ void

This method returns an undefined value.

Parameters:

  • comment (RBS::Inline::AnnotationParser::ParsingResult)


102
103
104
105
106
107
108
109
110
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 102

def check_method_annotation(comment) #: void
  return unless method_related_annotation?(comment)

  processed_comments << comment

  last_comment = comment.comments.last or return
  next_line_number = last_comment.location.start_line + 1
  check_spacing_after_annotation(comment, next_line_number)
end

#check_method_comment_spacingvoid

This method returns an undefined value.

: void



80
81
82
83
84
85
86
87
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 80

def check_method_comment_spacing #: void
  parsed_comments.each do |comment|
    next if processed_comments.include?(comment)
    next if trailing_comment?(comment)

    check_method_annotation(comment)
  end
end

#check_spacing_after_annotation(comment, next_line_number) ⇒ void

This method returns an undefined value.

Parameters:

  • comment (RBS::Inline::AnnotationParser::ParsingResult)
  • next_line_number (Integer)


136
137
138
139
140
141
142
143
144
145
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 136

def check_spacing_after_annotation(comment, next_line_number) #: void
  last_comment = comment.comments.last or return

  if blank_line?(next_line_number)
    check_blank_line_case(last_comment, next_line_number)
  elsif !method_definition_line?(next_line_number) && !skip_only_annotation?(comment)
    range = comment_range(last_comment)
    add_offense(range, message: MSG)
  end
end

#method_definition_line?(line_number) ⇒ Boolean

Parameters:

  • line_number (Integer)

Returns:

  • (Boolean)


175
176
177
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 175

def method_definition_line?(line_number) #: bool
  METHOD_DEFINITION_PATTERN.match?(source_code_at(line_number).strip)
end

Parameters:

  • comment (RBS::Inline::AnnotationParser::ParsingResult)

Returns:

  • (Boolean)


113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 113

def method_related_annotation?(comment) #: bool
  comment.each_annotation do |annotation|
    case annotation
    when RBS::Inline::AST::Annotations::VarType
      # VarType includes both parameter types and variable types (@ivar, @@cvar)
      # Only consider it method-related if it's a parameter annotation (name doesn't start with @)
      return true unless annotation.name.start_with?("@")
    when RBS::Inline::AST::Annotations::ReturnType,
         RBS::Inline::AST::Annotations::BlockType,
         RBS::Inline::AST::Annotations::Override,
         RBS::Inline::AST::Annotations::RBSAnnotation,
         RBS::Inline::AST::Annotations::Skip,
         RBS::Inline::AST::Annotations::Method,
         RBS::Inline::AST::Annotations::MethodTypeAssertion
      return true
    end
  end

  false
end

#on_new_investigationvoid

This method returns an undefined value.

: void



72
73
74
75
76
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 72

def on_new_investigation #: void
  @processed_comments = []
  parse_comments
  check_method_comment_spacing
end

#register_blank_line_offense(blank_line_number) ⇒ void

This method returns an undefined value.

Parameters:

  • blank_line_number (Integer)


160
161
162
163
164
165
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 160

def register_blank_line_offense(blank_line_number) #: void
  range = line_range(blank_line_number)
  add_offense(range, message: MSG_WITH_BLANK_LINE) do |corrector|
    corrector.remove(range_by_whole_lines(range, include_final_newline: true))
  end
end

#skip_only_annotation?(comment) ⇒ Boolean

Parameters:

  • comment (RBS::Inline::AnnotationParser::ParsingResult)

Returns:

  • (Boolean)


168
169
170
171
172
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 168

def skip_only_annotation?(comment) #: bool
  annotations = [] #: Array[RBS::Inline::AST::Annotations::t]
  comment.each_annotation { annotations << _1 }
  annotations.any? && annotations.all?(RBS::Inline::AST::Annotations::Skip)
end

#trailing_comment?(comment) ⇒ Boolean

Check if this is a trailing comment after Ruby code on the same line

Parameters:

  • comment (RBS::Inline::AnnotationParser::ParsingResult)

Returns:

  • (Boolean)


91
92
93
94
95
96
97
98
99
# File 'lib/rubocop/cop/style/rbs_inline/method_comment_spacing.rb', line 91

def trailing_comment?(comment) #: bool
  return false unless comment.comments.size == 1

  first_comment = comment.comments.first or return false
  line_number = first_comment.location.start_line
  line = processed_source.lines[line_number - 1] or return false
  source = line[0, first_comment.location.start_column] || raise
  !source.strip.empty?
end