@skastr0/pulsar-rs-pack 0.1.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.
Files changed (189) hide show
  1. package/dist/cargo-metadata.d.ts +59 -0
  2. package/dist/cargo-metadata.d.ts.map +1 -0
  3. package/dist/cargo-metadata.js +162 -0
  4. package/dist/cargo-metadata.js.map +1 -0
  5. package/dist/index.d.ts +7 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +7 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/lock-file.d.ts +20 -0
  10. package/dist/lock-file.d.ts.map +1 -0
  11. package/dist/lock-file.js +144 -0
  12. package/dist/lock-file.js.map +1 -0
  13. package/dist/pack.d.ts +6 -0
  14. package/dist/pack.d.ts.map +1 -0
  15. package/dist/pack.js +55 -0
  16. package/dist/pack.js.map +1 -0
  17. package/dist/project.d.ts +26 -0
  18. package/dist/project.d.ts.map +1 -0
  19. package/dist/project.js +172 -0
  20. package/dist/project.js.map +1 -0
  21. package/dist/rust-analysis-collect.d.ts +4 -0
  22. package/dist/rust-analysis-collect.d.ts.map +1 -0
  23. package/dist/rust-analysis-collect.js +84 -0
  24. package/dist/rust-analysis-collect.js.map +1 -0
  25. package/dist/rust-analysis-functions.d.ts +4 -0
  26. package/dist/rust-analysis-functions.d.ts.map +1 -0
  27. package/dist/rust-analysis-functions.js +140 -0
  28. package/dist/rust-analysis-functions.js.map +1 -0
  29. package/dist/rust-analysis-identifiers.d.ts +3 -0
  30. package/dist/rust-analysis-identifiers.d.ts.map +1 -0
  31. package/dist/rust-analysis-identifiers.js +5 -0
  32. package/dist/rust-analysis-identifiers.js.map +1 -0
  33. package/dist/rust-analysis-items.d.ts +4 -0
  34. package/dist/rust-analysis-items.d.ts.map +1 -0
  35. package/dist/rust-analysis-items.js +58 -0
  36. package/dist/rust-analysis-items.js.map +1 -0
  37. package/dist/rust-analysis-modules.d.ts +7 -0
  38. package/dist/rust-analysis-modules.d.ts.map +1 -0
  39. package/dist/rust-analysis-modules.js +52 -0
  40. package/dist/rust-analysis-modules.js.map +1 -0
  41. package/dist/rust-analysis-syntax.d.ts +12 -0
  42. package/dist/rust-analysis-syntax.d.ts.map +1 -0
  43. package/dist/rust-analysis-syntax.js +102 -0
  44. package/dist/rust-analysis-syntax.js.map +1 -0
  45. package/dist/rust-analysis-types.d.ts +109 -0
  46. package/dist/rust-analysis-types.d.ts.map +1 -0
  47. package/dist/rust-analysis-types.js +2 -0
  48. package/dist/rust-analysis-types.js.map +1 -0
  49. package/dist/rust-analysis-uses.d.ts +4 -0
  50. package/dist/rust-analysis-uses.d.ts.map +1 -0
  51. package/dist/rust-analysis-uses.js +63 -0
  52. package/dist/rust-analysis-uses.js.map +1 -0
  53. package/dist/rust-analysis.d.ts +5 -0
  54. package/dist/rust-analysis.d.ts.map +1 -0
  55. package/dist/rust-analysis.js +4 -0
  56. package/dist/rust-analysis.js.map +1 -0
  57. package/dist/signals/rs-ab-01-unused-pub.d.ts +29 -0
  58. package/dist/signals/rs-ab-01-unused-pub.d.ts.map +1 -0
  59. package/dist/signals/rs-ab-01-unused-pub.js +184 -0
  60. package/dist/signals/rs-ab-01-unused-pub.js.map +1 -0
  61. package/dist/signals/rs-ab-02-trait-object-depth.d.ts +26 -0
  62. package/dist/signals/rs-ab-02-trait-object-depth.d.ts.map +1 -0
  63. package/dist/signals/rs-ab-02-trait-object-depth.js +157 -0
  64. package/dist/signals/rs-ab-02-trait-object-depth.js.map +1 -0
  65. package/dist/signals/rs-ab-03-generic-proliferation.d.ts +28 -0
  66. package/dist/signals/rs-ab-03-generic-proliferation.d.ts.map +1 -0
  67. package/dist/signals/rs-ab-03-generic-proliferation.js +108 -0
  68. package/dist/signals/rs-ab-03-generic-proliferation.js.map +1 -0
  69. package/dist/signals/rs-ab-04-derive-density.d.ts +25 -0
  70. package/dist/signals/rs-ab-04-derive-density.d.ts.map +1 -0
  71. package/dist/signals/rs-ab-04-derive-density.js +101 -0
  72. package/dist/signals/rs-ab-04-derive-density.js.map +1 -0
  73. package/dist/signals/rs-ad-01-visibility-surface.d.ts +29 -0
  74. package/dist/signals/rs-ad-01-visibility-surface.d.ts.map +1 -0
  75. package/dist/signals/rs-ad-01-visibility-surface.js +129 -0
  76. package/dist/signals/rs-ad-01-visibility-surface.js.map +1 -0
  77. package/dist/signals/rs-ad-02-boundary-rules.d.ts +3 -0
  78. package/dist/signals/rs-ad-02-boundary-rules.d.ts.map +1 -0
  79. package/dist/signals/rs-ad-02-boundary-rules.js +26 -0
  80. package/dist/signals/rs-ad-02-boundary-rules.js.map +1 -0
  81. package/dist/signals/rs-ad-02-crate-analysis.d.ts +9 -0
  82. package/dist/signals/rs-ad-02-crate-analysis.d.ts.map +1 -0
  83. package/dist/signals/rs-ad-02-crate-analysis.js +117 -0
  84. package/dist/signals/rs-ad-02-crate-analysis.js.map +1 -0
  85. package/dist/signals/rs-ad-02-crate-boundaries.d.ts +6 -0
  86. package/dist/signals/rs-ad-02-crate-boundaries.d.ts.map +1 -0
  87. package/dist/signals/rs-ad-02-crate-boundaries.js +84 -0
  88. package/dist/signals/rs-ad-02-crate-boundaries.js.map +1 -0
  89. package/dist/signals/rs-ad-02-types.d.ts +26 -0
  90. package/dist/signals/rs-ad-02-types.d.ts.map +1 -0
  91. package/dist/signals/rs-ad-02-types.js +6 -0
  92. package/dist/signals/rs-ad-02-types.js.map +1 -0
  93. package/dist/signals/rs-ad-03-circular-crate-deps.d.ts +30 -0
  94. package/dist/signals/rs-ad-03-circular-crate-deps.d.ts.map +1 -0
  95. package/dist/signals/rs-ad-03-circular-crate-deps.js +185 -0
  96. package/dist/signals/rs-ad-03-circular-crate-deps.js.map +1 -0
  97. package/dist/signals/rs-de-01-trait-coupling.d.ts +43 -0
  98. package/dist/signals/rs-de-01-trait-coupling.d.ts.map +1 -0
  99. package/dist/signals/rs-de-01-trait-coupling.js +222 -0
  100. package/dist/signals/rs-de-01-trait-coupling.js.map +1 -0
  101. package/dist/signals/rs-de-02-dep-tree.d.ts +28 -0
  102. package/dist/signals/rs-de-02-dep-tree.d.ts.map +1 -0
  103. package/dist/signals/rs-de-02-dep-tree.js +173 -0
  104. package/dist/signals/rs-de-02-dep-tree.js.map +1 -0
  105. package/dist/signals/rs-de-03-feature-flags.d.ts +32 -0
  106. package/dist/signals/rs-de-03-feature-flags.d.ts.map +1 -0
  107. package/dist/signals/rs-de-03-feature-flags.js +139 -0
  108. package/dist/signals/rs-de-03-feature-flags.js.map +1 -0
  109. package/dist/signals/rs-de-04-fan-in-fan-out.d.ts +28 -0
  110. package/dist/signals/rs-de-04-fan-in-fan-out.d.ts.map +1 -0
  111. package/dist/signals/rs-de-04-fan-in-fan-out.js +109 -0
  112. package/dist/signals/rs-de-04-fan-in-fan-out.js.map +1 -0
  113. package/dist/signals/rs-ld-01-unsafe.d.ts +28 -0
  114. package/dist/signals/rs-ld-01-unsafe.d.ts.map +1 -0
  115. package/dist/signals/rs-ld-01-unsafe.js +116 -0
  116. package/dist/signals/rs-ld-01-unsafe.js.map +1 -0
  117. package/dist/signals/rs-ld-02-lifetimes.d.ts +30 -0
  118. package/dist/signals/rs-ld-02-lifetimes.d.ts.map +1 -0
  119. package/dist/signals/rs-ld-02-lifetimes.js +79 -0
  120. package/dist/signals/rs-ld-02-lifetimes.js.map +1 -0
  121. package/dist/signals/rs-ld-03-match-catch-all.d.ts +26 -0
  122. package/dist/signals/rs-ld-03-match-catch-all.d.ts.map +1 -0
  123. package/dist/signals/rs-ld-03-match-catch-all.js +68 -0
  124. package/dist/signals/rs-ld-03-match-catch-all.js.map +1 -0
  125. package/dist/signals/rs-ld-04-error-granularity.d.ts +25 -0
  126. package/dist/signals/rs-ld-04-error-granularity.d.ts.map +1 -0
  127. package/dist/signals/rs-ld-04-error-granularity.js +77 -0
  128. package/dist/signals/rs-ld-04-error-granularity.js.map +1 -0
  129. package/dist/signals/rs-ld-05-complexity.d.ts +26 -0
  130. package/dist/signals/rs-ld-05-complexity.d.ts.map +1 -0
  131. package/dist/signals/rs-ld-05-complexity.js +70 -0
  132. package/dist/signals/rs-ld-05-complexity.js.map +1 -0
  133. package/dist/signals/rs-ld-06-domain-terms.d.ts +29 -0
  134. package/dist/signals/rs-ld-06-domain-terms.d.ts.map +1 -0
  135. package/dist/signals/rs-ld-06-domain-terms.js +158 -0
  136. package/dist/signals/rs-ld-06-domain-terms.js.map +1 -0
  137. package/dist/signals/rs-rp-01-hotspots.d.ts +27 -0
  138. package/dist/signals/rs-rp-01-hotspots.d.ts.map +1 -0
  139. package/dist/signals/rs-rp-01-hotspots.js +110 -0
  140. package/dist/signals/rs-rp-01-hotspots.js.map +1 -0
  141. package/dist/signals/rs-rp-02-compile-time.d.ts +25 -0
  142. package/dist/signals/rs-rp-02-compile-time.d.ts.map +1 -0
  143. package/dist/signals/rs-rp-02-compile-time.js +177 -0
  144. package/dist/signals/rs-rp-02-compile-time.js.map +1 -0
  145. package/dist/signals/rs-rp-03-pr-size.d.ts +23 -0
  146. package/dist/signals/rs-rp-03-pr-size.d.ts.map +1 -0
  147. package/dist/signals/rs-rp-03-pr-size.js +160 -0
  148. package/dist/signals/rs-rp-03-pr-size.js.map +1 -0
  149. package/dist/signals/rs-sl-01-duplication.d.ts +28 -0
  150. package/dist/signals/rs-sl-01-duplication.d.ts.map +1 -0
  151. package/dist/signals/rs-sl-01-duplication.js +172 -0
  152. package/dist/signals/rs-sl-01-duplication.js.map +1 -0
  153. package/dist/signals/rs-sl-02-suppressions.d.ts +30 -0
  154. package/dist/signals/rs-sl-02-suppressions.d.ts.map +1 -0
  155. package/dist/signals/rs-sl-02-suppressions.js +176 -0
  156. package/dist/signals/rs-sl-02-suppressions.js.map +1 -0
  157. package/dist/signals/rs-sl-03-unwrap-expect.d.ts +22 -0
  158. package/dist/signals/rs-sl-03-unwrap-expect.d.ts.map +1 -0
  159. package/dist/signals/rs-sl-03-unwrap-expect.js +94 -0
  160. package/dist/signals/rs-sl-03-unwrap-expect.js.map +1 -0
  161. package/dist/signals/rs-sl-04-clone-abuse.d.ts +22 -0
  162. package/dist/signals/rs-sl-04-clone-abuse.d.ts.map +1 -0
  163. package/dist/signals/rs-sl-04-clone-abuse.js +115 -0
  164. package/dist/signals/rs-sl-04-clone-abuse.js.map +1 -0
  165. package/dist/signals/shared-globs.d.ts +4 -0
  166. package/dist/signals/shared-globs.d.ts.map +1 -0
  167. package/dist/signals/shared-globs.js +12 -0
  168. package/dist/signals/shared-globs.js.map +1 -0
  169. package/dist/signals/shared-record-guards.d.ts +4 -0
  170. package/dist/signals/shared-record-guards.d.ts.map +1 -0
  171. package/dist/signals/shared-record-guards.js +4 -0
  172. package/dist/signals/shared-record-guards.js.map +1 -0
  173. package/dist/signals/shared-rust-ast.d.ts +30 -0
  174. package/dist/signals/shared-rust-ast.d.ts.map +1 -0
  175. package/dist/signals/shared-rust-ast.js +78 -0
  176. package/dist/signals/shared-rust-ast.js.map +1 -0
  177. package/dist/signals/shared-rust-resolution.d.ts +10 -0
  178. package/dist/signals/shared-rust-resolution.d.ts.map +1 -0
  179. package/dist/signals/shared-rust-resolution.js +53 -0
  180. package/dist/signals/shared-rust-resolution.js.map +1 -0
  181. package/dist/signals/shared-threshold-score.d.ts +2 -0
  182. package/dist/signals/shared-threshold-score.d.ts.map +1 -0
  183. package/dist/signals/shared-threshold-score.js +6 -0
  184. package/dist/signals/shared-threshold-score.js.map +1 -0
  185. package/dist/syn-walker.d.ts +7 -0
  186. package/dist/syn-walker.d.ts.map +1 -0
  187. package/dist/syn-walker.js +61 -0
  188. package/dist/syn-walker.js.map +1 -0
  189. package/package.json +56 -0
