rubocop-socketry 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45d8b27941b7424a315428ba04dd6dc98218896b54551f49f0cdde531c41b1af
4
- data.tar.gz: d602b39fd19660eb34908a17c0681fd884d7d2fb9b14395e1f2070ee54910ced
3
+ metadata.gz: 3bdf0fc2d1db810ae5d2bb5d15895f763062e83a4cff437e17f9707b4f9f7d27
4
+ data.tar.gz: f615231a3fa949865a21623f160195da22ad401e2198db990a5ebffa9554dea4
5
5
  SHA512:
6
- metadata.gz: f3dbbc01b527507b658225ad95dcce305a1c33764636a88d3f83f7772a9c5264e7729d4d554335c4427c13f66b3a7535ef05e11842c0acddfa88d143a9cea83b
7
- data.tar.gz: a09b66c884a2258df7e9f53010af7fe638ac6454b4c36195406136b694116f9f5cb16465e7d67bdbddfd872b525f10dfc104cd8bcc659c323209b5a4f759c2fe
6
+ metadata.gz: aa0b76dd902e2bbfc9409a54c242b15912cc850c9c2a3f26618adedc5a6275eb0d2c690eb224b23f0fbe620e777f2f0efbf0c58a3d7c2d7e324e233323e736d7
7
+ data.tar.gz: 40c8ad00c40ee7619c6f3f9ccf3e22bd5a4a71c1920683c326a21a80cd90620bb4f34acaeabe469c01991e4ee304e38aed89381fb05a2a7b66652e50bee9a0ff
checksums.yaml.gz.sig CHANGED
Binary file
@@ -11,19 +11,21 @@ module RuboCop
11
11
  # A RuboCop cop that enforces consistent spacing before block delimiters.
12
12
  #
13
13
  # This cop enforces the following style:
14
- # - `foo {bar}` - space when method has no parentheses and is not chained
15
- # - `foo(1, 2) {bar}` - space after closing paren for standalone methods
16
- # - `array.each{|x| x*2}.reverse` - no space for method chains (even with parens)
17
- # - `->(foo){foo}` - no space for lambdas (stabby lambda syntax)
18
- # - `lambda{foo}` - no space for lambda keyword
19
- # - `proc{foo}` - no space for proc keyword
20
- # - `Proc.new{foo}` - no space for Proc.new
14
+ # - `foo {bar}` - space for top-level statements without parentheses.
15
+ # - `x = foo{bar}` - no space when part of an expression (assignment, argument, etc).
16
+ # - `foo(1, 2) {bar}` - space after closing paren for top-level statements.
17
+ # - `array.each{|x| x*2}.reverse` - no space for method chains.
18
+ # - `->(foo){foo}` - no space for lambdas (stabby lambda syntax).
19
+ # - `lambda{foo}` - no space for lambda keyword.
20
+ # - `proc{foo}` - no space for proc keyword.
21
+ # - `Proc.new{foo}` - no space for `Proc.new`.
21
22
  class BlockDelimiterSpacing < RuboCop::Cop::Base
22
23
  extend Cop::AutoCorrector
23
24
 
24
25
  MSG_ADD_SPACE = "Add a space before the opening brace."
25
26
  MSG_REMOVE_SPACE = "Remove space before the opening brace for method chains."
26
27
  MSG_REMOVE_SPACE_LAMBDA = "Remove space before the opening brace for lambdas/procs."
28
+ MSG_REMOVE_SPACE_EXPRESSION = "Remove space before the opening brace for expressions."
27
29
 
28
30
  def on_block(node)
29
31
  return unless node.braces?
@@ -44,6 +46,12 @@ module RuboCop
44
46
  # array.each{|x| x*2}.reverse - no space
45
47
  # obj.method(1, 2){|x| x}.other - also no space
46
48
  check_no_space_before_brace(node, send_node)
