Class: RuboCop::Cop::Style::RbsInline::DataClassCommentAlignment

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

Overview

Checks that #: inline type annotations in Data.define blocks are aligned.

Each #: annotation comment should start at the same column, determined by the longest attribute name (plus trailing comma). Folded Data.define calls (where attributes are not one per line) are excluded.

Examples:

# bad
MethodEntry = Data.define(
  :name, #: Symbol
  :node,       #: Parser::AST::Node
  :visibility  #: Symbol
)

# good
MethodEntry = Data.define(
  :name,       #: Symbol
  :node,       #: Parser::AST::Node
  :visibility  #: Symbol
)

Constant Summary collapse

MSG =

Returns:

  • (::String)
'Misaligned inline type annotation for Data attribute.'

Instance Method Summary collapse

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 ASTUtils

#end_line, #name_location, #source!, #value_to_sym

Instance Method Details

#annotation_column(node) ⇒ Integer

Parameters:

  • node (RuboCop::AST::SendNode)

Returns:

  • (Integer)


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

def annotation_column(node) #: Integer
  last_arg = node.arguments.last
  max_end_col = node.arguments.map do |arg|
    comma_length = arg.equal?(last_arg) ? 0 : 1
    arg.location.column + source!(arg).length + comma_length
  end.max || 0

  max_end_col + 2
end

#check_annotation_alignment(node) ⇒ void

This method returns an undefined value.

Parameters:

  • node (RuboCop::AST::SendNode)


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 78

def check_annotation_alignment(node) #: void
  annotated = data_attributes(node).filter_map do |arg|
    comment = inline_type_comment(arg.location.line)
    [arg, comment] if comment #: [RuboCop::AST::Node, Parser::Source::Comment]
  end
  return if annotated.size < 2

  expected_col = annotation_column(node)
  annotated.each do |arg, comment|
    actual_col = comment.location.column
    next if actual_col == expected_col

    add_offense(comment.source_range) do |corrector|
      correct_alignment(corrector, arg, comment, expected_col)
    end
  end
end

#correct_alignment(corrector, arg, comment, expected_col) ⇒ void

This method returns an undefined value.

Parameters:

  • corrector (RuboCop::Cop::Corrector)
  • arg (RuboCop::AST::Node)
  • comment (Parser::Source::Comment)
  • expected_col (Integer)


117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 117

def correct_alignment(corrector, arg, comment, expected_col) #: void # rubocop:disable Metrics/AbcSize
  line = arg.location.line
  line_source = source_code_at(line)
  source = line_source[...comment.location.column] || raise
  content_end_col = source.rstrip.length
  padding = [expected_col - content_end_col, 1].max || raise

  line_begin = processed_source.buffer.line_range(line).begin_pos
  replace_start = line_begin + content_end_col
  replace_end = line_begin + comment.location.column

  corrector.replace(range_between(replace_start, replace_end), ' ' * padding)
end

#data_attribute?(arg) ⇒ Boolean

Parameters:

  • arg (RuboCop::AST::Node)

Returns:

  • (Boolean)


59
60
61
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 59

def data_attribute?(arg) #: bool
  arg.sym_type? || arg.str_type?
end

#data_attributes(node) ⇒ Array[RuboCop::AST::Node]

Parameters:

  • node (RuboCop::AST::SendNode)

Returns:

  • (Array[RuboCop::AST::Node])


64
65
66
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 64

def data_attributes(node) #: Array[RuboCop::AST::Node]
  node.arguments.select { data_attribute?(_1) }
end

#data_define?(node) ⇒ Boolean

Parameters:

  • node (RuboCop::AST::SendNode)

Returns:

  • (Boolean)


47
48
49
50
51
52
53
54
55
56
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 47

def data_define?(node) #: bool
  return false unless node.method_name == :define

  case (r = node.receiver)
  when RuboCop::AST::ConstNode
    r.short_name == :Data
  else
    false
  end
end

#folded_data_class?(node) ⇒ Boolean

Parameters:

  • node (RuboCop::AST::SendNode)

Returns:

  • (Boolean)


69
70
71
72
73
74
75
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 69

def folded_data_class?(node) #: bool
  args = node.arguments
  return false if args.empty?

  lines = args.map { _1.location.line }
  lines.uniq.length < args.length || lines.include?(node.location.line)
end

#inline_type_comment(line) ⇒ Parser::Source::Comment?

Parameters:

  • line (Integer)

Returns:



97
98
99
100
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 97

def inline_type_comment(line) #: Parser::Source::Comment?
  comment = comment_at(line)
  comment if comment&.text&.match?(/\A#:/)
end

#on_send(node) ⇒ void

This method returns an undefined value.

Parameters:

  • node (RuboCop::AST::SendNode)


37
38
39
40
41
42
# File 'lib/rubocop/cop/style/rbs_inline/data_class_comment_alignment.rb', line 37

def on_send(node) #: void
  return unless data_define?(node)
  return if folded_data_class?(node)

  check_annotation_alignment(node)
end