specguard-ruby 0.3.27 → 0.3.29
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 +4 -4
- data/README.md +6 -4
- data/lib/specguard/rspec/cli.rb +18 -13
- data/lib/specguard/rspec/finding.rb +9 -3
- data/lib/specguard/rspec/linter.rb +10 -6
- data/lib/specguard/rspec/scanner.rb +143 -12
- data/lib/specguard/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 887fc6d71732b1947cc48c34f27c68ac5b9b71c729f604cf95c41e508cc8d6af
|
|
4
|
+
data.tar.gz: e2f20dbc68a7324cc34381e204bc8ac0f5db262002d86ffc3bd24c8c2cfb3663
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e3d18c2b2d19c8d25b0c5a9277847767996ff27be06f7f5802b052c7a4dab7f20e7244214a339fc0a4bc0343103572ad842bd381f95ccd6efa670422da445edc
|
|
7
|
+
data.tar.gz: 9a2b14e04e71e6e957134e29ad38f11fdaad0aede17893907a048d9e6414d93affb7844f4a99e630194f5095c0bc945e271800871f39f71f325ed8b4805492ae
|
data/README.md
CHANGED
|
@@ -48,8 +48,10 @@ bundle exec ruby -rminitest/specguard_plugin -e 'Minitest.extensions << "specgua
|
|
|
48
48
|
|
|
49
49
|
Validates `# @intent:` annotations in changed (or all) `*_spec.rb` files against the OpenTestIntent
|
|
50
50
|
JSON Schema. Exits `1` on a malformed annotation — or a well-formed but unreachable one (stacked
|
|
51
|
-
above another comment-form `@intent:` line, so the one-line lookback never claims it
|
|
52
|
-
|
|
51
|
+
above another comment-form `@intent:` line, so the one-line lookback never claims it; separated
|
|
52
|
+
from its `it` by one blank or ordinary comment line, which puts it out of the lookback's reach;
|
|
53
|
+
directly above an `it` that carries its own trailing `@intent:`, which extraction prefers, so the
|
|
54
|
+
comment is shadowed; or trailing on a `describe`/`context` group line, which no example's extraction can ever reach) — and
|
|
53
55
|
**never** fails on a *missing* one (adoption is opt-in and gradual).
|
|
54
56
|
|
|
55
57
|
```bash
|
|
@@ -335,8 +337,8 @@ Every way the backend can fail — the binary is missing, will not execute, exit
|
|
|
335
337
|
is not a verdict, or emits output that is not a report — is **exit 2**, the linter's "could not do
|
|
336
338
|
my job" code. It never becomes exit 1, which means "an annotation is malformed" — or well-formed
|
|
337
339
|
but unreachable: stacked above another comment-form `@intent:` line, so the one-line lookback
|
|
338
|
-
never claims it,
|
|
339
|
-
can ever reach — and nothing else.
|
|
340
|
+
never claims it, separated from its `it` by one blank or ordinary comment line, shadowed by its
|
|
341
|
+
`it`'s own trailing `@intent:`, or trailing on a `describe`/`context` group line, which no example's extraction can ever reach — and nothing else.
|
|
340
342
|
|
|
341
343
|
## The formatter — `SpecGuard::RSpecFormatter`
|
|
342
344
|
|
data/lib/specguard/rspec/cli.rb
CHANGED
|
@@ -126,10 +126,12 @@ module SpecGuard
|
|
|
126
126
|
# "Lint, don't require": a missing annotation is never an error.
|
|
127
127
|
EXIT_OK = 0
|
|
128
128
|
# One or more annotations are malformed — or well-formed but
|
|
129
|
-
# unreachable (SPGD-900/SPGD-1510: stacked above
|
|
130
|
-
# `@intent:` line so the one-line lookback never
|
|
131
|
-
#
|
|
132
|
-
#
|
|
129
|
+
# unreachable (SPGD-900/SPGD-1510/SPGD-1554/SPGD-1560: stacked above
|
|
130
|
+
# another comment-form `@intent:` line so the one-line lookback never
|
|
131
|
+
# claims it, separated from its example by one blank/comment line,
|
|
132
|
+
# shadowed by its example's own trailing `@intent:`, or trailing on
|
|
133
|
+
# an example-group line no example's extraction can reach — a dead
|
|
134
|
+
# contract in every case, and still a failure). The only code produced by
|
|
133
135
|
# inspecting content, and the only path that reaches it is a failed
|
|
134
136
|
# {Linter::Result}.
|
|
135
137
|
EXIT_MALFORMED = 1
|
|
@@ -167,12 +169,15 @@ module SpecGuard
|
|
|
167
169
|
|
|
168
170
|
results = backend.check(selection.files)
|
|
169
171
|
|
|
170
|
-
# The structural pass (SPGD-900, SPGD-1510): annotations
|
|
171
|
-
# valid in isolation but can never be extracted — stacked
|
|
172
|
-
# comment-form `@intent:` lines, where the one-line
|
|
173
|
-
# (SPGD-12 §2) claims only the line just above the example,
|
|
174
|
-
#
|
|
175
|
-
#
|
|
172
|
+
# The structural pass (SPGD-900, SPGD-1510, SPGD-1554, SPGD-1560): annotations
|
|
173
|
+
# that are valid in isolation but can never be extracted — stacked
|
|
174
|
+
# consecutive comment-form `@intent:` lines, where the one-line
|
|
175
|
+
# lookback (SPGD-12 §2) claims only the line just above the example,
|
|
176
|
+
# comment-form runs separated from their example by one blank/comment
|
|
177
|
+
# line, comment-form lines shadowed by their example's own trailing
|
|
178
|
+
# `@intent:` (extraction is own-line-first), and `@intent:` tokens
|
|
179
|
+
# trailing on an example-group line, which no extraction rule can ever
|
|
180
|
+
# reach. These are Linter::Results like any
|
|
176
181
|
# other, so both renderers and the exit code pick them up with no
|
|
177
182
|
# second path to keep in step.
|
|
178
183
|
results += unreachable_results(selection.files)
|
|
@@ -480,15 +485,15 @@ module SpecGuard
|
|
|
480
485
|
#
|
|
481
486
|
# The boundary is the set-difference the partition above already
|
|
482
487
|
# supports: a file counts as covered when it yielded any line-scoped
|
|
483
|
-
# result (an annotation, valid or not — the SPGD-900/SPGD-1510
|
|
488
|
+
# result (an annotation, valid or not — the SPGD-900/SPGD-1510/SPGD-1554/SPGD-1560
|
|
484
489
|
# unreachable findings are line-scoped too and require an annotation to
|
|
485
490
|
# exist, so they cover their file) or when it could not be read at all
|
|
486
491
|
# (KIND_READ → `unread`). An unread file is NOT a zero-annotation file:
|
|
487
492
|
# it already gets its own summary clause and FAIL line, and calling it
|
|
488
493
|
# annotation-free would be the same overstatement `summary_line` refuses
|
|
489
494
|
# to make. Unreachable findings need an annotation to exist — a stacked
|
|
490
|
-
# comment-form pair,
|
|
491
|
-
# in a file with none, so `annotations ∪ unread` already covers every
|
|
495
|
+
# comment-form pair, a separated comment-form run, a shadowed comment-form
|
|
496
|
+
# run, or an `@intent:` token on a group line — impossible in a file with none, so `annotations ∪ unread` already covers every
|
|
492
497
|
# non-bare file and no third subtraction is needed.
|
|
493
498
|
#
|
|
494
499
|
# It is a NOTE on stderr, never a warning and never an exit code: "lint,
|
|
@@ -49,7 +49,7 @@ module SpecGuard
|
|
|
49
49
|
KIND_SCHEMA = :schema
|
|
50
50
|
|
|
51
51
|
# The annotation is well-formed in isolation but can never be EXTRACTED.
|
|
52
|
-
#
|
|
52
|
+
# Four shapes, all dead the moment they are written:
|
|
53
53
|
#
|
|
54
54
|
# * a comment-form `@intent:` stacked above another comment-form
|
|
55
55
|
# `@intent:` line, so the one-line lookback (SPGD-12 §2 —
|
|
@@ -58,9 +58,15 @@ module SpecGuard
|
|
|
58
58
|
# * an `@intent:` trailing on an example-group line
|
|
59
59
|
# ({Scanner::GROUP_LINE} — SPGD-1510): a group line is no example's
|
|
60
60
|
# own and is not comment-only, so no extraction rule can ever reach
|
|
61
|
-
# it, and the example beneath it silently ingests unannotated
|
|
61
|
+
# it, and the example beneath it silently ingests unannotated;
|
|
62
|
+
# * a comment-form `@intent:` run separated from its example by ONE
|
|
63
|
+
# blank or ordinary comment line (SPGD-1554): the one-line lookback
|
|
64
|
+
# reads only that interleave, so the whole run is out of reach;
|
|
65
|
+
# * a comment-form `@intent:` directly above an example whose OWN line
|
|
66
|
+
# also carries a trailing `@intent:` (SPGD-1560): extraction is
|
|
67
|
+
# own-line-first, so the example's own annotation shadows the comment.
|
|
62
68
|
#
|
|
63
|
-
#
|
|
69
|
+
# Whichever shape, the contract is dead metadata: counted by the linter,
|
|
64
70
|
# discarded by extraction. Produced by {Scanner}'s structural pass, not
|
|
65
71
|
# by {AnnotationScanner} — each annotation is scanned in isolation
|
|
66
72
|
# there, which is exactly why these defects are invisible to it.
|
|
@@ -40,13 +40,17 @@ module SpecGuard
|
|
|
40
40
|
# `Finding::KIND_UNREACHABLE` — an annotation that is well-formed in
|
|
41
41
|
# isolation but can never be extracted, so the one-line lookback
|
|
42
42
|
# (SPGD-12 §2) never claims it — is also reported as a failure, and
|
|
43
|
-
# therefore also exits 1.
|
|
43
|
+
# therefore also exits 1. Four shapes reach this kind: a comment-form
|
|
44
44
|
# `@intent:` stacked above another comment-form `@intent:` line (SPGD-900),
|
|
45
|
-
#
|
|
46
|
-
# example's own and not comment-only (SPGD-1510)
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
45
|
+
# an `@intent:` trailing on an example-group line, which is no
|
|
46
|
+
# example's own and not comment-only (SPGD-1510), and a comment-form
|
|
47
|
+
# `@intent:` run separated from its example by one blank or ordinary
|
|
48
|
+
# comment line (SPGD-1554), and a comment-form `@intent:` directly above an
|
|
49
|
+
# example whose own line carries a trailing `@intent:`, which own-line-first
|
|
50
|
+
# extraction prefers (SPGD-1560). Nothing about any of them is malformed; the
|
|
51
|
+
# contract is dead metadata, counted by the linter and discarded by
|
|
52
|
+
# extraction, and the structural pass that flags them (SPGD-900,
|
|
53
|
+
# SPGD-1510, SPGD-1554, SPGD-1560) reports it loudly rather than letting it pass as
|
|
50
54
|
# clean.
|
|
51
55
|
module Linter
|
|
52
56
|
# One annotation's verdict. `problem` is set when discovery could not
|
|
@@ -261,7 +261,7 @@ module SpecGuard
|
|
|
261
261
|
# ({AnnotationLookup}, SPGD-12 §2) is positional: an annotation reaches
|
|
262
262
|
# an example only through its own line (the trailing form, and only when
|
|
263
263
|
# that line IS the example's) or through the comment-form line
|
|
264
|
-
# IMMEDIATELY ABOVE it.
|
|
264
|
+
# IMMEDIATELY ABOVE it. Four shapes fall outside both and are dead the
|
|
265
265
|
# moment they are written — silently discarded by extraction while this
|
|
266
266
|
# pipeline counts them as valid annotations and the run exits 0:
|
|
267
267
|
#
|
|
@@ -276,8 +276,22 @@ module SpecGuard
|
|
|
276
276
|
# ever claim it — the example beneath it silently ingests as
|
|
277
277
|
# unannotated. A describe insertion that swallowed a newline is how
|
|
278
278
|
# the shape is born.
|
|
279
|
-
#
|
|
280
|
-
#
|
|
279
|
+
# * the SEPARATED annotation (SPGD-1554, ported from specguard-ts
|
|
280
|
+
# SPGD-1550): a comment-form `@intent:` run followed by exactly ONE
|
|
281
|
+
# blank line or ordinary (non-`@intent:`) comment line and THEN an
|
|
282
|
+
# example. The one-line lookback reads only the line directly above
|
|
283
|
+
# the example, which is the interleave — so the annotation is out of
|
|
284
|
+
# reach and every line of the run is dead.
|
|
285
|
+
#
|
|
286
|
+
# * the SHADOWED annotation (SPGD-1560, ported from specguard-ts
|
|
287
|
+
# SPGD-1556): a comment-form `@intent:` run directly above an example
|
|
288
|
+
# whose OWN line also carries a trailing `@intent: {`. Extraction is
|
|
289
|
+
# own-line-first ({AnnotationLookup::Index#intent_for} returns the
|
|
290
|
+
# example's own annotation before it ever consults the line above),
|
|
291
|
+
# so the comment is shadowed and its LAST line is dead. (Any earlier
|
|
292
|
+
# lines of the run are the stacked arm's.)
|
|
293
|
+
#
|
|
294
|
+
# All four are reported loudly, not skipped — the same stance
|
|
281
295
|
# {AnnotationScanner} takes for NO_PAYLOAD.
|
|
282
296
|
#
|
|
283
297
|
# A comment-form annotation line is a comment-only line
|
|
@@ -323,6 +337,19 @@ module SpecGuard
|
|
|
323
337
|
"claim it. Move it onto its example's `it` line, or to the comment line directly " \
|
|
324
338
|
"above that `it`"
|
|
325
339
|
|
|
340
|
+
UNREACHABLE_SEPARATED_ANNOTATION =
|
|
341
|
+
"unreachable annotation: this @intent: is separated from its example by an " \
|
|
342
|
+
"intervening comment or blank line, and the one-line lookback (SPGD-12 §2) claims " \
|
|
343
|
+
"only the line directly above the example — so this annotation is silently " \
|
|
344
|
+
"discarded at extraction. Move it directly above the example (or merge it into " \
|
|
345
|
+
"the line that is)"
|
|
346
|
+
|
|
347
|
+
UNREACHABLE_SHADOWED_ANNOTATION =
|
|
348
|
+
"unreachable annotation: the example directly below carries its own trailing @intent:, " \
|
|
349
|
+
"and extraction is own-line-first (SPGD-12 §2), so this comment-form annotation is " \
|
|
350
|
+
"shadowed and silently discarded. Delete this comment, or merge it into the trailing " \
|
|
351
|
+
"payload"
|
|
352
|
+
|
|
326
353
|
# A comment-only line carrying an `@intent:` token — the only form an
|
|
327
354
|
# example on the NEXT line may claim ({AnnotationLookup::COMMENT_LINE}).
|
|
328
355
|
COMMENT_INTENT_LINE = /\A\s*#.*@intent:/
|
|
@@ -337,18 +364,36 @@ module SpecGuard
|
|
|
337
364
|
# spec/fixtures/): there is no lookback there to silently discard
|
|
338
365
|
# anything, so there is nothing to report. The defects this pass exists
|
|
339
366
|
# for are annotations an author believed were attached to a real
|
|
340
|
-
# example: stacked lines above it (SPGD-897),
|
|
341
|
-
# a group line directly above it (SPGD-1510)
|
|
367
|
+
# example: stacked lines above it (SPGD-897), the trailing form on
|
|
368
|
+
# a group line directly above it (SPGD-1510), and a comment-form run
|
|
369
|
+
# separated from it by one blank/comment line (SPGD-1554).
|
|
342
370
|
EXAMPLE_LINE = /\A\s*(?:it|specify)\b/
|
|
343
371
|
|
|
372
|
+
# A blank (whitespace-only) line — one of the two interleaves the
|
|
373
|
+
# separated pass reads. `\n` is whitespace, so a `text.lines` element
|
|
374
|
+
# that is only its newline matches.
|
|
375
|
+
BLANK_LINE = /\A\s*\z/
|
|
376
|
+
|
|
377
|
+
# A comment-only line (`#`-leading). The separated pass reads it as the
|
|
378
|
+
# interleave only when it carries no `@intent:` token (a comment-form
|
|
379
|
+
# `@intent:` line is part of the run itself, never an interleave).
|
|
380
|
+
COMMENT_ONLY_LINE = /\A\s*#/
|
|
381
|
+
|
|
344
382
|
# @param text [String] source of one file
|
|
345
383
|
# @param file [String] path to record on each Finding
|
|
346
384
|
# @return [Array<Finding>] the structural findings for one file, in line
|
|
347
385
|
# order: one per comment-form `@intent:` line in a stacked run (see
|
|
348
|
-
# {stacked_findings_in_text})
|
|
349
|
-
#
|
|
350
|
-
#
|
|
351
|
-
#
|
|
386
|
+
# {stacked_findings_in_text}), one per line of a separated run (see
|
|
387
|
+
# {separated_findings_in_text}), one per run whose example carries its own
|
|
388
|
+
# trailing annotation (see {own_line_shadow_findings_in_text}) plus one
|
|
389
|
+
# per group line carrying the trailing form (see
|
|
390
|
+
# {group_line_findings_in_text}). The four passes are disjoint by
|
|
391
|
+
# construction: the stacked pass flags all but the LAST line of a run
|
|
392
|
+
# when an example directly follows, the shadow pass flags ONLY that last
|
|
393
|
+
# line (and only when the example also carries `@intent: {`), the
|
|
394
|
+
# separated pass needs a blank or comment-only line after the run (one
|
|
395
|
+
# slot cannot be both that and an example), and the group pass reads
|
|
396
|
+
# only lines that are not comment-only — so the merge cannot double-flag
|
|
352
397
|
# a line, and the sort restores the file-then-line order
|
|
353
398
|
# {unreachable_findings} promises.
|
|
354
399
|
def unreachable_findings_in_text(text, file:)
|
|
@@ -358,6 +403,8 @@ module SpecGuard
|
|
|
358
403
|
return [] unless text.valid_encoding?
|
|
359
404
|
|
|
360
405
|
(stacked_findings_in_text(text, file: file) +
|
|
406
|
+
separated_findings_in_text(text, file: file) +
|
|
407
|
+
own_line_shadow_findings_in_text(text, file: file) +
|
|
361
408
|
group_line_findings_in_text(text, file: file)).sort_by(&:line)
|
|
362
409
|
end
|
|
363
410
|
|
|
@@ -399,6 +446,90 @@ module SpecGuard
|
|
|
399
446
|
findings
|
|
400
447
|
end
|
|
401
448
|
|
|
449
|
+
# @param text [String] source of one file
|
|
450
|
+
# @param file [String] path to record on each Finding
|
|
451
|
+
# @return [Array<Finding>] one per comment-form `@intent:` line of a
|
|
452
|
+
# SEPARATED run — a maximal run of consecutive comment-form `@intent:`
|
|
453
|
+
# lines whose following line (the interleave) is blank or a comment
|
|
454
|
+
# without an `@intent:` token AND whose line after that is an example
|
|
455
|
+
# ({EXAMPLE_LINE}). The one-line lookback reads only the interleave,
|
|
456
|
+
# so NOTHING in the run is claimed: every line is flagged, the run's
|
|
457
|
+
# last line included — the discriminating difference from
|
|
458
|
+
# {stacked_findings_in_text}, which spares the last line. The window
|
|
459
|
+
# is exactly one line: a second interleave, a code interleave, a
|
|
460
|
+
# `describe` target or a run at end of file is not flagged.
|
|
461
|
+
def separated_findings_in_text(text, file:)
|
|
462
|
+
# Same UTF-8 contract as the stacked pass above.
|
|
463
|
+
return [] unless text.valid_encoding?
|
|
464
|
+
|
|
465
|
+
lines = text.lines
|
|
466
|
+
i = 0
|
|
467
|
+
findings = []
|
|
468
|
+
|
|
469
|
+
while i < lines.length
|
|
470
|
+
unless COMMENT_INTENT_LINE.match?(lines[i])
|
|
471
|
+
i += 1
|
|
472
|
+
next
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
run_start = i
|
|
476
|
+
i += 1 while i < lines.length && COMMENT_INTENT_LINE.match?(lines[i])
|
|
477
|
+
run_end = i # exclusive; lines[run_end] is the interleave candidate
|
|
478
|
+
|
|
479
|
+
between = lines[run_end]
|
|
480
|
+
example = lines[run_end + 1]
|
|
481
|
+
next if between.nil? || example.nil?
|
|
482
|
+
next unless BLANK_LINE.match?(between) ||
|
|
483
|
+
(COMMENT_ONLY_LINE.match?(between) && !COMMENT_INTENT_LINE.match?(between))
|
|
484
|
+
next unless EXAMPLE_LINE.match?(example)
|
|
485
|
+
|
|
486
|
+
(run_start...run_end).each do |j|
|
|
487
|
+
findings << Finding.new(file: file, line: j + 1, problem: UNREACHABLE_SEPARATED_ANNOTATION,
|
|
488
|
+
kind: Finding::KIND_UNREACHABLE)
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
findings
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# @param text [String] source of one file
|
|
496
|
+
# @param file [String] path to record on each Finding
|
|
497
|
+
# @return [Array<Finding>] one per maximal run of comment-form
|
|
498
|
+
# `@intent:` lines whose following line is an example
|
|
499
|
+
# ({EXAMPLE_LINE}) that ALSO carries a trailing `@intent: {`
|
|
500
|
+
# ({INTENT_WITH_PAYLOAD}) — reported on the run's LAST line, the one
|
|
501
|
+
# the lookback would otherwise claim. Extraction is own-line-first
|
|
502
|
+
# (SPGD-12 §2), so the example's own annotation always wins and that
|
|
503
|
+
# comment is dead. Earlier lines of a longer run are the stacked arm's
|
|
504
|
+
# ({stacked_findings_in_text}); each dead line is flagged exactly once.
|
|
505
|
+
def own_line_shadow_findings_in_text(text, file:)
|
|
506
|
+
# Same UTF-8 contract as the stacked pass above.
|
|
507
|
+
return [] unless text.valid_encoding?
|
|
508
|
+
|
|
509
|
+
lines = text.lines
|
|
510
|
+
i = 0
|
|
511
|
+
findings = []
|
|
512
|
+
|
|
513
|
+
while i < lines.length
|
|
514
|
+
unless COMMENT_INTENT_LINE.match?(lines[i])
|
|
515
|
+
i += 1
|
|
516
|
+
next
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
i += 1 while i < lines.length && COMMENT_INTENT_LINE.match?(lines[i])
|
|
520
|
+
run_end = i # exclusive; lines[run_end] is the line after the run
|
|
521
|
+
|
|
522
|
+
example = lines[run_end]
|
|
523
|
+
next if example.nil?
|
|
524
|
+
next unless EXAMPLE_LINE.match?(example) && INTENT_WITH_PAYLOAD.match?(example)
|
|
525
|
+
|
|
526
|
+
findings << Finding.new(file: file, line: run_end, problem: UNREACHABLE_SHADOWED_ANNOTATION,
|
|
527
|
+
kind: Finding::KIND_UNREACHABLE)
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
findings
|
|
531
|
+
end
|
|
532
|
+
|
|
402
533
|
# RSpec's example-group keywords, at the start of a line and optionally
|
|
403
534
|
# `RSpec.`-prefixed. A line matching this hosts a GROUP, not an example
|
|
404
535
|
# — and {AnnotationLookup}'s extraction can claim an annotation only
|
|
@@ -450,9 +581,9 @@ module SpecGuard
|
|
|
450
581
|
# @return [Array<Finding>] one per example-group line carrying an
|
|
451
582
|
# `@intent:` token, for lines that define no example of their own
|
|
452
583
|
# ({EXAMPLE_ON_LINE}). Comment-only lines are left to
|
|
453
|
-
# {stacked_findings_in_text}
|
|
454
|
-
#
|
|
455
|
-
# line ITSELF).
|
|
584
|
+
# {stacked_findings_in_text} and {separated_findings_in_text}: a
|
|
585
|
+
# comment above a group is a different shape, deliberately out of
|
|
586
|
+
# scope here (SPGD-1510 flags the group line ITSELF).
|
|
456
587
|
def group_line_findings_in_text(text, file:)
|
|
457
588
|
# Same UTF-8 contract as the stacked pass above.
|
|
458
589
|
return [] unless text.valid_encoding?
|
data/lib/specguard/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: specguard-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.29
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- specguard Agent
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|