49
+ # Priority 3: Check if it's part of an expression (not top-level)
50
+ # Blocks within expressions should have no space
51
+ elsif part_of_expression?(node)
52
+ # x = Async{server.run} - no space (part of assignment)
53
+ # foo(bar{baz}) - no space (part of argument)
54
+ check_no_space_for_expression(node, send_node)
47
55
  elsif has_parentheses?(send_node)
48
56
  # foo(1, 2) {bar} - space after ) for standalone methods
49
57
  check_space_after_parentheses(node, send_node)
@@ -71,6 +79,18 @@ module RuboCop
71
79
  false
72
80
  end
73
81
 
82
+ # Check if the block is part of an expression (not a top-level statement)
83
+ # Top-level statements are directly inside a :begin node (file/method body)
84
+ # and should have space. Everything else (expressions, nested blocks) should not.
85
+ def part_of_expression?(node)
86
+ parent = node.parent
87
+ return false unless parent
88
+
89
+ # If parent is a :begin node (sequence of statements), this is top-level
90
+ # Otherwise, it's part of an expression or nested context
91
+ parent.type != :begin
92
+ end
93
+
74
94
  # Check that there's no space before the opening brace for lambdas
75
95
  def check_no_space_for_lambda(block_node, send_node)
76
96
  brace_begin = block_node.loc.begin
@@ -105,6 +125,40 @@ module RuboCop
105
125
  end
106
126
  end
107
127
 
128
+ # Check that there's no space before the opening brace for expressions
129
+ def check_no_space_for_expression(block_node, send_node)
130
+ brace_begin = block_node.loc.begin
131
+
132
+ # Find the position just before the brace
133
+ char_before_pos = brace_begin.begin_pos - 1
134
+
135
+ return if char_before_pos < 0
136
+
137
+ char_before = processed_source.buffer.source[char_before_pos]
138
+
139
+ # If there's no space before the brace, we're good
140
+ return unless char_before == " "
141
+
142
+ # Find the extent of whitespace before the brace
143
+ start_pos = char_before_pos
144
+ while start_pos > 0 && processed_source.buffer.source[start_pos - 1] =~ /\s/
145
+ start_pos -= 1
146
+ end
147
+
148
+ space_range = Parser::Source::Range.new(
149
+ processed_source.buffer,
150
+ start_pos,
151
+ brace_begin.begin_pos
152
+ )
153
+
154
+ add_offense(
155
+ space_range,
156
+ message: MSG_REMOVE_SPACE_EXPRESSION
157
+ ) do |corrector|
158
+ corrector.remove(space_range)
159
+ end
160
+ end
161
+
108
162
  # Check if the block is part of a method chain (e.g., foo{}.bar or foo.bar{}.baz)
109
163
  def part_of_method_chain?(block_node)
110
164
  send_node = block_node.send_node
@@ -8,7 +8,7 @@ require "rubocop"
8
8
  module RuboCop
9
9
  module Socketry
10
10
  module Style
11
- # A RuboCop cop that warns against using global exception variables.
11
+ # A RuboCop cop that warns against using global exception variables in unsafe contexts.
12
12
  #
13
13
  # This cop discourages the use of:
14
14
  # - `$!` (last exception)
@@ -17,26 +17,52 @@ module RuboCop
17
17
  # - `$ERROR_POSITION` (English name for `$@`)
18
18
  #
19
19
  # These global variables are implicit and can make code harder to understand.
20
- # Instead, use explicit exception handling with rescue blocks and local variables.
20
+ #
21
+ # However, this cop allows their use in safe contexts where the scope is well-defined:
22
+ # - Inside rescue blocks (well-defined scope)
23
+ # - In rescue modifiers (`expression rescue $!`)
24
+ # - In method parameter defaults (`def foo(error = $!)`, `def bar(error: $!)`)
25
+ #
26
+ # This cop specifically flags their use in unsafe contexts:
27
+ # - Inside ensure blocks (extremely unsafe - exception state is unpredictable)
28
+ # - Outside of exception handling contexts
21
29
  #
22
30
  # @example
23
- # # bad
31
+ # # bad - unsafe in ensure block
24
32
  # begin
