@herbertgao/pi-extensions 2026.8.13 → 2026.8.15
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.
- package/README.md +19 -14
- package/THIRD_PARTY_NOTICES.md +0 -1
- package/node_modules/@czottmann/pi-automode/CHANGELOG.md +19 -0
- package/node_modules/@czottmann/pi-automode/README.md +5 -1
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +126 -13
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +7 -3
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +2 -0
- package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +134 -23
- package/node_modules/@czottmann/pi-automode/package.json +1 -1
- package/node_modules/@herbertgao/{pi-stash → pi-bark}/LICENSE +4 -5
- package/node_modules/@herbertgao/pi-bark/README.md +44 -0
- package/node_modules/@herbertgao/pi-bark/assets/pi-icon.png +0 -0
- package/node_modules/@herbertgao/pi-bark/package.json +53 -0
- package/node_modules/@herbertgao/pi-bark/src/index.ts +178 -0
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +2 -1
- package/node_modules/@herbertgao/pi-subagents/src/ui/conversation-viewer.ts +8 -1
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
- package/node_modules/@narumitw/pi-btw/README.md +125 -88
- package/node_modules/@narumitw/pi-btw/dist/index.ts +140 -45
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-btw/package.json +5 -8
- package/node_modules/@narumitw/pi-btw/src/btw.ts +31 -26
- package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +106 -17
- package/node_modules/@narumitw/pi-btw/src/menu.ts +34 -2
- package/node_modules/@narumitw/pi-btw/src/settings.ts +16 -0
- package/node_modules/@tifan/pi-handoff/README.md +36 -13
- package/node_modules/@tifan/pi-handoff/package.json +2 -2
- package/node_modules/@tifan/pi-handoff/src/index.ts +160 -4
- package/node_modules/@tifan/pi-preferred-thinking/README.md +1 -1
- package/node_modules/@tifan/pi-preferred-thinking/package.json +1 -1
- package/node_modules/@tifan/pi-preferred-thinking/src/index.ts +15 -2
- package/node_modules/@tifan/pi-recap/README.md +1 -1
- package/node_modules/@tifan/pi-recap/package.json +2 -2
- package/node_modules/@tifan/pi-recap/src/index.ts +15 -0
- package/node_modules/@tifan/pi-rename/README.md +42 -21
- package/node_modules/@tifan/pi-rename/package.json +1 -1
- package/node_modules/@tifan/pi-rename/src/index.ts +62 -11
- package/node_modules/@tifan/pi-rename/src/language.ts +18 -0
- package/node_modules/@tifan/pi-rename/src/models.ts +69 -13
- package/node_modules/@tifan/pi-rename/src/naming.ts +36 -7
- package/node_modules/@tifan/pi-rename/src/sanitize.ts +22 -14
- package/node_modules/pi-lens/CHANGELOG.md +320 -0
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +79 -0
- package/node_modules/pi-lens/dist/clients/actionable-warnings-logger.js +16 -1
- package/node_modules/pi-lens/dist/clients/ast-grep-client.js +13 -6
- package/node_modules/pi-lens/dist/clients/availability-probe-flight.js +12 -0
- package/node_modules/pi-lens/dist/clients/biome-client.js +7 -0
- package/node_modules/pi-lens/dist/clients/blocker-freshness.js +20 -0
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +56 -19
- package/node_modules/pi-lens/dist/clients/build-identity.js +95 -0
- package/node_modules/pi-lens/dist/clients/bus-publish.js +3 -0
- package/node_modules/pi-lens/dist/clients/cache/rule-cache.js +2 -1
- package/node_modules/pi-lens/dist/clients/cache-observability.js +2 -1
- package/node_modules/pi-lens/dist/clients/cascade-logger.js +15 -1
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +14 -11
- package/node_modules/pi-lens/dist/clients/degradation-ledger.js +56 -4
- package/node_modules/pi-lens/dist/clients/demoted-finding-render.js +14 -0
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +20 -3
- package/node_modules/pi-lens/dist/clients/deps/minimatch.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/collect-later-tier.js +39 -0
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +150 -9
- package/node_modules/pi-lens/dist/clients/dispatch/fact-store.js +231 -9
- package/node_modules/pi-lens/dist/clients/dispatch/integration.js +205 -100
- package/node_modules/pi-lens/dist/clients/dispatch/pending-runner-findings.js +87 -0
- package/node_modules/pi-lens/dist/clients/dispatch/plan.js +21 -5
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ast-grep-napi.js +174 -55
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +16 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +54 -9
- package/node_modules/pi-lens/dist/clients/dispatch/runners/index.js +1 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +4 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +11 -4
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +8 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +76 -25
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/spawn-outcome.js +4 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/tool-failure.js +19 -7
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +7 -13
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yaml-rule-parser.js +51 -17
- package/node_modules/pi-lens/dist/clients/event-loop-monitor.js +66 -1
- package/node_modules/pi-lens/dist/clients/file-utils.js +157 -7
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +5 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +28 -3
- package/node_modules/pi-lens/dist/clients/git-guard.js +197 -47
- package/node_modules/pi-lens/dist/clients/git-tracked-ignore.js +32 -1
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +5 -1
- package/node_modules/pi-lens/dist/clients/gzip-stage-write.js +7 -0
- package/node_modules/pi-lens/dist/clients/installer/index.js +131 -34
- package/node_modules/pi-lens/dist/clients/installer/managed-tool-refresh.js +3 -1
- package/node_modules/pi-lens/dist/clients/instance-reaper.js +4 -13
- package/node_modules/pi-lens/dist/clients/instance-registry-lock.js +145 -0
- package/node_modules/pi-lens/dist/clients/instance-registry.js +432 -122
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +6 -1
- package/node_modules/pi-lens/dist/clients/language-profile.js +4 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +19 -1
- package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
- package/node_modules/pi-lens/dist/clients/lens-map.js +3 -1
- package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +4 -0
- package/node_modules/pi-lens/dist/clients/lsp/client.js +516 -31
- package/node_modules/pi-lens/dist/clients/lsp/config.js +25 -2
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +9 -4
- package/node_modules/pi-lens/dist/clients/lsp/index.js +509 -182
- package/node_modules/pi-lens/dist/clients/lsp/jvm-runtime.js +5 -0
- package/node_modules/pi-lens/dist/clients/lsp/launch.js +36 -8
- package/node_modules/pi-lens/dist/clients/lsp/pending-aux-coverage.js +116 -4
- package/node_modules/pi-lens/dist/clients/lsp/server.js +142 -13
- package/node_modules/pi-lens/dist/clients/lsp/session-roots.js +99 -0
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +16 -6
- package/node_modules/pi-lens/dist/clients/lsp/workspace-sweep-hold.js +32 -23
- package/node_modules/pi-lens/dist/clients/lsp-mutation.js +16 -2
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +12 -6
- package/node_modules/pi-lens/dist/clients/mcp/session.js +14 -1
- package/node_modules/pi-lens/dist/clients/memory-sampler.js +23 -1
- package/node_modules/pi-lens/dist/clients/message-end-attribution.js +30 -0
- package/node_modules/pi-lens/dist/clients/metrics-history.js +21 -4
- package/node_modules/pi-lens/dist/clients/opaque-mutation-scan.js +232 -18
- package/node_modules/pi-lens/dist/clients/package-manager.js +6 -18
- package/node_modules/pi-lens/dist/clients/path-keyed-map.js +14 -1
- package/node_modules/pi-lens/dist/clients/path-utils.js +32 -0
- package/node_modules/pi-lens/dist/clients/pipeline.js +0 -8
- package/node_modules/pi-lens/dist/clients/process-singletons.js +210 -0
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/madge.js +2 -1
- package/node_modules/pi-lens/dist/clients/project-diagnostics/scanner.js +10 -4
- package/node_modules/pi-lens/dist/clients/project-snapshot.js +33 -11
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +24 -2
- package/node_modules/pi-lens/dist/clients/read-guard.js +74 -7
- package/node_modules/pi-lens/dist/clients/recent-touches.js +17 -5
- package/node_modules/pi-lens/dist/clients/review-graph/builder.js +639 -62
- package/node_modules/pi-lens/dist/clients/review-graph/tsconfig-paths.js +5 -1
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +21 -1
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +68 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +30 -12
- package/node_modules/pi-lens/dist/clients/runtime-tool-call.js +15 -0
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +106 -7
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +242 -27
- package/node_modules/pi-lens/dist/clients/safe-spawn.js +158 -18
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +48 -17
- package/node_modules/pi-lens/dist/clients/session-lifecycle.js +238 -42
- package/node_modules/pi-lens/dist/clients/sg-runner.js +27 -15
- package/node_modules/pi-lens/dist/clients/shared-checkout-guard.js +338 -0
- package/node_modules/pi-lens/dist/clients/spawn-output-cap.js +37 -0
- package/node_modules/pi-lens/dist/clients/spawn-timeout-cooldown.js +15 -1
- package/node_modules/pi-lens/dist/clients/startup-scan.js +6 -7
- package/node_modules/pi-lens/dist/clients/startup-timing.js +69 -4
- package/node_modules/pi-lens/dist/clients/string-utils.js +20 -0
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +419 -88
- package/node_modules/pi-lens/dist/clients/tool-policy.js +1 -1
- package/node_modules/pi-lens/dist/clients/tree-sitter-client.js +11 -5
- package/node_modules/pi-lens/dist/clients/tree-sitter-logger.js +17 -1
- package/node_modules/pi-lens/dist/clients/warm-attach.js +8 -10
- package/node_modules/pi-lens/dist/clients/word-index-logger.js +13 -1
- package/node_modules/pi-lens/dist/clients/word-index-store.js +448 -0
- package/node_modules/pi-lens/dist/clients/word-index.js +582 -104
- package/node_modules/pi-lens/dist/clients/workspace-topology.js +17 -0
- package/node_modules/pi-lens/dist/clients/zizmor-config.js +26 -6
- package/node_modules/pi-lens/dist/index.js +11243 -6651
- package/node_modules/pi-lens/dist/mcp/build-staleness.js +8 -7
- package/node_modules/pi-lens/dist/mcp/cli.js +3 -1
- package/node_modules/pi-lens/dist/mcp/server.js +6 -1
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +4 -0
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +14 -5
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +3 -1
- package/node_modules/pi-lens/docs/agent-guide.md +4 -2
- package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +41 -10
- package/node_modules/pi-lens/docs/astplayground.md +26 -17
- package/node_modules/pi-lens/package.json +5 -1
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/c-no-malloc-free-test.yml +20 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-malloc-free-test.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/cpp-no-printf-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-fmt-println-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-panic-in-lib-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-no-underscore-func-name-test.yml +18 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-errors-is-test.yml +15 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/go-prefer-string-builder-test.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/hyphenated-svg-attribute-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-lateinit-test.yml +17 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-nullable-boolean-test.yml +15 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-println-test.yml +19 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unnecessary-let-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-unsafe-cast-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-no-var-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-data-class-test.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/kotlin-prefer-is-empty-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-important-test.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-raw-types-test.yml +30 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-string-concat-in-loop-test.yml +100 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-system-out-println-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/prefer-string-is-empty-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/python-optional-type-test.yml +9 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/reducible-list-comprehension-test.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/ruby-symbol-to-proc-candidate-test.yml +10 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/rust-no-chars-enumerate-test.yml +7 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/c-no-malloc-free.yml +39 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-malloc-free.yml +45 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/cpp-no-printf.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-fmt-println.yml +43 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-panic-in-lib.yml +41 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-no-underscore-func-name.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-errors-is.yml +40 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/go-prefer-string-builder.yml +43 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/hyphenated-svg-attribute.yml +32 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-lateinit.yml +37 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-nullable-boolean.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-println.yml +42 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unnecessary-let.yml +28 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-unsafe-cast.yml +28 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-no-var.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-data-class.yml +66 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/kotlin-prefer-is-empty.yml +35 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-important.yml +21 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-raw-types.yml +33 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-string-concat-in-loop.yml +90 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-system-out-println.yml +16 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/prefer-string-is-empty.yml +11 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/python-optional-type.yml +22 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/reducible-list-comprehension.yml +29 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/ruby-symbol-to-proc-candidate.yml +25 -0
- package/node_modules/pi-lens/rules/ast-grep-rules/rules/rust-no-chars-enumerate.yml +19 -0
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +35 -0
- package/node_modules/pi-mcp-adapter/README.md +5 -2
- package/node_modules/pi-mcp-adapter/cli.js +4 -4
- package/node_modules/pi-mcp-adapter/commands.ts +1 -1
- package/node_modules/pi-mcp-adapter/config.ts +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.js +1 -0
- package/node_modules/pi-mcp-adapter/dist/config.js.map +1 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.d.ts +24 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +336 -0
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +1 -0
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +3 -1
- package/node_modules/pi-mcp-adapter/dist/types.js.map +1 -1
- package/node_modules/pi-mcp-adapter/index.ts +90 -6
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +124 -32
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +0 -2
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +89 -0
- package/node_modules/pi-mcp-adapter/mcp-references.ts +9 -1
- package/node_modules/pi-mcp-adapter/package.json +1 -1
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +62 -22
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +1 -1
- package/node_modules/pi-mcp-adapter/server-manager.ts +6 -0
- package/node_modules/pi-mcp-adapter/types.ts +3 -1
- package/node_modules/pi-web-access/CHANGELOG.md +42 -0
- package/node_modules/pi-web-access/README.md +26 -10
- package/node_modules/pi-web-access/curator-page.ts +12 -3
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/extract.ts +88 -13
- package/node_modules/pi-web-access/gemini-search.ts +10 -5
- package/node_modules/pi-web-access/github-extract.ts +47 -3
- package/node_modules/pi-web-access/index.ts +40 -40
- package/node_modules/pi-web-access/package.json +1 -1
- package/node_modules/pi-web-access/page-query.ts +61 -8
- package/node_modules/pi-web-access/xcrawl.ts +264 -0
- package/package.json +19 -22
- package/node_modules/@herbertgao/pi-stash/README.md +0 -34
- package/node_modules/@herbertgao/pi-stash/package.json +0 -51
- package/node_modules/@herbertgao/pi-stash/src/index.ts +0 -118
- package/node_modules/@tifan/pi-titlebar-spinner/README.md +0 -24
- package/node_modules/@tifan/pi-titlebar-spinner/package.json +0 -39
- package/node_modules/@tifan/pi-titlebar-spinner/src/index.ts +0 -49
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Go Prefer strings.Builder Over Repeated Concatenation
|
|
2
|
+
# Detects repeated string concatenation (x = x + y / x += "y") that should use
|
|
3
|
+
# strings.Builder instead. The += arm requires a string literal because
|
|
4
|
+
# ast-grep has no type information to distinguish string and numeric operands.
|
|
5
|
+
# Ported from vinhnx/VTCode rules/go/prefer-string-builder.yml
|
|
6
|
+
# (#2200, refs #2195).
|
|
7
|
+
id: go-prefer-string-builder
|
|
8
|
+
language: go
|
|
9
|
+
severity: hint
|
|
10
|
+
message: "Use strings.Builder instead of repeated string concatenation"
|
|
11
|
+
note: |
|
|
12
|
+
Repeated string concatenation with + creates a new string allocation on
|
|
13
|
+
each operation (O(n^2) total). strings.Builder accumulates bytes in a
|
|
14
|
+
buffer and produces the final string with one allocation.
|
|
15
|
+
Detection-only, with no autofix: a single concatenation site cannot show
|
|
16
|
+
this is a genuine accumulation loop rather than a one-off assignment where
|
|
17
|
+
concatenation is clearer, and no fix can safely introduce the surrounding
|
|
18
|
+
strings.Builder declaration and .String() call. Scoped to non-test Go
|
|
19
|
+
files.
|
|
20
|
+
rule:
|
|
21
|
+
any:
|
|
22
|
+
- pattern: $VAR = $VAR + $OTHER
|
|
23
|
+
- pattern: $VAR += "$OTHER"
|
|
24
|
+
ignores:
|
|
25
|
+
- "**/*_test.go"
|
|
26
|
+
tags:
|
|
27
|
+
- go
|
|
28
|
+
- idiom
|
|
29
|
+
- performance
|
|
30
|
+
examples:
|
|
31
|
+
bad: |
|
|
32
|
+
func concat(a string, b string) string {
|
|
33
|
+
a = a + b
|
|
34
|
+
return a
|
|
35
|
+
}
|
|
36
|
+
good: |
|
|
37
|
+
func build(parts []string) string {
|
|
38
|
+
var b strings.Builder
|
|
39
|
+
for _, p := range parts {
|
|
40
|
+
b.WriteString(p)
|
|
41
|
+
}
|
|
42
|
+
return b.String()
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
id: hyphenated-svg-attribute
|
|
2
|
+
language: Tsx
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "Use camelCase for this known SVG attribute in JSX"
|
|
5
|
+
note: |
|
|
6
|
+
React JSX uses camelCase for SVG attributes. The rule is limited to the
|
|
7
|
+
known SVG attribute names below, so custom elements and web components with
|
|
8
|
+
legitimate hyphenated properties are not reported.
|
|
9
|
+
rule:
|
|
10
|
+
kind: jsx_attribute
|
|
11
|
+
has:
|
|
12
|
+
kind: property_identifier
|
|
13
|
+
regex: "^(stroke-width|stroke-linecap|stroke-linejoin|stroke-miterlimit|stroke-dasharray|stroke-dashoffset|fill-rule|clip-rule|clip-path|font-family|font-size|font-weight|text-anchor|dominant-baseline|stop-color|stop-opacity|fill-opacity|stroke-opacity|color-interpolation-filters|shape-rendering|vector-effect|flood-color|flood-opacity|lighting-color|marker-height|marker-width|xlink-href|mask-type)$"
|
|
14
|
+
inside:
|
|
15
|
+
any:
|
|
16
|
+
- kind: jsx_self_closing_element
|
|
17
|
+
has:
|
|
18
|
+
field: name
|
|
19
|
+
regex: '^(svg|path|circle|rect|line|polyline|polygon|ellipse|g|defs|use|symbol|clipPath|mask|pattern|marker|linearGradient|radialGradient|stop|text|tspan|foreignObject)$'
|
|
20
|
+
- kind: jsx_opening_element
|
|
21
|
+
has:
|
|
22
|
+
field: name
|
|
23
|
+
regex: '^(svg|path|circle|rect|line|polyline|polygon|ellipse|g|defs|use|symbol|clipPath|mask|pattern|marker|linearGradient|radialGradient|stop|text|tspan|foreignObject)$'
|
|
24
|
+
has_fix: false
|
|
25
|
+
tags:
|
|
26
|
+
- tsx
|
|
27
|
+
- jsx
|
|
28
|
+
- svg
|
|
29
|
+
- modernization
|
|
30
|
+
examples:
|
|
31
|
+
bad: "const icon = <svg stroke-width=\"2\" />;"
|
|
32
|
+
good: "const icon = <svg strokeWidth=\"2\" />;"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Kotlin No lateinit
|
|
2
|
+
# Detects `lateinit var` property declarations.
|
|
3
|
+
# Ported from vinhnx/VTCode rules/kotlin/no-lateinit.yml (#2198, refs #2195).
|
|
4
|
+
id: kotlin-no-lateinit
|
|
5
|
+
language: kotlin
|
|
6
|
+
severity: warning
|
|
7
|
+
message: "Avoid lateinit var; prefer a nullable type or constructor injection"
|
|
8
|
+
note: |
|
|
9
|
+
lateinit defers initialization past the constructor and throws
|
|
10
|
+
UninitializedPropertyAccessException on an unguarded read before the
|
|
11
|
+
property is set. A nullable type, a lazy delegate, or constructor/DI-based
|
|
12
|
+
initialization all make the missing-value case visible to the type system
|
|
13
|
+
instead of a runtime crash.
|
|
14
|
+
Detection-only: the correct replacement depends on the property's
|
|
15
|
+
lifecycle (Android view binding, DI-managed fields, test fixtures) and on
|
|
16
|
+
whether callers can tolerate a nullable read, both of which this rule
|
|
17
|
+
cannot verify.
|
|
18
|
+
rule:
|
|
19
|
+
kind: property_declaration
|
|
20
|
+
has:
|
|
21
|
+
kind: modifiers
|
|
22
|
+
has:
|
|
23
|
+
kind: member_modifier
|
|
24
|
+
regex: "^lateinit$"
|
|
25
|
+
files:
|
|
26
|
+
- "**/*.kt"
|
|
27
|
+
tags:
|
|
28
|
+
- kotlin
|
|
29
|
+
- idiom
|
|
30
|
+
- null-safety
|
|
31
|
+
examples:
|
|
32
|
+
bad: |
|
|
33
|
+
class UserRepository {
|
|
34
|
+
lateinit var apiClient: ApiClient
|
|
35
|
+
}
|
|
36
|
+
good: |
|
|
37
|
+
class UserRepository(private val apiClient: ApiClient)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Kotlin No nullable Boolean
|
|
2
|
+
# Detects a `Boolean?` type on a property, parameter, or return type.
|
|
3
|
+
# Ported from vinhnx/VTCode rules/kotlin/no-nullable-boolean.yml (#2198, refs #2195).
|
|
4
|
+
id: kotlin-no-nullable-boolean
|
|
5
|
+
language: kotlin
|
|
6
|
+
severity: warning
|
|
7
|
+
message: "Avoid a nullable Boolean; a two-value enum or a non-null default is usually clearer"
|
|
8
|
+
note: |
|
|
9
|
+
A nullable Boolean gives a value three states (true/false/unknown) but the
|
|
10
|
+
type only documents two, so callers routinely forget to handle null and
|
|
11
|
+
either crash on an unboxing NPE or silently treat it as false.
|
|
12
|
+
Detection-only: null is a legitimate third state for some APIs (an
|
|
13
|
+
unanswered tri-state form field, a "not yet loaded" flag), and this rule
|
|
14
|
+
cannot tell that case from an accidental nullable Boolean.
|
|
15
|
+
rule:
|
|
16
|
+
kind: nullable_type
|
|
17
|
+
has:
|
|
18
|
+
kind: user_type
|
|
19
|
+
has:
|
|
20
|
+
kind: type_identifier
|
|
21
|
+
regex: "^Boolean$"
|
|
22
|
+
files:
|
|
23
|
+
- "**/*.kt"
|
|
24
|
+
tags:
|
|
25
|
+
- kotlin
|
|
26
|
+
- idiom
|
|
27
|
+
- null-safety
|
|
28
|
+
examples:
|
|
29
|
+
bad: |
|
|
30
|
+
fun isEligible(): Boolean? = null
|
|
31
|
+
good: |
|
|
32
|
+
enum class Eligibility { ELIGIBLE, INELIGIBLE, UNKNOWN }
|
|
33
|
+
fun eligibility(): Eligibility = Eligibility.UNKNOWN
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Kotlin No println
|
|
2
|
+
# Detects bare `println`/`print` calls.
|
|
3
|
+
# Ported from vinhnx/VTCode rules/kotlin/no-println.yml (#2198, refs #2195).
|
|
4
|
+
id: kotlin-no-println
|
|
5
|
+
language: kotlin
|
|
6
|
+
severity: warning
|
|
7
|
+
message: "Avoid println/print; use a structured logger"
|
|
8
|
+
note: |
|
|
9
|
+
Direct console output via println/print is not configurable and cannot be
|
|
10
|
+
routed to log aggregators or filtered by level. Use a structured logging
|
|
11
|
+
library (kotlin-logging, SLF4J, android.util.Log) for production code.
|
|
12
|
+
Detection-only: which logger to use, and whether a given call site is a
|
|
13
|
+
CLI entry point or a test where println is acceptable, is a project
|
|
14
|
+
policy call this rule cannot make.
|
|
15
|
+
Scoped to the unqualified call. A fully-qualified spelling
|
|
16
|
+
(`kotlin.io.println(...)`) is missed on purpose: syntactically it is the
|
|
17
|
+
same navigation-call shape as `anyReceiver.println(...)`, a member call on
|
|
18
|
+
an arbitrary type (a Writer, a PrintStream) that this rule must not flag,
|
|
19
|
+
and there is no type information here to tell the two apart.
|
|
20
|
+
rule:
|
|
21
|
+
any:
|
|
22
|
+
- pattern: println($$$ARGS)
|
|
23
|
+
- pattern: print($$$ARGS)
|
|
24
|
+
files:
|
|
25
|
+
- "**/*.kt"
|
|
26
|
+
ignores:
|
|
27
|
+
- "**/*Test.kt"
|
|
28
|
+
- "**/test/**"
|
|
29
|
+
tags:
|
|
30
|
+
- kotlin
|
|
31
|
+
- idiom
|
|
32
|
+
- logging
|
|
33
|
+
examples:
|
|
34
|
+
bad: |
|
|
35
|
+
fun main() {
|
|
36
|
+
println("starting up")
|
|
37
|
+
}
|
|
38
|
+
good: |
|
|
39
|
+
private val logger = KotlinLogging.logger {}
|
|
40
|
+
fun main() {
|
|
41
|
+
logger.info { "starting up" }
|
|
42
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Kotlin No unnecessary let
|
|
2
|
+
# Detects `.let { }` called on a receiver without a preceding safe-call `?.`.
|
|
3
|
+
# Ported from vinhnx/VTCode rules/kotlin/no-unnecessary-let.yml (#2198, refs #2195).
|
|
4
|
+
id: kotlin-no-unnecessary-let
|
|
5
|
+
language: kotlin
|
|
6
|
+
severity: warning
|
|
7
|
+
message: "let on a non-null-checked receiver adds indirection; use the value directly"
|
|
8
|
+
note: |
|
|
9
|
+
`.let { }` exists to scope a null-safe transform after `?.`. Calling it on
|
|
10
|
+
a receiver that is already known non-null (no `?.`) just renames the
|
|
11
|
+
receiver to `it` inside a lambda, adding a level of nesting with no
|
|
12
|
+
null-safety benefit.
|
|
13
|
+
Detection-only: the receiver's own nullability is not tracked here, and
|
|
14
|
+
`.let` also has legitimate uses for scoping a mutable local or chaining a
|
|
15
|
+
builder, which this pattern cannot distinguish from the redundant case.
|
|
16
|
+
rule:
|
|
17
|
+
pattern: $RECEIVER.let { $$$BODY }
|
|
18
|
+
files:
|
|
19
|
+
- "**/*.kt"
|
|
20
|
+
tags:
|
|
21
|
+
- kotlin
|
|
22
|
+
- idiom
|
|
23
|
+
- readability
|
|
24
|
+
examples:
|
|
25
|
+
bad: |
|
|
26
|
+
val length = name.let { it.length }
|
|
27
|
+
good: |
|
|
28
|
+
val length = name.length
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Kotlin No unsafe cast
|
|
2
|
+
# Detects the unsafe `as` cast (as opposed to the safe `as?` cast).
|
|
3
|
+
# Ported from vinhnx/VTCode rules/kotlin/no-unsafe-cast.yml (#2198, refs #2195).
|
|
4
|
+
id: kotlin-no-unsafe-cast
|
|
5
|
+
language: kotlin
|
|
6
|
+
severity: warning
|
|
7
|
+
message: "Unsafe cast (as) throws ClassCastException on a mismatch; consider as?"
|
|
8
|
+
note: |
|
|
9
|
+
`as` throws ClassCastException immediately when the value isn't the target
|
|
10
|
+
type. `as?` returns null instead, which is usually safer when the caller
|
|
11
|
+
can handle a missing value.
|
|
12
|
+
Detection-only: `as?` changes a thrown failure into a silent null, which
|
|
13
|
+
is a real behavior change a caller must handle explicitly. Whether the
|
|
14
|
+
unsafe throw is actually the intended contract (a precondition the caller
|
|
15
|
+
already established) is a call this rule cannot make.
|
|
16
|
+
rule:
|
|
17
|
+
pattern: $VALUE as $TYPE
|
|
18
|
+
files:
|
|
19
|
+
- "**/*.kt"
|
|
20
|
+
tags:
|
|
21
|
+
- kotlin
|
|
22
|
+
- idiom
|
|
23
|
+
- null-safety
|
|
24
|
+
examples:
|
|
25
|
+
bad: |
|
|
26
|
+
val name = value as String
|
|
27
|
+
good: |
|
|
28
|
+
val name = value as? String ?: return
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Kotlin No var
|
|
2
|
+
# Detects a `var` property or local declaration (as opposed to `val`).
|
|
3
|
+
# Ported from vinhnx/VTCode rules/kotlin/no-var.yml (#2198, refs #2195).
|
|
4
|
+
id: kotlin-no-var
|
|
5
|
+
language: kotlin
|
|
6
|
+
severity: warning
|
|
7
|
+
message: "Prefer val over var; mutability should be explicit and scoped"
|
|
8
|
+
note: |
|
|
9
|
+
Kotlin favors immutability by default. A `var` where a `val` would do
|
|
10
|
+
widens the set of places a value can change, which makes reasoning about
|
|
11
|
+
the surrounding code harder as it grows.
|
|
12
|
+
Detection-only: some `var`s are load-bearing (loop counters, accumulators,
|
|
13
|
+
lifecycle-managed fields), and rewriting to `val` requires knowing every
|
|
14
|
+
call site's mutation intent, which this rule cannot verify.
|
|
15
|
+
A `lateinit var` also matches `kotlin-no-lateinit`, so it reports twice.
|
|
16
|
+
That's intentional, not a bug to dedupe: the two findings name different
|
|
17
|
+
problems (mutability here, deferred-initialization risk there) and either
|
|
18
|
+
one could be fixed independently of the other.
|
|
19
|
+
rule:
|
|
20
|
+
kind: property_declaration
|
|
21
|
+
has:
|
|
22
|
+
kind: binding_pattern_kind
|
|
23
|
+
regex: "^var$"
|
|
24
|
+
files:
|
|
25
|
+
- "**/*.kt"
|
|
26
|
+
tags:
|
|
27
|
+
- kotlin
|
|
28
|
+
- idiom
|
|
29
|
+
- immutability
|
|
30
|
+
examples:
|
|
31
|
+
bad: |
|
|
32
|
+
var total = 0
|
|
33
|
+
total = total + price
|
|
34
|
+
good: |
|
|
35
|
+
val total = prices.sum()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Kotlin Prefer data class
|
|
2
|
+
# Detects a plain class whose primary constructor is made entirely of
|
|
3
|
+
# val/var properties and that carries no additional behavior (a function, an
|
|
4
|
+
# init block, or a computed property), a strong signal it's a value holder
|
|
5
|
+
# that should be a data class.
|
|
6
|
+
# Ported from vinhnx/VTCode rules/kotlin/prefer-data-class.yml (#2198, refs #2195).
|
|
7
|
+
id: kotlin-prefer-data-class
|
|
8
|
+
language: kotlin
|
|
9
|
+
severity: warning
|
|
10
|
+
message: "This class looks like a plain value holder; consider a data class"
|
|
11
|
+
note: |
|
|
12
|
+
A data class generates equals/hashCode/toString/copy/componentN for free,
|
|
13
|
+
which a hand-written value holder either lacks or must maintain by hand.
|
|
14
|
+
Detection-only: adding `data` changes equality semantics (structural
|
|
15
|
+
instead of referential) and generates a public API surface (copy,
|
|
16
|
+
componentN) the class may not want to expose, neither of which this rule
|
|
17
|
+
can verify — it excludes classes carrying any modifier
|
|
18
|
+
(open/abstract/sealed/inner/data/enum/…) and classes with a function, an
|
|
19
|
+
init block, or a computed property in their body, but a caller may still
|
|
20
|
+
have other reasons the plain class is intentional.
|
|
21
|
+
A class that extends another type through its primary constructor (a
|
|
22
|
+
delegation specifier, e.g. `class Ok(val v: String) : Result()`) still
|
|
23
|
+
matches: since Kotlin 1.1, a data class may extend another class, so
|
|
24
|
+
inheritance alone is not a reason to exclude it.
|
|
25
|
+
rule:
|
|
26
|
+
all:
|
|
27
|
+
- kind: class_declaration
|
|
28
|
+
- not:
|
|
29
|
+
has:
|
|
30
|
+
kind: modifiers
|
|
31
|
+
- has:
|
|
32
|
+
kind: primary_constructor
|
|
33
|
+
has:
|
|
34
|
+
kind: class_parameter
|
|
35
|
+
not:
|
|
36
|
+
has:
|
|
37
|
+
kind: class_parameter
|
|
38
|
+
not:
|
|
39
|
+
has:
|
|
40
|
+
kind: binding_pattern_kind
|
|
41
|
+
- not:
|
|
42
|
+
has:
|
|
43
|
+
kind: class_body
|
|
44
|
+
any:
|
|
45
|
+
- has:
|
|
46
|
+
kind: function_declaration
|
|
47
|
+
- has:
|
|
48
|
+
kind: anonymous_initializer
|
|
49
|
+
- has:
|
|
50
|
+
kind: property_declaration
|
|
51
|
+
any:
|
|
52
|
+
- has:
|
|
53
|
+
kind: getter
|
|
54
|
+
- has:
|
|
55
|
+
kind: setter
|
|
56
|
+
files:
|
|
57
|
+
- "**/*.kt"
|
|
58
|
+
tags:
|
|
59
|
+
- kotlin
|
|
60
|
+
- idiom
|
|
61
|
+
- readability
|
|
62
|
+
examples:
|
|
63
|
+
bad: |
|
|
64
|
+
class Point(val x: Int, val y: Int)
|
|
65
|
+
good: |
|
|
66
|
+
data class Point(val x: Int, val y: Int)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Kotlin Prefer isEmpty
|
|
2
|
+
# Detects `.size == 0` / `.length == 0`, which usually reads better as
|
|
3
|
+
# `.isEmpty()`.
|
|
4
|
+
# Ported from vinhnx/VTCode rules/kotlin/prefer-is-empty.yml (#2198, refs #2195).
|
|
5
|
+
id: kotlin-prefer-is-empty
|
|
6
|
+
language: kotlin
|
|
7
|
+
severity: warning
|
|
8
|
+
message: "Use isEmpty() instead of comparing size/length to zero"
|
|
9
|
+
note: |
|
|
10
|
+
`.isEmpty()` states intent directly. It is exactly equivalent to a
|
|
11
|
+
zero-size/zero-length comparison for every Kotlin collection, Array, and
|
|
12
|
+
String, but this rule cannot see the receiver's actual type — a class with
|
|
13
|
+
its own unrelated `size`/`length` property (e.g. `class Segment(val
|
|
14
|
+
length: Int)`) has no `isEmpty()` member at all, so a blind rewrite would
|
|
15
|
+
emit code that doesn't compile.
|
|
16
|
+
Detection-only for that reason, matching the Java sibling
|
|
17
|
+
`prefer-string-is-empty.yml`: the receiver's type is not checked here
|
|
18
|
+
either. Only the exact `== 0` comparison is flagged; `> 0`, `!= 0`, and
|
|
19
|
+
other comparisons are left alone because their natural rewrite
|
|
20
|
+
(isNotEmpty, or none) differs per shape.
|
|
21
|
+
rule:
|
|
22
|
+
any:
|
|
23
|
+
- pattern: $RECEIVER.size == 0
|
|
24
|
+
- pattern: $RECEIVER.length == 0
|
|
25
|
+
files:
|
|
26
|
+
- "**/*.kt"
|
|
27
|
+
tags:
|
|
28
|
+
- kotlin
|
|
29
|
+
- idiom
|
|
30
|
+
- readability
|
|
31
|
+
examples:
|
|
32
|
+
bad: |
|
|
33
|
+
if (items.size == 0) return
|
|
34
|
+
good: |
|
|
35
|
+
if (items.isEmpty()) return
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
id: no-important
|
|
2
|
+
language: Css
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "Avoid `!important` — increase selector specificity instead"
|
|
5
|
+
note: |
|
|
6
|
+
Ported from VTCode (https://github.com/vinhnx/VTCode/tree/main/rules/css,
|
|
7
|
+
Apache-2.0 license) per #2199 / #2195.
|
|
8
|
+
|
|
9
|
+
`!important` overrides normal cascade order and makes CSS harder to
|
|
10
|
+
maintain: once one declaration reaches for it, overriding it later
|
|
11
|
+
requires either another `!important` or a `style` attribute, and the
|
|
12
|
+
arms race compounds. Prefer raising selector specificity or restructuring
|
|
13
|
+
the cascade so the intended rule already wins.
|
|
14
|
+
|
|
15
|
+
Detection-only: a safe fix would need to know which selector should win
|
|
16
|
+
and why, which this rule cannot infer from the declaration alone. Ported
|
|
17
|
+
as-is from upstream (no fix upstream either).
|
|
18
|
+
rule:
|
|
19
|
+
pattern: "!important"
|
|
20
|
+
files:
|
|
21
|
+
- "**/*.css"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
id: no-raw-types
|
|
2
|
+
language: java
|
|
3
|
+
severity: warning
|
|
4
|
+
message: Use parameterized types instead of raw types.
|
|
5
|
+
note: Ported from VTCode (https://github.com/vinhnx/VTCode), refs #2195. Raw collection types need a project/API-aware generic choice, so this rule is detection-only. Contexts with no valid parameterized form are excluded.
|
|
6
|
+
rule:
|
|
7
|
+
kind: type_identifier
|
|
8
|
+
regex: "^(List|Map|Set|Collection|Iterator|Iterable|Queue|Deque|Optional)$"
|
|
9
|
+
not:
|
|
10
|
+
any:
|
|
11
|
+
# Already parameterized. These carry no `stopBy`, so they test only the
|
|
12
|
+
# immediate parent. `stopBy: end` walked every ancestor instead, which
|
|
13
|
+
# silenced a raw type nested in a type argument: in `Map<String, List>`
|
|
14
|
+
# the inner `List` has a generic_type ancestor and went unreported.
|
|
15
|
+
- inside:
|
|
16
|
+
kind: generic_type
|
|
17
|
+
# The qualifier chain of a parameterized qualified type, so
|
|
18
|
+
# `java.util.List<String>` and `Map.Entry<K, V>` stay silent.
|
|
19
|
+
- inside:
|
|
20
|
+
kind: scoped_type_identifier
|
|
21
|
+
inside:
|
|
22
|
+
kind: generic_type
|
|
23
|
+
# `o instanceof List` and `List.class` have no valid parameterized form.
|
|
24
|
+
# `o instanceof List<String>` and `List<String>.class` are both compile
|
|
25
|
+
# errors, so a finding here is unactionable.
|
|
26
|
+
- inside:
|
|
27
|
+
kind: instanceof_expression
|
|
28
|
+
- inside:
|
|
29
|
+
kind: class_literal
|
|
30
|
+
files:
|
|
31
|
+
- "**/*.java"
|
|
32
|
+
ignores:
|
|
33
|
+
- "**/test/**"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
id: no-string-concat-in-loop
|
|
2
|
+
language: java
|
|
3
|
+
severity: warning
|
|
4
|
+
message: Use StringBuilder instead of string concatenation in loops.
|
|
5
|
+
note: Ported from VTCode (https://github.com/vinhnx/VTCode), refs #2195. Loop allocation and readability context make this detection-only. The accumulator must be a String, established either by a direct string-literal operand or by the declaration that binds the same name in scope.
|
|
6
|
+
rule:
|
|
7
|
+
all:
|
|
8
|
+
# An accumulating assignment: `s += x` or `s = s + x`.
|
|
9
|
+
- any:
|
|
10
|
+
- pattern: $VAR += $EXPR
|
|
11
|
+
- pattern: $VAR = $VAR + $EXPR
|
|
12
|
+
# ... inside a loop body.
|
|
13
|
+
- any:
|
|
14
|
+
- inside:
|
|
15
|
+
kind: for_statement
|
|
16
|
+
stopBy: end
|
|
17
|
+
- inside:
|
|
18
|
+
kind: enhanced_for_statement
|
|
19
|
+
stopBy: end
|
|
20
|
+
- inside:
|
|
21
|
+
kind: while_statement
|
|
22
|
+
stopBy: end
|
|
23
|
+
- inside:
|
|
24
|
+
kind: do_statement
|
|
25
|
+
stopBy: end
|
|
26
|
+
# ... where the accumulator is a String. Without this arm the rule fires on
|
|
27
|
+
# `total += counts.get("label")`, because a `stopBy: end` search for a
|
|
28
|
+
# string_literal accepts one anywhere in the subtree, including inside a
|
|
29
|
+
# call argument. Each branch below binds String-ness to $VAR itself.
|
|
30
|
+
- any:
|
|
31
|
+
# The right-hand side is, or directly contains, a string literal. The
|
|
32
|
+
# inner `has` carries no `stopBy`, so it only inspects direct operands
|
|
33
|
+
# of the `+` and never descends into a call's arguments.
|
|
34
|
+
- has:
|
|
35
|
+
field: right
|
|
36
|
+
any:
|
|
37
|
+
- kind: string_literal
|
|
38
|
+
- all:
|
|
39
|
+
- kind: binary_expression
|
|
40
|
+
- has:
|
|
41
|
+
kind: string_literal
|
|
42
|
+
# $VAR is declared String in the enclosing method or constructor,
|
|
43
|
+
# either as a local or as a parameter. $VAR is the same metavariable
|
|
44
|
+
# the pattern above bound, so the names must match.
|
|
45
|
+
- inside:
|
|
46
|
+
stopBy: end
|
|
47
|
+
any:
|
|
48
|
+
- kind: method_declaration
|
|
49
|
+
- kind: constructor_declaration
|
|
50
|
+
has:
|
|
51
|
+
stopBy: end
|
|
52
|
+
any:
|
|
53
|
+
- all:
|
|
54
|
+
- kind: local_variable_declaration
|
|
55
|
+
- has:
|
|
56
|
+
field: type
|
|
57
|
+
regex: ^String$
|
|
58
|
+
- has:
|
|
59
|
+
kind: variable_declarator
|
|
60
|
+
has:
|
|
61
|
+
field: name
|
|
62
|
+
pattern: $VAR
|
|
63
|
+
- all:
|
|
64
|
+
- kind: formal_parameter
|
|
65
|
+
- has:
|
|
66
|
+
field: type
|
|
67
|
+
regex: ^String$
|
|
68
|
+
- has:
|
|
69
|
+
field: name
|
|
70
|
+
pattern: $VAR
|
|
71
|
+
# $VAR is a String field of the enclosing class.
|
|
72
|
+
- inside:
|
|
73
|
+
stopBy: end
|
|
74
|
+
kind: class_body
|
|
75
|
+
has:
|
|
76
|
+
stopBy: end
|
|
77
|
+
all:
|
|
78
|
+
- kind: field_declaration
|
|
79
|
+
- has:
|
|
80
|
+
field: type
|
|
81
|
+
regex: ^String$
|
|
82
|
+
- has:
|
|
83
|
+
kind: variable_declarator
|
|
84
|
+
has:
|
|
85
|
+
field: name
|
|
86
|
+
pattern: $VAR
|
|
87
|
+
files:
|
|
88
|
+
- "**/*.java"
|
|
89
|
+
ignores:
|
|
90
|
+
- "**/test/**"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
id: no-system-out-println
|
|
2
|
+
language: java
|
|
3
|
+
severity: warning
|
|
4
|
+
message: Avoid System.out.println; use a logging framework instead.
|
|
5
|
+
note: Ported from VTCode (https://github.com/vinhnx/VTCode), refs #2195. Logger policy and intentional console output vary by project, so this rule is detection-only.
|
|
6
|
+
rule:
|
|
7
|
+
any:
|
|
8
|
+
- pattern: System.out.println($$$)
|
|
9
|
+
- pattern: System.out.print($$$)
|
|
10
|
+
- pattern: System.err.println($$$)
|
|
11
|
+
- pattern: System.err.print($$$)
|
|
12
|
+
files:
|
|
13
|
+
- "**/*.java"
|
|
14
|
+
ignores:
|
|
15
|
+
- "**/test/**"
|
|
16
|
+
- "**/*Test.java"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
id: prefer-string-is-empty
|
|
2
|
+
language: java
|
|
3
|
+
severity: warning
|
|
4
|
+
message: Use .isEmpty() instead of .length() == 0 or .equals("").
|
|
5
|
+
note: Ported from VTCode (https://github.com/vinhnx/VTCode), refs #2195. The receiver's type is not checked, so this rule is detection-only. Detection is limited to the exact empty-string and zero-length forms.
|
|
6
|
+
rule:
|
|
7
|
+
any:
|
|
8
|
+
- pattern: $S.length() == 0
|
|
9
|
+
- pattern: $S.equals("")
|
|
10
|
+
files:
|
|
11
|
+
- "**/*.java"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
id: python-optional-type
|
|
2
|
+
language: Python
|
|
3
|
+
severity: hint
|
|
4
|
+
message: "Optional[T] requires Python 3.10+ to rewrite as T | None"
|
|
5
|
+
note: |
|
|
6
|
+
`Optional[T]` can be written as `T | None` on Python 3.10 and later.
|
|
7
|
+
A CPython 3.11 stdlib census found 1,675 matches and zero defects, so this
|
|
8
|
+
remains a modernization hint rather than a warning about incorrect code.
|
|
9
|
+
This rule is detection-only because the target project's Python version is
|
|
10
|
+
not available to a syntax-only matcher. Do not apply the rewrite to Python
|
|
11
|
+
3.8 or 3.9 code.
|
|
12
|
+
rule:
|
|
13
|
+
kind: type
|
|
14
|
+
regex: '^(Optional|typing\.Optional|t\.Optional)\['
|
|
15
|
+
has_fix: false
|
|
16
|
+
tags:
|
|
17
|
+
- python
|
|
18
|
+
- typing
|
|
19
|
+
- modernization
|
|
20
|
+
examples:
|
|
21
|
+
bad: "value: Optional[str]"
|
|
22
|
+
good: "value: str | None"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
id: reducible-list-comprehension
|
|
2
|
+
language: Python
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "A list comprehension is unnecessary here; use a generator expression"
|
|
5
|
+
note: |
|
|
6
|
+
`any`, `all`, and `sum` consume iterables directly, so the list allocation
|
|
7
|
+
can be removed without changing the operation's result. The generator
|
|
8
|
+
expression can short-circuit, so evaluation counts differ; apply this fix
|
|
9
|
+
only when the element expression is side-effect-free.
|
|
10
|
+
rule:
|
|
11
|
+
pattern: $FN([$X for $Y in $Z])
|
|
12
|
+
not:
|
|
13
|
+
any:
|
|
14
|
+
- pattern: $FN([$X async for $Y in $Z])
|
|
15
|
+
- pattern: $FN([await $X for $Y in $Z])
|
|
16
|
+
- has:
|
|
17
|
+
pattern: await $$$
|
|
18
|
+
stopBy: end
|
|
19
|
+
constraints:
|
|
20
|
+
FN:
|
|
21
|
+
regex: "^(any|all|sum)$"
|
|
22
|
+
fix: "$FN($X for $Y in $Z)"
|
|
23
|
+
tags:
|
|
24
|
+
- python
|
|
25
|
+
- performance
|
|
26
|
+
- modernization
|
|
27
|
+
examples:
|
|
28
|
+
bad: "total = sum([price for price in prices])"
|
|
29
|
+
good: "total = sum(price for price in prices)"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
id: ruby-symbol-to-proc-candidate
|
|
2
|
+
language: Ruby
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "Use a symbol-to-proc shorthand for this simple iterator block"
|
|
5
|
+
note: |
|
|
6
|
+
A block that forwards its one parameter to one receiver method can use the
|
|
7
|
+
equivalent symbol-to-proc form. Side-effect-oriented iterators such as
|
|
8
|
+
`each` are excluded because replacing them is less useful as a default hint.
|
|
9
|
+
rule:
|
|
10
|
+
pattern: $LIST.$ITER { |$X| $X.$METHOD }
|
|
11
|
+
not:
|
|
12
|
+
pattern: $LIST.$ITER { |$X| $X.$METHOD($$$ARGS) }
|
|
13
|
+
constraints:
|
|
14
|
+
ITER:
|
|
15
|
+
regex: '^(map|collect|filter|select|reject|find|detect|any\?|all\?|none\?|one\?|flat_map|sort_by|group_by)$'
|
|
16
|
+
METHOD:
|
|
17
|
+
regex: '^[A-Za-z_][A-Za-z0-9_]*[?!=]?$'
|
|
18
|
+
fix: "$LIST.$ITER(&:$METHOD)"
|
|
19
|
+
tags:
|
|
20
|
+
- ruby
|
|
21
|
+
- style
|
|
22
|
+
- modernization
|
|
23
|
+
examples:
|
|
24
|
+
bad: "names.map { |name| name.upcase }"
|
|
25
|
+
good: "names.map(&:upcase)"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
id: rust-no-chars-enumerate
|
|
2
|
+
language: Rust
|
|
3
|
+
severity: warning
|
|
4
|
+
message: "chars().enumerate() may confuse character indices with byte offsets"
|
|
5
|
+
note: |
|
|
6
|
+
`chars().enumerate()` produces character positions, while many Rust APIs
|
|
7
|
+
require byte offsets. This rule is detection-only because the intended index
|
|
8
|
+
semantics cannot be proved from syntax. Use `char_indices()` only when the
|
|
9
|
+
consumer requires byte offsets.
|
|
10
|
+
rule:
|
|
11
|
+
pattern: $TEXT.chars().enumerate()
|
|
12
|
+
has_fix: false
|
|
13
|
+
tags:
|
|
14
|
+
- rust
|
|
15
|
+
- correctness
|
|
16
|
+
- unicode
|
|
17
|
+
examples:
|
|
18
|
+
bad: "for (index, character) in text.chars().enumerate() {}"
|
|
19
|
+
good: "for (index, character) in text.char_indices() {}"
|