CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/rubocop/cop/layout/module_description_indentation.rb
Views: 11784
1
module RuboCop
2
module Cop
3
module Layout
4
class ModuleDescriptionIndentation < Base
5
extend AutoCorrector
6
include Alignment
7
8
MSG = "Module descriptions should be properly aligned to the 'Description' key, and within %q{ ... }"
9
10
def_node_matcher :find_update_info_node, <<~PATTERN
11
(def :initialize _args (begin (super $(send nil? {:update_info :merge_info} (lvar :info) (hash ...))) ...))
12
PATTERN
13
14
def_node_matcher :find_nested_update_info_node, <<~PATTERN
15
(def :initialize _args (super $(send nil? {:update_info :merge_info} (lvar :info) (hash ...)) ...))
16
PATTERN
17
18
def on_def(node)
19
update_info_node = find_update_info_node(node) || find_nested_update_info_node(node)
20
return if update_info_node.nil?
21
22
hash = update_info_node.arguments.find { |argument| hash_arg?(argument) }
23
hash.each_pair do |key, value|
24
if key.value == "Description"
25
if requires_correction?(key, value)
26
add_offense(value.location.end, &autocorrector(value))
27
end
28
end
29
end
30
end
31
32
private
33
34
def autocorrector(description_value)
35
lambda do |corrector|
36
description_key = description_value.parent.key
37
new_content = indent_description_value_correctly(description_key, description_value)
38
39
corrector.replace(description_value.source_range, new_content)
40
end
41
end
42
43
def requires_correction?(description_key, description_value)
44
return false if description_value.single_line?
45
46
current_content = description_value.source
47
expected_content = indent_description_value_correctly(description_key, description_value)
48
expected_content != current_content
49
end
50
51
def indent_description_value_correctly(description_key, description_value)
52
content_whitespace = indentation(description_key)
53
final_line_whitespace = offset(description_key)
54
55
description_lines = node_content(description_value).strip.lines
56
indented_description = description_lines.map do |line|
57
cleaned_content = line.strip
58
if cleaned_content.empty?
59
"\n"
60
else
61
"#{content_whitespace}#{cleaned_content}\n"
62
end
63
end.join
64
65
new_literal = "%q{\n"
66
new_literal <<= indented_description
67
new_literal <<= final_line_whitespace
68
new_literal <<= '}'
69
70
new_literal
71
end
72
73
def node_content(node)
74
if node.str_type?
75
node.value
76
elsif node.dstr_type?
77
node.children.map(&:value).join
78
else
79
raise "Module description should be a string, instead found '#{node.type}'"
80
end
81
end
82
83
def hash_arg?(node)
84
node.type == :hash
85
end
86
end
87
end
88
end
89
end
90
91