@ttsc/lint 0.18.4 → 0.19.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/README.md +95 -43
- package/go.mod +5 -4
- package/internal/rulecode/rulecode.go +88 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +45 -17
- package/lib/index.js.map +1 -1
- package/lib/structures/ITtscLintConfig.d.ts +3 -0
- package/lib/structures/TtscLintRuleSetting.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormat.d.ts +3 -2
- package/lib/structures/format/ITtscLintFormatSortImports.d.ts +20 -4
- package/lib/structures/rules/ITtscLintBoundariesRuleOptions.d.ts +80 -8
- package/lib/structures/rules/ITtscLintBoundariesRules.d.ts +6 -8
- package/lib/structures/rules/ITtscLintContributorRules.d.ts +18 -2
- package/lib/structures/rules/ITtscLintCoreRuleOptions.d.ts +347 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintCoreRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintCoreRules.d.ts +101 -67
- package/lib/structures/rules/ITtscLintRegexpRules.d.ts +7 -2
- package/lib/structures/rules/ITtscLintRuleOptionsMap.d.ts +53 -7
- package/lib/structures/rules/ITtscLintRules.d.ts +6 -2
- package/lib/structures/rules/ITtscLintSolidRules.d.ts +0 -8
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.d.ts +206 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js +8 -0
- package/lib/structures/rules/ITtscLintTypeScriptRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintTypeScriptRules.d.ts +55 -42
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.d.ts +276 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js +3 -0
- package/lib/structures/rules/ITtscLintUnicornRuleOptions.js.map +1 -0
- package/lib/structures/rules/ITtscLintUnicornRules.d.ts +44 -36
- package/lib/structures/rules/index.d.ts +3 -0
- package/lib/structures/rules/index.js +3 -0
- package/lib/structures/rules/index.js.map +1 -1
- package/linthost/ast_helpers.go +521 -23
- package/linthost/ast_selector_matcher.go +1628 -0
- package/linthost/ast_selector_parser.go +628 -0
- package/linthost/comment_scan.go +154 -0
- package/linthost/compile.go +129 -79
- package/linthost/config.go +352 -88
- package/linthost/config_format.go +15 -1
- package/linthost/contrib_adapter.go +47 -8
- package/linthost/declaration_rules.go +12 -11
- package/linthost/directives.go +9 -67
- package/linthost/dispatch.go +37 -3
- package/linthost/engine.go +368 -53
- package/linthost/escape_string.go +77 -0
- package/linthost/fix.go +133 -23
- package/linthost/flags_gen.go +18 -17
- package/linthost/format.go +73 -167
- package/linthost/format_editor_settings.go +134 -26
- package/linthost/grapheme_tables_gen.go +1920 -0
- package/linthost/host.go +160 -61
- package/linthost/literal_escape_scan.go +117 -0
- package/linthost/lsp.go +545 -98
- package/linthost/numeric_literal_precision.go +228 -0
- package/linthost/polyfill_browserslist.go +1439 -0
- package/linthost/polyfill_browserslist_config.go +603 -0
- package/linthost/polyfill_corejs.go +750 -0
- package/linthost/polyfill_data.go +113 -0
- package/linthost/polyfill_data_gen.json +39868 -0
- package/linthost/print_nodes_array.go +7 -1
- package/linthost/print_nodes_object.go +7 -1
- package/linthost/project_engine.go +151 -0
- package/linthost/project_rules.go +78 -0
- package/linthost/regex_clean.go +191 -0
- package/linthost/regex_tree.go +1544 -0
- package/linthost/regex_tree_optimizer.go +1685 -0
- package/linthost/regexp_cache.go +44 -0
- package/linthost/rule_codes.go +105 -0
- package/linthost/rule_codes.json +745 -0
- package/linthost/rules_ban_ts_comment.go +312 -0
- package/linthost/rules_boundaries.go +128 -43
- package/linthost/rules_boundaries_dependencies.go +1013 -0
- package/linthost/rules_core_extra.go +62 -92
- package/linthost/rules_cypress.go +1 -1
- package/linthost/rules_default_case.go +87 -11
- package/linthost/rules_dupes.go +38 -13
- package/linthost/rules_empty.go +188 -20
- package/linthost/rules_escape.go +62 -48
- package/linthost/rules_format_arrow_parens.go +19 -5
- package/linthost/rules_format_bracket_spacing.go +1 -1
- package/linthost/rules_format_clause_join.go +1 -1
- package/linthost/rules_format_declaration_header.go +31 -19
- package/linthost/rules_format_indent.go +1 -1
- package/linthost/rules_format_jsdoc.go +1 -1
- package/linthost/rules_format_orphan_semi.go +1 -1
- package/linthost/rules_format_parameter_properties.go +17 -7
- package/linthost/rules_format_print_width.go +1 -1
- package/linthost/rules_format_quote_props.go +1 -1
- package/linthost/rules_format_quotes.go +1 -1
- package/linthost/rules_format_semi.go +106 -79
- package/linthost/rules_format_sort_imports.go +79 -45
- package/linthost/rules_format_statement_split.go +1 -1
- package/linthost/rules_format_ternary_nullish_parens.go +1 -1
- package/linthost/rules_format_trailing_comma.go +64 -1
- package/linthost/rules_format_whitespace.go +1 -1
- package/linthost/rules_functional.go +21 -22
- package/linthost/rules_gap.go +80 -3
- package/linthost/rules_grouped_accessor_pairs.go +84 -25
- package/linthost/rules_guard_for_in.go +64 -147
- package/linthost/rules_jsx_a11y.go +232 -22
- package/linthost/rules_logic.go +84 -38
- package/linthost/rules_nextjs.go +105 -16
- package/linthost/rules_no_class_assign.go +70 -0
- package/linthost/rules_no_duplicate_imports.go +266 -0
- package/linthost/rules_no_else_return.go +156 -38
- package/linthost/rules_no_extend_native.go +113 -17
- package/linthost/rules_no_fallthrough.go +1013 -0
- package/linthost/rules_no_func_assign.go +70 -0
- package/linthost/rules_no_import_assign.go +278 -86
- package/linthost/rules_no_inner_declarations.go +186 -0
- package/linthost/rules_no_loop_func.go +438 -53
- package/linthost/rules_no_mixed_operators.go +208 -52
- package/linthost/rules_no_param_reassign.go +376 -61
- package/linthost/rules_no_promise_executor_return.go +156 -0
- package/linthost/rules_no_restricted_imports.go +1013 -57
- package/linthost/rules_no_restricted_syntax.go +190 -26
- package/linthost/rules_no_useless_constructor.go +6 -36
- package/linthost/rules_problems.go +154 -311
- package/linthost/rules_promise.go +2578 -109
- package/linthost/rules_react_extras.go +71 -45
- package/linthost/rules_react_hooks.go +18 -2
- package/linthost/rules_react_perf.go +1 -0
- package/linthost/rules_react_refresh.go +1 -1
- package/linthost/rules_regexp.go +230 -14
- package/linthost/rules_security.go +45 -19
- package/linthost/rules_self.go +21 -1
- package/linthost/rules_solid.go +13 -7
- package/linthost/rules_storybook.go +1 -1
- package/linthost/rules_suggestions.go +490 -84
- package/linthost/rules_testing_library.go +12 -1
- package/linthost/rules_ts.go +227 -54
- package/linthost/rules_ts_async.go +0 -131
- package/linthost/rules_ts_no_base_to_string.go +3 -4
- package/linthost/rules_ts_no_misused_promises.go +817 -0
- package/linthost/rules_ts_no_restricted_types.go +320 -34
- package/linthost/rules_ts_no_unnecessary_qualifier.go +132 -55
- package/linthost/rules_ts_no_unsafe_assignment.go +608 -60
- package/linthost/rules_ts_require_array_sort_compare.go +5 -5
- package/linthost/rules_ts_switch_exhaustiveness_check.go +556 -86
- package/linthost/rules_unicorn_better_regex.go +224 -16
- package/linthost/rules_unicorn_consistent_destructuring.go +1002 -7
- package/linthost/rules_unicorn_consistent_existence_index_check.go +208 -29
- package/linthost/rules_unicorn_consistent_function_scoping.go +1041 -8
- package/linthost/rules_unicorn_consistent_template_literal_escape.go +178 -7
- package/linthost/rules_unicorn_escape_case.go +30 -19
- package/linthost/rules_unicorn_filename_case.go +763 -16
- package/linthost/rules_unicorn_import_style.go +783 -17
- package/linthost/rules_unicorn_isolated_functions.go +934 -14
- package/linthost/rules_unicorn_no_hex_escape.go +26 -11
- package/linthost/rules_unicorn_no_typeof_undefined.go +234 -25
- package/linthost/rules_unicorn_no_unnecessary_polyfills.go +376 -21
- package/linthost/rules_unicorn_no_unused_properties.go +756 -19
- package/linthost/rules_unicorn_number_literal_case.go +58 -39
- package/linthost/rules_unicorn_prefer_at.go +7 -3
- package/linthost/rules_unicorn_prefer_number_properties.go +381 -48
- package/linthost/rules_unicorn_prefer_optional_catch_binding.go +24 -24
- package/linthost/rules_unicorn_prefer_simple_condition_first.go +597 -19
- package/linthost/rules_unicorn_prefer_string_raw.go +147 -16
- package/linthost/rules_unicorn_prevent_abbreviations.go +1963 -95
- package/linthost/rules_unicorn_string_content.go +611 -15
- package/linthost/rules_unicorn_switch_case_break_position.go +172 -15
- package/linthost/rules_unicorn_template_indent.go +923 -18
- package/linthost/rules_unicorn_text_encoding_identifier_case.go +202 -42
- package/linthost/rules_unicorn_throw_new_error.go +218 -26
- package/linthost/rules_var.go +829 -142
- package/linthost/string_length.go +222 -0
- package/package.json +4 -3
- package/rule/project.go +145 -0
- package/rule/rule.go +58 -1
- package/src/index.ts +58 -17
- package/src/structures/ITtscLintConfig.ts +3 -0
- package/src/structures/TtscLintRuleSetting.ts +3 -2
- package/src/structures/format/ITtscLintFormat.ts +3 -2
- package/src/structures/format/ITtscLintFormatSortImports.ts +21 -4
- package/src/structures/rules/ITtscLintBoundariesRuleOptions.ts +122 -8
- package/src/structures/rules/ITtscLintBoundariesRules.ts +6 -8
- package/src/structures/rules/ITtscLintContributorRules.ts +18 -2
- package/src/structures/rules/ITtscLintCoreRuleOptions.ts +465 -0
- package/src/structures/rules/ITtscLintCoreRules.ts +122 -67
- package/src/structures/rules/ITtscLintRegexpRules.ts +7 -2
- package/src/structures/rules/ITtscLintRuleOptionsMap.ts +88 -7
- package/src/structures/rules/ITtscLintRules.ts +7 -2
- package/src/structures/rules/ITtscLintSolidRules.ts +0 -9
- package/src/structures/rules/ITtscLintTypeScriptRuleOptions.ts +248 -0
- package/src/structures/rules/ITtscLintTypeScriptRules.ts +64 -42
- package/src/structures/rules/ITtscLintUnicornRuleOptions.ts +350 -0
- package/src/structures/rules/ITtscLintUnicornRules.ts +60 -36
- package/src/structures/rules/index.ts +3 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// noFuncAssign rejects writes to bindings introduced by function declarations
|
|
2
|
+
// and named function expressions. It follows checker symbols instead of names,
|
|
3
|
+
// so hoisted references resolve correctly while parameter, catch, block, and
|
|
4
|
+
// sibling-scope shadows remain independent.
|
|
5
|
+
// https://eslint.org/docs/latest/rules/no-func-assign
|
|
6
|
+
package linthost
|
|
7
|
+
|
|
8
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
|
+
|
|
10
|
+
type noFuncAssign struct{}
|
|
11
|
+
|
|
12
|
+
func (noFuncAssign) Name() string { return "no-func-assign" }
|
|
13
|
+
func (noFuncAssign) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
|
|
14
|
+
func (noFuncAssign) NeedsTypeChecker() bool { return true }
|
|
15
|
+
func (noFuncAssign) Check(ctx *Context, node *shimast.Node) {
|
|
16
|
+
if ctx == nil || ctx.Checker == nil || node == nil {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
functionSymbols := make(map[*shimast.Symbol]struct{})
|
|
21
|
+
walkDescendants(node, func(child *shimast.Node) {
|
|
22
|
+
name := noFuncAssignDeclarationName(child)
|
|
23
|
+
symbol := canonicalValueSymbol(ctx, name)
|
|
24
|
+
if symbol != nil {
|
|
25
|
+
functionSymbols[symbol] = struct{}{}
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
if len(functionSymbols) == 0 {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
reported := make(map[*shimast.Node]struct{})
|
|
33
|
+
walkDescendants(node, func(child *shimast.Node) {
|
|
34
|
+
for _, target := range writeTargetIdentifiers(child) {
|
|
35
|
+
if _, duplicate := reported[target]; duplicate {
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
symbol := canonicalValueSymbol(ctx, target)
|
|
39
|
+
if _, isFunction := functionSymbols[symbol]; !isFunction {
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
reported[target] = struct{}{}
|
|
43
|
+
ctx.Report(target, "'"+identifierText(target)+"' is a function.")
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// noFuncAssignDeclarationName returns the value-binding name introduced by a
|
|
49
|
+
// function declaration or named function expression. Anonymous expressions do
|
|
50
|
+
// not introduce a function-name binding.
|
|
51
|
+
func noFuncAssignDeclarationName(node *shimast.Node) *shimast.Node {
|
|
52
|
+
if node == nil {
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
switch node.Kind {
|
|
56
|
+
case shimast.KindFunctionDeclaration:
|
|
57
|
+
if declaration := node.AsFunctionDeclaration(); declaration != nil {
|
|
58
|
+
return declaration.Name()
|
|
59
|
+
}
|
|
60
|
+
case shimast.KindFunctionExpression:
|
|
61
|
+
if expression := node.AsFunctionExpression(); expression != nil {
|
|
62
|
+
return expression.Name()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func init() {
|
|
69
|
+
Register(noFuncAssign{})
|
|
70
|
+
}
|
|
@@ -1,138 +1,330 @@
|
|
|
1
|
-
// noImportAssign
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// module's view of its own exports.
|
|
1
|
+
// noImportAssign reports writes to bindings introduced by imports. Binding
|
|
2
|
+
// identity comes from TypeScript's checker, so a same-spelled declaration in a
|
|
3
|
+
// nested scope is independent from the import it shadows.
|
|
5
4
|
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// left-hand side is one of those names. Namespace imports are also
|
|
11
|
-
// flagged on `ns.foo = …` / `++ns.foo` because the namespace object
|
|
12
|
-
// is itself frozen — mutating any property throws in strict mode.
|
|
13
|
-
//
|
|
14
|
-
// Like `no-param-reassign`, this is an AST-only baseline: a later
|
|
15
|
-
// `const name = …` inside an inner scope that shadows an imported
|
|
16
|
-
// name will produce a false positive. Proper resolution needs the
|
|
17
|
-
// real binder, which the AST-only baseline avoids.
|
|
5
|
+
// Namespace imports additionally protect their direct members. This follows
|
|
6
|
+
// ESLint's contract for assignments, updates, deletes, loop targets, and the
|
|
7
|
+
// standard Object/Reflect mutation functions without treating deeper exported
|
|
8
|
+
// objects as namespace members.
|
|
18
9
|
// https://eslint.org/docs/latest/rules/no-import-assign
|
|
19
10
|
package linthost
|
|
20
11
|
|
|
21
|
-
import
|
|
12
|
+
import (
|
|
13
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
14
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
15
|
+
)
|
|
22
16
|
|
|
23
17
|
type noImportAssign struct{}
|
|
24
18
|
|
|
25
19
|
func (noImportAssign) Name() string { return "no-import-assign" }
|
|
26
20
|
func (noImportAssign) Visits() []shimast.Kind { return []shimast.Kind{shimast.KindSourceFile} }
|
|
21
|
+
func (noImportAssign) NeedsTypeChecker() bool { return true }
|
|
22
|
+
|
|
27
23
|
func (noImportAssign) Check(ctx *Context, node *shimast.Node) {
|
|
28
|
-
if node == nil {
|
|
24
|
+
if ctx == nil || ctx.Checker == nil || node == nil {
|
|
29
25
|
return
|
|
30
26
|
}
|
|
31
|
-
imported :=
|
|
32
|
-
namespaces := map[string]bool{}
|
|
33
|
-
collectImportedBindings(node, imported, namespaces)
|
|
27
|
+
imported, namespaces := collectNoImportAssignBindings(ctx, node)
|
|
34
28
|
if len(imported) == 0 {
|
|
35
29
|
return
|
|
36
30
|
}
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
|
|
32
|
+
walkDescendants(node, func(candidate *shimast.Node) {
|
|
33
|
+
switch candidate.Kind {
|
|
39
34
|
case shimast.KindBinaryExpression:
|
|
40
|
-
|
|
41
|
-
if
|
|
35
|
+
expression := candidate.AsBinaryExpression()
|
|
36
|
+
if expression == nil || expression.OperatorToken == nil ||
|
|
37
|
+
!isAssignmentOperator(expression.OperatorToken.Kind) ||
|
|
38
|
+
isDestructuringAssignmentTarget(candidate) {
|
|
42
39
|
return
|
|
43
40
|
}
|
|
44
|
-
|
|
41
|
+
reportNoImportAssignTarget(ctx, candidate, expression.Left, imported, namespaces, true)
|
|
42
|
+
|
|
45
43
|
case shimast.KindPrefixUnaryExpression:
|
|
46
|
-
|
|
47
|
-
if
|
|
44
|
+
expression := candidate.AsPrefixUnaryExpression()
|
|
45
|
+
if expression == nil ||
|
|
46
|
+
(expression.Operator != shimast.KindPlusPlusToken && expression.Operator != shimast.KindMinusMinusToken) {
|
|
48
47
|
return
|
|
49
48
|
}
|
|
50
|
-
|
|
49
|
+
reportNoImportAssignTarget(ctx, candidate, expression.Operand, imported, namespaces, true)
|
|
50
|
+
|
|
51
51
|
case shimast.KindPostfixUnaryExpression:
|
|
52
|
-
|
|
53
|
-
if
|
|
52
|
+
expression := candidate.AsPostfixUnaryExpression()
|
|
53
|
+
if expression == nil ||
|
|
54
|
+
(expression.Operator != shimast.KindPlusPlusToken && expression.Operator != shimast.KindMinusMinusToken) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
reportNoImportAssignTarget(ctx, candidate, expression.Operand, imported, namespaces, true)
|
|
58
|
+
|
|
59
|
+
case shimast.KindDeleteExpression:
|
|
60
|
+
expression := candidate.AsDeleteExpression()
|
|
61
|
+
if expression != nil {
|
|
62
|
+
// ESLint treats delete as a namespace-member mutation. Bare delete is
|
|
63
|
+
// covered by no-delete-var rather than as an import assignment.
|
|
64
|
+
reportNoImportAssignTarget(ctx, candidate, expression.Expression, imported, namespaces, false)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case shimast.KindForInStatement, shimast.KindForOfStatement:
|
|
68
|
+
statement := candidate.AsForInOrOfStatement()
|
|
69
|
+
if statement == nil || statement.Initializer == nil ||
|
|
70
|
+
statement.Initializer.Kind == shimast.KindVariableDeclarationList {
|
|
54
71
|
return
|
|
55
72
|
}
|
|
56
|
-
|
|
73
|
+
reportNoImportAssignTarget(ctx, candidate, statement.Initializer, imported, namespaces, true)
|
|
74
|
+
|
|
75
|
+
case shimast.KindCallExpression:
|
|
76
|
+
reportNoImportAssignMutationCall(ctx, candidate, namespaces)
|
|
57
77
|
}
|
|
58
78
|
})
|
|
59
79
|
}
|
|
60
80
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
clause := decl.ImportClause.AsImportClause()
|
|
75
|
-
if clause == nil {
|
|
81
|
+
// collectNoImportAssignBindings records the checker symbol owned by every
|
|
82
|
+
// default, named, namespace, type-only, and import-equals local name. References
|
|
83
|
+
// are compared against these symbols rather than their text.
|
|
84
|
+
func collectNoImportAssignBindings(
|
|
85
|
+
ctx *Context,
|
|
86
|
+
file *shimast.Node,
|
|
87
|
+
) (map[*shimast.Symbol]string, map[*shimast.Symbol]string) {
|
|
88
|
+
imported := map[*shimast.Symbol]string{}
|
|
89
|
+
namespaces := map[*shimast.Symbol]string{}
|
|
90
|
+
add := func(name *shimast.Node, namespace bool) {
|
|
91
|
+
symbol := noImportAssignValueSymbol(ctx, name)
|
|
92
|
+
localName := identifierText(name)
|
|
93
|
+
if symbol == nil || localName == "" {
|
|
76
94
|
return
|
|
77
95
|
}
|
|
78
|
-
|
|
79
|
-
if
|
|
80
|
-
|
|
96
|
+
imported[symbol] = localName
|
|
97
|
+
if namespace {
|
|
98
|
+
namespaces[symbol] = localName
|
|
81
99
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
switch
|
|
86
|
-
case shimast.
|
|
87
|
-
|
|
88
|
-
if
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
walkDescendants(file, func(node *shimast.Node) {
|
|
103
|
+
switch node.Kind {
|
|
104
|
+
case shimast.KindImportDeclaration:
|
|
105
|
+
declaration := node.AsImportDeclaration()
|
|
106
|
+
if declaration == nil || declaration.ImportClause == nil {
|
|
89
107
|
return
|
|
90
108
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
continue
|
|
95
|
-
}
|
|
96
|
-
if name := identifierText(spec.Name()); name != "" {
|
|
97
|
-
imported[name] = true
|
|
98
|
-
}
|
|
109
|
+
clause := declaration.ImportClause.AsImportClause()
|
|
110
|
+
if clause == nil {
|
|
111
|
+
return
|
|
99
112
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if ns == nil {
|
|
113
|
+
add(clause.Name(), false)
|
|
114
|
+
if clause.NamedBindings == nil {
|
|
103
115
|
return
|
|
104
116
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
switch clause.NamedBindings.Kind {
|
|
118
|
+
case shimast.KindNamedImports:
|
|
119
|
+
named := clause.NamedBindings.AsNamedImports()
|
|
120
|
+
if named == nil || named.Elements == nil {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
for _, element := range named.Elements.Nodes {
|
|
124
|
+
if specifier := element.AsImportSpecifier(); specifier != nil {
|
|
125
|
+
add(specifier.Name(), false)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
case shimast.KindNamespaceImport:
|
|
129
|
+
if namespace := clause.NamedBindings.AsNamespaceImport(); namespace != nil {
|
|
130
|
+
add(namespace.Name(), true)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
case shimast.KindImportEqualsDeclaration:
|
|
135
|
+
if declaration := node.AsImportEqualsDeclaration(); declaration != nil {
|
|
136
|
+
add(declaration.Name(), false)
|
|
108
137
|
}
|
|
109
138
|
}
|
|
110
139
|
})
|
|
140
|
+
return imported, namespaces
|
|
111
141
|
}
|
|
112
142
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
// `ns
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
143
|
+
// reportNoImportAssignTarget walks only the write positions of one assignment
|
|
144
|
+
// target. Object keys, computed keys, and default initializers are reads; member
|
|
145
|
+
// accesses are leaves so `ns.export.value = ...` remains allowed while a direct
|
|
146
|
+
// `ns.export = ...` write is reported.
|
|
147
|
+
func reportNoImportAssignTarget(
|
|
148
|
+
ctx *Context,
|
|
149
|
+
reportNode *shimast.Node,
|
|
150
|
+
target *shimast.Node,
|
|
151
|
+
imported map[*shimast.Symbol]string,
|
|
152
|
+
namespaces map[*shimast.Symbol]string,
|
|
153
|
+
allowBinding bool,
|
|
154
|
+
) {
|
|
155
|
+
target = unwrapReferenceExpression(target)
|
|
156
|
+
if target == nil {
|
|
119
157
|
return
|
|
120
158
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
159
|
+
switch target.Kind {
|
|
160
|
+
case shimast.KindIdentifier:
|
|
161
|
+
if !allowBinding {
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
if symbol := noImportAssignValueSymbol(ctx, target); symbol != nil {
|
|
165
|
+
if name, ok := imported[symbol]; ok {
|
|
166
|
+
ctx.Report(reportNode, "'"+name+"' is read-only.")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
case shimast.KindPropertyAccessExpression, shimast.KindElementAccessExpression:
|
|
171
|
+
if name := noImportAssignDirectNamespaceName(ctx, target, namespaces); name != "" {
|
|
172
|
+
ctx.Report(reportNode, "The members of '"+name+"' are read-only.")
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
case shimast.KindArrayLiteralExpression:
|
|
176
|
+
if array := target.AsArrayLiteralExpression(); array != nil && array.Elements != nil {
|
|
177
|
+
for _, element := range array.Elements.Nodes {
|
|
178
|
+
reportNoImportAssignTarget(ctx, reportNode, element, imported, namespaces, allowBinding)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
case shimast.KindObjectLiteralExpression:
|
|
183
|
+
if object := target.AsObjectLiteralExpression(); object != nil && object.Properties != nil {
|
|
184
|
+
for _, property := range object.Properties.Nodes {
|
|
185
|
+
reportNoImportAssignTarget(ctx, reportNode, property, imported, namespaces, allowBinding)
|
|
186
|
+
}
|
|
124
187
|
}
|
|
188
|
+
|
|
189
|
+
case shimast.KindSpreadElement:
|
|
190
|
+
if spread := target.AsSpreadElement(); spread != nil {
|
|
191
|
+
reportNoImportAssignTarget(ctx, reportNode, spread.Expression, imported, namespaces, allowBinding)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
case shimast.KindSpreadAssignment:
|
|
195
|
+
if spread := target.AsSpreadAssignment(); spread != nil {
|
|
196
|
+
reportNoImportAssignTarget(ctx, reportNode, spread.Expression, imported, namespaces, allowBinding)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case shimast.KindShorthandPropertyAssignment:
|
|
200
|
+
if property := target.AsShorthandPropertyAssignment(); property != nil {
|
|
201
|
+
reportNoImportAssignTarget(ctx, reportNode, property.Name(), imported, namespaces, allowBinding)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
case shimast.KindPropertyAssignment:
|
|
205
|
+
if property := target.AsPropertyAssignment(); property != nil {
|
|
206
|
+
reportNoImportAssignTarget(ctx, reportNode, property.Initializer, imported, namespaces, allowBinding)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
case shimast.KindBinaryExpression:
|
|
210
|
+
expression := target.AsBinaryExpression()
|
|
211
|
+
if expression != nil && expression.OperatorToken != nil &&
|
|
212
|
+
expression.OperatorToken.Kind == shimast.KindEqualsToken {
|
|
213
|
+
reportNoImportAssignTarget(ctx, reportNode, expression.Left, imported, namespaces, allowBinding)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
func reportNoImportAssignMutationCall(
|
|
219
|
+
ctx *Context,
|
|
220
|
+
node *shimast.Node,
|
|
221
|
+
namespaces map[*shimast.Symbol]string,
|
|
222
|
+
) {
|
|
223
|
+
call := node.AsCallExpression()
|
|
224
|
+
if call == nil || call.Arguments == nil || len(call.Arguments.Nodes) == 0 ||
|
|
225
|
+
!isNoImportAssignMutationFunction(ctx, call.Expression) {
|
|
125
226
|
return
|
|
126
227
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
228
|
+
target := unwrapReferenceExpression(call.Arguments.Nodes[0])
|
|
229
|
+
if target == nil || target.Kind != shimast.KindIdentifier {
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
if symbol := noImportAssignValueSymbol(ctx, target); symbol != nil {
|
|
233
|
+
if name, ok := namespaces[symbol]; ok {
|
|
234
|
+
ctx.Report(node, "The members of '"+name+"' are read-only.")
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
func isNoImportAssignMutationFunction(ctx *Context, callee *shimast.Node) bool {
|
|
240
|
+
callee = unwrapReferenceExpression(callee)
|
|
241
|
+
if callee == nil {
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
member, ok := referenceMemberParts(callee)
|
|
245
|
+
if !ok || member.staticKey == nil {
|
|
246
|
+
return false
|
|
247
|
+
}
|
|
248
|
+
object := unwrapReferenceExpression(member.object)
|
|
249
|
+
if object == nil || object.Kind != shimast.KindIdentifier {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
objectName := identifierText(object)
|
|
253
|
+
allowed := false
|
|
254
|
+
switch objectName {
|
|
255
|
+
case "Object":
|
|
256
|
+
switch *member.staticKey {
|
|
257
|
+
case "assign", "defineProperty", "defineProperties", "freeze", "setPrototypeOf":
|
|
258
|
+
allowed = true
|
|
131
259
|
}
|
|
132
|
-
|
|
133
|
-
|
|
260
|
+
case "Reflect":
|
|
261
|
+
switch *member.staticKey {
|
|
262
|
+
case "defineProperty", "deleteProperty", "set", "setPrototypeOf":
|
|
263
|
+
allowed = true
|
|
134
264
|
}
|
|
135
265
|
}
|
|
266
|
+
if !allowed {
|
|
267
|
+
return false
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
resolved := noImportAssignValueSymbol(ctx, object)
|
|
271
|
+
global := ctx.Checker.GetGlobalSymbol(objectName, shimast.SymbolFlagsValue, nil)
|
|
272
|
+
if global != nil {
|
|
273
|
+
global = ctx.Checker.GetMergedSymbol(global)
|
|
274
|
+
}
|
|
275
|
+
return resolved != nil && resolved == global
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
func noImportAssignDirectNamespaceName(
|
|
279
|
+
ctx *Context,
|
|
280
|
+
memberNode *shimast.Node,
|
|
281
|
+
namespaces map[*shimast.Symbol]string,
|
|
282
|
+
) string {
|
|
283
|
+
member, ok := referenceMemberParts(memberNode)
|
|
284
|
+
if !ok {
|
|
285
|
+
return ""
|
|
286
|
+
}
|
|
287
|
+
receiver := unwrapReferenceExpression(member.object)
|
|
288
|
+
if receiver == nil || receiver.Kind != shimast.KindIdentifier {
|
|
289
|
+
return ""
|
|
290
|
+
}
|
|
291
|
+
return namespaces[noImportAssignValueSymbol(ctx, receiver)]
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// noImportAssignValueSymbol resolves shorthand destructuring targets through
|
|
295
|
+
// their value slot, then normalizes merged declarations to a stable map key.
|
|
296
|
+
// A type-only import used illegally as a value has no value-position symbol;
|
|
297
|
+
// in that case the checker's all-meanings entity lookup still returns the
|
|
298
|
+
// lexical import alias without falling back to textual name matching.
|
|
299
|
+
func noImportAssignValueSymbol(ctx *Context, identifier *shimast.Node) *shimast.Symbol {
|
|
300
|
+
if ctx == nil || ctx.Checker == nil || identifier == nil {
|
|
301
|
+
return nil
|
|
302
|
+
}
|
|
303
|
+
var symbol *shimast.Symbol
|
|
304
|
+
shorthandTarget := false
|
|
305
|
+
if parent := identifier.Parent; parent != nil && parent.Kind == shimast.KindShorthandPropertyAssignment {
|
|
306
|
+
if shorthand := parent.AsShorthandPropertyAssignment(); shorthand != nil && shorthand.Name() == identifier {
|
|
307
|
+
shorthandTarget = true
|
|
308
|
+
symbol = ctx.Checker.GetShorthandAssignmentValueSymbol(parent)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if symbol == nil && !shorthandTarget {
|
|
312
|
+
symbol = ctx.Checker.GetSymbolAtLocation(identifier)
|
|
313
|
+
}
|
|
314
|
+
if symbol == nil {
|
|
315
|
+
symbol = shimchecker.Checker_resolveEntityName(
|
|
316
|
+
ctx.Checker,
|
|
317
|
+
identifier,
|
|
318
|
+
shimast.SymbolFlagsValue|shimast.SymbolFlagsType|shimast.SymbolFlagsNamespace,
|
|
319
|
+
true,
|
|
320
|
+
true,
|
|
321
|
+
nil,
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
if symbol == nil {
|
|
325
|
+
return nil
|
|
326
|
+
}
|
|
327
|
+
return ctx.Checker.GetMergedSymbol(symbol)
|
|
136
328
|
}
|
|
137
329
|
|
|
138
330
|
func init() {
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
package linthost
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
|
|
7
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
8
|
+
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// noInnerDeclarations enforces the root-declaration positions and option
|
|
12
|
+
// defaults of ESLint's core no-inner-declarations rule. TypeScript-Go parses
|
|
13
|
+
// JavaScript and TypeScript with the current ECMAScript grammar, so strict
|
|
14
|
+
// functions in ordinary parsed source have ES2015 block-function semantics.
|
|
15
|
+
// https://eslint.org/docs/latest/rules/no-inner-declarations
|
|
16
|
+
type noInnerDeclarations struct{ optionsRule }
|
|
17
|
+
|
|
18
|
+
type noInnerDeclarationsOptions struct {
|
|
19
|
+
both bool
|
|
20
|
+
allowBlockScopedFunc bool
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type noInnerDeclarationsBlockOptions struct {
|
|
24
|
+
BlockScopedFunctions string `json:"blockScopedFunctions"`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
func (noInnerDeclarations) Name() string { return "no-inner-declarations" }
|
|
28
|
+
|
|
29
|
+
func (noInnerDeclarations) Visits() []shimast.Kind {
|
|
30
|
+
return []shimast.Kind{
|
|
31
|
+
shimast.KindFunctionDeclaration,
|
|
32
|
+
shimast.KindVariableDeclarationList,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func (noInnerDeclarations) Check(ctx *Context, node *shimast.Node) {
|
|
37
|
+
options := resolveNoInnerDeclarationsOptions(ctx)
|
|
38
|
+
declaration := node
|
|
39
|
+
declarationType := "function"
|
|
40
|
+
|
|
41
|
+
if node.Kind == shimast.KindVariableDeclarationList {
|
|
42
|
+
if !options.both || !shimast.IsVar(node) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
declarationType = "variable"
|
|
46
|
+
// A statement-level list is wrapped by VariableStatement in the tsgo AST.
|
|
47
|
+
// Use that wrapper for root classification and for the diagnostic range;
|
|
48
|
+
// loop-header lists have no wrapper and remain nested declarations.
|
|
49
|
+
if node.Parent != nil && node.Parent.Kind == shimast.KindVariableStatement {
|
|
50
|
+
declaration = node.Parent
|
|
51
|
+
}
|
|
52
|
+
} else if options.allowBlockScopedFunc &&
|
|
53
|
+
noInnerDeclarationsSupportsBlockFunctions(ctx.File) &&
|
|
54
|
+
noInnerDeclarationsIsStrict(ctx.File, node) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if noInnerDeclarationsIsRoot(declaration) {
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
ctx.Report(
|
|
62
|
+
declaration,
|
|
63
|
+
"Move "+declarationType+" declaration to "+noInnerDeclarationsAllowedBody(declaration)+" root.",
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// resolveNoInnerDeclarationsOptions reads the canonical ESLint positional
|
|
68
|
+
// options. The config transport preserves one positional value directly and
|
|
69
|
+
// two or more values as an array, so both [severity, mode] and
|
|
70
|
+
// [severity, mode, object] arrive without rule-specific parser behavior.
|
|
71
|
+
func resolveNoInnerDeclarationsOptions(ctx *Context) noInnerDeclarationsOptions {
|
|
72
|
+
resolved := noInnerDeclarationsOptions{allowBlockScopedFunc: true}
|
|
73
|
+
if ctx == nil || len(ctx.Options) == 0 {
|
|
74
|
+
return resolved
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
raw := bytes.TrimSpace(ctx.Options)
|
|
78
|
+
if len(raw) == 0 {
|
|
79
|
+
return resolved
|
|
80
|
+
}
|
|
81
|
+
var slots []json.RawMessage
|
|
82
|
+
if raw[0] == '[' {
|
|
83
|
+
// Decode into a zero-value slice. Seeding it with raw would let the first
|
|
84
|
+
// RawMessage reuse and overwrite Context.Options' backing bytes, changing
|
|
85
|
+
// the option seen by every later declaration in the same file.
|
|
86
|
+
if err := json.Unmarshal(raw, &slots); err != nil {
|
|
87
|
+
return resolved
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
slots = []json.RawMessage{raw}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if len(slots) > 0 {
|
|
94
|
+
var mode string
|
|
95
|
+
if json.Unmarshal(slots[0], &mode) == nil && mode == "both" {
|
|
96
|
+
resolved.both = true
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if len(slots) > 1 {
|
|
100
|
+
var options noInnerDeclarationsBlockOptions
|
|
101
|
+
if json.Unmarshal(slots[1], &options) == nil && options.BlockScopedFunctions == "disallow" {
|
|
102
|
+
resolved.allowBlockScopedFunc = false
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return resolved
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
func noInnerDeclarationsSupportsBlockFunctions(file *shimast.SourceFile) bool {
|
|
109
|
+
return file != nil && file.ScriptKind != shimcore.ScriptKindJSON
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// noInnerDeclarationsIsStrict derives strictness from parser-owned AST facts:
|
|
113
|
+
// external-module identity, class ancestry, and real directive prologues. It
|
|
114
|
+
// deliberately does not infer semantics from filenames or substring searches.
|
|
115
|
+
func noInnerDeclarationsIsStrict(file *shimast.SourceFile, node *shimast.Node) bool {
|
|
116
|
+
if file != nil && file.ExternalModuleIndicator != nil {
|
|
117
|
+
return true
|
|
118
|
+
}
|
|
119
|
+
for ancestor := node.Parent; ancestor != nil; ancestor = ancestor.Parent {
|
|
120
|
+
switch ancestor.Kind {
|
|
121
|
+
case shimast.KindClassDeclaration, shimast.KindClassExpression:
|
|
122
|
+
return true
|
|
123
|
+
case shimast.KindSourceFile, shimast.KindModuleBlock:
|
|
124
|
+
if noInnerDeclarationsHasUseStrictDirective(file, ancestor) {
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
case shimast.KindBlock:
|
|
128
|
+
if isFunctionLikeKind(ancestor.Parent) && noInnerDeclarationsHasUseStrictDirective(file, ancestor) {
|
|
129
|
+
return true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
func noInnerDeclarationsHasUseStrictDirective(file *shimast.SourceFile, container *shimast.Node) bool {
|
|
137
|
+
for _, statement := range parentStatements(container) {
|
|
138
|
+
if statement == nil || statement.Kind != shimast.KindExpressionStatement {
|
|
139
|
+
return false
|
|
140
|
+
}
|
|
141
|
+
expressionStatement := statement.AsExpressionStatement()
|
|
142
|
+
if expressionStatement == nil || expressionStatement.Expression == nil ||
|
|
143
|
+
expressionStatement.Expression.Kind != shimast.KindStringLiteral {
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
// ES5 forbids escapes in a Use Strict Directive. Match the parser/binder
|
|
147
|
+
// contract against the parser-classified literal token, not its cooked
|
|
148
|
+
// value (`"use\x20strict"` must stay sloppy).
|
|
149
|
+
raw := nodeText(file, expressionStatement.Expression)
|
|
150
|
+
if raw == `"use strict"` || raw == `'use strict'` {
|
|
151
|
+
return true
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
func noInnerDeclarationsIsRoot(declaration *shimast.Node) bool {
|
|
158
|
+
if declaration == nil || declaration.Parent == nil {
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
parent := declaration.Parent
|
|
162
|
+
switch parent.Kind {
|
|
163
|
+
case shimast.KindSourceFile, shimast.KindModuleBlock, shimast.KindClassStaticBlockDeclaration:
|
|
164
|
+
return true
|
|
165
|
+
case shimast.KindBlock:
|
|
166
|
+
return isFunctionLikeKind(parent.Parent) ||
|
|
167
|
+
(parent.Parent != nil && parent.Parent.Kind == shimast.KindClassStaticBlockDeclaration)
|
|
168
|
+
}
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
func noInnerDeclarationsAllowedBody(declaration *shimast.Node) string {
|
|
173
|
+
for ancestor := declaration.Parent; ancestor != nil; ancestor = ancestor.Parent {
|
|
174
|
+
if ancestor.Kind == shimast.KindClassStaticBlockDeclaration {
|
|
175
|
+
return "class static block body"
|
|
176
|
+
}
|
|
177
|
+
if isFunctionLikeKind(ancestor) {
|
|
178
|
+
return "function body"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return "program"
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
func init() {
|
|
185
|
+
Register(noInnerDeclarations{})
|
|
186
|
+
}
|