@atlisp/lint 0.1.5 → 0.1.6
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/LICENSE +21 -0
- package/README.md +182 -58
- package/atlisp-lint.default.json +31 -1
- package/dist/atlisp-lint.default.json +90 -0
- package/dist/cache.d.ts +2 -2
- package/dist/cache.js +6 -6
- package/dist/checks/append-single.d.ts +3 -0
- package/dist/checks/append-single.js +17 -0
- package/dist/checks/arg-count.d.ts +3 -0
- package/dist/checks/arg-count.js +123 -0
- package/dist/checks/assoc-without-cdr.d.ts +5 -0
- package/dist/checks/assoc-without-cdr.js +32 -0
- package/dist/checks/comment-style.d.ts +3 -0
- package/dist/checks/comment-style.js +24 -0
- package/dist/checks/cond-duplicate.d.ts +5 -0
- package/dist/checks/cond-duplicate.js +52 -0
- package/dist/checks/cond-simplify.d.ts +3 -0
- package/dist/checks/cond-simplify.js +45 -0
- package/dist/checks/constant-condition.js +4 -4
- package/dist/checks/dangerous-calls.js +2 -2
- package/dist/checks/dangling-defun.d.ts +3 -0
- package/dist/checks/dangling-defun.js +10 -28
- package/dist/checks/double-not.js +1 -1
- package/dist/checks/duplicate-defun.d.ts +6 -0
- package/dist/checks/duplicate-defun.js +50 -0
- package/dist/checks/dynamic-doc.d.ts +3 -0
- package/dist/checks/dynamic-doc.js +21 -0
- package/dist/checks/empty-branch.js +1 -1
- package/dist/checks/empty-catch.d.ts +3 -0
- package/dist/checks/empty-catch.js +34 -0
- package/dist/checks/eq-usage.d.ts +3 -0
- package/dist/checks/eq-usage.js +25 -0
- package/dist/checks/error-handling.d.ts +3 -0
- package/dist/checks/error-handling.js +56 -0
- package/dist/checks/extra-parens.d.ts +3 -0
- package/dist/checks/extra-parens.js +45 -0
- package/dist/checks/function-complexity.js +1 -1
- package/dist/checks/function-order.d.ts +3 -0
- package/dist/checks/function-order.js +33 -0
- package/dist/checks/global-naming.d.ts +3 -0
- package/dist/checks/global-naming.js +62 -0
- package/dist/checks/identical-branches.d.ts +5 -0
- package/dist/checks/identical-branches.js +54 -0
- package/dist/checks/index.d.ts +3 -0
- package/dist/checks/index.js +117 -0
- package/dist/checks/lambda-syntax.d.ts +3 -0
- package/dist/checks/lambda-syntax.js +25 -0
- package/dist/checks/long-function-call.d.ts +3 -0
- package/dist/checks/long-function-call.js +54 -0
- package/dist/checks/loop-optimization.d.ts +3 -0
- package/dist/checks/loop-optimization.js +17 -0
- package/dist/checks/magic-number.d.ts +3 -0
- package/dist/checks/magic-number.js +21 -0
- package/dist/checks/misplaced-else.js +1 -1
- package/dist/checks/missing-export.js +2 -2
- package/dist/checks/mixed-indent.d.ts +3 -0
- package/dist/checks/mixed-indent.js +19 -0
- package/dist/checks/module-reg.js +1 -1
- package/dist/checks/multiple-setq.js +1 -1
- package/dist/checks/no-return.d.ts +3 -0
- package/dist/checks/no-return.js +51 -0
- package/dist/checks/nth-usage.d.ts +3 -0
- package/dist/checks/nth-usage.js +17 -0
- package/dist/checks/quote-style.d.ts +3 -0
- package/dist/checks/quote-style.js +25 -0
- package/dist/checks/quote-vs-function.js +1 -1
- package/dist/checks/recursive-call.js +1 -1
- package/dist/checks/redundant-if.d.ts +3 -0
- package/dist/checks/redundant-if.js +17 -0
- package/dist/checks/redundant-let.js +1 -1
- package/dist/checks/redundant-nil-else.js +1 -1
- package/dist/checks/redundant-progn.js +1 -1
- package/dist/checks/redundant-quotes.js +1 -1
- package/dist/checks/redundant-setq.js +1 -1
- package/dist/checks/self-compare.js +1 -1
- package/dist/checks/setq-multiple.d.ts +3 -0
- package/dist/checks/setq-multiple.js +17 -0
- package/dist/checks/setq-single-arg.d.ts +5 -0
- package/dist/checks/setq-single-arg.js +30 -0
- package/dist/checks/shadow-builtin.d.ts +3 -0
- package/dist/checks/shadow-builtin.js +77 -0
- package/dist/checks/single-arg-and-or.js +1 -1
- package/dist/checks/strcat-usage.d.ts +3 -0
- package/dist/checks/strcat-usage.js +25 -0
- package/dist/checks/type-check.d.ts +3 -0
- package/dist/checks/type-check.js +26 -0
- package/dist/checks/unused-let.js +1 -1
- package/dist/checks/unused-package-dep.js +3 -3
- package/dist/checks/variable-shadow.js +1 -1
- package/dist/checks/while-constant.d.ts +5 -0
- package/dist/checks/while-constant.js +40 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +70 -2
- package/dist/disable.js +1 -1
- package/dist/formatters.d.ts +1 -0
- package/dist/formatters.js +18 -2
- package/dist/index.js +53 -13
- package/dist/lib/lint-sbcl.lisp +161 -0
- package/dist/locale.js +24 -0
- package/dist/presets.d.ts +4 -0
- package/dist/presets.js +158 -0
- package/dist/project.js +37 -6
- package/dist/rules.d.ts +9 -0
- package/dist/rules.js +238 -0
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +198 -11
- package/dist/sbcl.js +1 -1
- package/dist/stub-packages.json +41 -0
- package/dist/types.d.ts +6 -0
- package/dist/validate.d.ts +8 -0
- package/dist/validate.js +125 -0
- package/dist/watch.d.ts +9 -0
- package/dist/watch.js +113 -0
- package/package.json +1 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkGlobalNaming = checkGlobalNaming;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkGlobalNaming(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
let depth = 0;
|
|
10
|
+
let inDefun = false;
|
|
11
|
+
let defunLocals = new Set();
|
|
12
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
14
|
+
// Track depth
|
|
15
|
+
for (const ch of stripped) {
|
|
16
|
+
if (ch === '(')
|
|
17
|
+
depth++;
|
|
18
|
+
else if (ch === ')')
|
|
19
|
+
depth--;
|
|
20
|
+
}
|
|
21
|
+
// Detect defun start and extract local variables
|
|
22
|
+
if (!inDefun) {
|
|
23
|
+
const defunMatch = stripped.match(/\(defun\s+\S+\s+\(([^)]*\/\s+[^)]*)\)/);
|
|
24
|
+
if (defunMatch) {
|
|
25
|
+
inDefun = true;
|
|
26
|
+
const all = defunMatch[1];
|
|
27
|
+
const slashIdx = all.indexOf('/');
|
|
28
|
+
defunLocals = new Set(all
|
|
29
|
+
.slice(slashIdx + 1)
|
|
30
|
+
.split(/\s+/)
|
|
31
|
+
.filter(Boolean));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (depth <= 0) {
|
|
35
|
+
inDefun = false;
|
|
36
|
+
defunLocals = new Set();
|
|
37
|
+
}
|
|
38
|
+
// setq at depth 0 or 1 (top-level or directly inside progn) is global
|
|
39
|
+
if (depth <= 1) {
|
|
40
|
+
const m = stripped.match(/\(setq\s+(\S+)/);
|
|
41
|
+
if (m) {
|
|
42
|
+
const v = m[1];
|
|
43
|
+
if (v !== 'T' &&
|
|
44
|
+
v !== 'nil' &&
|
|
45
|
+
!v.startsWith('*') &&
|
|
46
|
+
!v.endsWith('*') &&
|
|
47
|
+
!v.includes('/') &&
|
|
48
|
+
!defunLocals.has(v)) {
|
|
49
|
+
issues.push({
|
|
50
|
+
file,
|
|
51
|
+
line: i + 1,
|
|
52
|
+
severity: 'warn',
|
|
53
|
+
rule: 'global_naming',
|
|
54
|
+
message: (0, locale_1.t)('global_naming', v),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return issues;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=global-naming.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkIdenticalBranches(content: string, file: string): Issue[];
|
|
4
|
+
export declare function checkIdenticalBranchesAst(ast: AstNode, file: string): Issue[];
|
|
5
|
+
//# sourceMappingURL=identical-branches.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkIdenticalBranches = checkIdenticalBranches;
|
|
4
|
+
exports.checkIdenticalBranchesAst = checkIdenticalBranchesAst;
|
|
5
|
+
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
function astEqual(a, b) {
|
|
8
|
+
if (a.type !== b.type)
|
|
9
|
+
return false;
|
|
10
|
+
if (a.type === 'symbol')
|
|
11
|
+
return a.name === b.name;
|
|
12
|
+
if (a.type === 'number')
|
|
13
|
+
return a.value === b.value;
|
|
14
|
+
if (a.type === 'string')
|
|
15
|
+
return a.value === b.value;
|
|
16
|
+
if (a.type === 'list') {
|
|
17
|
+
const aChildren = a.children || [];
|
|
18
|
+
const bChildren = b.children || [];
|
|
19
|
+
if (aChildren.length !== bChildren.length)
|
|
20
|
+
return false;
|
|
21
|
+
for (let i = 0; i < aChildren.length; i++) {
|
|
22
|
+
if (!astEqual(aChildren[i], bChildren[i]))
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function checkIdenticalBranches(content, file) {
|
|
30
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
31
|
+
return checkIdenticalBranchesAst(ast, file);
|
|
32
|
+
}
|
|
33
|
+
function checkIdenticalBranchesAst(ast, file) {
|
|
34
|
+
const issues = [];
|
|
35
|
+
// Check if statements: (if cond then-branch else-branch)
|
|
36
|
+
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
37
|
+
for (const node of ifNodes) {
|
|
38
|
+
if (!node.children || node.children.length < 4)
|
|
39
|
+
continue;
|
|
40
|
+
const thenBranch = node.children[2];
|
|
41
|
+
const elseBranch = node.children[3];
|
|
42
|
+
if (astEqual(thenBranch, elseBranch)) {
|
|
43
|
+
issues.push({
|
|
44
|
+
file,
|
|
45
|
+
line: node.pos.line,
|
|
46
|
+
severity: 'warn',
|
|
47
|
+
rule: 'identical_branches',
|
|
48
|
+
message: (0, locale_1.t)('identical_branches'),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return issues;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=identical-branches.js.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runChecks = runChecks;
|
|
4
|
+
const config_1 = require("../config");
|
|
5
|
+
const disable_1 = require("../disable");
|
|
6
|
+
const encoding_1 = require("./encoding");
|
|
7
|
+
const parens_1 = require("./parens");
|
|
8
|
+
const cl_syntax_1 = require("./cl-syntax");
|
|
9
|
+
const dangerous_calls_1 = require("./dangerous-calls");
|
|
10
|
+
const vlax_1 = require("./vlax");
|
|
11
|
+
const token_in_url_1 = require("./token-in-url");
|
|
12
|
+
const open_close_1 = require("./open-close");
|
|
13
|
+
const bare_names_1 = require("./bare-names");
|
|
14
|
+
const module_reg_1 = require("./module-reg");
|
|
15
|
+
const namespace_header_1 = require("./namespace-header");
|
|
16
|
+
const line_length_1 = require("./line-length");
|
|
17
|
+
const function_complexity_1 = require("./function-complexity");
|
|
18
|
+
const parameter_naming_1 = require("./parameter-naming");
|
|
19
|
+
const unused_variable_1 = require("./unused-variable");
|
|
20
|
+
const missing_doc_1 = require("./missing-doc");
|
|
21
|
+
const error_handling_1 = require("./error-handling");
|
|
22
|
+
const global_naming_1 = require("./global-naming");
|
|
23
|
+
const extra_parens_1 = require("./extra-parens");
|
|
24
|
+
const arg_count_1 = require("./arg-count");
|
|
25
|
+
const strcat_usage_1 = require("./strcat-usage");
|
|
26
|
+
const cond_simplify_1 = require("./cond-simplify");
|
|
27
|
+
const redundant_progn_1 = require("./redundant-progn");
|
|
28
|
+
const quote_style_1 = require("./quote-style");
|
|
29
|
+
const eq_usage_1 = require("./eq-usage");
|
|
30
|
+
const lambda_syntax_1 = require("./lambda-syntax");
|
|
31
|
+
const comment_style_1 = require("./comment-style");
|
|
32
|
+
const empty_catch_1 = require("./empty-catch");
|
|
33
|
+
const nth_usage_1 = require("./nth-usage");
|
|
34
|
+
const append_single_1 = require("./append-single");
|
|
35
|
+
const setq_multiple_1 = require("./setq-multiple");
|
|
36
|
+
const function_order_1 = require("./function-order");
|
|
37
|
+
const magic_number_1 = require("./magic-number");
|
|
38
|
+
const mixed_indent_1 = require("./mixed-indent");
|
|
39
|
+
const long_function_call_1 = require("./long-function-call");
|
|
40
|
+
const no_return_1 = require("./no-return");
|
|
41
|
+
const shadow_builtin_1 = require("./shadow-builtin");
|
|
42
|
+
const dynamic_doc_1 = require("./dynamic-doc");
|
|
43
|
+
const loop_optimization_1 = require("./loop-optimization");
|
|
44
|
+
const type_check_1 = require("./type-check");
|
|
45
|
+
const redundant_if_1 = require("./redundant-if");
|
|
46
|
+
const trailing_ws_1 = require("./trailing-ws");
|
|
47
|
+
function runChecks(content, file, config) {
|
|
48
|
+
const issues = [];
|
|
49
|
+
const disableMap = (0, disable_1.parseDisableComments)(content);
|
|
50
|
+
function addIfEnabled(rule, fn) {
|
|
51
|
+
if ((0, config_1.getCheckSeverity)(config, rule) === 'off')
|
|
52
|
+
return;
|
|
53
|
+
const results = fn();
|
|
54
|
+
for (const r of results) {
|
|
55
|
+
if (!(0, disable_1.isDisabled)(r.rule, r.line, disableMap)) {
|
|
56
|
+
issues.push(r);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const checks = [
|
|
61
|
+
{ rule: 'encoding', fn: () => (0, encoding_1.checkEncoding)(content, file) },
|
|
62
|
+
{ rule: 'parens', fn: () => (0, parens_1.checkParens)(content, file) },
|
|
63
|
+
{ rule: 'cl_syntax', fn: () => (0, cl_syntax_1.checkClSyntax)(content, file, config.cl_syntax.keywords) },
|
|
64
|
+
{ rule: 'quit_exit', fn: () => (0, dangerous_calls_1.checkDangerousCalls)(content, file, config.dangerous_calls) },
|
|
65
|
+
{ rule: 'vlax_without_loading', fn: () => (0, vlax_1.checkVlaxWithoutLoading)(content, file) },
|
|
66
|
+
{ rule: 'token_in_url', fn: () => (0, token_in_url_1.checkTokenInUrl)(content, file) },
|
|
67
|
+
{ rule: 'open_without_close', fn: () => (0, open_close_1.checkOpenWithoutClose)(content, file) },
|
|
68
|
+
{
|
|
69
|
+
rule: 'bare_function_names',
|
|
70
|
+
fn: () => (0, bare_names_1.checkBareFunctionNames)(content, file, config.bare_function_names.allowlist, config.bare_function_names.namespace_pattern),
|
|
71
|
+
},
|
|
72
|
+
{ rule: 'module_registration', fn: () => (0, module_reg_1.checkModuleRegistration)(content, file, file, config.module_registration) },
|
|
73
|
+
{ rule: 'namespace_header', fn: () => (0, namespace_header_1.checkNamespaceHeader)(content, file, config.namespace_header) },
|
|
74
|
+
{
|
|
75
|
+
rule: 'line_length',
|
|
76
|
+
fn: () => (0, line_length_1.checkLineLength)(content, file, config.line_length.max, config.line_length.tab_width),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
rule: 'function_complexity',
|
|
80
|
+
fn: () => (0, function_complexity_1.checkFunctionComplexity)(content, file, config.function_complexity.max_lines, config.function_complexity.max_nesting),
|
|
81
|
+
},
|
|
82
|
+
{ rule: 'parameter_naming', fn: () => (0, parameter_naming_1.checkParameterNaming)(content, file) },
|
|
83
|
+
{ rule: 'unused_variable', fn: () => (0, unused_variable_1.checkUnusedVariable)(content, file) },
|
|
84
|
+
{ rule: 'missing_doc', fn: () => (0, missing_doc_1.checkMissingDoc)(content, file) },
|
|
85
|
+
{ rule: 'error_handling', fn: () => (0, error_handling_1.checkErrorHandling)(content, file) },
|
|
86
|
+
{ rule: 'global_naming', fn: () => (0, global_naming_1.checkGlobalNaming)(content, file) },
|
|
87
|
+
{ rule: 'extra_parens', fn: () => (0, extra_parens_1.checkExtraParens)(content, file) },
|
|
88
|
+
{ rule: 'arg_count', fn: () => (0, arg_count_1.checkArgCount)(content, file) },
|
|
89
|
+
{ rule: 'strcat_usage', fn: () => (0, strcat_usage_1.checkStrcatUsage)(content, file) },
|
|
90
|
+
{ rule: 'cond_simplify', fn: () => (0, cond_simplify_1.checkCondSimplify)(content, file) },
|
|
91
|
+
{ rule: 'redundant_progn', fn: () => (0, redundant_progn_1.checkRedundantProgn)(content, file) },
|
|
92
|
+
{ rule: 'quote_style', fn: () => (0, quote_style_1.checkQuoteStyle)(content, file) },
|
|
93
|
+
{ rule: 'eq_usage', fn: () => (0, eq_usage_1.checkEqUsage)(content, file) },
|
|
94
|
+
{ rule: 'lambda_syntax', fn: () => (0, lambda_syntax_1.checkLambdaSyntax)(content, file) },
|
|
95
|
+
{ rule: 'comment_style', fn: () => (0, comment_style_1.checkCommentStyle)(content, file) },
|
|
96
|
+
{ rule: 'empty_catch', fn: () => (0, empty_catch_1.checkEmptyCatch)(content, file) },
|
|
97
|
+
{ rule: 'nth_usage', fn: () => (0, nth_usage_1.checkNthUsage)(content, file) },
|
|
98
|
+
{ rule: 'append_single', fn: () => (0, append_single_1.checkAppendSingle)(content, file) },
|
|
99
|
+
{ rule: 'setq_multiple', fn: () => (0, setq_multiple_1.checkSetqMultiple)(content, file) },
|
|
100
|
+
{ rule: 'function_order', fn: () => (0, function_order_1.checkFunctionOrder)(content, file) },
|
|
101
|
+
{ rule: 'magic_number', fn: () => (0, magic_number_1.checkMagicNumber)(content, file) },
|
|
102
|
+
{ rule: 'mixed_indent', fn: () => (0, mixed_indent_1.checkMixedIndent)(content, file) },
|
|
103
|
+
{ rule: 'long_function_call', fn: () => (0, long_function_call_1.checkLongFunctionCall)(content, file, 6) },
|
|
104
|
+
{ rule: 'no_return', fn: () => (0, no_return_1.checkNoReturn)(content, file) },
|
|
105
|
+
{ rule: 'shadow_builtin', fn: () => (0, shadow_builtin_1.checkShadowBuiltin)(content, file) },
|
|
106
|
+
{ rule: 'dynamic_doc', fn: () => (0, dynamic_doc_1.checkDynamicDoc)(content, file) },
|
|
107
|
+
{ rule: 'loop_optimization', fn: () => (0, loop_optimization_1.checkLoopOptimization)(content, file) },
|
|
108
|
+
{ rule: 'type_check', fn: () => (0, type_check_1.checkTypeCheck)(content, file) },
|
|
109
|
+
{ rule: 'redundant_if', fn: () => (0, redundant_if_1.checkRedundantIf)(content, file) },
|
|
110
|
+
{ rule: 'trailing_whitespace', fn: () => (0, trailing_ws_1.checkTrailingWhitespace)(content, file) },
|
|
111
|
+
];
|
|
112
|
+
for (const c of checks) {
|
|
113
|
+
addIfEnabled(c.rule, c.fn);
|
|
114
|
+
}
|
|
115
|
+
return issues;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkLambdaSyntax = checkLambdaSyntax;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkLambdaSyntax(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
// Detect (function (lambda ...)) → can be #'
|
|
12
|
+
const m = stripped.match(/\(function\s+\(lambda\b/);
|
|
13
|
+
if (m) {
|
|
14
|
+
issues.push({
|
|
15
|
+
file,
|
|
16
|
+
line: i + 1,
|
|
17
|
+
severity: 'warn',
|
|
18
|
+
rule: 'lambda_syntax',
|
|
19
|
+
message: (0, locale_1.t)('lambda_syntax'),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return issues;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=lambda-syntax.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkLongFunctionCall = checkLongFunctionCall;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkLongFunctionCall(content, file, maxArgs) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
const m = stripped.match(/\((\S+)\s+/);
|
|
12
|
+
if (!m)
|
|
13
|
+
continue;
|
|
14
|
+
const afterName = stripped.slice(m[0].length);
|
|
15
|
+
if (!afterName || afterName === ')')
|
|
16
|
+
continue;
|
|
17
|
+
let argCount = 0, depth = 0, inArg = false;
|
|
18
|
+
for (const ch of afterName) {
|
|
19
|
+
if (ch === '(') {
|
|
20
|
+
depth++;
|
|
21
|
+
if (!inArg)
|
|
22
|
+
inArg = true;
|
|
23
|
+
}
|
|
24
|
+
else if (ch === ')') {
|
|
25
|
+
depth--;
|
|
26
|
+
if (depth === 0 && inArg) {
|
|
27
|
+
argCount++;
|
|
28
|
+
inArg = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (depth === 0 && (ch === ' ' || ch === '\t')) {
|
|
32
|
+
if (inArg) {
|
|
33
|
+
argCount++;
|
|
34
|
+
inArg = false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (!inArg)
|
|
38
|
+
inArg = true;
|
|
39
|
+
}
|
|
40
|
+
if (inArg)
|
|
41
|
+
argCount++;
|
|
42
|
+
if (argCount > maxArgs && !m[1].startsWith('(') && !m[1].startsWith('"') && m[1] !== 'if') {
|
|
43
|
+
issues.push({
|
|
44
|
+
file,
|
|
45
|
+
line: i + 1,
|
|
46
|
+
severity: 'warn',
|
|
47
|
+
rule: 'long_function_call',
|
|
48
|
+
message: (0, locale_1.t)('long_function_call', argCount),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return issues;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=long-function-call.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkLoopOptimization = checkLoopOptimization;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkLoopOptimization(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
if (/(foreach|while)\s+\S+\s+\S+/.test(stripped) && stripped.includes('assoc')) {
|
|
12
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'loop_optimization', message: (0, locale_1.t)('loop_optimization') });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return issues;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=loop-optimization.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkMagicNumber = checkMagicNumber;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkMagicNumber(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
const m = stripped.match(/\([a-z]+\s+.*?(\d{2,})\b/);
|
|
12
|
+
if (m) {
|
|
13
|
+
const num = m[1];
|
|
14
|
+
if (num !== '10' && num !== '20' && num !== '100' && num !== '1000') {
|
|
15
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'magic_number', message: (0, locale_1.t)('magic_number', num) });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return issues;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=magic-number.js.map
|
|
@@ -10,7 +10,7 @@ function checkMisplacedElse(content, file) {
|
|
|
10
10
|
}
|
|
11
11
|
function checkMisplacedElseAst(ast, file) {
|
|
12
12
|
const issues = [];
|
|
13
|
-
const ifNodes = (0, parser_1.astFindAll)(ast,
|
|
13
|
+
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
14
14
|
for (const node of ifNodes) {
|
|
15
15
|
if (!node.children || node.children.length !== 4)
|
|
16
16
|
continue;
|
|
@@ -45,7 +45,7 @@ function checkMissingExport(file, pkgDir, _allDefuns) {
|
|
|
45
45
|
return issues;
|
|
46
46
|
const pkgContent = fs.readFileSync(pkgLspPath, 'utf-8');
|
|
47
47
|
const pkgAst = (0, parser_1.parseAst)(pkgContent);
|
|
48
|
-
const modulesNode = (0, parser_1.astFindAll)(pkgAst,
|
|
48
|
+
const modulesNode = (0, parser_1.astFindAll)(pkgAst, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
49
49
|
const registered = new Set();
|
|
50
50
|
for (const node of modulesNode) {
|
|
51
51
|
if (!node.children)
|
|
@@ -69,7 +69,7 @@ function checkMissingExport(file, pkgDir, _allDefuns) {
|
|
|
69
69
|
}
|
|
70
70
|
const fileContent = fs.readFileSync(file, 'utf-8');
|
|
71
71
|
const ast = (0, parser_1.parseAst)(fileContent);
|
|
72
|
-
const defunNodes = (0, parser_1.astFindAll)(ast,
|
|
72
|
+
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
73
73
|
for (const node of defunNodes) {
|
|
74
74
|
if (!node.children || node.children.length < 2)
|
|
75
75
|
continue;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkMixedIndent = checkMixedIndent;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
function checkMixedIndent(content, file) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const lines = content.split('\n');
|
|
8
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9
|
+
const line = lines[i];
|
|
10
|
+
if (!line.trim())
|
|
11
|
+
continue;
|
|
12
|
+
const indent = line.substring(0, line.length - line.trimStart().length);
|
|
13
|
+
if (indent.includes('\t') && indent.includes(' ')) {
|
|
14
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'mixed_indent', message: (0, locale_1.t)('mixed_indent') });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return issues;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=mixed-indent.js.map
|
|
@@ -12,7 +12,7 @@ function checkModuleRegistration(content, file, relPath, config) {
|
|
|
12
12
|
if (!config.dirs.includes(topDir))
|
|
13
13
|
return [];
|
|
14
14
|
const lines = content.split('\n');
|
|
15
|
-
const relevant = lines.filter(
|
|
15
|
+
const relevant = lines.filter(l => l.trim().length > 0);
|
|
16
16
|
const tail = relevant.slice(-20).join('\n');
|
|
17
17
|
if (!tail.includes('*modules*')) {
|
|
18
18
|
return [
|
|
@@ -12,7 +12,7 @@ function checkMultipleSetqAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
// Find all parent lists that contain at least one setq
|
|
14
14
|
const parents = new Set();
|
|
15
|
-
const setqNodes = (0, parser_1.astFindAll)(ast,
|
|
15
|
+
const setqNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
16
16
|
for (const n of setqNodes) {
|
|
17
17
|
if (n.parent)
|
|
18
18
|
parents.add(n.parent);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkNoReturn = checkNoReturn;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkNoReturn(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
let inDefun = false;
|
|
10
|
+
let defunName = '';
|
|
11
|
+
let defunStart = 0;
|
|
12
|
+
let depth = 0;
|
|
13
|
+
let hasReturn = false;
|
|
14
|
+
for (let i = 0; i < lines.length; i++) {
|
|
15
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
16
|
+
if (!inDefun) {
|
|
17
|
+
const m = stripped.match(/\(defun\s+(\S+)/);
|
|
18
|
+
if (m) {
|
|
19
|
+
inDefun = true;
|
|
20
|
+
defunName = m[1];
|
|
21
|
+
defunStart = i + 1;
|
|
22
|
+
depth = 0;
|
|
23
|
+
hasReturn = false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (inDefun) {
|
|
27
|
+
for (const ch of stripped) {
|
|
28
|
+
if (ch === '(')
|
|
29
|
+
depth++;
|
|
30
|
+
else if (ch === ')')
|
|
31
|
+
depth--;
|
|
32
|
+
}
|
|
33
|
+
if (stripped.trim().startsWith('(') && !stripped.includes('defun'))
|
|
34
|
+
hasReturn = true;
|
|
35
|
+
if (depth === 0 && defunName && !defunName.startsWith('c:')) {
|
|
36
|
+
if (!hasReturn) {
|
|
37
|
+
issues.push({
|
|
38
|
+
file,
|
|
39
|
+
line: defunStart,
|
|
40
|
+
severity: 'warn',
|
|
41
|
+
rule: 'no_return',
|
|
42
|
+
message: (0, locale_1.t)('no_return', defunName),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
inDefun = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return issues;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=no-return.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkNthUsage = checkNthUsage;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkNthUsage(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
if (/\(nth\s+0\b/.test(stripped)) {
|
|
12
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'nth_usage', message: (0, locale_1.t)('nth_usage') });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return issues;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=nth-usage.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkQuoteStyle = checkQuoteStyle;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkQuoteStyle(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
// Detect (quote ...) that could be '
|
|
12
|
+
const m = stripped.match(/\(quote\s+/);
|
|
13
|
+
if (m) {
|
|
14
|
+
issues.push({
|
|
15
|
+
file,
|
|
16
|
+
line: i + 1,
|
|
17
|
+
severity: 'warn',
|
|
18
|
+
rule: 'quote_style',
|
|
19
|
+
message: (0, locale_1.t)('quote_style'),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return issues;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=quote-style.js.map
|
|
@@ -22,7 +22,7 @@ function checkQuoteVsFunction(content, file) {
|
|
|
22
22
|
}
|
|
23
23
|
function checkQuoteVsFunctionAst(ast, file) {
|
|
24
24
|
const issues = [];
|
|
25
|
-
const quotedLambdas = (0, parser_1.astFindAll)(ast,
|
|
25
|
+
const quotedLambdas = (0, parser_1.astFindAll)(ast, n => {
|
|
26
26
|
if (!(0, parser_1.astIsList)(n, 'quote'))
|
|
27
27
|
return false;
|
|
28
28
|
if (!n.children || n.children.length < 2)
|
|
@@ -10,7 +10,7 @@ function checkRecursiveCall(content, file) {
|
|
|
10
10
|
}
|
|
11
11
|
function checkRecursiveCallAst(ast, file) {
|
|
12
12
|
const issues = [];
|
|
13
|
-
const defuns = (0, parser_1.astFindAll)(ast,
|
|
13
|
+
const defuns = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun'));
|
|
14
14
|
for (const defun of defuns) {
|
|
15
15
|
if (!defun.children || defun.children.length < 3)
|
|
16
16
|
continue;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkRedundantIf = checkRedundantIf;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkRedundantIf(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
if (/\((if|when|unless)\s+\S+\s+\(progn\b/.test(stripped)) {
|
|
12
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'redundant_if', message: (0, locale_1.t)('redundant_if') });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return issues;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=redundant-if.js.map
|
|
@@ -10,7 +10,7 @@ function checkRedundantLet(content, file) {
|
|
|
10
10
|
}
|
|
11
11
|
function checkRedundantLetAst(ast, file) {
|
|
12
12
|
const issues = [];
|
|
13
|
-
const letNodes = (0, parser_1.astFindAll)(ast,
|
|
13
|
+
const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
|
|
14
14
|
for (const node of letNodes) {
|
|
15
15
|
if (!node.children || node.children.length < 2)
|
|
16
16
|
continue;
|
|
@@ -11,7 +11,7 @@ function checkRedundantNilElse(content, file) {
|
|
|
11
11
|
function checkRedundantNilElseAst(ast, file) {
|
|
12
12
|
const issues = [];
|
|
13
13
|
// Find (if cond then nil) — else branch is nil which is the default
|
|
14
|
-
const ifNodes = (0, parser_1.astFindAll)(ast,
|
|
14
|
+
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
15
15
|
for (const node of ifNodes) {
|
|
16
16
|
if (!node.children || node.children.length !== 4)
|
|
17
17
|
continue;
|
|
@@ -10,7 +10,7 @@ function checkRedundantProgn(content, file) {
|
|
|
10
10
|
}
|
|
11
11
|
function checkRedundantPrognAst(ast, file) {
|
|
12
12
|
const issues = [];
|
|
13
|
-
const prognNodes = (0, parser_1.astFindAll)(ast,
|
|
13
|
+
const prognNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'progn'));
|
|
14
14
|
for (const node of prognNodes) {
|
|
15
15
|
if (!node.children)
|
|
16
16
|
continue;
|