25
33
  # risky_operation
26
- # rescue
34
+ # ensure
35
+ # log($!.message) if $! # unsafe!
36
+ # end
37
+ #
38
+ # # bad - outside exception handling
39
+ # def process
27
40
  # puts $!.message
28
- # puts $@.first
29
41
  # end
30
42
  #
31
- # # good
43
+ # # good - explicit exception handling
32
44
  # begin
33
45
  # risky_operation
34
46
  # rescue => error
35
47
  # puts error.message
36
- # puts error.backtrace.first
37
48
  # end
49
+ #
50
+ # # allowed - inside rescue block (well-defined scope)
51
+ # begin
52
+ # risky_operation
53
+ # rescue
54
+ # puts $!.message
55
+ # end
56
+ #
57
+ # # allowed - rescue modifier
58
+ # result = risky_operation rescue $!
59
+ #
60
+ # # allowed - parameter defaults
61
+ # def foo(error = $!)
62
+ # def bar(error: $!)
38
63
  class GlobalExceptionVariables < RuboCop::Cop::Base
39
- MSG = "Avoid using global exception variable `%<variable>s`. Use explicit exception handling with `rescue => error` instead."
64
+ MSG = "Avoid using global exception variable `%<variable>s` in %<context>s. Use explicit exception handling with `rescue => error` instead."
65
+ ENSURE_MSG = "Using global exception variable `%<variable>s` in an ensure block is extremely unsafe."
40
66
 
41
67
  EXCEPTION_VARIABLES = %i[$! $@ $ERROR_INFO $ERROR_POSITION].freeze
42
68
 
@@ -45,11 +71,53 @@ module RuboCop
45
71
 
46
72
  return unless EXCEPTION_VARIABLES.include?(variable_name)
47
73
 
74
+ # Allow in parameter defaults (explicitly opting in)
75
+ return if in_parameter_default?(node)
76
+
77
+ # Allow in rescue modifier (well-defined scope)
78
+ return if in_rescue_modifier?(node)
79
+
80
+ # Allow in rescue block (well-defined scope)
81
+ return if in_rescue_block?(node)
82
+
83
+ # Flag if in ensure block (extremely unsafe)
84
+ if in_ensure_block?(node)
85
+ add_offense(
86
+ node,
87
+ message: format(ENSURE_MSG, variable: variable_name)
88
+ )
89
+ return
90
+ end
91
+
92
+ # Flag in all other contexts
48
93
  add_offense(
49
94
  node,
50
- message: format(MSG, variable: variable_name)
95
+ message: format(MSG, variable: variable_name, context: "this context")
51
96
  )
52
97
  end
98
+
99
+ private
100
+
101
+ def in_parameter_default?(node)
102
+ node.each_ancestor(:args, :optarg, :kwoptarg).any?
103
+ end
104
+
105
+ def in_rescue_modifier?(node)
106
+ node.each_ancestor(:rescue).any? do |ancestor|
107
+ # A rescue modifier has no resbody children
108
+ # e.g., `expression rescue $!` is a rescue node with 2 children: expression and handler
109
+ # A regular rescue has resbody children
110
+ !ancestor.children.any?{|child| child.is_a?(RuboCop::AST::Node) && child.type == :resbody}
111
+ end
112
+ end
113
+
114
+ def in_rescue_block?(node)
115
+ node.each_ancestor(:resbody).any?
116
+ end
117
+
118
+ def in_ensure_block?(node)
119
+ node.each_ancestor(:ensure).any?
120
+ end
53
121
  end
54
122
  end
55
123
  end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module RuboCop
7
7
  module Socketry
8
- VERSION = "0.6.0"
8
+ VERSION = "0.7.0"
9
9
  end
10
10
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-socketry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
105
  requirements: []
106
- rubygems_version: 3.6.9
106
+ rubygems_version: 4.0.3
107
107
  specification_version: 4
108
108
  summary: RuboCop rules for Socketry projects
109
109
  test_files: []
metadata.gz.sig CHANGED
Binary file