rubocop-dev_doc 0.3.0 → 0.3.1
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 163ac4a232d22f5d731e680d4ae882172570cdcf3085a07be7d6c3195223e365
|
|
4
|
+
data.tar.gz: a3eae8c77dcb707ddc4875be90255bea2fa33bcaeba370c7aff06436cd045661
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a605681c5f22058138abe3d8bdcd1e12eb94073f74e02fde30bc1f1df0baa538022b69fe33c4f83279cfaed995e60e5c9a4936de43cad7a89f07105c4b6e7a24
|
|
7
|
+
data.tar.gz: 0e845bcf2236245a8c4b0289a02d7731e12c9ac0023732f6135127f02f436c78f349012b98bd18127e89cdb4e70736597c2fc5313229ed3faaf572294e847168
|
data/config/default.yml
CHANGED
|
@@ -348,12 +348,17 @@ Rails/HasManyOrHasOneDependent:
|
|
|
348
348
|
Enabled: true
|
|
349
349
|
|
|
350
350
|
DevDoc/Auth/LoadResourceCurrentUserGuard:
|
|
351
|
-
Description: "Require a `return unless current_user` guard before using `current_user` inside `glib_load_resource`, and forbid `current_user&.`."
|
|
351
|
+
Description: "Require a `return unless current_user` (or `assert_current_user_present`) guard before using `current_user` inside `glib_load_resource`, and forbid `current_user&.`."
|
|
352
352
|
Enabled: true
|
|
353
353
|
# LoadResourceMethodNames: list of method names where the guard rule applies.
|
|
354
354
|
# Extend if your project uses a different lifecycle hook name.
|
|
355
355
|
LoadResourceMethodNames:
|
|
356
356
|
- glib_load_resource
|
|
357
|
+
# CurrentUserAssertionMethodNames: calls that raise when current_user is nil
|
|
358
|
+
# (e.g. glib's assert_current_user_present). A call to one before the first
|
|
359
|
+
# current_user use satisfies the guard. Add custom assertion helpers here.
|
|
360
|
+
CurrentUserAssertionMethodNames:
|
|
361
|
+
- assert_current_user_present
|
|
357
362
|
|
|
358
363
|
DevDoc/Auth/CurrentUserBranching:
|
|
359
364
|
Description: "Avoid branching on `current_user` / `user_signed_in?` in page-specific code. Use shared layouts or an inline disable with a reason for genuinely dual-state pages."
|
|
@@ -36,6 +36,24 @@ module RuboCop
|
|
|
36
36
|
# end
|
|
37
37
|
# end
|
|
38
38
|
#
|
|
39
|
+
# glib's `assert_current_user_present` raises when `current_user` is nil,
|
|
40
|
+
# so it guarantees non-nil just as well as the early return — and any
|
|
41
|
+
# `raise` guard works like the `return` form:
|
|
42
|
+
#
|
|
43
|
+
# ✔️
|
|
44
|
+
# def glib_load_resource
|
|
45
|
+
# assert_current_user_present
|
|
46
|
+
#
|
|
47
|
+
# @post = current_user.posts.find(params[:id])
|
|
48
|
+
# end
|
|
49
|
+
#
|
|
50
|
+
# ✔️
|
|
51
|
+
# def glib_load_resource
|
|
52
|
+
# raise UnauthorizedError unless current_user
|
|
53
|
+
#
|
|
54
|
+
# @post = current_user.posts.find(params[:id])
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
39
57
|
# ❌ Safe navigation — hides the pre-auth nil risk
|
|
40
58
|
# def glib_load_resource
|
|
41
59
|
# @post = current_user&.posts&.find(params[:id])
|
|
@@ -51,6 +69,13 @@ module RuboCop
|
|
|
51
69
|
# method names where the guard rule applies. Projects standardising on a
|
|
52
70
|
# different lifecycle method can add it here.
|
|
53
71
|
#
|
|
72
|
+
# `CurrentUserAssertionMethodNames` (default:
|
|
73
|
+
# `[assert_current_user_present]`) — calls that raise when `current_user`
|
|
74
|
+
# is nil. A call to one of these before the first `current_user` use
|
|
75
|
+
# satisfies the guard. Add project-specific assertion helpers here. NOTE:
|
|
76
|
+
# the cop trusts the named method to actually raise on nil — a configured
|
|
77
|
+
# name that doesn't will turn into a false negative.
|
|
78
|
+
#
|
|
54
79
|
# NOTE: The cop performs structural analysis of the method body and does
|
|
55
80
|
# not track aliasing. If you assign `current_user` to a local variable
|
|
56
81
|
# and then call methods on that variable, the cop will not flag it —
|
|
@@ -73,16 +98,32 @@ module RuboCop
|
|
|
73
98
|
#
|
|
74
99
|
# @post = current_user.posts.find(params[:id])
|
|
75
100
|
# end
|
|
101
|
+
#
|
|
102
|
+
# # good — guarded with the glib assertion (raises when nil)
|
|
103
|
+
# def glib_load_resource
|
|
104
|
+
# assert_current_user_present
|
|
105
|
+
#
|
|
106
|
+
# @post = current_user.posts.find(params[:id])
|
|
107
|
+
# end
|
|
76
108
|
class LoadResourceCurrentUserGuard < Base
|
|
77
109
|
MSG_SAFE_NAV = 'Avoid `current_user&.` inside `%<method>s` — use ' \
|
|
78
110
|
'`return unless current_user` then `current_user.` instead.'.freeze
|
|
79
111
|
MSG_MISSING_GUARD = '`current_user` is used without a prior ' \
|
|
80
|
-
'`return unless current_user`
|
|
112
|
+
'`return unless current_user` (or `assert_current_user_present`) ' \
|
|
113
|
+
'guard in `%<method>s`. ' \
|
|
81
114
|
'This method runs before the policy, so `current_user` may be nil.'.freeze
|
|
82
115
|
|
|
83
|
-
#
|
|
116
|
+
# Predicates safe to call on a nil `current_user` — a `current_user.nil?`
|
|
117
|
+
# etc. is not itself an unguarded use that risks NoMethodError.
|
|
84
118
|
NIL_CHECK_METHODS = %i[nil? blank? present? empty?].freeze
|
|
85
119
|
|
|
120
|
+
# Predicates split by polarity, so a guard's exit branch can be matched
|
|
121
|
+
# to the path on which `current_user` is nil. `present?` is a PRESENCE
|
|
122
|
+
# check (reversed from `nil?`/`blank?`), so `unless current_user.present?`
|
|
123
|
+
# is a valid guard while `if current_user.present?` is not.
|
|
124
|
+
ABSENCE_CHECK_METHODS = %i[nil? blank? empty?].freeze
|
|
125
|
+
PRESENCE_CHECK_METHODS = %i[present?].freeze
|
|
126
|
+
|
|
86
127
|
def on_def(node)
|
|
87
128
|
check_load_resource_method(node)
|
|
88
129
|
end
|
|
@@ -94,6 +135,10 @@ module RuboCop
|
|
|
94
135
|
Array(cop_config.fetch('LoadResourceMethodNames', ['glib_load_resource'])).map(&:to_sym)
|
|
95
136
|
end
|
|
96
137
|
|
|
138
|
+
def current_user_assertion_method_names
|
|
139
|
+
Array(cop_config.fetch('CurrentUserAssertionMethodNames', ['assert_current_user_present'])).map(&:to_sym)
|
|
140
|
+
end
|
|
141
|
+
|
|
97
142
|
def load_resource_method?(method_name)
|
|
98
143
|
load_resource_method_names.include?(method_name.to_sym)
|
|
99
144
|
end
|
|
@@ -122,42 +167,30 @@ module RuboCop
|
|
|
122
167
|
|
|
123
168
|
# `current_user&.something` — always an offense regardless of guards.
|
|
124
169
|
def safe_nav_on_current_user?(node)
|
|
125
|
-
node.csend_type? &&
|
|
170
|
+
node.csend_type? && bare_current_user?(node.receiver)
|
|
126
171
|
end
|
|
127
172
|
|
|
128
|
-
# `current_user.something` where `something` is not a nil check and
|
|
129
|
-
#
|
|
173
|
+
# `current_user.something` where `something` is not a nil check and no
|
|
174
|
+
# dominating guard protects the call — i.e. not inside an `if current_user`
|
|
175
|
+
# then-branch and not preceded by a guard statement in its enclosing `begin`.
|
|
130
176
|
def unguarded_current_user_call?(node)
|
|
131
177
|
return false unless node.send_type?
|
|
132
|
-
return false unless
|
|
133
|
-
return false if
|
|
134
|
-
|
|
135
|
-
!dominated_by_guard?(node)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Does the direct receiver of `node` resolve to bare `current_user`?
|
|
139
|
-
def current_user_receiver?(node)
|
|
140
|
-
recv = node.receiver
|
|
141
|
-
recv&.send_type? && recv.method_name == :current_user && recv.receiver.nil?
|
|
142
|
-
end
|
|
178
|
+
return false unless bare_current_user?(node.receiver)
|
|
179
|
+
return false if NIL_CHECK_METHODS.include?(node.method_name)
|
|
143
180
|
|
|
144
|
-
|
|
145
|
-
NIL_CHECK_METHODS.include?(method_name)
|
|
181
|
+
!(inside_current_user_branch?(node) || preceded_by_guard?(node))
|
|
146
182
|
end
|
|
147
183
|
|
|
148
|
-
#
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
# `begin` sequence (handles flat sequences AND nested `when` branches).
|
|
152
|
-
def dominated_by_guard?(node)
|
|
153
|
-
inside_current_user_branch?(node) || preceded_by_guard?(node)
|
|
184
|
+
# True if `node` is a bare `current_user` send (no receiver).
|
|
185
|
+
def bare_current_user?(node)
|
|
186
|
+
node&.send_type? && node.method_name == :current_user && node.receiver.nil?
|
|
154
187
|
end
|
|
155
188
|
|
|
156
189
|
# Walk ancestor `if` nodes and return true when `node` is inside the
|
|
157
190
|
# then-branch of an `if current_user` (not the else-branch).
|
|
158
191
|
def inside_current_user_branch?(node)
|
|
159
192
|
node.each_ancestor(:if) do |if_node|
|
|
160
|
-
next unless
|
|
193
|
+
next unless bare_current_user?(if_node.condition)
|
|
161
194
|
|
|
162
195
|
if_br = if_node.if_branch
|
|
163
196
|
return true if if_br && (if_br.equal?(node) || descendant_by_identity?(if_br, node))
|
|
@@ -186,42 +219,66 @@ module RuboCop
|
|
|
186
219
|
root.each_descendant.any? { |d| d.equal?(descendant) }
|
|
187
220
|
end
|
|
188
221
|
|
|
189
|
-
# True if `stmt` is
|
|
190
|
-
#
|
|
191
|
-
# return if current_user.nil? — condition: current_user.nil?, one branch: (return)
|
|
192
|
-
# return if current_user.blank? — condition: current_user.blank?, one branch: (return)
|
|
222
|
+
# True if `stmt` guarantees `current_user` is non-nil for everything
|
|
223
|
+
# that follows it — either:
|
|
193
224
|
#
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
#
|
|
225
|
+
# 1. a bare call to a configured assertion helper (default
|
|
226
|
+
# `assert_current_user_present`), which raises when nil; or
|
|
227
|
+
# 2. an early-exit guard whose branch returns OR raises:
|
|
228
|
+
# return unless current_user raise ... unless current_user
|
|
229
|
+
# return if current_user.nil? raise ... if current_user.blank?
|
|
197
230
|
def guard_statement?(stmt)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
condition = stmt.condition
|
|
201
|
-
return false unless current_user_truthy_condition?(condition) ||
|
|
202
|
-
current_user_nil_condition?(condition)
|
|
231
|
+
assertion_guard_call?(stmt) || exit_guard_statement?(stmt)
|
|
232
|
+
end
|
|
203
233
|
|
|
204
|
-
|
|
234
|
+
# A bare call (nil receiver) to a "raise when current_user is nil"
|
|
235
|
+
# helper, e.g. glib's `assert_current_user_present`.
|
|
236
|
+
def assertion_guard_call?(stmt)
|
|
237
|
+
stmt.send_type? && stmt.receiver.nil? &&
|
|
238
|
+
current_user_assertion_method_names.include?(stmt.method_name)
|
|
205
239
|
end
|
|
206
240
|
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
241
|
+
# A `return`/`raise` guard whose exit happens on the NIL path — the only
|
|
242
|
+
# polarity that actually protects later `current_user` use:
|
|
243
|
+
# `... unless current_user` exits via the else branch
|
|
244
|
+
# `... if current_user.nil?` exits via the if branch
|
|
245
|
+
# (Ruby models an `if`/`unless` modifier as an `if` node with an empty
|
|
246
|
+
# opposite branch, so we tie the required exit to the condition polarity.
|
|
247
|
+
# This rejects inverted guards like `return if current_user`.)
|
|
248
|
+
# A `return`/`raise` guard is valid only when the branch that runs while
|
|
249
|
+
# `current_user` is nil exits. `if_branch` is the written body regardless
|
|
250
|
+
# of `if`/`unless`, so combine the condition's polarity with the keyword to
|
|
251
|
+
# pick that branch. This rejects inverted guards (`return if current_user`).
|
|
252
|
+
def exit_guard_statement?(stmt)
|
|
253
|
+
return false unless stmt.if_type?
|
|
254
|
+
|
|
255
|
+
polarity = condition_polarity(stmt.condition)
|
|
256
|
+
return false unless polarity
|
|
257
|
+
|
|
258
|
+
body_runs_when_nil = polarity == (stmt.unless? ? :presence : :absence)
|
|
259
|
+
branch_exits?(body_runs_when_nil ? stmt.if_branch : stmt.else_branch)
|
|
212
260
|
end
|
|
213
261
|
|
|
214
|
-
#
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
262
|
+
# :presence — condition truthy when current_user is present (`current_user`,
|
|
263
|
+
# `current_user.present?`); :absence — truthy when absent (`.nil?`/`.blank?`/
|
|
264
|
+
# `.empty?`); nil — not a current_user condition.
|
|
265
|
+
def condition_polarity(condition)
|
|
266
|
+
return :presence if bare_current_user?(condition)
|
|
267
|
+
return unless condition&.send_type? && bare_current_user?(condition.receiver)
|
|
268
|
+
return :presence if PRESENCE_CHECK_METHODS.include?(condition.method_name)
|
|
218
269
|
|
|
219
|
-
|
|
220
|
-
recv&.send_type? && recv.method_name == :current_user && recv.receiver.nil?
|
|
270
|
+
:absence if ABSENCE_CHECK_METHODS.include?(condition.method_name)
|
|
221
271
|
end
|
|
222
272
|
|
|
223
|
-
|
|
224
|
-
|
|
273
|
+
# True if `branch` terminates on entry via an early `return` or a bare
|
|
274
|
+
# `raise`/`fail`. A multi-statement branch (a `begin`) counts when its
|
|
275
|
+
# LAST statement does.
|
|
276
|
+
def branch_exits?(branch)
|
|
277
|
+
return false unless branch
|
|
278
|
+
|
|
279
|
+
branch = branch.children.last if branch.begin_type?
|
|
280
|
+
branch&.return_type? ||
|
|
281
|
+
(branch&.send_type? && branch.receiver.nil? && %i[raise fail].include?(branch.method_name))
|
|
225
282
|
end
|
|
226
283
|
end
|
|
227
284
|
end
|