@@ -0,0 +1,176 @@
1
+ import { parseBypasses, SignalContextTag, SignalComputeError, } from "@skastr0/pulsar-core/signal";
2
+ import { readFile } from "node:fs/promises";
3
+ import { Effect, Schema } from "effect";
4
+ import { RustProjectTag } from "../project.js";
5
+ import { parseRustFile } from "../syn-walker.js";
6
+ import { lineRangeOverlapsChangedHunks, modulePathForAncestors, resolveRustFileScope, walkAttributedNodes, } from "./shared-rust-ast.js";
7
+ import { isExcluded } from "./shared-globs.js";
8
+ const RsSl02Config = Schema.Struct({
9
+ exclude_globs: Schema.Array(Schema.String),
10
+ top_n_diagnostics: Schema.Number,
11
+ });
12
+ export const RsSl02 = {
13
+ id: "RS-SL-02-suppressions",
14
+ title: "Suppressions",
15
+ aliases: ["RS-SL-02"],
16
+ tier: 1,
17
+ category: "generated-slop",
18
+ kind: "structural",
19
+ cacheVersion: "unused-allows-ordinary-v1",
20
+ configSchema: RsSl02Config,
21
+ defaultConfig: {
22
+ exclude_globs: ["**/target/**"],
23
+ top_n_diagnostics: 20,
24
+ },
25
+ inputs: [],
26
+ compute: (config) => Effect.gen(function* () {
27
+ const project = yield* RustProjectTag;
28
+ const context = yield* SignalContextTag;
29
+ return yield* Effect.tryPromise({
30
+ try: async () => {
31
+ const suppressions = [];
32
+ let ordinaryAllowAttributeCount = 0;
33
+ let ordinaryAllowLintCount = 0;
34
+ for (const file of project.sourceFiles) {
35
+ if (isExcluded(file, config.exclude_globs))
36
+ continue;
37
+ const source = await readFile(file, "utf8");
38
+ const bypasses = parseBypasses(source);
39
+ const scope = resolveRustFileScope(project, file);
40
+ const tree = await parseRustFile(file);
41
+ walkAttributedNodes(tree.rootNode, ({ node, ancestors, attachedAttributes }) => {
42
+ const { modulePath } = modulePathForAncestors(scope, ancestors);
43
+ for (const attribute of attachedAttributes) {
44
+ const lints = extractAllowLints(attribute.text);
45
+ if (lints.length === 0)
46
+ continue;
47
+ const classified = classifyAllowLints(lints);
48
+ ordinaryAllowLintCount += classified.ordinary.length;
49
+ if (classified.governed.length === 0) {
50
+ ordinaryAllowAttributeCount += 1;
51
+ continue;
52
+ }
53
+ const line = attribute.startPosition.row + 1;
54
+ if (!lineRangeOverlapsChangedHunks(file, line, line, context.worktreePath, context.changedHunks)) {
55
+ continue;
56
+ }
57
+ const attachedBypass = bypasses.find((bypass) => Math.abs(bypass.line - line) <= 1);
58
+ suppressions.push({
59
+ file,
60
+ module: modulePath,
61
+ line,
62
+ lints: classified.governed,
63
+ ordinaryLints: classified.ordinary,
64
+ justification: attachedBypass === undefined ? "missing" : attachedBypass.status,
65
+ classification: "requires-governance",
66
+ });
67
+ }
68
+ void node;
69
+ });
70
+ }
71
+ return {
72
+ suppressions: suppressions.sort((left, right) => left.file.localeCompare(right.file) || left.line - right.line),
73
+ ordinaryAllowAttributeCount,
74
+ ordinaryAllowLintCount,
75
+ governedAllowAttributeCount: suppressions.length,
76
+ missingJustificationCount: suppressions.filter((suppression) => suppression.justification === "missing").length,
77
+ expiredJustificationCount: suppressions.filter((suppression) => suppression.justification === "expired").length,
78
+ scopeMode: context.changedHunks.length > 0 ? "changed-hunks" : "whole-tree",
79
+ analysisMode: "allow-attributes-with-rust-lint-governance",
80
+ };
81
+ },
82
+ catch: (cause) => new SignalComputeError({ signalId: "RS-SL-02-suppressions", message: String(cause), cause }),
83
+ });
84
+ }),
85
+ score: (out) => {
86
+ if (out.suppressions.length === 0)
87
+ return 1;
88
+ if (out.missingJustificationCount > 0 || out.expiredJustificationCount > 0)
89
+ return 0;
90
+ return Math.max(0, 1 - out.suppressions.length / 25);
91
+ },
92
+ diagnose: (out) => out.suppressions.slice(0, 20).map((suppression) => ({
93
+ severity: suppression.justification === "active"
94
+ ? "info"
95
+ : "block",
96
+ message: `Governed allow suppression for ${suppression.lints.join(", ")} is ${suppression.justification}`,
97
+ location: { file: suppression.file, line: suppression.line },
98
+ data: {
99
+ module: suppression.module,
100
+ lints: suppression.lints,
101
+ ordinaryLints: suppression.ordinaryLints,
102
+ justification: suppression.justification,
103
+ classification: suppression.classification,
104
+ requiresJustification: true,
105
+ scopeMode: out.scopeMode,
106
+ analysisMode: out.analysisMode,
107
+ },
108
+ })),
109
+ };
110
+ const extractAllowLints = (text) => {
111
+ const match = /#\s*!?\s*\[\s*allow\s*\(([^\)]*)\)\s*\]/.exec(text);
112
+ if (match === null)
113
+ return [];
114
+ return match[1]
115
+ .split(",")
116
+ .map((value) => value.trim())
117
+ .filter((value) => value.length > 0);
118
+ };
119
+ const classifyAllowLints = (lints) => {
120
+ const ordinary = [];
121
+ const governed = [];
122
+ for (const lint of lints) {
123
+ if (requiresPulsarAllow(lint)) {
124
+ governed.push(lint);
125
+ }
126
+ else {
127
+ ordinary.push(lint);
128
+ }
129
+ }
130
+ return { ordinary, governed };
131
+ };
132
+ const broadlyScopedLintNames = new Set([
133
+ "warnings",
134
+ "future_incompatible",
135
+ "keyword_idents",
136
+ "nonstandard_style",
137
+ "rust_2018_idioms",
138
+ "rust_2021_compatibility",
139
+ "clippy::all",
140
+ "clippy::cargo",
141
+ "clippy::complexity",
142
+ "clippy::correctness",
143
+ "clippy::nursery",
144
+ "clippy::pedantic",
145
+ "clippy::perf",
146
+ "clippy::restriction",
147
+ "clippy::style",
148
+ "clippy::suspicious",
149
+ ]);
150
+ const slopHidingLintNames = new Set([
151
+ "unsafe_code",
152
+ "unreachable_code",
153
+ "unused_must_use",
154
+ "clippy::allow_attributes",
155
+ "clippy::allow_attributes_without_reason",
156
+ "clippy::dbg_macro",
157
+ "clippy::expect_used",
158
+ "clippy::indexing_slicing",
159
+ "clippy::panic",
160
+ "clippy::print_stderr",
161
+ "clippy::print_stdout",
162
+ "clippy::todo",
163
+ "clippy::unimplemented",
164
+ "clippy::unreachable",
165
+ "clippy::unwrap_in_result",
166
+ "clippy::unwrap_used",
167
+ ]);
168
+ const requiresPulsarAllow = (lint) => {
169
+ const normalized = lint.replace(/\s+/g, "");
170
+ if (broadlyScopedLintNames.has(normalized))
171
+ return true;
172
+ if (slopHidingLintNames.has(normalized))
173
+ return true;
174
+ return false;
175
+ };
176
+ //# sourceMappingURL=rs-sl-02-suppressions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rs-sl-02-suppressions.js","sourceRoot":"","sources":["../../src/signals/rs-sl-02-suppressions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type Signal } from "@skastr0/pulsar-core/signal";
2
+ import { Schema } from "effect";
3
+ import { RustProjectTag } from "../project.js";
4
+ declare const RsSl03Config: Schema.Struct<{
5
+ exclude_globs: Schema.Array$<typeof Schema.String>;
6
+ top_n_diagnostics: typeof Schema.Number;
7
+ }>;
8
+ type RsSl03Config = typeof RsSl03Config.Type;
9
+ interface PanicDensityModule {
10
+ readonly module: string;
11
+ readonly file: string;
12
+ readonly unwrapExpectCalls: number;
13
+ readonly density: number;
14
+ }
15
+ interface RsSl03Output {
16
+ readonly modules: ReadonlyArray<PanicDensityModule>;
17
+ readonly totalCalls: number;
18
+ readonly analysisMode: "call-expression-field-scan";
19
+ }
20
+ export declare const RsSl03: Signal<RsSl03Config, RsSl03Output, RustProjectTag>;
21
+ export {};
22
+ //# sourceMappingURL=rs-sl-03-unwrap-expect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rs-sl-03-unwrap-expect.d.ts","sourceRoot":"","sources":["../../src/signals/rs-sl-03-unwrap-expect.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAEZ,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAW9C,QAAA,MAAM,YAAY;;;EAGhB,CAAA;AACF,KAAK,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAE5C,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,YAAY,EAAE,4BAA4B,CAAA;CACpD;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,cAAc,CAiFrE,CAAA"}
@@ -0,0 +1,94 @@
1
+ import { SignalComputeError, } from "@skastr0/pulsar-core/signal";
2
+ import { Effect, Schema } from "effect";
3
+ import { collectRustProjectFacts } from "../rust-analysis.js";
4
+ import { RustProjectTag } from "../project.js";
5
+ import { parseRustFile } from "../syn-walker.js";
6
+ import { DEFAULT_RUST_EXCLUDE_GLOBS, modulePathForAncestors, namedChildrenOf, resolveRustFileScope, walkAttributedNodes, } from "./shared-rust-ast.js";
7
+ import { isExcluded } from "./shared-globs.js";
8
+ const RsSl03Config = Schema.Struct({
9
+ exclude_globs: Schema.Array(Schema.String),
10
+ top_n_diagnostics: Schema.Number,
11
+ });
12
+ export const RsSl03 = {
13
+ id: "RS-SL-03-unwrap-expect",
14
+ title: "Unwrap/expect usage",
15
+ aliases: ["RS-SL-03"],
16
+ tier: 1,
17
+ category: "generated-slop",
18
+ kind: "legibility",
19
+ cacheVersion: "advisory-density-scaled-v1",
20
+ configSchema: RsSl03Config,
21
+ defaultConfig: {
22
+ exclude_globs: [...DEFAULT_RUST_EXCLUDE_GLOBS],
23
+ top_n_diagnostics: 10,
24
+ },
25
+ inputs: [],
26
+ compute: (config) => Effect.gen(function* () {
27
+ const project = yield* RustProjectTag;
28
+ return yield* Effect.tryPromise({
29
+ try: async () => {
30
+ const facts = await collectRustProjectFacts(project);
31
+ const functionCounts = new Map();
32
+ for (const fn of facts.functions) {
33
+ if (isExcluded(fn.file, config.exclude_globs))
34
+ continue;
35
+ functionCounts.set(fn.modulePath, (functionCounts.get(fn.modulePath) ?? 0) + 1);
36
+ }
37
+ const callCounts = new Map();
38
+ for (const file of project.sourceFiles) {
39
+ if (isExcluded(file, config.exclude_globs))
40
+ continue;
41
+ const scope = resolveRustFileScope(project, file);
42
+ const tree = await parseRustFile(file);
43
+ walkAttributedNodes(tree.rootNode, ({ node, ancestors, testGated }) => {
44
+ if (testGated || node.type !== "call_expression")
45
+ return;
46
+ const fieldExpression = namedChildrenOf(node)[0];
47
+ if (fieldExpression?.type !== "field_expression")
48
+ return;
49
+ const methodName = namedChildrenOf(fieldExpression).at(-1)?.text;
50
+ if (methodName !== "unwrap" && methodName !== "expect")
51
+ return;
52
+ const { modulePath } = modulePathForAncestors(scope, ancestors);
53
+ const current = callCounts.get(modulePath) ?? { file, count: 0 };
54
+ current.count += 1;
55
+ callCounts.set(modulePath, current);
56
+ });
57
+ }
58
+ const modules = [...callCounts.entries()]
59
+ .map(([module, entry]) => ({
60
+ module,
61
+ file: entry.file,
62
+ unwrapExpectCalls: entry.count,
63
+ density: entry.count / Math.max(1, functionCounts.get(module) ?? 1),
64
+ }))
65
+ .sort((left, right) => right.density - left.density || left.module.localeCompare(right.module));
66
+ return {
67
+ modules,
68
+ totalCalls: modules.reduce((sum, module) => sum + module.unwrapExpectCalls, 0),
69
+ analysisMode: "call-expression-field-scan",
70
+ };
71
+ },
72
+ catch: (cause) => new SignalComputeError({ signalId: "RS-SL-03-unwrap-expect", message: String(cause), cause }),
73
+ });
74
+ }),
75
+ score: (out) => {
76
+ if (out.totalCalls === 0)
77
+ return 1;
78
+ const riskyModules = out.modules.filter((module) => module.density >= 1).length;
79
+ const penalty = riskyModules * 0.05 + out.totalCalls * 0.001;
80
+ return Math.max(0, 1 - Math.min(0.5, penalty));
81
+ },
82
+ diagnose: (out) => out.modules.slice(0, 10).map((module) => ({
83
+ severity: module.density >= 1 ? "warn" : "info",
84
+ message: `${module.module} contains ${module.unwrapExpectCalls} unwrap/expect call sites`,
85
+ location: { file: module.file },
86
+ data: {
87
+ module: module.module,
88
+ unwrapExpectCalls: module.unwrapExpectCalls,
89
+ density: module.density,
90
+ analysisMode: out.analysisMode,
91
+ },
92
+ })),
93
+ };
94
+ //# sourceMappingURL=rs-sl-03-unwrap-expect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rs-sl-03-unwrap-expect.js","sourceRoot":"","sources":["../../src/signals/rs-sl-03-unwrap-expect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type Signal } from "@skastr0/pulsar-core/signal";
2
+ import { Schema } from "effect";
3
+ import { RustProjectTag } from "../project.js";
4
+ declare const RsSl04Config: Schema.Struct<{
5
+ exclude_globs: Schema.Array$<typeof Schema.String>;
6
+ top_n_diagnostics: typeof Schema.Number;
7
+ }>;
8
+ type RsSl04Config = typeof RsSl04Config.Type;
9
+ interface CloneAbuseModule {
10
+ readonly module: string;
11
+ readonly file: string;
12
+ readonly cloneCalls: number;
13
+ readonly likelyExpensiveClones: number;
14
+ }
15
+ interface RsSl04Output {
16
+ readonly modules: ReadonlyArray<CloneAbuseModule>;
17
+ readonly totalCloneCalls: number;
18
+ readonly analysisMode: "syntax-heuristic-clone-scan";
19
+ }
20
+ export declare const RsSl04: Signal<RsSl04Config, RsSl04Output, RustProjectTag>;
21
+ export {};
22
+ //# sourceMappingURL=rs-sl-04-clone-abuse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rs-sl-04-clone-abuse.d.ts","sourceRoot":"","sources":["../../src/signals/rs-sl-04-clone-abuse.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAEZ,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAW9C,QAAA,MAAM,YAAY;;;EAGhB,CAAA;AACF,KAAK,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAE5C,UAAU,gBAAgB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAA;CACvC;AAED,UAAU,YAAY;IACpB,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAA;IACjD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,YAAY,EAAE,6BAA6B,CAAA;CACrD;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,cAAc,CA8FrE,CAAA"}
@@ -0,0 +1,115 @@
1
+ import { SignalComputeError, } from "@skastr0/pulsar-core/signal";
2
+ import { Effect, Schema } from "effect";
3
+ import { collectRustProjectFacts } from "../rust-analysis.js";
4
+ import { RustProjectTag } from "../project.js";
5
+ import { parseRustFile } from "../syn-walker.js";
6
+ import { DEFAULT_RUST_EXCLUDE_GLOBS, modulePathForAncestors, namedChildrenOf, resolveRustFileScope, walkAttributedNodes, } from "./shared-rust-ast.js";
7
+ import { isExcluded } from "./shared-globs.js";
8
+ const RsSl04Config = Schema.Struct({
9
+ exclude_globs: Schema.Array(Schema.String),
10
+ top_n_diagnostics: Schema.Number,
11
+ });
12
+ export const RsSl04 = {
13
+ id: "RS-SL-04-clone-abuse",
14
+ title: "Clone abuse",
15
+ aliases: ["RS-SL-04"],
16
+ tier: 1,
17
+ category: "generated-slop",
18
+ kind: "legibility",
19
+ cacheVersion: "likely-expensive-score-v1",
20
+ configSchema: RsSl04Config,
21
+ defaultConfig: {
22
+ exclude_globs: [...DEFAULT_RUST_EXCLUDE_GLOBS],
23
+ top_n_diagnostics: 10,
24
+ },
25
+ inputs: [],
26
+ compute: (config) => Effect.gen(function* () {
27
+ const project = yield* RustProjectTag;
28
+ return yield* Effect.tryPromise({
29
+ try: async () => {
30
+ const facts = await collectRustProjectFacts(project);
31
+ const functionCounts = new Map();
32
+ for (const fn of facts.functions) {
33
+ if (isExcluded(fn.file, config.exclude_globs))
34
+ continue;
35
+ functionCounts.set(fn.modulePath, (functionCounts.get(fn.modulePath) ?? 0) + 1);
36
+ }
37
+ const cloneCounts = new Map();
38
+ for (const file of project.sourceFiles) {
39
+ if (isExcluded(file, config.exclude_globs))
40
+ continue;
41
+ const scope = resolveRustFileScope(project, file);
42
+ const tree = await parseRustFile(file);
43
+ walkAttributedNodes(tree.rootNode, ({ node, ancestors, testGated }) => {
44
+ if (testGated || node.type !== "call_expression")
45
+ return;
46
+ const fieldExpression = namedChildrenOf(node)[0];
47
+ if (fieldExpression?.type !== "field_expression")
48
+ return;
49
+ const children = namedChildrenOf(fieldExpression);
50
+ const receiver = children[0]?.text ?? "";
51
+ const methodName = children.at(-1)?.text;
52
+ if (methodName !== "clone")
53
+ return;
54
+ const { modulePath } = modulePathForAncestors(scope, ancestors);
55
+ const current = cloneCounts.get(modulePath) ?? {
56
+ file,
57
+ count: 0,
58
+ likelyExpensive: 0,
59
+ };
60
+ current.count += 1;
61
+ if (classifyClone(receiver) === "likely-expensive") {
62
+ current.likelyExpensive += 1;
63
+ }
64
+ cloneCounts.set(modulePath, current);
65
+ });
66
+ }
67
+ const modules = [...cloneCounts.entries()]
68
+ .map(([module, entry]) => ({
69
+ module,
70
+ file: entry.file,
71
+ cloneCalls: entry.count,
72
+ likelyExpensiveClones: entry.likelyExpensive,
73
+ density: entry.count / Math.max(1, functionCounts.get(module) ?? 1),
74
+ }))
75
+ .sort((left, right) => right.density - left.density || left.module.localeCompare(right.module))
76
+ .map(({ density, ...rest }) => rest);
77
+ return {
78
+ modules,
79
+ totalCloneCalls: modules.reduce((sum, module) => sum + module.cloneCalls, 0),
80
+ analysisMode: "syntax-heuristic-clone-scan",
81
+ };
82
+ },
83
+ catch: (cause) => new SignalComputeError({ signalId: "RS-SL-04-clone-abuse", message: String(cause), cause }),
84
+ });
85
+ }),
86
+ score: (out) => {
87
+ const likelyExpensiveClones = out.modules.reduce((sum, module) => sum + module.likelyExpensiveClones, 0);
88
+ if (likelyExpensiveClones === 0)
89
+ return 1;
90
+ return Math.max(0, 1 - Math.min(0.8, likelyExpensiveClones / 25));
91
+ },
92
+ diagnose: (out) => out.modules.filter((module) => module.likelyExpensiveClones > 0).slice(0, 10).map((module) => ({
93
+ severity: module.likelyExpensiveClones > 0 ? "warn" : "info",
94
+ message: `${module.module} contains ${module.cloneCalls} clone() calls`,
95
+ location: { file: module.file },
96
+ data: {
97
+ module: module.module,
98
+ cloneCalls: module.cloneCalls,
99
+ likelyExpensiveClones: module.likelyExpensiveClones,
100
+ analysisMode: out.analysisMode,
101
+ },
102
+ })),
103
+ };
104
+ const classifyClone = (receiver) => {
105
+ if (/\b(?:Arc|Rc)\b/.test(receiver))
106
+ return "cheap-likely";
107
+ if (/\b(?:String|Vec|HashMap|BTreeMap|HashSet|BTreeSet)\b/.test(receiver)) {
108
+ return "likely-expensive";
109
+ }
110
+ if (receiver.startsWith("vec!") || receiver.includes("to_string") || receiver.includes("format!")) {
111
+ return "likely-expensive";
112
+ }
113
+ return "unknown";
114
+ };
115
+ //# sourceMappingURL=rs-sl-04-clone-abuse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rs-sl-04-clone-abuse.js","sourceRoot":"","sources":["../../src/signals/rs-sl-04-clone-abuse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare const normalizePath: (path: string) => string;
2
+ export declare const matchesAnyGlob: (path: string, globs: ReadonlyArray<string>) => boolean;
3
+ export declare const isExcluded: (path: string, globs: ReadonlyArray<string>) => boolean;
4
+ //# sourceMappingURL=shared-globs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-globs.d.ts","sourceRoot":"","sources":["../../src/signals/shared-globs.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,KAAG,MAAoC,CAAA;AAKjF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,OAAO,aAAa,CAAC,MAAM,CAAC,KAC3B,OAAwD,CAAA;AAE3D,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,OAAO,aAAa,CAAC,MAAM,CAAC,KAAG,OAC3C,CAAA"}
@@ -0,0 +1,12 @@
1
+ const toGlobRegex = (glob) => new RegExp("^" +
2
+ glob
3
+ .replace(/\./g, "\\.")
4
+ .replace(/\*\*/g, "§§")
5
+ .replace(/\*/g, "[^/]*")
6
+ .replace(/§§/g, ".*") +
7
+ "$");
8
+ export const normalizePath = (path) => path.replaceAll("\\", "/");
9
+ const matchesGlob = (path, glob) => toGlobRegex(glob).test(normalizePath(path));
10
+ export const matchesAnyGlob = (path, globs) => globs.some((glob) => matchesGlob(path, glob));
11
+ export const isExcluded = (path, globs) => matchesAnyGlob(path, globs);
12
+ //# sourceMappingURL=shared-globs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-globs.js","sourceRoot":"","sources":["../../src/signals/shared-globs.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG,CAAC,IAAY,EAAU,EAAE,CAC3C,IAAI,MAAM,CACR,GAAG;IACD,IAAI;SACD,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC;SACtB,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;SACvB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;IACvB,GAAG,CACN,CAAA;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;AAEjF,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,IAAY,EAAW,EAAE,CAC1D,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;AAE7C,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,IAAY,EACZ,KAA4B,EACnB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA;AAE3D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,KAA4B,EAAW,EAAE,CAChF,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ type UnknownRecord = Record<string, unknown>;
2
+ export declare const asUnknownRecord: (value: unknown) => UnknownRecord | undefined;
3
+ export {};
4
+ //# sourceMappingURL=shared-record-guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-record-guards.d.ts","sourceRoot":"","sources":["../../src/signals/shared-record-guards.ts"],"names":[],"mappings":"AAAA,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE5C,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,aAAa,GAAG,SAGlD,CAAA"}
@@ -0,0 +1,4 @@
1
+ export const asUnknownRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value)
2
+ ? value
3
+ : undefined;
4
+ //# sourceMappingURL=shared-record-guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-record-guards.js","sourceRoot":"","sources":["../../src/signals/shared-record-guards.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAc,EAA6B,EAAE,CAC3E,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IAClE,CAAC,CAAE,KAAuB;IAC1B,CAAC,CAAC,SAAS,CAAA"}
@@ -0,0 +1,30 @@
1
+ import type { ChangedHunk } from "@skastr0/pulsar-core/signal";
2
+ import type { RustManifestInfo, RustProject } from "../project.js";
3
+ import type { RustSyntaxNode } from "../syn-walker.js";
4
+ export declare const DEFAULT_RUST_EXCLUDE_GLOBS: readonly ["**/target/**", "**/tests/**", "**/examples/**", "**/benches/**"];
5
+ interface RustFileScope {
6
+ readonly file: string;
7
+ readonly crateName: string;
8
+ readonly manifest: RustManifestInfo | undefined;
9
+ readonly baseModuleSegments: ReadonlyArray<string>;
10
+ readonly relativeModulePath: string;
11
+ readonly modulePath: string;
12
+ }
13
+ interface AttributedNodeVisit {
14
+ readonly node: RustSyntaxNode;
15
+ readonly ancestors: ReadonlyArray<RustSyntaxNode>;
16
+ readonly attachedAttributes: ReadonlyArray<RustSyntaxNode>;
17
+ readonly testGated: boolean;
18
+ }
19
+ export declare const namedChildrenOf: (node: RustSyntaxNode) => ReadonlyArray<RustSyntaxNode>;
20
+ export declare const firstNamedChild: (node: RustSyntaxNode, type: string) => RustSyntaxNode | undefined;
21
+ export declare const allNamedChildren: (node: RustSyntaxNode, type: string) => ReadonlyArray<RustSyntaxNode>;
22
+ export declare const resolveRustFileScope: (project: RustProject, file: string) => RustFileScope;
23
+ export declare const modulePathForAncestors: (scope: RustFileScope, ancestors: ReadonlyArray<RustSyntaxNode>) => {
24
+ readonly relativeModulePath: string;
25
+ readonly modulePath: string;
26
+ };
27
+ export declare const walkAttributedNodes: (root: RustSyntaxNode, visit: (entry: AttributedNodeVisit) => void) => void;
28
+ export declare const lineRangeOverlapsChangedHunks: (file: string, startLine: number, endLine: number, worktreePath: string, hunks: ReadonlyArray<ChangedHunk>) => boolean;
29
+ export {};
30
+ //# sourceMappingURL=shared-rust-ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared-rust-ast.d.ts","sourceRoot":"","sources":["../../src/signals/shared-rust-ast.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAC9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAMlE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAGtD,eAAO,MAAM,0BAA0B,6EAK7B,CAAA;AAEV,UAAU,aAAa;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,GAAG,SAAS,CAAA;IAC/C,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IAClD,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IACjD,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,cAAc,CAAC,CAAA;IAC1D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED,eAAO,MAAM,eAAe,GAAI,MAAM,cAAc,KAAG,aAAa,CAAC,cAAc,CACJ,CAAA;AAE/E,eAAO,MAAM,eAAe,GAC1B,MAAM,cAAc,EACpB,MAAM,MAAM,KACX,cAAc,GAAG,SAAuE,CAAA;AAE3F,eAAO,MAAM,gBAAgB,GAC3B,MAAM,cAAc,EACpB,MAAM,MAAM,KACX,aAAa,CAAC,cAAc,CAAiE,CAAA;AAEhG,eAAO,MAAM,oBAAoB,GAC/B,SAAS,WAAW,EACpB,MAAM,MAAM,KACX,aAaF,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,OAAO,aAAa,EACpB,WAAW,aAAa,CAAC,cAAc,CAAC,KACvC;IAAE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAUpE,CAAA;AAOD,eAAO,MAAM,mBAAmB,GAC9B,MAAM,cAAc,EACpB,OAAO,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,KAC1C,IA2BF,CAAA;AAED,eAAO,MAAM,6BAA6B,GACxC,MAAM,MAAM,EACZ,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,cAAc,MAAM,EACpB,OAAO,aAAa,CAAC,WAAW,CAAC,KAChC,OAYF,CAAA"}
@@ -0,0 +1,78 @@
1
+ import { relative } from "node:path";
2
+ import { moduleSegmentsFromFile, resolveManifestForFile, toModulePath, } from "../rust-analysis-modules.js";
3
+ import { normalizePath } from "./shared-globs.js";
4
+ export const DEFAULT_RUST_EXCLUDE_GLOBS = [
5
+ "**/target/**",
6
+ "**/tests/**",
7
+ "**/examples/**",
8
+ "**/benches/**",
9
+ ];
10
+ export const namedChildrenOf = (node) => node.namedChildren.filter((child) => child !== null);
11
+ export const firstNamedChild = (node, type) => namedChildrenOf(node).find((child) => child.type === type);
12
+ export const allNamedChildren = (node, type) => namedChildrenOf(node).filter((child) => child.type === type);
13
+ export const resolveRustFileScope = (project, file) => {
14
+ const manifest = resolveManifestForFile(file, project.manifests);
15
+ const crateName = manifest?.packageName ?? manifest?.name ?? "crate";
16
+ const baseModuleSegments = moduleSegmentsFromFile(file, manifest);
17
+ const relativeModulePath = baseModuleSegments.join("::");
18
+ return {
19
+ file,
20
+ crateName,
21
+ manifest,
22
+ baseModuleSegments,
23
+ relativeModulePath,
24
+ modulePath: toModulePath(crateName, relativeModulePath),
25
+ };
26
+ };
27
+ export const modulePathForAncestors = (scope, ancestors) => {
28
+ const inlineSegments = ancestors
29
+ .filter((ancestor) => ancestor.type === "mod_item")
30
+ .map((ancestor) => firstNamedChild(ancestor, "identifier")?.text)
31
+ .filter((name) => name !== undefined);
32
+ const relativeModulePath = [...scope.baseModuleSegments, ...inlineSegments].join("::");
33
+ return {
34
+ relativeModulePath,
35
+ modulePath: toModulePath(scope.crateName, relativeModulePath),
36
+ };
37
+ };
38
+ const isCfgTestAttribute = (value) => {
39
+ const text = typeof value === "string" ? value : value.text;
40
+ return /#\s*!?\[\s*cfg\s*\(\s*test\s*\)\s*\]/.test(text);
41
+ };
42
+ export const walkAttributedNodes = (root, visit) => {
43
+ const walkContainer = (node, ancestors, inheritedTestGated) => {
44
+ let pendingAttributes = [];
45
+ for (const child of namedChildrenOf(node)) {
46
+ if (child.type === "attribute_item" || child.type === "inner_attribute_item") {
47
+ pendingAttributes.push(child);
48
+ continue;
49
+ }
50
+ const childAncestors = [...ancestors, node];
51
+ const testGated = inheritedTestGated || pendingAttributes.some(isCfgTestAttribute);
52
+ visit({
53
+ node: child,
54
+ ancestors: childAncestors,
55
+ attachedAttributes: pendingAttributes,
56
+ testGated,
57
+ });
58
+ walkContainer(child, childAncestors, testGated);
59
+ pendingAttributes = [];
60
+ }
61
+ };
62
+ walkContainer(root, [], false);
63
+ };
64
+ export const lineRangeOverlapsChangedHunks = (file, startLine, endLine, worktreePath, hunks) => {
65
+ if (hunks.length === 0)
66
+ return true;
67
+ const normalizedFile = normalizePath(file);
68
+ const relativeFile = normalizePath(relative(worktreePath, file));
69
+ return hunks.some((hunk) => {
70
+ const hunkFile = normalizePath(hunk.file);
71
+ if (hunkFile !== normalizedFile && hunkFile !== relativeFile)
72
+ return false;
73
+ const hunkStart = hunk.newStart;
74
+ const hunkEnd = hunk.newLines === 0 ? hunk.newStart : hunk.newStart + hunk.newLines - 1;
75
+ return startLine <= hunkEnd && endLine >= hunkStart;
76
+ });
77
+ };
78
+ //# sourceMappingURL=shared-rust-ast.js.map