phpstan-mcp-mx 0.23.0 → 0.25.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.
- package/CHANGELOG.md +29 -0
- package/README.md +146 -2
- package/dist/baseline.d.ts +10 -0
- package/dist/baseline.js +13 -3
- package/dist/baseline.js.map +1 -1
- package/dist/changed.d.ts +17 -0
- package/dist/changed.js +4 -3
- package/dist/changed.js.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/dumptype.d.ts +2 -0
- package/dist/dumptype.js +26 -4
- package/dist/dumptype.js.map +1 -1
- package/dist/failure.d.ts +1 -1
- package/dist/failure.js.map +1 -1
- package/dist/finding.d.ts +65 -0
- package/dist/finding.js +177 -0
- package/dist/finding.js.map +1 -0
- package/dist/history.d.ts +61 -0
- package/dist/history.js +275 -0
- package/dist/history.js.map +1 -0
- package/dist/phpstan/process.d.ts +10 -0
- package/dist/phpstan/process.js +12 -0
- package/dist/phpstan/process.js.map +1 -1
- package/dist/phpstan.d.ts +14 -0
- package/dist/phpstan.js +70 -13
- package/dist/phpstan.js.map +1 -1
- package/dist/prune.d.ts +92 -0
- package/dist/prune.js +387 -0
- package/dist/prune.js.map +1 -0
- package/dist/tools/inspect.d.ts +4 -0
- package/dist/tools/inspect.js +135 -3
- package/dist/tools/inspect.js.map +1 -1
- package/dist/tools/prompts.js +13 -5
- package/dist/tools/prompts.js.map +1 -1
- package/dist/tools/writes.d.ts +5 -0
- package/dist/tools/writes.js +70 -0
- package/dist/tools/writes.js.map +1 -1
- package/dist/tools.js +5 -2
- package/dist/tools.js.map +1 -1
- package/dist/unmatched.d.ts +66 -0
- package/dist/unmatched.js +166 -31
- package/dist/unmatched.js.map +1 -1
- package/package.json +1 -1
package/dist/prune.js
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, normalize, resolve } from 'node:path';
|
|
3
|
+
import { BaselineError, includedBaselineFiles, unquote } from './baseline.js';
|
|
4
|
+
import { PhpstanError } from './phpstan.js';
|
|
5
|
+
import { analyseUnmatched, readStaleInline, readStaleRule, unmatchedGeneralPattern } from './unmatched.js';
|
|
6
|
+
/** Tekens die van een pad een fnmatch-patroon maken; zo'n pad bestaat nooit letterlijk. */
|
|
7
|
+
const GLOB = /[*?[\]]/;
|
|
8
|
+
/**
|
|
9
|
+
* Snoeit de baselines die de config includeert: entries die niets meer matchen gaan eruit,
|
|
10
|
+
* een te hoge `count` gaat omlaag naar wat nog voorkomt. Er komt nooit iets bij.
|
|
11
|
+
*
|
|
12
|
+
* Dat is het verschil met `generate_baseline`: die ruimt ook op, maar zet alles wat nu
|
|
13
|
+
* gemeld wordt erbij, en dan verdwijnt het uit elke volgende analyse. Opruimen en verstoppen
|
|
14
|
+
* horen twee handelingen te zijn.
|
|
15
|
+
*
|
|
16
|
+
* Geen verse baseline naast de oude leggen: zolang de oude geïncludeerd is, negeert PHPStan
|
|
17
|
+
* die fouten al, en een verse baseline zou dan bijna leeg zijn. PHPStans eigen
|
|
18
|
+
* `reportUnmatchedIgnoredErrors` zegt per entry wat er niet meer klopt; dat is dezelfde run
|
|
19
|
+
* als `unmatched_ignores`.
|
|
20
|
+
*
|
|
21
|
+
* Alles wat niet eenduidig terug te vinden is, blijft staan. Een fout in het koppelen laat
|
|
22
|
+
* dus hooguit een verouderde entry achter, en haalt nooit een entry weg die nog iets doet.
|
|
23
|
+
*/
|
|
24
|
+
export async function pruneBaseline(project, options = {}, analyse = analyseUnmatched) {
|
|
25
|
+
const files = includedBaselineFiles(project);
|
|
26
|
+
// Zonder config includeert includedBaselineFiles niets; dit dekt dus ook dat geval.
|
|
27
|
+
if (project.config === null || files.length === 0) {
|
|
28
|
+
return nothingIncluded(project, noBaselineReason(project));
|
|
29
|
+
}
|
|
30
|
+
const before = new Map(files.map((file) => [file, readParsed(project, file)]));
|
|
31
|
+
// Eerst de entries voor een bestand dat er niet meer is. Met reportUnmatchedIgnoredErrors
|
|
32
|
+
// aan weigert PHPStan de héle run op zo'n pad ("is neither a directory, nor a file path"),
|
|
33
|
+
// dus dit moet vóór de analyse op schijf staan. Een bestand dat er niet is, kan geen fout
|
|
34
|
+
// hebben: weghalen verbergt niets.
|
|
35
|
+
const missing = new Map();
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const parsed = before.get(file);
|
|
38
|
+
const dead = parsed.blocks.filter((block) => block.known && block.path !== null && !GLOB.test(block.path) && !existsSync(resolve(project.path, dirname(file), block.path)));
|
|
39
|
+
if (dead.length > 0) {
|
|
40
|
+
writeBaseline(project, file, applyEdits(parsed, new Set(dead), new Map()));
|
|
41
|
+
missing.set(file, dead.length);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = await analyse(project, project.config, options);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw withMissingNotice(error, missing);
|
|
50
|
+
}
|
|
51
|
+
requireComplete(result, missing);
|
|
52
|
+
const rules = [];
|
|
53
|
+
let currentFindings = 0;
|
|
54
|
+
let inline = 0;
|
|
55
|
+
for (const finding of result.findings) {
|
|
56
|
+
// Een inline ignore die niets meer doet, staat in de code: dat is niet aan deze tool, en
|
|
57
|
+
// ook geen huidige bevinding die de baseline zou kunnen verstoppen.
|
|
58
|
+
if (readStaleInline(finding) !== null) {
|
|
59
|
+
inline++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const rule = readStaleRule(finding, project.path);
|
|
63
|
+
// Een count die te laag is, is geen verouderde regel: de fout komt vaker voor, en die
|
|
64
|
+
// extra keer is een bevinding. Verhogen zou hem verstoppen.
|
|
65
|
+
if (rule === null || rule.kind === 'countTooLow') {
|
|
66
|
+
currentFindings++;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
rules.push(rule);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const general = result.generalErrors.filter((error) => unmatchedGeneralPattern(error) !== null).length;
|
|
73
|
+
const perFile = [];
|
|
74
|
+
const claimed = new Set();
|
|
75
|
+
let unknownCount = 0;
|
|
76
|
+
// Opnieuw inlezen: het bestand kan hierboven al gesnoeid zijn, en de regelnummers van
|
|
77
|
+
// toen kloppen dan niet meer.
|
|
78
|
+
const current = new Map(files.map((file) => [file, readParsed(project, file)]));
|
|
79
|
+
const edits = new Map(files.map((file) => [file, { remove: new Set(), lower: new Map() }]));
|
|
80
|
+
for (const rule of rules) {
|
|
81
|
+
const found = findBlock(project, current, rule, claimed);
|
|
82
|
+
if (found === null) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
claimed.add(found.block);
|
|
86
|
+
const edit = edits.get(found.file);
|
|
87
|
+
// Alleen omlaag, en alleen naar een getal dat PHPStan zelf noemde.
|
|
88
|
+
if (rule.kind === 'unmatched') {
|
|
89
|
+
edit.remove.add(found.block);
|
|
90
|
+
}
|
|
91
|
+
else if (rule.occurred === null || found.block.countLine === null || rule.occurred >= found.block.count) {
|
|
92
|
+
unknownCount++;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
edit.lower.set(found.block, rule.occurred);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const file of files) {
|
|
99
|
+
const parsed = current.get(file);
|
|
100
|
+
const edit = edits.get(file);
|
|
101
|
+
if (edit.remove.size > 0 || edit.lower.size > 0) {
|
|
102
|
+
writeBaseline(project, file, applyEdits(parsed, edit.remove, edit.lower));
|
|
103
|
+
}
|
|
104
|
+
const original = before.get(file);
|
|
105
|
+
const errorsAfter = errorsIn(parsed) - sumCounts(edit.remove) - [...edit.lower].reduce((sum, [block, count]) => sum + block.count - count, 0);
|
|
106
|
+
perFile.push({
|
|
107
|
+
file,
|
|
108
|
+
removed: edit.remove.size + (missing.get(file) ?? 0),
|
|
109
|
+
lowered: edit.lower.size,
|
|
110
|
+
errorsBefore: errorsIn(original),
|
|
111
|
+
errorsAfter,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
project: project.path,
|
|
116
|
+
files: perFile,
|
|
117
|
+
totals: {
|
|
118
|
+
removed: perFile.reduce((sum, file) => sum + file.removed, 0),
|
|
119
|
+
lowered: perFile.reduce((sum, file) => sum + file.lowered, 0),
|
|
120
|
+
missingFiles: [...missing.values()].reduce((sum, count) => sum + count, 0),
|
|
121
|
+
errorsBefore: perFile.reduce((sum, file) => sum + file.errorsBefore, 0),
|
|
122
|
+
errorsAfter: perFile.reduce((sum, file) => sum + file.errorsAfter, 0),
|
|
123
|
+
},
|
|
124
|
+
left: { outsideBaseline: rules.length - claimed.size, general, unknownCount, inline },
|
|
125
|
+
currentFindings,
|
|
126
|
+
notices: [],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Leest een baseline in blokken, met regelnummers, zodat een wijziging alleen die blokken
|
|
131
|
+
* raakt en de rest van het bestand byte voor byte blijft staan. Regelgericht, net als
|
|
132
|
+
* `parseBaseline`: PHPStan genereert dit bestand, dus de vorm ligt vast.
|
|
133
|
+
*/
|
|
134
|
+
export function parseBaselineBlocks(content) {
|
|
135
|
+
const eol = content.includes('\r\n') ? '\r\n' : '\n';
|
|
136
|
+
const lines = content.split(/\r?\n/);
|
|
137
|
+
const listLine = lines.findIndex((line) => /^\s*ignoreErrors:\s*$/.test(line));
|
|
138
|
+
if (listLine === -1) {
|
|
139
|
+
return { lines, eol, listLine: null, blocks: [] };
|
|
140
|
+
}
|
|
141
|
+
const listIndent = indentOf(lines[listLine] ?? '');
|
|
142
|
+
const blocks = [];
|
|
143
|
+
let itemIndent = null;
|
|
144
|
+
let open = null;
|
|
145
|
+
let index = listLine + 1;
|
|
146
|
+
const close = (end) => {
|
|
147
|
+
if (open !== null) {
|
|
148
|
+
blocks.push(toBlock(open.start, end, open.known, open.fields));
|
|
149
|
+
}
|
|
150
|
+
open = null;
|
|
151
|
+
};
|
|
152
|
+
for (; index < lines.length; index++) {
|
|
153
|
+
const line = lines[index] ?? '';
|
|
154
|
+
const trimmed = line.trim();
|
|
155
|
+
if (trimmed === '') {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const indent = indentOf(line);
|
|
159
|
+
if (indent <= listIndent) {
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
// Een nieuw lijstitem op dezelfde diepte als het eerste. `- message: …` op één regel
|
|
163
|
+
// maakt PHPStan niet; zo'n blok is een grens, maar wordt niet gelezen of aangeraakt.
|
|
164
|
+
if (trimmed.startsWith('-') && (itemIndent === null || indent === itemIndent)) {
|
|
165
|
+
itemIndent ??= indent;
|
|
166
|
+
close(index);
|
|
167
|
+
open = { start: index, known: trimmed === '-', fields: new Map() };
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const field = /^(identifier|path|count|message|rawMessage):\s*(.*)$/.exec(trimmed);
|
|
171
|
+
if (open !== null && field?.[1] !== undefined) {
|
|
172
|
+
open.fields.set(field[1], { value: (field[2] ?? '').trim(), line: index });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
close(index);
|
|
176
|
+
return { lines, eol, listLine, blocks };
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Past de wijzigingen toe en geeft de nieuwe inhoud. Weghalen gaat per blok inclusief de
|
|
180
|
+
* lege regels erna; een lagere count vervangt alleen het getal op zijn eigen regel.
|
|
181
|
+
*/
|
|
182
|
+
export function applyEdits(parsed, remove, lower) {
|
|
183
|
+
const lines = [...parsed.lines];
|
|
184
|
+
for (const [block, count] of lower) {
|
|
185
|
+
if (block.countLine !== null) {
|
|
186
|
+
lines[block.countLine] = (lines[block.countLine] ?? '').replace(/count:\s*\d+/, `count: ${String(count)}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const dropped = new Set();
|
|
190
|
+
for (const block of remove) {
|
|
191
|
+
for (let line = block.start; line < block.end; line++) {
|
|
192
|
+
dropped.add(line);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// De laatste regel is leeg als het bestand op een regeleinde eindigt; die hoort bij het
|
|
196
|
+
// bestand en niet bij het laatste blok.
|
|
197
|
+
const last = lines.length - 1;
|
|
198
|
+
if (lines[last] === '') {
|
|
199
|
+
dropped.delete(last);
|
|
200
|
+
}
|
|
201
|
+
const kept = lines.filter((_, index) => !dropped.has(index));
|
|
202
|
+
// Alles weg: `ignoreErrors:` zonder lijst eronder is in NEON null, en daar valt PHPStan
|
|
203
|
+
// over. Een lege lijst is wat er dan bedoeld wordt.
|
|
204
|
+
if (parsed.listLine !== null && remove.size === parsed.blocks.length) {
|
|
205
|
+
const at = kept.indexOf(parsed.lines[parsed.listLine] ?? '');
|
|
206
|
+
if (at !== -1) {
|
|
207
|
+
kept[at] = (kept[at] ?? '').replace(/ignoreErrors:\s*$/, 'ignoreErrors: []');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return kept.join(parsed.eol);
|
|
211
|
+
}
|
|
212
|
+
/** Schrijft een overzicht van wat er gedaan is, als tekst voor het model. */
|
|
213
|
+
export function renderPruned(result) {
|
|
214
|
+
const lines = [];
|
|
215
|
+
if (result.notices.length > 0) {
|
|
216
|
+
lines.push(...result.notices.map((notice) => `Note: ${notice}`), '');
|
|
217
|
+
}
|
|
218
|
+
const changed = result.files.filter((file) => file.removed > 0 || file.lowered > 0);
|
|
219
|
+
if (changed.length === 0) {
|
|
220
|
+
lines.push(result.files.length === 0
|
|
221
|
+
? 'Nothing to prune.'
|
|
222
|
+
: `Nothing to prune: every entry in ${result.files.map((file) => file.file).join(', ')} still matches.`);
|
|
223
|
+
}
|
|
224
|
+
for (const file of changed) {
|
|
225
|
+
const delen = [`removed ${file.removed} ${file.removed === 1 ? 'entry' : 'entries'}`];
|
|
226
|
+
if (file.lowered > 0) {
|
|
227
|
+
delen.push(`lowered the count of ${file.lowered}`);
|
|
228
|
+
}
|
|
229
|
+
lines.push(`${file.file}: ${delen.join(', ')}; ${file.errorsBefore} → ${file.errorsAfter} ignored error(s).`);
|
|
230
|
+
}
|
|
231
|
+
if (result.totals.missingFiles > 0) {
|
|
232
|
+
lines.push(`${result.totals.missingFiles} of the removed entries named a file that no longer exists.`);
|
|
233
|
+
}
|
|
234
|
+
lines.push('', result.currentFindings > 0
|
|
235
|
+
? `Nothing was added: the ${result.currentFindings} current finding(s) are still reported. Fix them at the ` +
|
|
236
|
+
'source; generate_baseline would hide them in the baseline.'
|
|
237
|
+
: 'Nothing was added, and there are no current findings.');
|
|
238
|
+
const { left } = result;
|
|
239
|
+
if (left.outsideBaseline + left.general + left.unknownCount + left.inline > 0) {
|
|
240
|
+
lines.push('', 'Left alone:');
|
|
241
|
+
if (left.outsideBaseline > 0) {
|
|
242
|
+
lines.push(`- ${left.outsideBaseline} stale rule(s) with a path that are not in an included baseline: they are in ignoreErrors of the config. Edit that yourself.`);
|
|
243
|
+
}
|
|
244
|
+
if (left.general > 0) {
|
|
245
|
+
lines.push(`- ${left.general} pattern(s) without a path: from ignoreErrors of a config, often one a package ships. A baseline change does nothing about them.`);
|
|
246
|
+
}
|
|
247
|
+
if (left.unknownCount > 0) {
|
|
248
|
+
lines.push(`- ${left.unknownCount} count(s) that are too high, where PHPStan did not say how often the error still occurs.`);
|
|
249
|
+
}
|
|
250
|
+
if (left.inline > 0) {
|
|
251
|
+
lines.push(`- ${left.inline} inline @phpstan-ignore comment(s) that no longer suppress anything: those are in the code. unmatched_ignores lists them.`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return lines.join('\n');
|
|
255
|
+
}
|
|
256
|
+
function toBlock(start, end, known, fields) {
|
|
257
|
+
const value = (key) => {
|
|
258
|
+
const field = fields.get(key);
|
|
259
|
+
return field === undefined ? null : unquote(field.value);
|
|
260
|
+
};
|
|
261
|
+
const message = value('message');
|
|
262
|
+
const raw = value('rawMessage');
|
|
263
|
+
const count = fields.get('count');
|
|
264
|
+
return {
|
|
265
|
+
start,
|
|
266
|
+
end,
|
|
267
|
+
known,
|
|
268
|
+
path: value('path'),
|
|
269
|
+
identifier: value('identifier'),
|
|
270
|
+
// Zoals PHPStans IgnoredError::stringifyPattern het schrijft: `rawMessage` tussen dubbele
|
|
271
|
+
// quotes, zodat hij te koppelen is aan de melding over een verouderde regel.
|
|
272
|
+
pattern: message ?? (raw === null ? null : `"${raw}"`),
|
|
273
|
+
count: count === undefined ? 1 : Number.parseInt(count.value, 10) || 1,
|
|
274
|
+
countLine: count?.line ?? null,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Het blok dat bij een verouderde regel hoort, of null als het niet eenduidig te vinden is.
|
|
279
|
+
* Een blok wordt maar één keer gebruikt: twee gelijke entries leveren twee meldingen op.
|
|
280
|
+
*/
|
|
281
|
+
function findBlock(project, baselines, rule, claimed) {
|
|
282
|
+
for (const [file, parsed] of baselines) {
|
|
283
|
+
for (const block of parsed.blocks) {
|
|
284
|
+
if (!claimed.has(block) && matches(project, file, block, rule)) {
|
|
285
|
+
return { file, block };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
function matches(project, file, block, rule) {
|
|
292
|
+
if (!block.known || block.path === null || !samePath(resolve(project.path, dirname(file), block.path), rule.file)) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
// Een regel met alleen een identifier noemt PHPStan bij die identifier, zonder haakjes.
|
|
296
|
+
if (block.pattern === null) {
|
|
297
|
+
return rule.identifier === null && rule.pattern === block.identifier;
|
|
298
|
+
}
|
|
299
|
+
return rule.pattern === block.pattern && rule.identifier === block.identifier;
|
|
300
|
+
}
|
|
301
|
+
function samePath(a, b) {
|
|
302
|
+
// Windows vergelijkt zonder hoofdletters, en PHPStan schrijft het pad zoals het die kreeg.
|
|
303
|
+
return process.platform === 'win32' ? normalize(a).toLowerCase() === normalize(b).toLowerCase() : normalize(a) === normalize(b);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Een onvolledige analyse laat entries verouderd lijken die dat niet zijn. Dan wordt er
|
|
307
|
+
* niets geschreven: liever niets snoeien dan een baseline half leeghalen omdat PHPStan
|
|
308
|
+
* halverwege klapte.
|
|
309
|
+
*/
|
|
310
|
+
function requireComplete(result, missing) {
|
|
311
|
+
const incomplete = result.warnings.find((warning) => /Result is incomplete/i.test(warning));
|
|
312
|
+
const errors = result.generalErrors.filter((error) => unmatchedGeneralPattern(error) === null);
|
|
313
|
+
if (incomplete === undefined && errors.length === 0) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const reason = incomplete ?? errors.slice(0, 3).join('\n');
|
|
317
|
+
throw withMissingNotice(new PhpstanError(`The analysis did not cover the whole project, so the baseline was not pruned: an entry can look stale ` +
|
|
318
|
+
`when PHPStan simply did not get that far.\n\n${reason}`, 'phpstan_failed'), missing);
|
|
319
|
+
}
|
|
320
|
+
/** Een fout na het weghalen van entries voor verdwenen bestanden: dat is dan wél gebeurd. */
|
|
321
|
+
function withMissingNotice(error, missing) {
|
|
322
|
+
if (missing.size === 0 || !(error instanceof PhpstanError)) {
|
|
323
|
+
return error;
|
|
324
|
+
}
|
|
325
|
+
const wat = [...missing].map(([file, count]) => `${count} from ${file}`).join(', ');
|
|
326
|
+
return new PhpstanError(`${error.message}\n\nAlready removed before the analysis: the entries for files that no longer exist (${wat}).`, error.reason, error.details);
|
|
327
|
+
}
|
|
328
|
+
function readParsed(project, file) {
|
|
329
|
+
let content;
|
|
330
|
+
try {
|
|
331
|
+
content = readFileSync(join(project.path, file), 'utf8');
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
throw new BaselineError(`${file} cannot be read: ${error instanceof Error ? error.message : String(error)}.`);
|
|
335
|
+
}
|
|
336
|
+
const parsed = parseBaselineBlocks(content);
|
|
337
|
+
// Hetzelfde onderscheid als bij baseline_summary: ignoreErrors noemen en niets opleveren is
|
|
338
|
+
// een vorm die we niet kennen, en daar wordt niet in geschreven.
|
|
339
|
+
if (parsed.blocks.filter((block) => block.known).length === 0 && parsed.blocks.length > 0) {
|
|
340
|
+
throw new BaselineError(`${file} contains ignoreErrors, but not a single entry could be read from it; it was not changed.`);
|
|
341
|
+
}
|
|
342
|
+
return parsed;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Via een tijdelijk bestand naast het origineel en een rename: een analyse die tegelijk
|
|
346
|
+
* leest, ziet het oude of het nieuwe bestand en nooit een half.
|
|
347
|
+
*/
|
|
348
|
+
function writeBaseline(project, file, content) {
|
|
349
|
+
const target = join(project.path, file);
|
|
350
|
+
const tmp = `${target}.phpstan-mcp-${String(process.pid)}`;
|
|
351
|
+
try {
|
|
352
|
+
writeFileSync(tmp, content);
|
|
353
|
+
renameSync(tmp, target);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
throw new BaselineError(`${file} could not be written: ${error instanceof Error ? error.message : String(error)}.`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function nothingIncluded(project, notice) {
|
|
360
|
+
return {
|
|
361
|
+
project: project.path,
|
|
362
|
+
files: [],
|
|
363
|
+
totals: { removed: 0, lowered: 0, missingFiles: 0, errorsBefore: 0, errorsAfter: 0 },
|
|
364
|
+
left: { outsideBaseline: 0, general: 0, unknownCount: 0, inline: 0 },
|
|
365
|
+
currentFindings: 0,
|
|
366
|
+
notices: [notice],
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function noBaselineReason(project) {
|
|
370
|
+
return project.config === null
|
|
371
|
+
? `${project.path} has no phpstan.neon(.dist), so no baseline is included.`
|
|
372
|
+
: `The config of ${project.path} includes no phpstan-baseline*.neon from the project root; there is nothing to prune.`;
|
|
373
|
+
}
|
|
374
|
+
function errorsIn(parsed) {
|
|
375
|
+
return parsed.blocks.reduce((sum, block) => sum + block.count, 0);
|
|
376
|
+
}
|
|
377
|
+
function sumCounts(blocks) {
|
|
378
|
+
let sum = 0;
|
|
379
|
+
for (const block of blocks) {
|
|
380
|
+
sum += block.count;
|
|
381
|
+
}
|
|
382
|
+
return sum;
|
|
383
|
+
}
|
|
384
|
+
function indentOf(line) {
|
|
385
|
+
return /^\s*/.exec(line)?.[0].length ?? 0;
|
|
386
|
+
}
|
|
387
|
+
//# sourceMappingURL=prune.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prune.js","sourceRoot":"","sources":["../src/prune.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/inspect.d.ts
CHANGED
|
@@ -12,7 +12,11 @@ export declare function registerServerConfig(server: McpServer, roots: RootSourc
|
|
|
12
12
|
export declare function registerDiagnose(server: McpServer, roots: RootSource): void;
|
|
13
13
|
/** Hangt `baseline_summary` aan de server: wat de baseline verbergt. */
|
|
14
14
|
export declare function registerBaselineSummary(server: McpServer, roots: RootSource): void;
|
|
15
|
+
/** Hangt `baseline_history` aan de server: hoe de baseline door de git-geschiedenis veranderde. */
|
|
16
|
+
export declare function registerBaselineHistory(server: McpServer, roots: RootSource): void;
|
|
15
17
|
/** Hangt `unmatched_ignores` aan de server: negeerregels die niets meer doen. */
|
|
16
18
|
export declare function registerUnmatchedIgnores(server: McpServer, roots: RootSource): void;
|
|
17
19
|
/** Hangt `inspect_type` aan de server: het type dat PHPStan op een regel afleidt. */
|
|
18
20
|
export declare function registerInspectType(server: McpServer, roots: RootSource): void;
|
|
21
|
+
/** Hangt `show_finding` aan de server: een bevinding met de code en de types eromheen. */
|
|
22
|
+
export declare function registerShowFinding(server: McpServer, roots: RootSource): void;
|
package/dist/tools/inspect.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { baselineEntriesFor, renderBaseline, summariseBaseline } from '../baseline.js';
|
|
3
3
|
import { GitError, countInlineIgnores } from '../changed.js';
|
|
4
|
+
import { DEFAULT_HISTORY_LIMIT, MAX_HISTORY_LIMIT, baselineHistory, renderHistory } from '../history.js';
|
|
4
5
|
import { diagnose, loadDefaultMemoryLimit, loadMaxParallel, serverInvocation } from '../phpstan.js';
|
|
5
6
|
import { findMissingExtensions, readComposerScripts, renderComposerScripts, renderMissingExtensions } from '../composer.js';
|
|
6
7
|
import { discoverProjects } from '../project.js';
|
|
@@ -12,6 +13,7 @@ import { DEFAULT_LIMIT, MAX_LIMIT } from '../report.js';
|
|
|
12
13
|
import { loadTmpDirMode } from '../tmpdir.js';
|
|
13
14
|
import { findUnmatchedIgnores, renderUnmatched } from '../unmatched.js';
|
|
14
15
|
import { MAX_EXPRESSIONS, inspectType, renderTypes } from '../dumptype.js';
|
|
16
|
+
import { DEFAULT_CONTEXT, MAX_CONTEXT, renderFinding, showFinding } from '../finding.js';
|
|
15
17
|
import { renderParameters } from '../phpstan/parameters.js';
|
|
16
18
|
import { VERSION } from '../version.js';
|
|
17
19
|
import { common, configArgument, failure, log, nullable, projectArgument, readOnly, toFailure } from './shared.js';
|
|
@@ -447,7 +449,8 @@ function renderInlineIgnores({ inlineIgnores: inline, inlineIgnoresProblem: prob
|
|
|
447
449
|
}
|
|
448
450
|
const lines = [
|
|
449
451
|
`${String(inline.total)} inline @phpstan-ignore comment(s) in ${String(inline.files)} file(s) counted in all PHP ` +
|
|
450
|
-
'files git knows, also outside the analysed paths. These are hidden too, and a new baseline does not touch them.'
|
|
452
|
+
'files git knows, also outside the analysed paths. These are hidden too, and a new baseline does not touch them. ' +
|
|
453
|
+
'unmatched_ignores says which of them no longer suppress anything.',
|
|
451
454
|
];
|
|
452
455
|
if (inline.withoutIdentifier > 0) {
|
|
453
456
|
lines.push(`${String(inline.withoutIdentifier)} have no identifier (@phpstan-ignore-line, -next-line): they ignore every error on their line.`);
|
|
@@ -474,13 +477,71 @@ function renderEntries(path, entries) {
|
|
|
474
477
|
...entries.map((entry) => ` ${String(entry.count).padStart(3)} ${entry.identifier}${kort(entry.message)}`),
|
|
475
478
|
].join('\n');
|
|
476
479
|
}
|
|
480
|
+
/** Hangt `baseline_history` aan de server: hoe de baseline door de git-geschiedenis veranderde. */
|
|
481
|
+
export function registerBaselineHistory(server, roots) {
|
|
482
|
+
const count = z.object({ entries: z.number(), errors: z.number().describe('Ignored errors; one entry can cover several') });
|
|
483
|
+
server.registerTool('baseline_history', {
|
|
484
|
+
title: 'Baseline history',
|
|
485
|
+
description: 'Shows how the included phpstan-baseline*.neon changed through the git history: per commit that touched ' +
|
|
486
|
+
'it, the number of ignored errors, the change against the previous version, and which identifiers grew or ' +
|
|
487
|
+
'shrank most. Answers whether a baseline is being paid off or is quietly growing, and which commit added a ' +
|
|
488
|
+
'pile. Follows the first-parent line of HEAD, so a merged branch is one change. Reads git only; does not ' +
|
|
489
|
+
'run PHPStan.',
|
|
490
|
+
inputSchema: {
|
|
491
|
+
project: projectArgument,
|
|
492
|
+
limit: z
|
|
493
|
+
.number()
|
|
494
|
+
.int()
|
|
495
|
+
.min(1)
|
|
496
|
+
.max(MAX_HISTORY_LIMIT)
|
|
497
|
+
.optional()
|
|
498
|
+
.describe(`How many changes per baseline file (default ${String(DEFAULT_HISTORY_LIMIT)})`),
|
|
499
|
+
},
|
|
500
|
+
outputSchema: {
|
|
501
|
+
project: z.string(),
|
|
502
|
+
files: z.array(z.object({
|
|
503
|
+
file: z.string(),
|
|
504
|
+
rows: z
|
|
505
|
+
.array(z.object({
|
|
506
|
+
commit: nullable(z.string()).describe('Null for the working copy, when it differs from the last commit'),
|
|
507
|
+
date: nullable(z.string()).describe('Commit date, ISO 8601'),
|
|
508
|
+
subject: nullable(z.string()),
|
|
509
|
+
file: z.string().describe('The name of the baseline in that commit; differs when it was renamed later'),
|
|
510
|
+
count: nullable(count).describe('Null if the baseline could not be read in that commit'),
|
|
511
|
+
delta: nullable(count).describe('Against the row below; null if either could not be read'),
|
|
512
|
+
changes: z
|
|
513
|
+
.array(z.object({ identifier: z.string(), change: z.number() }))
|
|
514
|
+
.describe('The identifiers that changed most, in ignored errors; positive means added'),
|
|
515
|
+
}))
|
|
516
|
+
.describe('Newest first'),
|
|
517
|
+
start: nullable(count).describe('The baseline before the oldest row; zero when it was created there'),
|
|
518
|
+
olderCommits: z.boolean().describe('Whether older commits changed it too; a higher limit shows them'),
|
|
519
|
+
})),
|
|
520
|
+
notices: z.array(z.string()),
|
|
521
|
+
},
|
|
522
|
+
annotations: readOnly(),
|
|
523
|
+
}, async ({ project, limit }) => {
|
|
524
|
+
try {
|
|
525
|
+
const resolved = await roots.resolveProject(project);
|
|
526
|
+
const history = await baselineHistory(resolved, limit);
|
|
527
|
+
return {
|
|
528
|
+
content: [{ type: 'text', text: renderHistory(history) }],
|
|
529
|
+
structuredContent: { ...history },
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
catch (error) {
|
|
533
|
+
return toFailure(error);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
}
|
|
477
537
|
/** Hangt `unmatched_ignores` aan de server: negeerregels die niets meer doen. */
|
|
478
538
|
export function registerUnmatchedIgnores(server, roots) {
|
|
479
539
|
server.registerTool('unmatched_ignores', {
|
|
480
540
|
title: 'Find stale ignore rules',
|
|
481
541
|
description: 'Runs a full analysis with reportUnmatchedIgnoredErrors enabled and returns the ' +
|
|
482
542
|
'ignore rules that no longer match anything, or match less often than their count ' +
|
|
483
|
-
'from the baseline and from ignoreErrors in the config
|
|
543
|
+
'from the baseline and from ignoreErrors in the config, plus the inline @phpstan-ignore ' +
|
|
544
|
+
'comments that no longer suppress anything. Such a rule belongs to an ' +
|
|
484
545
|
'error that has been fixed and can be removed. Does not change anything in the ' +
|
|
485
546
|
'project: the temporary config lives outside it for a project with a runner, in ' +
|
|
486
547
|
"that runner's temp directory.",
|
|
@@ -508,6 +569,8 @@ export function registerUnmatchedIgnores(server, roots) {
|
|
|
508
569
|
countTooHigh: z.number(),
|
|
509
570
|
inBaseline: z.number(),
|
|
510
571
|
general: z.number(),
|
|
572
|
+
inline: z.number().describe('Inline @phpstan-ignore comments in the code that no longer suppress anything'),
|
|
573
|
+
inlineWithoutIdentifier: z.number().describe('How many of those name no identifier: they ignore every error on their line'),
|
|
511
574
|
otherFindings: z
|
|
512
575
|
.number()
|
|
513
576
|
.describe('Regular findings from the same run; a new baseline would hide those too'),
|
|
@@ -525,12 +588,19 @@ export function registerUnmatchedIgnores(server, roots) {
|
|
|
525
588
|
general: z
|
|
526
589
|
.array(z.string())
|
|
527
590
|
.describe('Patterns without a path that no longer match anything; these are not in the baseline'),
|
|
591
|
+
inline: z
|
|
592
|
+
.array(z.object({
|
|
593
|
+
path: z.string().describe('Path relative to the project root'),
|
|
594
|
+
line: nullable(z.number()).describe('The line the ignore applies to; for a comment on a line of its own, the line below it'),
|
|
595
|
+
identifier: nullable(z.string()).describe('The identifier that no longer occurs there; null for an ignore without identifier'),
|
|
596
|
+
}))
|
|
597
|
+
.describe('Inline @phpstan-ignore comments that no longer suppress anything; these are in the code, not in a baseline'),
|
|
528
598
|
generalErrors: z.array(z.string()),
|
|
529
599
|
warnings: z
|
|
530
600
|
.array(z.string())
|
|
531
601
|
.describe('If this contains "Result is incomplete", PHPStan may have missed stale rules'),
|
|
532
602
|
notices: z.array(z.string()),
|
|
533
|
-
omitted: z.object({ general: z.number(), generalErrors: z.number(), warnings: z.number() }),
|
|
603
|
+
omitted: z.object({ general: z.number(), inline: z.number(), generalErrors: z.number(), warnings: z.number() }),
|
|
534
604
|
page: z.object({
|
|
535
605
|
offset: z.number(),
|
|
536
606
|
limit: z.number(),
|
|
@@ -555,6 +625,7 @@ export function registerUnmatchedIgnores(server, roots) {
|
|
|
555
625
|
project: resolved.path,
|
|
556
626
|
stale: report.totals.unmatched + report.totals.countTooHigh,
|
|
557
627
|
general: report.totals.general,
|
|
628
|
+
inline: report.totals.inline,
|
|
558
629
|
durationMs: Date.now() - begin,
|
|
559
630
|
});
|
|
560
631
|
return {
|
|
@@ -641,4 +712,65 @@ export function registerInspectType(server, roots) {
|
|
|
641
712
|
}
|
|
642
713
|
});
|
|
643
714
|
}
|
|
715
|
+
/** Hangt `show_finding` aan de server: een bevinding met de code en de types eromheen. */
|
|
716
|
+
export function registerShowFinding(server, roots) {
|
|
717
|
+
server.registerTool('show_finding', {
|
|
718
|
+
title: 'Show finding in context',
|
|
719
|
+
description: 'Shows the code around one line of a PHP file, what PHPStan reports on that line, and the types PHPStan ' +
|
|
720
|
+
'infers right before it for the variables and properties used there. For looking at a finding from ' +
|
|
721
|
+
'analyse_project, analyse_file or analyse_changed when you cannot open the file yourself. Does not change ' +
|
|
722
|
+
'the project: the types come from a temporary copy, like inspect_type. On a line without variables, ' +
|
|
723
|
+
'PHPStan is not started and only the code is shown.',
|
|
724
|
+
inputSchema: {
|
|
725
|
+
project: projectArgument,
|
|
726
|
+
path: z.string().describe('PHP file inside the project, as the finding names it'),
|
|
727
|
+
line: z.number().int().min(1).describe('The line of the finding'),
|
|
728
|
+
context: z
|
|
729
|
+
.number()
|
|
730
|
+
.int()
|
|
731
|
+
.min(0)
|
|
732
|
+
.max(MAX_CONTEXT)
|
|
733
|
+
.optional()
|
|
734
|
+
.describe(`Lines of code before and after it (default ${String(DEFAULT_CONTEXT)})`),
|
|
735
|
+
expressions: z
|
|
736
|
+
.array(z.string())
|
|
737
|
+
.min(1)
|
|
738
|
+
.max(MAX_EXPRESSIONS)
|
|
739
|
+
.optional()
|
|
740
|
+
.describe('Expressions to show the type of instead of the variables on the line'),
|
|
741
|
+
config: configArgument,
|
|
742
|
+
memoryLimit: z.string().optional(),
|
|
743
|
+
timeoutSeconds: z.number().int().positive().max(1800).optional(),
|
|
744
|
+
},
|
|
745
|
+
outputSchema: {
|
|
746
|
+
project: z.string(),
|
|
747
|
+
file: z.string().describe('Path relative to the project root'),
|
|
748
|
+
line: z.number(),
|
|
749
|
+
code: z.array(z.object({ number: z.number(), text: z.string() })).describe('The lines around it, with their numbers'),
|
|
750
|
+
findings: nullable(z.array(z.object({ message: z.string(), identifier: nullable(z.string()), tip: nullable(z.string()) }))).describe('What PHPStan reports on this line; null if PHPStan was not started or could not read the copy'),
|
|
751
|
+
types: z.array(z.object({
|
|
752
|
+
expression: z.string(),
|
|
753
|
+
type: nullable(z.string()).describe('As PHPStan infers it; null if PHPStan reported none'),
|
|
754
|
+
problems: z.array(z.object({ identifier: nullable(z.string()), message: z.string() })),
|
|
755
|
+
})),
|
|
756
|
+
typesProblem: nullable(z.string()).describe('Why there are no types; null when there are'),
|
|
757
|
+
parseErrors: z.array(z.object({ line: nullable(z.number()), inserted: z.boolean(), message: z.string() })),
|
|
758
|
+
generalErrors: z.array(z.string()),
|
|
759
|
+
warnings: z.array(z.string()),
|
|
760
|
+
},
|
|
761
|
+
annotations: readOnly(),
|
|
762
|
+
}, async (args, extra) => {
|
|
763
|
+
try {
|
|
764
|
+
const resolved = await roots.resolveProject(args.project);
|
|
765
|
+
const view = await showFinding(resolved, { path: args.path, line: args.line, context: args.context, expressions: args.expressions }, { ...common(resolved, args), signal: extra.signal });
|
|
766
|
+
return {
|
|
767
|
+
content: [{ type: 'text', text: renderFinding(view) }],
|
|
768
|
+
structuredContent: { ...view },
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
catch (error) {
|
|
772
|
+
return toFailure(error);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
}
|
|
644
776
|
//# sourceMappingURL=inspect.js.map
|