@atlisp/lint 0.2.16 → 0.2.18

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 (80) hide show
  1. package/README.md +1 -1
  2. package/atlisp-lint.default.json +1 -1
  3. package/dist/checks/arg-count.d.ts +2 -1
  4. package/dist/checks/arg-count.js +37 -19
  5. package/dist/checks/builtin-arg-count.d.ts +2 -1
  6. package/dist/checks/builtin-arg-count.js +3 -2
  7. package/dist/checks/cond-duplicate.js +5 -6
  8. package/dist/checks/constant-condition.js +9 -9
  9. package/dist/checks/dangerous-calls.js +1 -1
  10. package/dist/checks/dangling-defun.d.ts +0 -7
  11. package/dist/checks/dangling-defun.js +0 -63
  12. package/dist/checks/double-not.js +1 -1
  13. package/dist/checks/empty-branch.js +1 -1
  14. package/dist/checks/empty-catch.d.ts +2 -1
  15. package/dist/checks/empty-catch.js +8 -7
  16. package/dist/checks/function-complexity.js +5 -18
  17. package/dist/checks/global-naming.js +1 -1
  18. package/dist/checks/identical-branches.js +3 -3
  19. package/dist/checks/if-without-else.js +1 -1
  20. package/dist/checks/misplaced-else.js +2 -2
  21. package/dist/checks/missing-export.js +3 -3
  22. package/dist/checks/missing-princ.js +1 -1
  23. package/dist/checks/multiple-setq.js +1 -1
  24. package/dist/checks/no-return.d.ts +2 -1
  25. package/dist/checks/no-return.js +4 -3
  26. package/dist/checks/nth-usage.js +1 -1
  27. package/dist/checks/parameter-naming.js +2 -2
  28. package/dist/checks/quote-vs-function.js +1 -1
  29. package/dist/checks/recursive-call.js +1 -1
  30. package/dist/checks/redundant-let.js +1 -1
  31. package/dist/checks/redundant-nil-else.js +1 -1
  32. package/dist/checks/redundant-progn.js +1 -1
  33. package/dist/checks/redundant-setq.js +1 -1
  34. package/dist/checks/self-compare.js +1 -1
  35. package/dist/checks/setq-single-arg.js +3 -2
  36. package/dist/checks/single-arg-and-or.js +1 -1
  37. package/dist/checks/undeclared-setq.d.ts +2 -1
  38. package/dist/checks/undeclared-setq.js +6 -5
  39. package/dist/checks/unused-let.js +3 -3
  40. package/dist/checks/unused-local-fun.js +2 -2
  41. package/dist/checks/unused-package-dep.d.ts +0 -1
  42. package/dist/checks/unused-package-dep.js +6 -93
  43. package/dist/checks/unused-param.js +2 -2
  44. package/dist/checks/unused-variable.js +2 -2
  45. package/dist/checks/variable-shadow.js +3 -3
  46. package/dist/checks/while-constant.js +1 -1
  47. package/dist/cli/collector.js +26 -19
  48. package/dist/config.d.ts +1 -1
  49. package/dist/config.js +18 -9
  50. package/dist/fixes/index.d.ts +4 -0
  51. package/dist/fixes/index.js +843 -140
  52. package/dist/formatter.d.ts +0 -1
  53. package/dist/formatter.js +141 -31
  54. package/dist/index.js +59 -51
  55. package/dist/project.d.ts +1 -1
  56. package/dist/project.js +98 -112
  57. package/dist/runner.d.ts +1 -1
  58. package/dist/runner.js +17 -8
  59. package/dist/utils.js +21 -30
  60. package/dist/validate.js +3 -3
  61. package/dist/visitor-runner.d.ts +0 -28
  62. package/dist/visitor-runner.js +3 -809
  63. package/dist/visitors/equal-nil.d.ts +2 -2
  64. package/dist/visitors/equal-nil.js +5 -4
  65. package/dist/visitors/index.d.ts +1 -1
  66. package/dist/visitors/progn-in-cond.d.ts +2 -2
  67. package/dist/visitors/progn-in-cond.js +5 -4
  68. package/dist/visitors/redundant-and-or.d.ts +2 -2
  69. package/dist/visitors/redundant-and-or.js +3 -3
  70. package/dist/visitors/redundant-car-cdr.d.ts +2 -2
  71. package/dist/visitors/redundant-car-cdr.js +3 -3
  72. package/dist/visitors/register.d.ts +6 -0
  73. package/dist/visitors/register.js +625 -0
  74. package/dist/visitors/shared.d.ts +40 -0
  75. package/dist/visitors/shared.js +129 -0
  76. package/dist/visitors/types.d.ts +2 -7
  77. package/dist/visitors/types.js +0 -69
  78. package/dist/watch.js +33 -12
  79. package/dist/worker.js +1 -1
  80. package/package.json +2 -2
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.higherOrderFuncs = void 0;
4
+ exports.isInQuote = isInQuote;
5
+ exports.isLiteral = isLiteral;
6
+ exports.astEqual = astEqual;
7
+ exports.getCondKey = getCondKey;
8
+ exports.hasConditionalForm = hasConditionalForm;
9
+ exports.hasSelfCall = hasSelfCall;
10
+ exports.defunParams = defunParams;
11
+ exports.defunLocals = defunLocals;
12
+ exports.collectSymbolRefs = collectSymbolRefs;
13
+ const parser_1 = require("@atlisp/parser");
14
+ function isInQuote(node, checkFunction = true) {
15
+ for (const p of (0, parser_1.astAncestors)(node)) {
16
+ if ((0, parser_1.astIsList)(p, 'quote') || (checkFunction && (0, parser_1.astIsList)(p, 'function'))) {
17
+ return true;
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ function isLiteral(node) {
23
+ if ((0, parser_1.astIsSymbol)(node, 'T') || (0, parser_1.astIsSymbol)(node, 't') || (0, parser_1.astIsSymbol)(node, 'nil'))
24
+ return true;
25
+ if ((0, parser_1.astIsNumber)(node))
26
+ return true;
27
+ return false;
28
+ }
29
+ function astEqual(a, b) {
30
+ if ((0, parser_1.astIsSymbol)(a) && (0, parser_1.astIsSymbol)(b))
31
+ return a.name === b.name;
32
+ if ((0, parser_1.astIsString)(a) && (0, parser_1.astIsString)(b))
33
+ return a.value === b.value;
34
+ if ((0, parser_1.astIsNumber)(a) && (0, parser_1.astIsNumber)(b))
35
+ return a.value === b.value;
36
+ if ((0, parser_1.astIsList)(a) && (0, parser_1.astIsList)(b)) {
37
+ if (a.children.length !== b.children.length)
38
+ return false;
39
+ for (let i = 0; i < a.children.length; i++) {
40
+ if (!astEqual(a.children[i], b.children[i]))
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ return false;
46
+ }
47
+ function getCondKey(node) {
48
+ if ((0, parser_1.astIsSymbol)(node) || (0, parser_1.astIsKeyword)(node))
49
+ return node.name || '';
50
+ if ((0, parser_1.astIsString)(node) || (0, parser_1.astIsNumber)(node))
51
+ return String(node.value);
52
+ if ((0, parser_1.astIsList)(node)) {
53
+ return '(' + node.children.map(c => getCondKey(c)).join(' ') + ')';
54
+ }
55
+ return '';
56
+ }
57
+ function hasConditionalForm(body) {
58
+ for (const form of body) {
59
+ if ((0, parser_1.astIsList)(form) && form.children.length > 0 && (0, parser_1.astIsSymbol)(form.children[0])) {
60
+ const name = form.children[0].name;
61
+ if (name === 'if' || name === 'cond' || name === 'when' || name === 'unless') {
62
+ return true;
63
+ }
64
+ }
65
+ }
66
+ return false;
67
+ }
68
+ function hasSelfCall(body, fnName) {
69
+ for (const form of body) {
70
+ if ((0, parser_1.astIsList)(form, fnName)) {
71
+ return true;
72
+ }
73
+ }
74
+ return false;
75
+ }
76
+ function defunParams(defunNode) {
77
+ const children = (0, parser_1.astChildren)(defunNode);
78
+ if (children.length < 3)
79
+ return [];
80
+ const paramList = children[2];
81
+ const params = [];
82
+ for (const child of (0, parser_1.astChildren)(paramList)) {
83
+ if ((0, parser_1.astIsSymbol)(child) && child.name !== '/') {
84
+ params.push(child.name);
85
+ }
86
+ }
87
+ return params;
88
+ }
89
+ function defunLocals(defunNode) {
90
+ const children = (0, parser_1.astChildren)(defunNode);
91
+ if (children.length < 3)
92
+ return [];
93
+ const paramList = children[2];
94
+ const locals = [];
95
+ let afterSlash = false;
96
+ for (const child of (0, parser_1.astChildren)(paramList)) {
97
+ if (!(0, parser_1.astIsSymbol)(child))
98
+ continue;
99
+ if (child.name === '/') {
100
+ afterSlash = true;
101
+ continue;
102
+ }
103
+ if (afterSlash && child.name) {
104
+ locals.push(child.name);
105
+ }
106
+ }
107
+ return locals;
108
+ }
109
+ function collectSymbolRefs(node, result) {
110
+ if ((0, parser_1.astIsSymbol)(node) && node.name) {
111
+ result.add(node.name);
112
+ }
113
+ for (const c of (0, parser_1.astChildren)(node)) {
114
+ collectSymbolRefs(c, result);
115
+ }
116
+ }
117
+ exports.higherOrderFuncs = new Set([
118
+ 'mapcar',
119
+ 'apply',
120
+ 'lambda',
121
+ 'vl-sort',
122
+ 'vl-sort-i',
123
+ 'vl-remove-if',
124
+ 'vl-remove-if-not',
125
+ 'vl-member-if',
126
+ 'vl-some',
127
+ 'vl-every',
128
+ ]);
129
+ //# sourceMappingURL=shared.js.map
@@ -1,13 +1,8 @@
1
- import { AstNode, AstVisitor } from '@atlisp/parser';
2
- import { CollectState } from '../visitor-runner';
1
+ import { AstVisitor } from '@atlisp/parser';
2
+ import { CollectState } from './shared';
3
3
  export type AddIssueFn = (rule: string, line: number, message: string) => void;
4
4
  export interface VisitorModule {
5
5
  name: string;
6
6
  register: (visitor: AstVisitor, state: CollectState, checks: Record<string, string>, addIssue: AddIssueFn) => void;
7
7
  }
8
- export declare function isInQuote(node: AstNode): boolean;
9
- export declare function isLiteral(node: AstNode): boolean;
10
- export declare function astEqual(a: AstNode, b: AstNode): boolean;
11
- export declare function getCondKey(node: AstNode): string;
12
- export declare function hasConditionalForm(body: AstNode[]): boolean;
13
8
  //# sourceMappingURL=types.d.ts.map
@@ -1,72 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isInQuote = isInQuote;
4
- exports.isLiteral = isLiteral;
5
- exports.astEqual = astEqual;
6
- exports.getCondKey = getCondKey;
7
- exports.hasConditionalForm = hasConditionalForm;
8
- function isInQuote(node) {
9
- let p = node.parent;
10
- while (p) {
11
- if (p.type === 'list' &&
12
- p.children &&
13
- p.children.length > 0 &&
14
- p.children[0].type === 'symbol' &&
15
- (p.children[0].name === 'quote' || p.children[0].name === 'function')) {
16
- return true;
17
- }
18
- p = p.parent;
19
- }
20
- return false;
21
- }
22
- function isLiteral(node) {
23
- if (node.type === 'symbol' && (node.name === 'T' || node.name === 't' || node.name === 'nil'))
24
- return true;
25
- if (node.type === 'number')
26
- return true;
27
- return false;
28
- }
29
- function astEqual(a, b) {
30
- if (a.type !== b.type)
31
- return false;
32
- if (a.type === 'symbol')
33
- return a.name === b.name;
34
- if (a.type === 'string')
35
- return a.value === b.value;
36
- if (a.type === 'number')
37
- return a.value === b.value;
38
- if (a.type === 'list') {
39
- const ac = a.children || [];
40
- const bc = b.children || [];
41
- if (ac.length !== bc.length)
42
- return false;
43
- for (let i = 0; i < ac.length; i++) {
44
- if (!astEqual(ac[i], bc[i]))
45
- return false;
46
- }
47
- return true;
48
- }
49
- return false;
50
- }
51
- function getCondKey(node) {
52
- if (node.type === 'symbol' || node.type === 'keyword')
53
- return node.name || '';
54
- if (node.type === 'string' || node.type === 'number')
55
- return String(node.value);
56
- if (node.type === 'list' && node.children) {
57
- return '(' + node.children.map(c => getCondKey(c)).join(' ') + ')';
58
- }
59
- return '';
60
- }
61
- function hasConditionalForm(body) {
62
- for (const form of body) {
63
- if (form.type === 'list' && form.children && form.children.length > 0 && form.children[0].type === 'symbol') {
64
- const name = form.children[0].name;
65
- if (name === 'if' || name === 'cond' || name === 'when' || name === 'unless') {
66
- return true;
67
- }
68
- }
69
- }
70
- return false;
71
- }
72
3
  //# sourceMappingURL=types.js.map
package/dist/watch.js CHANGED
@@ -39,11 +39,31 @@ const path = __importStar(require("path"));
39
39
  const runner_1 = require("./runner");
40
40
  const formatters_1 = require("./formatters");
41
41
  const locale_1 = require("./locale");
42
+ let previousIssues = [];
43
+ let debounceTimer;
44
+ let pendingFiles = new Set();
45
+ function scheduleLint(files, rootDir, format, config, changedFile) {
46
+ pendingFiles.add(changedFile);
47
+ if (debounceTimer)
48
+ clearTimeout(debounceTimer);
49
+ debounceTimer = setTimeout(() => {
50
+ const toLint = Array.from(pendingFiles);
51
+ pendingFiles.clear();
52
+ debounceTimer = undefined;
53
+ // Only lint the changed files, merge with previous results
54
+ const newIssues = (0, runner_1.lintFiles)(toLint, config, rootDir);
55
+ const changedSet = new Set(toLint.map(f => path.relative(rootDir, f)));
56
+ const merged = previousIssues.filter(i => !changedSet.has(i.file)).concat(newIssues);
57
+ previousIssues = merged;
58
+ displayResults(merged, rootDir, format, files.length);
59
+ }, 200);
60
+ }
42
61
  function watchFiles(files, options) {
43
62
  const { rootDir, format, config } = options;
44
63
  (0, locale_1.setLocale)(config.locale || 'zh');
45
64
  // Initial lint
46
- runLint(files, rootDir, format, config);
65
+ previousIssues = (0, runner_1.lintFiles)(files, config, rootDir);
66
+ displayResults(previousIssues, rootDir, format, files.length);
47
67
  // Watch all files for changes
48
68
  const watchers = [];
49
69
  const watchedDirs = new Set();
@@ -52,15 +72,12 @@ function watchFiles(files, options) {
52
72
  if (!watchedDirs.has(dir)) {
53
73
  watchedDirs.add(dir);
54
74
  try {
55
- const watcher = fs.watch(dir, (eventType, filename) => {
75
+ const watcher = fs.watch(dir, (_eventType, filename) => {
56
76
  if (!filename)
57
77
  return;
58
78
  const fullPath = path.join(dir, filename);
59
79
  if (files.includes(fullPath)) {
60
- // Debounce: wait a bit for the file to finish writing
61
- setTimeout(() => {
62
- runLint(files, rootDir, format, config);
63
- }, 200);
80
+ scheduleLint(files, rootDir, format, config, fullPath);
64
81
  }
65
82
  });
66
83
  watchers.push(watcher);
@@ -84,10 +101,8 @@ function watchFiles(files, options) {
84
101
  process.exit(0);
85
102
  });
86
103
  }
87
- function runLint(files, rootDir, format, config) {
88
- // Clear console
104
+ function displayResults(issues, rootDir, format, totalFiles) {
89
105
  process.stdout.write('\x1Bc');
90
- const issues = (0, runner_1.lintFiles)(files, config, rootDir);
91
106
  const fileContents = new Map();
92
107
  for (const iss of issues) {
93
108
  if (!fileContents.has(iss.file)) {
@@ -106,8 +121,14 @@ function runLint(files, rootDir, format, config) {
106
121
  else {
107
122
  console.log((0, formatters_1.formatDefault)(issues, fileContents));
108
123
  }
109
- const errorCount = issues.filter(i => i.severity === 'error').length;
110
- const warningCount = issues.filter(i => i.severity === 'warn').length;
111
- console.log(`\n[${new Date().toLocaleTimeString()}] ${(0, locale_1.t)('index.watch_status', files.length, errorCount, warningCount)}`);
124
+ let errorCount = 0;
125
+ let warningCount = 0;
126
+ for (const iss of issues) {
127
+ if (iss.severity === 'error')
128
+ errorCount++;
129
+ else if (iss.severity === 'warn')
130
+ warningCount++;
131
+ }
132
+ console.log(`\n[${new Date().toLocaleTimeString()}] ${(0, locale_1.t)('index.watch_status', totalFiles, errorCount, warningCount)}`);
112
133
  }
113
134
  //# sourceMappingURL=watch.js.map
package/dist/worker.js CHANGED
@@ -18,7 +18,7 @@ if (worker_threads_1.parentPort && worker_threads_1.workerData) {
18
18
  const input = worker_threads_1.workerData;
19
19
  (0, locale_1.setLocale)(input.config.locale || 'zh');
20
20
  try {
21
- const content = (0, fs_1.readFileSync)(input.filepath, 'utf-8');
21
+ const content = input.content ?? (0, fs_1.readFileSync)(input.filepath, 'utf-8');
22
22
  const issues = (0, runner_1.runChecks)(content, input.relPath, input.config);
23
23
  sendMessage({ filepath: input.filepath, issues });
24
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/lint",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "description": "AutoLISP static analysis tool — parens, security, conventions, SBCL syntax validation",
5
5
  "keywords": [
6
6
  "CAD",
@@ -54,6 +54,6 @@
54
54
  },
55
55
  "license": "MIT",
56
56
  "dependencies": {
57
- "@atlisp/parser": "^0.1.8"
57
+ "@atlisp/parser": "^0.1.9"
58
58
  }
59
59
  }