@ttsc/lint 0.18.3 → 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 +129 -25
- 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
|
@@ -1,127 +1,1995 @@
|
|
|
1
|
-
// unicorn/prevent-abbreviations
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// MVP carries the most universal abbreviations only and fires on any
|
|
6
|
-
// identifier whose lower-cased text matches one of them, in both
|
|
7
|
-
// reference and declaration positions, so a `param` parameter and a
|
|
8
|
-
// `void param` read are both reported.
|
|
1
|
+
// unicorn/prevent-abbreviations keeps the final pre-rename contract from
|
|
2
|
+
// eslint-plugin-unicorn. Upstream renamed the rule to `name-replacements` in
|
|
3
|
+
// June 2026; ttsc retains the established rule ID so existing configurations
|
|
4
|
+
// remain source-compatible while using the complete replacement semantics.
|
|
9
5
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
6
|
+
// Variable diagnostics are binding-based. The TypeScript checker connects one
|
|
7
|
+
// declaration to all of its references, including shadowed names, shorthand
|
|
8
|
+
// syntax, JSX, and TypeScript type predicates. A binding is reported once at
|
|
9
|
+
// its declaration. A single unambiguous replacement is autofixed only when the
|
|
10
|
+
// whole rename is safe; ambiguous replacements are exposed as suggestions.
|
|
14
11
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// without touching memory (the longest dictionary entry is six
|
|
19
|
-
// characters); an additional `isAllLowerASCII` check skips
|
|
20
|
-
// `strings.ToLower`'s allocation for the common case of already-
|
|
21
|
-
// lower-cased identifiers.
|
|
22
|
-
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prevent-abbreviations.md
|
|
12
|
+
// The canonical replacement table and defaults are pinned to the last
|
|
13
|
+
// `prevent-abbreviations` implementation before the upstream rename:
|
|
14
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/42abe74908e8/rules/prevent-abbreviations.js
|
|
23
15
|
package linthost
|
|
24
16
|
|
|
25
17
|
import (
|
|
18
|
+
"bytes"
|
|
19
|
+
"encoding/json"
|
|
20
|
+
"errors"
|
|
21
|
+
"fmt"
|
|
22
|
+
"io"
|
|
23
|
+
"math"
|
|
24
|
+
"path/filepath"
|
|
25
|
+
"regexp"
|
|
26
|
+
"sort"
|
|
26
27
|
"strings"
|
|
28
|
+
"unicode"
|
|
29
|
+
"unicode/utf8"
|
|
27
30
|
|
|
28
31
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
32
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
33
|
+
"golang.org/x/text/cases"
|
|
34
|
+
"golang.org/x/text/language"
|
|
29
35
|
)
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
37
|
+
type unicornPreventAbbreviations struct{ optionsRule }
|
|
38
|
+
|
|
39
|
+
type unicornPreventAbbreviationsImportMode uint8
|
|
40
|
+
|
|
41
|
+
const (
|
|
42
|
+
unicornPreventAbbreviationsImportOff unicornPreventAbbreviationsImportMode = iota
|
|
43
|
+
unicornPreventAbbreviationsImportInternal
|
|
44
|
+
unicornPreventAbbreviationsImportAll
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
type unicornPreventAbbreviationsOptions struct {
|
|
48
|
+
checkProperties bool
|
|
49
|
+
checkVariables bool
|
|
50
|
+
checkDefaultAndNamespaceImports unicornPreventAbbreviationsImportMode
|
|
51
|
+
checkShorthandImports unicornPreventAbbreviationsImportMode
|
|
52
|
+
checkShorthandProperties bool
|
|
53
|
+
checkFilenames bool
|
|
54
|
+
replacements map[string][]string
|
|
55
|
+
allowList map[string]bool
|
|
56
|
+
ignore []*regexp.Regexp
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type unicornPreventAbbreviationsRawOptions struct {
|
|
60
|
+
CheckProperties *bool `json:"checkProperties"`
|
|
61
|
+
CheckVariables *bool `json:"checkVariables"`
|
|
62
|
+
CheckDefaultAndNamespaceImports json.RawMessage `json:"checkDefaultAndNamespaceImports"`
|
|
63
|
+
CheckShorthandImports json.RawMessage `json:"checkShorthandImports"`
|
|
64
|
+
CheckShorthandProperties *bool `json:"checkShorthandProperties"`
|
|
65
|
+
CheckFilenames *bool `json:"checkFilenames"`
|
|
66
|
+
ExtendDefaultReplacements *bool `json:"extendDefaultReplacements"`
|
|
67
|
+
Replacements json.RawMessage `json:"replacements"`
|
|
68
|
+
ExtendDefaultAllowList *bool `json:"extendDefaultAllowList"`
|
|
69
|
+
AllowList json.RawMessage `json:"allowList"`
|
|
70
|
+
Ignore json.RawMessage `json:"ignore"`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type unicornPreventAbbreviationsReplacementPatch struct {
|
|
74
|
+
disabled bool
|
|
75
|
+
values map[string]bool
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type unicornPreventAbbreviationsNameReplacements struct {
|
|
79
|
+
total int
|
|
80
|
+
samples []string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
type unicornPreventAbbreviationsBinding struct {
|
|
84
|
+
declaration *shimast.Node
|
|
85
|
+
nameNode *shimast.Node
|
|
86
|
+
name string
|
|
87
|
+
symbol *shimast.Symbol
|
|
88
|
+
scope *shimast.Node
|
|
89
|
+
references []*shimast.Node
|
|
90
|
+
seen map[*shimast.Node]struct{}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type unicornPreventAbbreviationsOccupiedName struct {
|
|
94
|
+
scope *shimast.Node
|
|
95
|
+
reference bool
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// This is the complete canonical table, not a project-specific subset.
|
|
99
|
+
// Replacement order is normalized before diagnostics and suggestions.
|
|
100
|
+
var unicornPreventAbbreviationsDefaultReplacements = map[string][]string{
|
|
101
|
+
"acc": {"accumulator"},
|
|
102
|
+
"arg": {"argument"},
|
|
103
|
+
"args": {"arguments"},
|
|
104
|
+
"arr": {"array"},
|
|
105
|
+
"attr": {"attribute"},
|
|
106
|
+
"attrs": {"attributes"},
|
|
107
|
+
"btn": {"button"},
|
|
108
|
+
"cb": {"callback"},
|
|
109
|
+
"conf": {"config"},
|
|
110
|
+
"ctx": {"context"},
|
|
111
|
+
"cur": {"current"},
|
|
112
|
+
"curr": {"current"},
|
|
113
|
+
"db": {"database"},
|
|
114
|
+
"def": {"defer", "deferred", "define", "definition"},
|
|
115
|
+
"dest": {"destination"},
|
|
116
|
+
"dev": {"development"},
|
|
117
|
+
"dir": {"direction", "directory"},
|
|
118
|
+
"dirs": {"directories"},
|
|
119
|
+
"dist": {"distribution"},
|
|
120
|
+
"doc": {"document"},
|
|
121
|
+
"docs": {"documentation", "documents"},
|
|
122
|
+
"dst": {"daylightSavingTime", "destination", "distribution"},
|
|
123
|
+
"e": {"error", "event"},
|
|
124
|
+
"el": {"element"},
|
|
125
|
+
"elem": {"element"},
|
|
126
|
+
"elems": {"elements"},
|
|
127
|
+
"env": {"environment"},
|
|
128
|
+
"envs": {"environments"},
|
|
129
|
+
"err": {"error"},
|
|
130
|
+
"ev": {"event"},
|
|
131
|
+
"evt": {"event"},
|
|
132
|
+
"ext": {"extension"},
|
|
133
|
+
"exts": {"extensions"},
|
|
134
|
+
"fn": {"function"},
|
|
135
|
+
"func": {"function"},
|
|
136
|
+
"i": {"index"},
|
|
137
|
+
"idx": {"index"},
|
|
138
|
+
"j": {"index"},
|
|
139
|
+
"len": {"length"},
|
|
140
|
+
"lib": {"library"},
|
|
141
|
+
"mod": {"module"},
|
|
142
|
+
"msg": {"message"},
|
|
143
|
+
"num": {"number"},
|
|
144
|
+
"obj": {"object"},
|
|
145
|
+
"opts": {"options"},
|
|
146
|
+
"param": {"parameter"},
|
|
147
|
+
"params": {"parameters"},
|
|
148
|
+
"pkg": {"package"},
|
|
149
|
+
"prev": {"previous"},
|
|
150
|
+
"prod": {"production"},
|
|
151
|
+
"prop": {"property"},
|
|
152
|
+
"props": {"properties"},
|
|
153
|
+
"ref": {"reference"},
|
|
154
|
+
"refs": {"references"},
|
|
155
|
+
"rel": {"related", "relationship", "relative"},
|
|
156
|
+
"req": {"request"},
|
|
157
|
+
"res": {"resource", "response", "result"},
|
|
158
|
+
"ret": {"returnValue"},
|
|
159
|
+
"retval": {"returnValue"},
|
|
160
|
+
"sep": {"separator"},
|
|
161
|
+
"src": {"source"},
|
|
162
|
+
"stdDev": {"standardDeviation"},
|
|
163
|
+
"str": {"string"},
|
|
164
|
+
"tbl": {"table"},
|
|
165
|
+
"temp": {"temporary"},
|
|
166
|
+
"tit": {"title"},
|
|
167
|
+
"tmp": {"temporary"},
|
|
168
|
+
"util": {"utility"},
|
|
169
|
+
"utils": {"utilities"},
|
|
170
|
+
"val": {"value"},
|
|
171
|
+
"var": {"variable"},
|
|
172
|
+
"vars": {"variables"},
|
|
173
|
+
"ver": {"version"},
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
var unicornPreventAbbreviationsDefaultAllowList = map[string]bool{
|
|
177
|
+
"defaultProps": true,
|
|
178
|
+
"devDependencies": true,
|
|
179
|
+
"EmberENV": true,
|
|
180
|
+
"getDerivedStateFromProps": true,
|
|
181
|
+
"getInitialProps": true,
|
|
182
|
+
"getServerSideProps": true,
|
|
183
|
+
"getStaticProps": true,
|
|
184
|
+
"iOS": true,
|
|
185
|
+
"propTypes": true,
|
|
186
|
+
"setupFilesAfterEnv": true,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var unicornPreventAbbreviationsDefaultIgnore = []string{
|
|
190
|
+
"i18n",
|
|
191
|
+
"l10n",
|
|
192
|
+
"a11y",
|
|
193
|
+
"e2e",
|
|
194
|
+
"jQuery",
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// A source-only rename must remain a valid binding in modules and strict-mode
|
|
198
|
+
// code. TypeScript's contextual keywords are deliberately absent: names such
|
|
199
|
+
// as `type`, `module`, and `constructor` remain legal lexical bindings.
|
|
200
|
+
var unicornPreventAbbreviationsReservedWords = map[string]struct{}{
|
|
201
|
+
"arguments": {}, "await": {},
|
|
202
|
+
"break": {}, "case": {}, "catch": {}, "class": {}, "const": {},
|
|
203
|
+
"continue": {}, "debugger": {}, "default": {}, "delete": {},
|
|
204
|
+
"do": {}, "else": {}, "enum": {}, "export": {}, "extends": {},
|
|
205
|
+
"eval": {}, "false": {}, "finally": {}, "for": {}, "function": {},
|
|
206
|
+
"if": {}, "implements": {}, "import": {}, "in": {}, "instanceof": {},
|
|
207
|
+
"interface": {}, "let": {}, "new": {}, "null": {}, "package": {},
|
|
208
|
+
"private": {}, "protected": {}, "public": {}, "return": {}, "static": {},
|
|
209
|
+
"super": {}, "switch": {}, "this": {}, "throw": {}, "true": {},
|
|
210
|
+
"try": {}, "typeof": {}, "var": {}, "void": {}, "while": {},
|
|
211
|
+
"with": {}, "yield": {},
|
|
212
|
+
}
|
|
86
213
|
|
|
87
214
|
func (unicornPreventAbbreviations) Name() string { return "unicorn/prevent-abbreviations" }
|
|
215
|
+
func (unicornPreventAbbreviations) NeedsTypeChecker() bool {
|
|
216
|
+
return true
|
|
217
|
+
}
|
|
88
218
|
func (unicornPreventAbbreviations) Visits() []shimast.Kind {
|
|
89
|
-
return []shimast.Kind{shimast.
|
|
219
|
+
return []shimast.Kind{shimast.KindSourceFile}
|
|
220
|
+
}
|
|
221
|
+
func (unicornPreventAbbreviations) ValidateOptions(raw json.RawMessage) error {
|
|
222
|
+
_, err := decodeUnicornPreventAbbreviationsOptions(raw)
|
|
223
|
+
return err
|
|
90
224
|
}
|
|
225
|
+
|
|
91
226
|
func (unicornPreventAbbreviations) Check(ctx *Context, node *shimast.Node) {
|
|
227
|
+
if ctx == nil || ctx.File == nil || ctx.Checker == nil || node == nil {
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
options, err := decodeUnicornPreventAbbreviationsOptions(ctx.Options)
|
|
231
|
+
if err != nil {
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
bindings, bySymbol, byDeclaration, occupied := collectUnicornPreventAbbreviationsBindings(ctx, node)
|
|
236
|
+
collectUnicornPreventAbbreviationsReferences(ctx, node, bySymbol, byDeclaration)
|
|
237
|
+
|
|
238
|
+
generated := make(map[string][]*unicornPreventAbbreviationsBinding)
|
|
239
|
+
if options.checkVariables {
|
|
240
|
+
comments := collectUnicornPreventAbbreviationsComments(ctx.File)
|
|
241
|
+
for _, binding := range bindings {
|
|
242
|
+
reportUnicornPreventAbbreviationsBinding(ctx, binding, options, occupied, generated, comments)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if options.checkProperties {
|
|
246
|
+
walkDescendants(node, func(candidate *shimast.Node) {
|
|
247
|
+
reportUnicornPreventAbbreviationsProperty(ctx, candidate, options)
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
if options.checkFilenames {
|
|
251
|
+
reportUnicornPreventAbbreviationsFilename(ctx, node, options)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
func decodeUnicornPreventAbbreviationsOptions(raw json.RawMessage) (unicornPreventAbbreviationsOptions, error) {
|
|
256
|
+
options := unicornPreventAbbreviationsOptions{
|
|
257
|
+
checkVariables: true,
|
|
258
|
+
checkDefaultAndNamespaceImports: unicornPreventAbbreviationsImportInternal,
|
|
259
|
+
checkShorthandImports: unicornPreventAbbreviationsImportInternal,
|
|
260
|
+
checkFilenames: true,
|
|
261
|
+
replacements: cloneUnicornPreventAbbreviationsReplacements(unicornPreventAbbreviationsDefaultReplacements),
|
|
262
|
+
allowList: cloneUnicornPreventAbbreviationsAllowList(unicornPreventAbbreviationsDefaultAllowList),
|
|
263
|
+
}
|
|
264
|
+
for _, pattern := range unicornPreventAbbreviationsDefaultIgnore {
|
|
265
|
+
options.ignore = append(options.ignore, regexp.MustCompile(pattern))
|
|
266
|
+
}
|
|
267
|
+
trimmed := bytes.TrimSpace(raw)
|
|
268
|
+
if len(trimmed) == 0 {
|
|
269
|
+
return options, nil
|
|
270
|
+
}
|
|
271
|
+
if trimmed[0] != '{' {
|
|
272
|
+
return options, errors.New("options must be an object")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
var rawFields map[string]json.RawMessage
|
|
276
|
+
if err := json.Unmarshal(trimmed, &rawFields); err != nil {
|
|
277
|
+
return options, fmt.Errorf("options must be an object: %w", err)
|
|
278
|
+
}
|
|
279
|
+
// The value marks boolean fields, whose pointer-backed decoding would
|
|
280
|
+
// otherwise make an explicit null indistinguishable from omission.
|
|
281
|
+
knownFields := map[string]bool{
|
|
282
|
+
"checkProperties": true,
|
|
283
|
+
"checkVariables": true,
|
|
284
|
+
"checkDefaultAndNamespaceImports": false,
|
|
285
|
+
"checkShorthandImports": false,
|
|
286
|
+
"checkShorthandProperties": true,
|
|
287
|
+
"checkFilenames": true,
|
|
288
|
+
"extendDefaultReplacements": true,
|
|
289
|
+
"replacements": false,
|
|
290
|
+
"extendDefaultAllowList": true,
|
|
291
|
+
"allowList": false,
|
|
292
|
+
"ignore": false,
|
|
293
|
+
}
|
|
294
|
+
for name, value := range rawFields {
|
|
295
|
+
boolean, known := knownFields[name]
|
|
296
|
+
if !known {
|
|
297
|
+
return options, fmt.Errorf("unknown option %q", name)
|
|
298
|
+
}
|
|
299
|
+
if boolean && bytes.Equal(bytes.TrimSpace(value), []byte("null")) {
|
|
300
|
+
return options, fmt.Errorf("option %q must be a boolean", name)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
decoder := json.NewDecoder(bytes.NewReader(trimmed))
|
|
305
|
+
decoder.DisallowUnknownFields()
|
|
306
|
+
var configured unicornPreventAbbreviationsRawOptions
|
|
307
|
+
if err := decoder.Decode(&configured); err != nil {
|
|
308
|
+
return options, fmt.Errorf("options must be an object: %w", err)
|
|
309
|
+
}
|
|
310
|
+
if err := requireUnicornPreventAbbreviationsEOF(decoder); err != nil {
|
|
311
|
+
return options, err
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if configured.CheckProperties != nil {
|
|
315
|
+
options.checkProperties = *configured.CheckProperties
|
|
316
|
+
}
|
|
317
|
+
if configured.CheckVariables != nil {
|
|
318
|
+
options.checkVariables = *configured.CheckVariables
|
|
319
|
+
}
|
|
320
|
+
if configured.CheckShorthandProperties != nil {
|
|
321
|
+
options.checkShorthandProperties = *configured.CheckShorthandProperties
|
|
322
|
+
}
|
|
323
|
+
if configured.CheckFilenames != nil {
|
|
324
|
+
options.checkFilenames = *configured.CheckFilenames
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
var err error
|
|
328
|
+
options.checkDefaultAndNamespaceImports, err = decodeUnicornPreventAbbreviationsImportMode(
|
|
329
|
+
configured.CheckDefaultAndNamespaceImports,
|
|
330
|
+
"checkDefaultAndNamespaceImports",
|
|
331
|
+
unicornPreventAbbreviationsImportInternal,
|
|
332
|
+
)
|
|
333
|
+
if err != nil {
|
|
334
|
+
return options, err
|
|
335
|
+
}
|
|
336
|
+
options.checkShorthandImports, err = decodeUnicornPreventAbbreviationsImportMode(
|
|
337
|
+
configured.CheckShorthandImports,
|
|
338
|
+
"checkShorthandImports",
|
|
339
|
+
unicornPreventAbbreviationsImportInternal,
|
|
340
|
+
)
|
|
341
|
+
if err != nil {
|
|
342
|
+
return options, err
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
extendReplacements := configured.ExtendDefaultReplacements == nil || *configured.ExtendDefaultReplacements
|
|
346
|
+
if !extendReplacements {
|
|
347
|
+
options.replacements = make(map[string][]string)
|
|
348
|
+
}
|
|
349
|
+
patches, err := decodeUnicornPreventAbbreviationsReplacementPatches(configured.Replacements)
|
|
350
|
+
if err != nil {
|
|
351
|
+
return options, err
|
|
352
|
+
}
|
|
353
|
+
applyUnicornPreventAbbreviationsReplacementPatches(options.replacements, patches)
|
|
354
|
+
|
|
355
|
+
extendAllowList := configured.ExtendDefaultAllowList == nil || *configured.ExtendDefaultAllowList
|
|
356
|
+
if !extendAllowList {
|
|
357
|
+
options.allowList = make(map[string]bool)
|
|
358
|
+
}
|
|
359
|
+
allowList, err := decodeUnicornPreventAbbreviationsBooleanMap(configured.AllowList, "allowList")
|
|
360
|
+
if err != nil {
|
|
361
|
+
return options, err
|
|
362
|
+
}
|
|
363
|
+
for name, allowed := range allowList {
|
|
364
|
+
if allowed {
|
|
365
|
+
options.allowList[name] = true
|
|
366
|
+
} else {
|
|
367
|
+
delete(options.allowList, name)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
ignore, err := decodeUnicornPreventAbbreviationsIgnore(configured.Ignore)
|
|
372
|
+
if err != nil {
|
|
373
|
+
return options, err
|
|
374
|
+
}
|
|
375
|
+
options.ignore = append(options.ignore, ignore...)
|
|
376
|
+
return options, nil
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
func requireUnicornPreventAbbreviationsEOF(decoder *json.Decoder) error {
|
|
380
|
+
var trailing any
|
|
381
|
+
if err := decoder.Decode(&trailing); err != io.EOF {
|
|
382
|
+
if err == nil {
|
|
383
|
+
return errors.New("options must contain exactly one object")
|
|
384
|
+
}
|
|
385
|
+
return fmt.Errorf("options contain trailing data: %w", err)
|
|
386
|
+
}
|
|
387
|
+
return nil
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
func decodeUnicornPreventAbbreviationsImportMode(
|
|
391
|
+
raw json.RawMessage,
|
|
392
|
+
name string,
|
|
393
|
+
fallback unicornPreventAbbreviationsImportMode,
|
|
394
|
+
) (unicornPreventAbbreviationsImportMode, error) {
|
|
395
|
+
if len(raw) == 0 {
|
|
396
|
+
return fallback, nil
|
|
397
|
+
}
|
|
398
|
+
var value any
|
|
399
|
+
if err := json.Unmarshal(raw, &value); err != nil {
|
|
400
|
+
return fallback, fmt.Errorf("option %q must be a boolean or \"internal\"", name)
|
|
401
|
+
}
|
|
402
|
+
switch configured := value.(type) {
|
|
403
|
+
case bool:
|
|
404
|
+
if configured {
|
|
405
|
+
return unicornPreventAbbreviationsImportAll, nil
|
|
406
|
+
}
|
|
407
|
+
return unicornPreventAbbreviationsImportOff, nil
|
|
408
|
+
case string:
|
|
409
|
+
if configured == "internal" {
|
|
410
|
+
return unicornPreventAbbreviationsImportInternal, nil
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return fallback, fmt.Errorf("option %q must be a boolean or \"internal\"", name)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
func decodeUnicornPreventAbbreviationsReplacementPatches(
|
|
417
|
+
raw json.RawMessage,
|
|
418
|
+
) (map[string]unicornPreventAbbreviationsReplacementPatch, error) {
|
|
419
|
+
patches := make(map[string]unicornPreventAbbreviationsReplacementPatch)
|
|
420
|
+
if len(raw) == 0 {
|
|
421
|
+
return patches, nil
|
|
422
|
+
}
|
|
423
|
+
var entries map[string]json.RawMessage
|
|
424
|
+
if err := json.Unmarshal(raw, &entries); err != nil || entries == nil {
|
|
425
|
+
return nil, errors.New("option \"replacements\" must be an object")
|
|
426
|
+
}
|
|
427
|
+
for name, value := range entries {
|
|
428
|
+
var disabled *bool
|
|
429
|
+
if err := json.Unmarshal(value, &disabled); err == nil && disabled != nil {
|
|
430
|
+
if *disabled {
|
|
431
|
+
return nil, fmt.Errorf("option \"replacements.%s\" must be false or an object", name)
|
|
432
|
+
}
|
|
433
|
+
patches[name] = unicornPreventAbbreviationsReplacementPatch{disabled: true}
|
|
434
|
+
continue
|
|
435
|
+
}
|
|
436
|
+
replacements, err := decodeUnicornPreventAbbreviationsBooleanMap(value, "replacements."+name)
|
|
437
|
+
if err != nil {
|
|
438
|
+
return nil, err
|
|
439
|
+
}
|
|
440
|
+
patches[name] = unicornPreventAbbreviationsReplacementPatch{values: replacements}
|
|
441
|
+
}
|
|
442
|
+
return patches, nil
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
func decodeUnicornPreventAbbreviationsBooleanMap(raw json.RawMessage, name string) (map[string]bool, error) {
|
|
446
|
+
values := make(map[string]bool)
|
|
447
|
+
if len(raw) == 0 {
|
|
448
|
+
return values, nil
|
|
449
|
+
}
|
|
450
|
+
if err := json.Unmarshal(raw, &values); err != nil || values == nil {
|
|
451
|
+
return nil, fmt.Errorf("option %q must be an object with boolean values", name)
|
|
452
|
+
}
|
|
453
|
+
return values, nil
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
func decodeUnicornPreventAbbreviationsIgnore(raw json.RawMessage) ([]*regexp.Regexp, error) {
|
|
457
|
+
if len(raw) == 0 {
|
|
458
|
+
return nil, nil
|
|
459
|
+
}
|
|
460
|
+
var patterns []string
|
|
461
|
+
if err := json.Unmarshal(raw, &patterns); err != nil || patterns == nil {
|
|
462
|
+
return nil, errors.New("option \"ignore\" must be an array of regular-expression strings")
|
|
463
|
+
}
|
|
464
|
+
compiled := make([]*regexp.Regexp, 0, len(patterns))
|
|
465
|
+
seen := make(map[string]struct{}, len(patterns))
|
|
466
|
+
for index, pattern := range patterns {
|
|
467
|
+
if _, duplicate := seen[pattern]; duplicate {
|
|
468
|
+
return nil, fmt.Errorf("option \"ignore\"[%d] duplicates %q", index, pattern)
|
|
469
|
+
}
|
|
470
|
+
seen[pattern] = struct{}{}
|
|
471
|
+
expression, err := regexp.Compile(pattern)
|
|
472
|
+
if err != nil {
|
|
473
|
+
return nil, fmt.Errorf("option \"ignore\"[%d] must be a valid regular expression: %w", index, err)
|
|
474
|
+
}
|
|
475
|
+
compiled = append(compiled, expression)
|
|
476
|
+
}
|
|
477
|
+
return compiled, nil
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
func cloneUnicornPreventAbbreviationsReplacements(source map[string][]string) map[string][]string {
|
|
481
|
+
clone := make(map[string][]string, len(source))
|
|
482
|
+
for name, replacements := range source {
|
|
483
|
+
clone[name] = append([]string(nil), replacements...)
|
|
484
|
+
}
|
|
485
|
+
return clone
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
func cloneUnicornPreventAbbreviationsAllowList(source map[string]bool) map[string]bool {
|
|
489
|
+
clone := make(map[string]bool, len(source))
|
|
490
|
+
for name, allowed := range source {
|
|
491
|
+
clone[name] = allowed
|
|
492
|
+
}
|
|
493
|
+
return clone
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
func applyUnicornPreventAbbreviationsReplacementPatches(
|
|
497
|
+
replacements map[string][]string,
|
|
498
|
+
patches map[string]unicornPreventAbbreviationsReplacementPatch,
|
|
499
|
+
) {
|
|
500
|
+
for name, patch := range patches {
|
|
501
|
+
if patch.disabled {
|
|
502
|
+
// Keep an empty entry so lower-first lookup stops here instead of
|
|
503
|
+
// falling through to a differently cased custom key.
|
|
504
|
+
replacements[name] = nil
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
values := make(map[string]bool)
|
|
508
|
+
for _, replacement := range replacements[name] {
|
|
509
|
+
values[replacement] = true
|
|
510
|
+
}
|
|
511
|
+
for replacement, enabled := range patch.values {
|
|
512
|
+
values[replacement] = enabled
|
|
513
|
+
}
|
|
514
|
+
merged := make([]string, 0, len(values))
|
|
515
|
+
for replacement, enabled := range values {
|
|
516
|
+
if enabled {
|
|
517
|
+
merged = append(merged, replacement)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
sort.Strings(merged)
|
|
521
|
+
replacements[name] = merged
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
func collectUnicornPreventAbbreviationsBindings(
|
|
526
|
+
ctx *Context,
|
|
527
|
+
root *shimast.Node,
|
|
528
|
+
) (
|
|
529
|
+
[]*unicornPreventAbbreviationsBinding,
|
|
530
|
+
map[*shimast.Symbol]*unicornPreventAbbreviationsBinding,
|
|
531
|
+
map[*shimast.Node]*unicornPreventAbbreviationsBinding,
|
|
532
|
+
map[string][]unicornPreventAbbreviationsOccupiedName,
|
|
533
|
+
) {
|
|
534
|
+
bindings := make([]*unicornPreventAbbreviationsBinding, 0)
|
|
535
|
+
bySymbol := make(map[*shimast.Symbol]*unicornPreventAbbreviationsBinding)
|
|
536
|
+
byDeclaration := make(map[*shimast.Node]*unicornPreventAbbreviationsBinding)
|
|
537
|
+
occupied := make(map[string][]unicornPreventAbbreviationsOccupiedName)
|
|
538
|
+
seenNames := make(map[*shimast.Node]struct{})
|
|
539
|
+
|
|
540
|
+
walkDescendants(root, func(node *shimast.Node) {
|
|
541
|
+
nameNode := unicornPreventAbbreviationsBindingIdentifier(node)
|
|
542
|
+
if nameNode == nil {
|
|
543
|
+
return
|
|
544
|
+
}
|
|
545
|
+
if _, duplicate := seenNames[nameNode]; duplicate {
|
|
546
|
+
return
|
|
547
|
+
}
|
|
548
|
+
seenNames[nameNode] = struct{}{}
|
|
549
|
+
name := identifierText(nameNode)
|
|
550
|
+
if name == "" {
|
|
551
|
+
return
|
|
552
|
+
}
|
|
553
|
+
scope := unicornPreventAbbreviationsBindingScope(node)
|
|
554
|
+
occupied[name] = append(occupied[name], unicornPreventAbbreviationsOccupiedName{scope: scope})
|
|
555
|
+
|
|
556
|
+
symbol := unicornPreventAbbreviationsCanonicalSymbol(ctx, nameNode)
|
|
557
|
+
if symbol == nil {
|
|
558
|
+
return
|
|
559
|
+
}
|
|
560
|
+
if existing := bySymbol[symbol]; existing != nil {
|
|
561
|
+
byDeclaration[node] = existing
|
|
562
|
+
return
|
|
563
|
+
}
|
|
564
|
+
binding := &unicornPreventAbbreviationsBinding{
|
|
565
|
+
declaration: node,
|
|
566
|
+
nameNode: nameNode,
|
|
567
|
+
name: name,
|
|
568
|
+
symbol: symbol,
|
|
569
|
+
scope: scope,
|
|
570
|
+
seen: make(map[*shimast.Node]struct{}),
|
|
571
|
+
}
|
|
572
|
+
bindings = append(bindings, binding)
|
|
573
|
+
bySymbol[symbol] = binding
|
|
574
|
+
byDeclaration[node] = binding
|
|
575
|
+
for _, declaration := range symbol.Declarations {
|
|
576
|
+
byDeclaration[declaration] = binding
|
|
577
|
+
}
|
|
578
|
+
})
|
|
579
|
+
collectUnicornPreventAbbreviationsOccupiedReferences(
|
|
580
|
+
ctx,
|
|
581
|
+
root,
|
|
582
|
+
occupied,
|
|
583
|
+
)
|
|
584
|
+
return bindings, bySymbol, byDeclaration, occupied
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Unresolved lexical references also constrain a rename. Resolved local,
|
|
588
|
+
// ambient, and global symbols are covered by ResolveName at every declaration
|
|
589
|
+
// and reference site; retaining only unresolved reads here avoids treating
|
|
590
|
+
// property-like syntax as a lexical collision.
|
|
591
|
+
func collectUnicornPreventAbbreviationsOccupiedReferences(
|
|
592
|
+
ctx *Context,
|
|
593
|
+
root *shimast.Node,
|
|
594
|
+
occupied map[string][]unicornPreventAbbreviationsOccupiedName,
|
|
595
|
+
) {
|
|
596
|
+
walkDescendants(root, func(node *shimast.Node) {
|
|
597
|
+
if node.Kind != shimast.KindIdentifier || !unicornPreventAbbreviationsIsNameReference(node) {
|
|
598
|
+
return
|
|
599
|
+
}
|
|
600
|
+
symbol := unicornPreventAbbreviationsCanonicalSymbol(ctx, node)
|
|
601
|
+
if symbol != nil {
|
|
602
|
+
return
|
|
603
|
+
}
|
|
604
|
+
name := identifierText(node)
|
|
605
|
+
if name != "" {
|
|
606
|
+
occupied[name] = append(occupied[name], unicornPreventAbbreviationsOccupiedName{
|
|
607
|
+
scope: unicornPreventAbbreviationsReferenceScope(node),
|
|
608
|
+
reference: true,
|
|
609
|
+
})
|
|
610
|
+
}
|
|
611
|
+
})
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// GetExportSymbolOfSymbol normalizes both value and type exports. The shared
|
|
615
|
+
// canonicalValueSymbol helper intentionally performs only value-export
|
|
616
|
+
// normalization, while this rule also renames interfaces, aliases, enums, and
|
|
617
|
+
// their type-position references.
|
|
618
|
+
func unicornPreventAbbreviationsCanonicalSymbol(ctx *Context, node *shimast.Node) *shimast.Symbol {
|
|
619
|
+
symbol := valueSymbolAtIdentifier(ctx, node)
|
|
620
|
+
if symbol == nil {
|
|
621
|
+
return nil
|
|
622
|
+
}
|
|
623
|
+
return ctx.Checker.GetExportSymbolOfSymbol(symbol)
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
func unicornPreventAbbreviationsReferenceScope(node *shimast.Node) *shimast.Node {
|
|
627
|
+
for scope := node.Parent; scope != nil; scope = scope.Parent {
|
|
628
|
+
if unicornPreventAbbreviationsIsFunctionLike(scope) {
|
|
629
|
+
return scope
|
|
630
|
+
}
|
|
631
|
+
switch scope.Kind {
|
|
632
|
+
case shimast.KindMappedType,
|
|
633
|
+
shimast.KindConditionalType:
|
|
634
|
+
return scope
|
|
635
|
+
case shimast.KindCaseBlock,
|
|
636
|
+
shimast.KindForStatement,
|
|
637
|
+
shimast.KindForInStatement,
|
|
638
|
+
shimast.KindForOfStatement,
|
|
639
|
+
shimast.KindCatchClause:
|
|
640
|
+
return scope
|
|
641
|
+
case shimast.KindClassDeclaration,
|
|
642
|
+
shimast.KindClassExpression,
|
|
643
|
+
shimast.KindInterfaceDeclaration,
|
|
644
|
+
shimast.KindTypeAliasDeclaration,
|
|
645
|
+
shimast.KindModuleBlock,
|
|
646
|
+
shimast.KindSourceFile,
|
|
647
|
+
shimast.KindClassStaticBlockDeclaration:
|
|
648
|
+
return scope
|
|
649
|
+
case shimast.KindBlock:
|
|
650
|
+
if scope.Parent != nil && unicornPreventAbbreviationsIsFunctionLike(scope.Parent) {
|
|
651
|
+
return scope.Parent
|
|
652
|
+
}
|
|
653
|
+
return scope
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
return nil
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// TypeScript signature nodes introduce parameter/type-parameter scopes just
|
|
660
|
+
// like runtime functions, but the shared runtime-only helper deliberately does
|
|
661
|
+
// not classify them as function-like.
|
|
662
|
+
func unicornPreventAbbreviationsIsFunctionLike(node *shimast.Node) bool {
|
|
663
|
+
if isFunctionLikeKind(node) {
|
|
664
|
+
return true
|
|
665
|
+
}
|
|
666
|
+
if node == nil {
|
|
667
|
+
return false
|
|
668
|
+
}
|
|
669
|
+
switch node.Kind {
|
|
670
|
+
case shimast.KindFunctionType,
|
|
671
|
+
shimast.KindConstructorType,
|
|
672
|
+
shimast.KindCallSignature,
|
|
673
|
+
shimast.KindConstructSignature,
|
|
674
|
+
shimast.KindMethodSignature,
|
|
675
|
+
shimast.KindIndexSignature:
|
|
676
|
+
return true
|
|
677
|
+
}
|
|
678
|
+
return false
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
func unicornPreventAbbreviationsIsNameReference(node *shimast.Node) bool {
|
|
682
|
+
if node == nil || node.Parent == nil {
|
|
683
|
+
return true
|
|
684
|
+
}
|
|
685
|
+
if unicornPreventAbbreviationsIsIntrinsicJSXTagName(node) ||
|
|
686
|
+
unicornPreventAbbreviationsIsImportTypeQualifier(node) {
|
|
687
|
+
return false
|
|
688
|
+
}
|
|
689
|
+
parent := node.Parent
|
|
690
|
+
switch parent.Kind {
|
|
691
|
+
case shimast.KindPropertyAccessExpression:
|
|
692
|
+
access := parent.AsPropertyAccessExpression()
|
|
693
|
+
return access == nil || access.Name() != node
|
|
694
|
+
case shimast.KindQualifiedName:
|
|
695
|
+
name := parent.AsQualifiedName()
|
|
696
|
+
return name == nil || name.Right != node
|
|
697
|
+
case shimast.KindPropertyAssignment:
|
|
698
|
+
assignment := parent.AsPropertyAssignment()
|
|
699
|
+
return assignment == nil || assignment.Name() != node
|
|
700
|
+
case shimast.KindBindingElement:
|
|
701
|
+
element := parent.AsBindingElement()
|
|
702
|
+
return element == nil || element.PropertyName != node
|
|
703
|
+
case shimast.KindMethodDeclaration,
|
|
704
|
+
shimast.KindPropertyDeclaration,
|
|
705
|
+
shimast.KindGetAccessor,
|
|
706
|
+
shimast.KindSetAccessor,
|
|
707
|
+
shimast.KindMethodSignature,
|
|
708
|
+
shimast.KindPropertySignature,
|
|
709
|
+
shimast.KindEnumMember:
|
|
710
|
+
return parent.Name() != node
|
|
711
|
+
case shimast.KindImportSpecifier:
|
|
712
|
+
specifier := parent.AsImportSpecifier()
|
|
713
|
+
return specifier == nil || specifier.PropertyName != node
|
|
714
|
+
case shimast.KindExportSpecifier:
|
|
715
|
+
specifier := parent.AsExportSpecifier()
|
|
716
|
+
if unicornPreventAbbreviationsExportSpecifierHasModuleSource(parent) {
|
|
717
|
+
return false
|
|
718
|
+
}
|
|
719
|
+
return specifier == nil || specifier.Name() != node || specifier.PropertyName == nil
|
|
720
|
+
case shimast.KindLabeledStatement:
|
|
721
|
+
statement := parent.AsLabeledStatement()
|
|
722
|
+
return statement == nil || statement.Label != node
|
|
723
|
+
case shimast.KindBreakStatement:
|
|
724
|
+
statement := parent.AsBreakStatement()
|
|
725
|
+
return statement == nil || statement.Label != node
|
|
726
|
+
case shimast.KindContinueStatement:
|
|
727
|
+
statement := parent.AsContinueStatement()
|
|
728
|
+
return statement == nil || statement.Label != node
|
|
729
|
+
case shimast.KindJsxAttribute:
|
|
730
|
+
return parent.Name() != node
|
|
731
|
+
case shimast.KindNamedTupleMember,
|
|
732
|
+
shimast.KindImportAttribute,
|
|
733
|
+
shimast.KindNamespaceExport,
|
|
734
|
+
shimast.KindNamespaceExportDeclaration,
|
|
735
|
+
shimast.KindMetaProperty:
|
|
736
|
+
return parent.Name() != node
|
|
737
|
+
case shimast.KindJsxNamespacedName:
|
|
738
|
+
return false
|
|
739
|
+
}
|
|
740
|
+
return true
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
func unicornPreventAbbreviationsIsIntrinsicJSXTagName(node *shimast.Node) bool {
|
|
744
|
+
if node == nil || node.Parent == nil || !shimscanner.IsIntrinsicJsxName(identifierText(node)) {
|
|
745
|
+
return false
|
|
746
|
+
}
|
|
747
|
+
switch node.Parent.Kind {
|
|
748
|
+
case shimast.KindJsxOpeningElement,
|
|
749
|
+
shimast.KindJsxSelfClosingElement,
|
|
750
|
+
shimast.KindJsxClosingElement:
|
|
751
|
+
return node.Parent.TagName() == node
|
|
752
|
+
}
|
|
753
|
+
return false
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
func unicornPreventAbbreviationsIsImportTypeQualifier(node *shimast.Node) bool {
|
|
757
|
+
current := node
|
|
758
|
+
for current != nil && current.Parent != nil && current.Parent.Kind == shimast.KindQualifiedName {
|
|
759
|
+
current = current.Parent
|
|
760
|
+
}
|
|
761
|
+
if current == nil || current.Parent == nil || current.Parent.Kind != shimast.KindImportType {
|
|
762
|
+
return false
|
|
763
|
+
}
|
|
764
|
+
imported := current.Parent.AsImportTypeNode()
|
|
765
|
+
return imported != nil && imported.Qualifier == current
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
func unicornPreventAbbreviationsExportSpecifierHasModuleSource(specifier *shimast.Node) bool {
|
|
769
|
+
for current := specifier; current != nil; current = current.Parent {
|
|
770
|
+
if current.Kind == shimast.KindExportDeclaration {
|
|
771
|
+
declaration := current.AsExportDeclaration()
|
|
772
|
+
return declaration != nil && declaration.ModuleSpecifier != nil
|
|
773
|
+
}
|
|
774
|
+
if current.Kind == shimast.KindSourceFile {
|
|
775
|
+
return false
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return false
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
func unicornPreventAbbreviationsBindingIdentifier(node *shimast.Node) *shimast.Node {
|
|
782
|
+
if node == nil {
|
|
783
|
+
return nil
|
|
784
|
+
}
|
|
785
|
+
switch node.Kind {
|
|
786
|
+
case shimast.KindVariableDeclaration:
|
|
787
|
+
if declaration := node.AsVariableDeclaration(); declaration != nil {
|
|
788
|
+
return unicornPreventAbbreviationsPlainBindingName(declaration.Name())
|
|
789
|
+
}
|
|
790
|
+
case shimast.KindParameter:
|
|
791
|
+
if declaration := node.AsParameterDeclaration(); declaration != nil {
|
|
792
|
+
return unicornPreventAbbreviationsPlainBindingName(declaration.Name())
|
|
793
|
+
}
|
|
794
|
+
case shimast.KindBindingElement:
|
|
795
|
+
if element := node.AsBindingElement(); element != nil {
|
|
796
|
+
return unicornPreventAbbreviationsPlainBindingName(element.Name())
|
|
797
|
+
}
|
|
798
|
+
case shimast.KindFunctionDeclaration,
|
|
799
|
+
shimast.KindFunctionExpression,
|
|
800
|
+
shimast.KindClassDeclaration,
|
|
801
|
+
shimast.KindClassExpression,
|
|
802
|
+
shimast.KindEnumDeclaration,
|
|
803
|
+
shimast.KindModuleDeclaration,
|
|
804
|
+
shimast.KindInterfaceDeclaration,
|
|
805
|
+
shimast.KindTypeAliasDeclaration,
|
|
806
|
+
shimast.KindTypeParameter:
|
|
807
|
+
return unicornPreventAbbreviationsPlainBindingName(node.Name())
|
|
808
|
+
case shimast.KindImportClause,
|
|
809
|
+
shimast.KindNamespaceImport,
|
|
810
|
+
shimast.KindImportSpecifier,
|
|
811
|
+
shimast.KindImportEqualsDeclaration:
|
|
812
|
+
return unicornPreventAbbreviationsPlainBindingName(node.Name())
|
|
813
|
+
}
|
|
814
|
+
return nil
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
func unicornPreventAbbreviationsPlainBindingName(node *shimast.Node) *shimast.Node {
|
|
818
|
+
if node != nil && node.Kind == shimast.KindIdentifier {
|
|
819
|
+
return node
|
|
820
|
+
}
|
|
821
|
+
return nil
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
func unicornPreventAbbreviationsBindingScope(declaration *shimast.Node) *shimast.Node {
|
|
825
|
+
if declaration == nil {
|
|
826
|
+
return nil
|
|
827
|
+
}
|
|
828
|
+
if declaration.Kind == shimast.KindFunctionExpression || declaration.Kind == shimast.KindClassExpression {
|
|
829
|
+
return declaration
|
|
830
|
+
}
|
|
831
|
+
if parameter := unicornPreventAbbreviationsEnclosingParameter(declaration); parameter != nil {
|
|
832
|
+
for scope := parameter.Parent; scope != nil; scope = scope.Parent {
|
|
833
|
+
if unicornPreventAbbreviationsIsFunctionLike(scope) {
|
|
834
|
+
return scope
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return nil
|
|
838
|
+
}
|
|
839
|
+
root := unicornPreventAbbreviationsRootDeclaration(declaration)
|
|
840
|
+
if root != nil && root.Kind == shimast.KindVariableDeclaration && root.Parent != nil &&
|
|
841
|
+
root.Parent.Kind == shimast.KindVariableDeclarationList && shimast.IsVar(root.Parent) {
|
|
842
|
+
for scope := root.Parent.Parent; scope != nil; scope = scope.Parent {
|
|
843
|
+
if unicornPreventAbbreviationsIsFunctionLike(scope) {
|
|
844
|
+
return scope
|
|
845
|
+
}
|
|
846
|
+
switch scope.Kind {
|
|
847
|
+
case shimast.KindSourceFile, shimast.KindModuleBlock, shimast.KindClassStaticBlockDeclaration:
|
|
848
|
+
return scope
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return nil
|
|
852
|
+
}
|
|
853
|
+
if declaration.Kind == shimast.KindTypeParameter {
|
|
854
|
+
for scope := declaration.Parent; scope != nil; scope = scope.Parent {
|
|
855
|
+
if unicornPreventAbbreviationsIsFunctionLike(scope) {
|
|
856
|
+
return scope
|
|
857
|
+
}
|
|
858
|
+
switch scope.Kind {
|
|
859
|
+
case shimast.KindMappedType,
|
|
860
|
+
shimast.KindConditionalType:
|
|
861
|
+
return scope
|
|
862
|
+
case shimast.KindClassDeclaration,
|
|
863
|
+
shimast.KindClassExpression,
|
|
864
|
+
shimast.KindInterfaceDeclaration,
|
|
865
|
+
shimast.KindTypeAliasDeclaration,
|
|
866
|
+
shimast.KindMethodSignature:
|
|
867
|
+
return scope
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return nil
|
|
871
|
+
}
|
|
872
|
+
scope := preferConstLexicalScope(declaration)
|
|
873
|
+
if scope != nil && scope.Kind == shimast.KindBlock && scope.Parent != nil &&
|
|
874
|
+
scope.Parent.Kind == shimast.KindCatchClause {
|
|
875
|
+
return scope.Parent
|
|
876
|
+
}
|
|
877
|
+
return scope
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// Destructuring BindingElements inherit declaration-kind semantics from the
|
|
881
|
+
// root Parameter, catch VariableDeclaration, or variable declaration.
|
|
882
|
+
func unicornPreventAbbreviationsRootDeclaration(declaration *shimast.Node) *shimast.Node {
|
|
883
|
+
for declaration != nil && declaration.Kind == shimast.KindBindingElement &&
|
|
884
|
+
declaration.Parent != nil && declaration.Parent.Parent != nil {
|
|
885
|
+
declaration = declaration.Parent.Parent
|
|
886
|
+
}
|
|
887
|
+
return declaration
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
func unicornPreventAbbreviationsEnclosingParameter(declaration *shimast.Node) *shimast.Node {
|
|
891
|
+
for current := declaration; current != nil; current = current.Parent {
|
|
892
|
+
if current.Kind == shimast.KindParameter {
|
|
893
|
+
return current
|
|
894
|
+
}
|
|
895
|
+
if current != declaration && unicornPreventAbbreviationsIsFunctionLike(current) {
|
|
896
|
+
return nil
|
|
897
|
+
}
|
|
898
|
+
if current.Kind == shimast.KindSourceFile {
|
|
899
|
+
return nil
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
return nil
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
func collectUnicornPreventAbbreviationsReferences(
|
|
906
|
+
ctx *Context,
|
|
907
|
+
root *shimast.Node,
|
|
908
|
+
bySymbol map[*shimast.Symbol]*unicornPreventAbbreviationsBinding,
|
|
909
|
+
byDeclaration map[*shimast.Node]*unicornPreventAbbreviationsBinding,
|
|
910
|
+
) {
|
|
911
|
+
walkDescendants(root, func(node *shimast.Node) {
|
|
912
|
+
if node.Kind != shimast.KindIdentifier {
|
|
913
|
+
return
|
|
914
|
+
}
|
|
915
|
+
symbol := unicornPreventAbbreviationsReferenceSymbol(ctx, node)
|
|
916
|
+
if symbol == nil {
|
|
917
|
+
return
|
|
918
|
+
}
|
|
919
|
+
binding := bySymbol[symbol]
|
|
920
|
+
if binding == nil {
|
|
921
|
+
for _, declaration := range symbol.Declarations {
|
|
922
|
+
if candidate := byDeclaration[declaration]; candidate != nil {
|
|
923
|
+
binding = candidate
|
|
924
|
+
break
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if binding == nil {
|
|
929
|
+
return
|
|
930
|
+
}
|
|
931
|
+
if _, duplicate := binding.seen[node]; duplicate {
|
|
932
|
+
return
|
|
933
|
+
}
|
|
934
|
+
binding.seen[node] = struct{}{}
|
|
935
|
+
binding.references = append(binding.references, node)
|
|
936
|
+
})
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// The checker binds an export specifier's written name to the exported alias,
|
|
940
|
+
// not to the local declaration that supplies it. Resolve the local side through
|
|
941
|
+
// TypeScript's dedicated API so a local rename can preserve the public spelling
|
|
942
|
+
// (`export { err }` becomes `export { error as err }`). Re-exports have no local
|
|
943
|
+
// binding and stay on the ordinary symbol-at-location path.
|
|
944
|
+
func unicornPreventAbbreviationsReferenceSymbol(ctx *Context, node *shimast.Node) *shimast.Symbol {
|
|
945
|
+
if node != nil && node.Parent != nil && node.Parent.Kind == shimast.KindExportSpecifier &&
|
|
946
|
+
!unicornPreventAbbreviationsExportSpecifierHasModuleSource(node.Parent) {
|
|
947
|
+
specifier := node.Parent.AsExportSpecifier()
|
|
948
|
+
if specifier != nil && (specifier.PropertyName == node ||
|
|
949
|
+
specifier.PropertyName == nil && specifier.Name() == node) {
|
|
950
|
+
symbol := ctx.Checker.GetExportSpecifierLocalTargetSymbol(node.Parent)
|
|
951
|
+
if symbol != nil {
|
|
952
|
+
return ctx.Checker.GetExportSymbolOfSymbol(symbol)
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
return unicornPreventAbbreviationsCanonicalSymbol(ctx, node)
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
func reportUnicornPreventAbbreviationsBinding(
|
|
960
|
+
ctx *Context,
|
|
961
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
962
|
+
options unicornPreventAbbreviationsOptions,
|
|
963
|
+
occupied map[string][]unicornPreventAbbreviationsOccupiedName,
|
|
964
|
+
generated map[string][]*unicornPreventAbbreviationsBinding,
|
|
965
|
+
comments map[int]commentToken,
|
|
966
|
+
) {
|
|
967
|
+
if binding == nil || !unicornPreventAbbreviationsShouldCheckBinding(binding, options) {
|
|
968
|
+
return
|
|
969
|
+
}
|
|
970
|
+
replacements := getUnicornPreventAbbreviationsNameReplacements(binding.name, options, 3)
|
|
971
|
+
if replacements.total == 0 {
|
|
972
|
+
return
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
safeToRename := unicornPreventAbbreviationsCanRenameBinding(ctx, binding, comments)
|
|
976
|
+
scopes := unicornPreventAbbreviationsBindingReferenceScopes(binding)
|
|
977
|
+
available := make([]string, 0, len(replacements.samples))
|
|
978
|
+
for _, replacement := range replacements.samples {
|
|
979
|
+
name := unicornPreventAbbreviationsAvailableName(
|
|
980
|
+
ctx,
|
|
981
|
+
binding,
|
|
982
|
+
replacement,
|
|
983
|
+
scopes,
|
|
984
|
+
occupied,
|
|
985
|
+
generated,
|
|
986
|
+
)
|
|
987
|
+
if name != "" {
|
|
988
|
+
available = append(available, name)
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
replacements.samples = available
|
|
992
|
+
message := unicornPreventAbbreviationsMessage(binding.name, replacements, "variable")
|
|
993
|
+
|
|
994
|
+
if replacements.total == 1 && len(replacements.samples) == 1 && safeToRename {
|
|
995
|
+
replacement := replacements.samples[0]
|
|
996
|
+
edits := unicornPreventAbbreviationsRenameEdits(ctx, binding, replacement)
|
|
997
|
+
if len(edits) > 0 {
|
|
998
|
+
generated[replacement] = append(generated[replacement], binding)
|
|
999
|
+
ctx.ReportFix(binding.nameNode, message, edits...)
|
|
1000
|
+
return
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
suggestions := make([]Suggestion, 0, len(replacements.samples))
|
|
1005
|
+
if replacements.total > 1 && safeToRename {
|
|
1006
|
+
for _, replacement := range replacements.samples {
|
|
1007
|
+
edits := unicornPreventAbbreviationsRenameEdits(ctx, binding, replacement)
|
|
1008
|
+
if len(edits) == 0 {
|
|
1009
|
+
continue
|
|
1010
|
+
}
|
|
1011
|
+
suggestions = append(suggestions, Suggestion{
|
|
1012
|
+
Title: fmt.Sprintf("Rename to `%s`.", replacement),
|
|
1013
|
+
Edits: edits,
|
|
1014
|
+
})
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
ctx.ReportFixSuggestions(binding.nameNode, message, nil, suggestions...)
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
func unicornPreventAbbreviationsShouldCheckBinding(
|
|
1021
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1022
|
+
options unicornPreventAbbreviationsOptions,
|
|
1023
|
+
) bool {
|
|
1024
|
+
declaration := binding.declaration
|
|
1025
|
+
if declaration == nil {
|
|
1026
|
+
return false
|
|
1027
|
+
}
|
|
1028
|
+
if unicornPreventAbbreviationsIsShorthandBinding(declaration) && !options.checkShorthandProperties {
|
|
1029
|
+
return false
|
|
1030
|
+
}
|
|
1031
|
+
switch declaration.Kind {
|
|
1032
|
+
case shimast.KindVariableDeclaration:
|
|
1033
|
+
variable := declaration.AsVariableDeclaration()
|
|
1034
|
+
if variable != nil {
|
|
1035
|
+
if _, required := unicornPreventAbbreviationsStaticRequireModule(variable.Initializer); required {
|
|
1036
|
+
return unicornPreventAbbreviationsImportAllowed(
|
|
1037
|
+
options.checkDefaultAndNamespaceImports,
|
|
1038
|
+
unicornPreventAbbreviationsIsInternalImport(declaration),
|
|
1039
|
+
)
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
case shimast.KindImportEqualsDeclaration:
|
|
1043
|
+
if !unicornPreventAbbreviationsIsExternalImportEquals(declaration) {
|
|
1044
|
+
return true
|
|
1045
|
+
}
|
|
1046
|
+
return unicornPreventAbbreviationsImportAllowed(
|
|
1047
|
+
options.checkDefaultAndNamespaceImports,
|
|
1048
|
+
unicornPreventAbbreviationsIsInternalImport(declaration),
|
|
1049
|
+
)
|
|
1050
|
+
case shimast.KindImportClause, shimast.KindNamespaceImport:
|
|
1051
|
+
return unicornPreventAbbreviationsImportAllowed(
|
|
1052
|
+
options.checkDefaultAndNamespaceImports,
|
|
1053
|
+
unicornPreventAbbreviationsIsInternalImport(declaration),
|
|
1054
|
+
)
|
|
1055
|
+
case shimast.KindImportSpecifier:
|
|
1056
|
+
specifier := declaration.AsImportSpecifier()
|
|
1057
|
+
if specifier != nil && specifier.PropertyName != nil &&
|
|
1058
|
+
moduleExportNameText(specifier.PropertyName) == "default" {
|
|
1059
|
+
return unicornPreventAbbreviationsImportAllowed(
|
|
1060
|
+
options.checkDefaultAndNamespaceImports,
|
|
1061
|
+
unicornPreventAbbreviationsIsInternalImport(declaration),
|
|
1062
|
+
)
|
|
1063
|
+
}
|
|
1064
|
+
if specifier != nil && specifier.PropertyName == nil {
|
|
1065
|
+
return unicornPreventAbbreviationsImportAllowed(
|
|
1066
|
+
options.checkShorthandImports,
|
|
1067
|
+
unicornPreventAbbreviationsIsInternalImport(declaration),
|
|
1068
|
+
)
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
return true
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
func unicornPreventAbbreviationsIsShorthandBinding(declaration *shimast.Node) bool {
|
|
1075
|
+
if declaration == nil || declaration.Kind != shimast.KindBindingElement {
|
|
1076
|
+
return false
|
|
1077
|
+
}
|
|
1078
|
+
element := declaration.AsBindingElement()
|
|
1079
|
+
return element != nil && element.PropertyName == nil && element.DotDotDotToken == nil &&
|
|
1080
|
+
declaration.Parent != nil && declaration.Parent.Kind == shimast.KindObjectBindingPattern
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
func unicornPreventAbbreviationsImportAllowed(
|
|
1084
|
+
mode unicornPreventAbbreviationsImportMode,
|
|
1085
|
+
internal bool,
|
|
1086
|
+
) bool {
|
|
1087
|
+
return mode == unicornPreventAbbreviationsImportAll ||
|
|
1088
|
+
mode == unicornPreventAbbreviationsImportInternal && internal
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
func unicornPreventAbbreviationsIsInternalImport(declaration *shimast.Node) bool {
|
|
1092
|
+
if declaration != nil && declaration.Kind == shimast.KindVariableDeclaration {
|
|
1093
|
+
variable := declaration.AsVariableDeclaration()
|
|
1094
|
+
if variable == nil {
|
|
1095
|
+
return false
|
|
1096
|
+
}
|
|
1097
|
+
source, required := unicornPreventAbbreviationsStaticRequireModule(variable.Initializer)
|
|
1098
|
+
return required && unicornPreventAbbreviationsIsInternalModule(source)
|
|
1099
|
+
}
|
|
1100
|
+
for ancestor := declaration; ancestor != nil; ancestor = ancestor.Parent {
|
|
1101
|
+
if ancestor.Kind == shimast.KindImportDeclaration {
|
|
1102
|
+
imported := ancestor.AsImportDeclaration()
|
|
1103
|
+
if imported == nil {
|
|
1104
|
+
return false
|
|
1105
|
+
}
|
|
1106
|
+
source := stringLiteralText(imported.ModuleSpecifier)
|
|
1107
|
+
return unicornPreventAbbreviationsIsInternalModule(source)
|
|
1108
|
+
}
|
|
1109
|
+
if ancestor.Kind == shimast.KindImportEqualsDeclaration {
|
|
1110
|
+
imported := ancestor.AsImportEqualsDeclaration()
|
|
1111
|
+
if imported == nil || imported.ModuleReference == nil ||
|
|
1112
|
+
imported.ModuleReference.Kind != shimast.KindExternalModuleReference {
|
|
1113
|
+
return false
|
|
1114
|
+
}
|
|
1115
|
+
external := imported.ModuleReference.AsExternalModuleReference()
|
|
1116
|
+
if external == nil {
|
|
1117
|
+
return false
|
|
1118
|
+
}
|
|
1119
|
+
source := stringLiteralText(external.Expression)
|
|
1120
|
+
return unicornPreventAbbreviationsIsInternalModule(source)
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return false
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
func unicornPreventAbbreviationsIsInternalModule(source string) bool {
|
|
1127
|
+
return !strings.Contains(source, "node_modules") &&
|
|
1128
|
+
(strings.HasPrefix(source, ".") || strings.HasPrefix(source, "/"))
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Import controls apply only to upstream's exact static-require shape: one
|
|
1132
|
+
// ordinary string-literal argument on a non-optional bare require call.
|
|
1133
|
+
func unicornPreventAbbreviationsStaticRequireModule(node *shimast.Node) (string, bool) {
|
|
1134
|
+
node = stripParens(node)
|
|
1135
|
+
if node == nil || node.Kind != shimast.KindCallExpression {
|
|
1136
|
+
return "", false
|
|
1137
|
+
}
|
|
1138
|
+
call := node.AsCallExpression()
|
|
1139
|
+
if call == nil || call.QuestionDotToken != nil || callCalleeName(call) != "require" ||
|
|
1140
|
+
call.Arguments == nil || len(call.Arguments.Nodes) != 1 {
|
|
1141
|
+
return "", false
|
|
1142
|
+
}
|
|
1143
|
+
argument := call.Arguments.Nodes[0]
|
|
1144
|
+
if argument == nil || argument.Kind != shimast.KindStringLiteral {
|
|
1145
|
+
return "", false
|
|
1146
|
+
}
|
|
1147
|
+
literal := argument.AsStringLiteral()
|
|
1148
|
+
if literal == nil {
|
|
1149
|
+
return "", false
|
|
1150
|
+
}
|
|
1151
|
+
return literal.Text, true
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
func unicornPreventAbbreviationsIsExternalImportEquals(declaration *shimast.Node) bool {
|
|
1155
|
+
if declaration == nil || declaration.Kind != shimast.KindImportEqualsDeclaration {
|
|
1156
|
+
return false
|
|
1157
|
+
}
|
|
1158
|
+
imported := declaration.AsImportEqualsDeclaration()
|
|
1159
|
+
return imported != nil && imported.ModuleReference != nil &&
|
|
1160
|
+
imported.ModuleReference.Kind == shimast.KindExternalModuleReference
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
func unicornPreventAbbreviationsCanRenameBinding(
|
|
1164
|
+
ctx *Context,
|
|
1165
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1166
|
+
comments map[int]commentToken,
|
|
1167
|
+
) bool {
|
|
1168
|
+
if binding == nil || len(binding.references) == 0 || unicornPreventAbbreviationsBindingIsExternallyVisible(ctx, binding) {
|
|
1169
|
+
return false
|
|
1170
|
+
}
|
|
1171
|
+
if strings.EqualFold(filepath.Ext(ctx.File.FileName()), ".vue") {
|
|
1172
|
+
return false
|
|
1173
|
+
}
|
|
1174
|
+
parameter := unicornPreventAbbreviationsEnclosingParameter(binding.declaration)
|
|
1175
|
+
if parameter != nil && unicornPreventAbbreviationsParameterHasJSDoc(ctx, parameter, comments) {
|
|
1176
|
+
return false
|
|
1177
|
+
}
|
|
1178
|
+
if parameter != nil && isParameterProperty(parameter) {
|
|
1179
|
+
return false
|
|
1180
|
+
}
|
|
1181
|
+
for _, reference := range binding.references {
|
|
1182
|
+
if noFallthroughNodeIsJSXTagName(reference) {
|
|
1183
|
+
return false
|
|
1184
|
+
}
|
|
1185
|
+
if pos, end := tokenRange(ctx.File, reference); pos < 0 || end <= pos {
|
|
1186
|
+
return false
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
return true
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
func unicornPreventAbbreviationsBindingIsExternallyVisible(
|
|
1193
|
+
ctx *Context,
|
|
1194
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1195
|
+
) bool {
|
|
1196
|
+
if ctx == nil || ctx.File == nil || binding == nil || binding.symbol == nil || ctx.File.IsDeclarationFile {
|
|
1197
|
+
return true
|
|
1198
|
+
}
|
|
1199
|
+
declarations := make([]*shimast.Node, 0, len(binding.symbol.Declarations)+1)
|
|
1200
|
+
declarations = append(declarations, binding.declaration)
|
|
1201
|
+
declarations = append(declarations, binding.symbol.Declarations...)
|
|
1202
|
+
seen := make(map[*shimast.Node]struct{}, len(declarations))
|
|
1203
|
+
for _, declaration := range declarations {
|
|
1204
|
+
if declaration == nil {
|
|
1205
|
+
return true
|
|
1206
|
+
}
|
|
1207
|
+
if _, duplicate := seen[declaration]; duplicate {
|
|
1208
|
+
continue
|
|
1209
|
+
}
|
|
1210
|
+
seen[declaration] = struct{}{}
|
|
1211
|
+
source := shimast.GetSourceFileOfNode(declaration)
|
|
1212
|
+
// A source-only edit is safe only when every declaration is proven to
|
|
1213
|
+
// belong to the current file. Detached or synthetic declarations fail
|
|
1214
|
+
// closed just like declarations owned by another source file.
|
|
1215
|
+
if source == nil || source != ctx.File {
|
|
1216
|
+
return true
|
|
1217
|
+
}
|
|
1218
|
+
if unicornPreventAbbreviationsDeclarationIsExportedOrAmbient(declaration) {
|
|
1219
|
+
return true
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return false
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
func unicornPreventAbbreviationsDeclarationIsExportedOrAmbient(declaration *shimast.Node) bool {
|
|
1226
|
+
if declaration == nil {
|
|
1227
|
+
return true
|
|
1228
|
+
}
|
|
1229
|
+
for owner := declaration; owner != nil; owner = owner.Parent {
|
|
1230
|
+
if owner.Flags&shimast.NodeFlagsAmbient != 0 ||
|
|
1231
|
+
owner.ModifierFlags()&shimast.ModifierFlagsAmbient != 0 {
|
|
1232
|
+
return true
|
|
1233
|
+
}
|
|
1234
|
+
if owner.Kind == shimast.KindSourceFile {
|
|
1235
|
+
break
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
owner := declaration
|
|
1240
|
+
for current := declaration; current != nil; current = current.Parent {
|
|
1241
|
+
if current.Kind == shimast.KindVariableDeclaration {
|
|
1242
|
+
owner = current
|
|
1243
|
+
break
|
|
1244
|
+
}
|
|
1245
|
+
if current.Kind == shimast.KindParameter || current.Kind == shimast.KindCatchClause ||
|
|
1246
|
+
unicornPreventAbbreviationsIsFunctionLike(current) || current.Kind == shimast.KindSourceFile {
|
|
1247
|
+
break
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
flags := shimast.GetCombinedModifierFlags(owner)
|
|
1251
|
+
namedExport := flags&shimast.ModifierFlagsExport != 0 && flags&shimast.ModifierFlagsDefault == 0
|
|
1252
|
+
return namedExport || flags&shimast.ModifierFlagsAmbient != 0
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
func collectUnicornPreventAbbreviationsComments(file *shimast.SourceFile) map[int]commentToken {
|
|
1256
|
+
comments := make(map[int]commentToken)
|
|
1257
|
+
forEachCommentToken(file, func(kind shimast.Kind, pos, end int) {
|
|
1258
|
+
comments[end] = commentToken{kind: kind, pos: pos, end: end}
|
|
1259
|
+
})
|
|
1260
|
+
return comments
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
func unicornPreventAbbreviationsParameterHasJSDoc(
|
|
1264
|
+
ctx *Context,
|
|
1265
|
+
parameter *shimast.Node,
|
|
1266
|
+
comments map[int]commentToken,
|
|
1267
|
+
) bool {
|
|
1268
|
+
if ctx == nil || ctx.File == nil || parameter == nil {
|
|
1269
|
+
return false
|
|
1270
|
+
}
|
|
1271
|
+
function := parameter.Parent
|
|
1272
|
+
for function != nil && !unicornPreventAbbreviationsIsFunctionLike(function) {
|
|
1273
|
+
function = function.Parent
|
|
1274
|
+
}
|
|
1275
|
+
if function == nil {
|
|
1276
|
+
return false
|
|
1277
|
+
}
|
|
1278
|
+
commentable := function
|
|
1279
|
+
for commentable.Parent != nil {
|
|
1280
|
+
parent := commentable.Parent
|
|
1281
|
+
attachable := false
|
|
1282
|
+
switch parent.Kind {
|
|
1283
|
+
case shimast.KindVariableDeclaration,
|
|
1284
|
+
shimast.KindVariableDeclarationList,
|
|
1285
|
+
shimast.KindVariableStatement,
|
|
1286
|
+
shimast.KindPropertyDeclaration,
|
|
1287
|
+
shimast.KindPropertyAssignment,
|
|
1288
|
+
shimast.KindPropertySignature,
|
|
1289
|
+
shimast.KindTypeAliasDeclaration,
|
|
1290
|
+
shimast.KindExportAssignment,
|
|
1291
|
+
shimast.KindExpressionStatement:
|
|
1292
|
+
attachable = true
|
|
1293
|
+
case shimast.KindBinaryExpression:
|
|
1294
|
+
expression := parent.AsBinaryExpression()
|
|
1295
|
+
attachable = expression != nil && expression.Right == commentable &&
|
|
1296
|
+
expression.OperatorToken != nil && isAssignmentOperator(expression.OperatorToken.Kind)
|
|
1297
|
+
case shimast.KindParenthesizedExpression,
|
|
1298
|
+
shimast.KindAsExpression,
|
|
1299
|
+
shimast.KindSatisfiesExpression,
|
|
1300
|
+
shimast.KindNonNullExpression,
|
|
1301
|
+
shimast.KindTypeAssertionExpression:
|
|
1302
|
+
attachable = parent.Expression() == commentable
|
|
1303
|
+
}
|
|
1304
|
+
if !attachable {
|
|
1305
|
+
break
|
|
1306
|
+
}
|
|
1307
|
+
commentable = parent
|
|
1308
|
+
}
|
|
1309
|
+
start, _ := tokenRange(ctx.File, commentable)
|
|
1310
|
+
if start <= 0 {
|
|
1311
|
+
return false
|
|
1312
|
+
}
|
|
1313
|
+
text := ctx.File.Text()
|
|
1314
|
+
prefix := strings.TrimRightFunc(text[:start], unicode.IsSpace)
|
|
1315
|
+
attached, ok := comments[len(prefix)]
|
|
1316
|
+
if !ok || attached.kind != shimast.KindMultiLineCommentTrivia ||
|
|
1317
|
+
attached.pos < 0 || attached.end > len(text) ||
|
|
1318
|
+
!strings.HasPrefix(text[attached.pos:attached.end], "/**") {
|
|
1319
|
+
return false
|
|
1320
|
+
}
|
|
1321
|
+
if unicornPreventAbbreviationsLineBreakCount(text[attached.end:start]) > 1 {
|
|
1322
|
+
return false
|
|
1323
|
+
}
|
|
1324
|
+
comment := text[attached.pos:attached.end]
|
|
1325
|
+
for offset := 0; ; {
|
|
1326
|
+
index := strings.Index(comment[offset:], "@param")
|
|
1327
|
+
if index < 0 {
|
|
1328
|
+
return false
|
|
1329
|
+
}
|
|
1330
|
+
end := offset + index + len("@param")
|
|
1331
|
+
if end == len(comment) || !unicornPreventAbbreviationsRegExpWordByte(comment[end]) {
|
|
1332
|
+
return true
|
|
1333
|
+
}
|
|
1334
|
+
offset = end
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
func unicornPreventAbbreviationsLineBreakCount(text string) int {
|
|
1339
|
+
count := 0
|
|
1340
|
+
for index := 0; index < len(text); {
|
|
1341
|
+
character, size := utf8.DecodeRuneInString(text[index:])
|
|
1342
|
+
switch character {
|
|
1343
|
+
case '\r':
|
|
1344
|
+
count++
|
|
1345
|
+
if index+size < len(text) && text[index+size] == '\n' {
|
|
1346
|
+
size++
|
|
1347
|
+
}
|
|
1348
|
+
case '\n', '\u2028', '\u2029':
|
|
1349
|
+
count++
|
|
1350
|
+
}
|
|
1351
|
+
index += size
|
|
1352
|
+
}
|
|
1353
|
+
return count
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
// JavaScript's /\b/u word boundary still uses ASCII \w semantics.
|
|
1357
|
+
func unicornPreventAbbreviationsRegExpWordByte(character byte) bool {
|
|
1358
|
+
return character >= 'a' && character <= 'z' ||
|
|
1359
|
+
character >= 'A' && character <= 'Z' ||
|
|
1360
|
+
character >= '0' && character <= '9' || character == '_'
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
func unicornPreventAbbreviationsRenameEdits(
|
|
1364
|
+
ctx *Context,
|
|
1365
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1366
|
+
replacement string,
|
|
1367
|
+
) []TextEdit {
|
|
1368
|
+
if !unicornPreventAbbreviationsValidIdentifier(replacement) {
|
|
1369
|
+
return nil
|
|
1370
|
+
}
|
|
1371
|
+
edits := make([]TextEdit, 0, len(binding.references))
|
|
1372
|
+
seen := make(map[[2]int]struct{}, len(binding.references))
|
|
1373
|
+
for _, reference := range binding.references {
|
|
1374
|
+
pos, end := tokenRange(ctx.File, reference)
|
|
1375
|
+
if pos < 0 || end <= pos {
|
|
1376
|
+
return nil
|
|
1377
|
+
}
|
|
1378
|
+
key := [2]int{pos, end}
|
|
1379
|
+
if _, duplicate := seen[key]; duplicate {
|
|
1380
|
+
continue
|
|
1381
|
+
}
|
|
1382
|
+
seen[key] = struct{}{}
|
|
1383
|
+
edits = append(edits, TextEdit{
|
|
1384
|
+
Pos: pos,
|
|
1385
|
+
End: end,
|
|
1386
|
+
Text: unicornPreventAbbreviationsReferenceReplacement(reference, binding.name, replacement),
|
|
1387
|
+
})
|
|
1388
|
+
}
|
|
1389
|
+
sort.Slice(edits, func(i, j int) bool {
|
|
1390
|
+
if edits[i].Pos == edits[j].Pos {
|
|
1391
|
+
return edits[i].End < edits[j].End
|
|
1392
|
+
}
|
|
1393
|
+
return edits[i].Pos < edits[j].Pos
|
|
1394
|
+
})
|
|
1395
|
+
return edits
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
func unicornPreventAbbreviationsReferenceReplacement(node *shimast.Node, oldName, newName string) string {
|
|
1399
|
+
if node == nil || node.Parent == nil {
|
|
1400
|
+
return newName
|
|
1401
|
+
}
|
|
1402
|
+
parent := node.Parent
|
|
1403
|
+
switch parent.Kind {
|
|
1404
|
+
case shimast.KindShorthandPropertyAssignment:
|
|
1405
|
+
shorthand := parent.AsShorthandPropertyAssignment()
|
|
1406
|
+
if shorthand != nil && shorthand.Name() == node {
|
|
1407
|
+
return oldName + ": " + newName
|
|
1408
|
+
}
|
|
1409
|
+
case shimast.KindBindingElement:
|
|
1410
|
+
element := parent.AsBindingElement()
|
|
1411
|
+
if element != nil && element.Name() == node && element.PropertyName == nil && element.DotDotDotToken == nil &&
|
|
1412
|
+
parent.Parent != nil && parent.Parent.Kind == shimast.KindObjectBindingPattern {
|
|
1413
|
+
return oldName + ": " + newName
|
|
1414
|
+
}
|
|
1415
|
+
case shimast.KindImportSpecifier:
|
|
1416
|
+
specifier := parent.AsImportSpecifier()
|
|
1417
|
+
if specifier != nil && specifier.Name() == node && specifier.PropertyName == nil {
|
|
1418
|
+
return oldName + " as " + newName
|
|
1419
|
+
}
|
|
1420
|
+
case shimast.KindExportSpecifier:
|
|
1421
|
+
specifier := parent.AsExportSpecifier()
|
|
1422
|
+
if specifier != nil && specifier.Name() == node && specifier.PropertyName == nil {
|
|
1423
|
+
return newName + " as " + oldName
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return newName
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
func unicornPreventAbbreviationsAvailableName(
|
|
1430
|
+
ctx *Context,
|
|
1431
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1432
|
+
desired string,
|
|
1433
|
+
scopes []*shimast.Node,
|
|
1434
|
+
occupied map[string][]unicornPreventAbbreviationsOccupiedName,
|
|
1435
|
+
generated map[string][]*unicornPreventAbbreviationsBinding,
|
|
1436
|
+
) string {
|
|
1437
|
+
candidate := desired
|
|
1438
|
+
if !unicornPreventAbbreviationsValidIdentifier(candidate) {
|
|
1439
|
+
candidate += "_"
|
|
1440
|
+
if !unicornPreventAbbreviationsValidIdentifier(candidate) {
|
|
1441
|
+
return ""
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
for unicornPreventAbbreviationsCheckerNameCollides(ctx, candidate, binding) ||
|
|
1445
|
+
unicornPreventAbbreviationsOccupiedNameCollides(candidate, binding.scope, occupied) ||
|
|
1446
|
+
unicornPreventAbbreviationsGeneratedNameCollides(candidate, binding, scopes, generated) {
|
|
1447
|
+
candidate += "_"
|
|
1448
|
+
}
|
|
1449
|
+
return candidate
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// ResolveName covers every declaration visible where the renamed binding is
|
|
1453
|
+
// declared or read, including compiler-provided globals which do not occur as
|
|
1454
|
+
// identifier nodes in the source file. The unresolved-reference pass remains
|
|
1455
|
+
// necessary because the checker intentionally returns nil for those names.
|
|
1456
|
+
func unicornPreventAbbreviationsCheckerNameCollides(
|
|
1457
|
+
ctx *Context,
|
|
1458
|
+
name string,
|
|
1459
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1460
|
+
) bool {
|
|
1461
|
+
if ctx == nil || ctx.Checker == nil || binding == nil {
|
|
1462
|
+
return false
|
|
1463
|
+
}
|
|
1464
|
+
meaning := shimast.SymbolFlagsValue | shimast.SymbolFlagsType |
|
|
1465
|
+
shimast.SymbolFlagsNamespace | shimast.SymbolFlagsAlias
|
|
1466
|
+
locations := make([]*shimast.Node, 0, len(binding.references)+1)
|
|
1467
|
+
locations = append(locations, binding.nameNode)
|
|
1468
|
+
locations = append(locations, binding.references...)
|
|
1469
|
+
seen := make(map[*shimast.Node]struct{}, len(locations))
|
|
1470
|
+
for _, location := range locations {
|
|
1471
|
+
if location == nil {
|
|
1472
|
+
continue
|
|
1473
|
+
}
|
|
1474
|
+
if _, duplicate := seen[location]; duplicate {
|
|
1475
|
+
continue
|
|
1476
|
+
}
|
|
1477
|
+
seen[location] = struct{}{}
|
|
1478
|
+
if ctx.Checker.ResolveName(name, location, meaning, false /*excludeGlobals*/) != nil {
|
|
1479
|
+
return true
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return false
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Each generated name retains its binding plus the precise scopes where it is
|
|
1486
|
+
// declared or referenced. This allows legal shadowing while still detecting
|
|
1487
|
+
// a newly renamed inner declaration that would capture an outer binding's read
|
|
1488
|
+
// in a deeper descendant scope.
|
|
1489
|
+
func unicornPreventAbbreviationsBindingReferenceScopes(
|
|
1490
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1491
|
+
) []*shimast.Node {
|
|
1492
|
+
if binding == nil {
|
|
1493
|
+
return nil
|
|
1494
|
+
}
|
|
1495
|
+
scopes := make([]*shimast.Node, 0, len(binding.references)+1)
|
|
1496
|
+
seen := make(map[*shimast.Node]struct{}, len(binding.references)+1)
|
|
1497
|
+
add := func(scope *shimast.Node) {
|
|
1498
|
+
if _, duplicate := seen[scope]; duplicate {
|
|
1499
|
+
return
|
|
1500
|
+
}
|
|
1501
|
+
seen[scope] = struct{}{}
|
|
1502
|
+
scopes = append(scopes, scope)
|
|
1503
|
+
}
|
|
1504
|
+
add(binding.scope)
|
|
1505
|
+
for _, scope := range unicornPreventAbbreviationsVarRestrictedScopes(binding.declaration, binding.scope) {
|
|
1506
|
+
add(scope)
|
|
1507
|
+
}
|
|
1508
|
+
for _, reference := range binding.references {
|
|
1509
|
+
add(unicornPreventAbbreviationsReferenceScope(reference))
|
|
1510
|
+
}
|
|
1511
|
+
return scopes
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// A var declaration is function-scoped, but ECMAScript early errors also
|
|
1515
|
+
// prohibit it from crossing lexical declarations in any containing block,
|
|
1516
|
+
// loop, switch, or catch before that function boundary. Record those syntactic
|
|
1517
|
+
// containers in addition to the var binding's function scope.
|
|
1518
|
+
func unicornPreventAbbreviationsVarRestrictedScopes(
|
|
1519
|
+
declaration *shimast.Node,
|
|
1520
|
+
bindingScope *shimast.Node,
|
|
1521
|
+
) []*shimast.Node {
|
|
1522
|
+
root := unicornPreventAbbreviationsRootDeclaration(declaration)
|
|
1523
|
+
if root == nil || root.Kind != shimast.KindVariableDeclaration || root.Parent == nil ||
|
|
1524
|
+
root.Parent.Kind != shimast.KindVariableDeclarationList || !shimast.IsVar(root.Parent) {
|
|
1525
|
+
return nil
|
|
1526
|
+
}
|
|
1527
|
+
scopes := make([]*shimast.Node, 0)
|
|
1528
|
+
for current := root.Parent.Parent; current != nil && current != bindingScope; current = current.Parent {
|
|
1529
|
+
switch current.Kind {
|
|
1530
|
+
case shimast.KindBlock:
|
|
1531
|
+
if current.Parent == nil || !unicornPreventAbbreviationsIsFunctionLike(current.Parent) {
|
|
1532
|
+
scopes = append(scopes, current)
|
|
1533
|
+
}
|
|
1534
|
+
case shimast.KindCaseBlock,
|
|
1535
|
+
shimast.KindForStatement,
|
|
1536
|
+
shimast.KindForInStatement,
|
|
1537
|
+
shimast.KindForOfStatement,
|
|
1538
|
+
shimast.KindCatchClause:
|
|
1539
|
+
scopes = append(scopes, current)
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return scopes
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
func unicornPreventAbbreviationsOccupiedNameCollides(
|
|
1546
|
+
name string,
|
|
1547
|
+
scope *shimast.Node,
|
|
1548
|
+
names map[string][]unicornPreventAbbreviationsOccupiedName,
|
|
1549
|
+
) bool {
|
|
1550
|
+
for _, occupied := range names[name] {
|
|
1551
|
+
if occupied.reference {
|
|
1552
|
+
if scope == nil || occupied.scope == nil || scope == occupied.scope ||
|
|
1553
|
+
unicornPreventAbbreviationsIsAncestor(scope, occupied.scope) {
|
|
1554
|
+
return true
|
|
1555
|
+
}
|
|
1556
|
+
continue
|
|
1557
|
+
}
|
|
1558
|
+
if scope == nil || occupied.scope == nil || scope == occupied.scope ||
|
|
1559
|
+
unicornPreventAbbreviationsIsAncestor(occupied.scope, scope) {
|
|
1560
|
+
return true
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
return false
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
func unicornPreventAbbreviationsGeneratedNameCollides(
|
|
1567
|
+
name string,
|
|
1568
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1569
|
+
scopes []*shimast.Node,
|
|
1570
|
+
names map[string][]*unicornPreventAbbreviationsBinding,
|
|
1571
|
+
) bool {
|
|
1572
|
+
for _, existing := range names[name] {
|
|
1573
|
+
existingScopes := unicornPreventAbbreviationsBindingReferenceScopes(existing)
|
|
1574
|
+
for _, scope := range scopes {
|
|
1575
|
+
for _, existingScope := range existingScopes {
|
|
1576
|
+
if scope == existingScope {
|
|
1577
|
+
return true
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
if binding == nil || existing == nil || binding.scope == nil || existing.scope == nil {
|
|
1582
|
+
return true
|
|
1583
|
+
}
|
|
1584
|
+
if unicornPreventAbbreviationsIsAncestor(binding.scope, existing.scope) {
|
|
1585
|
+
if unicornPreventAbbreviationsBindingHasReferenceInScope(binding, existing.scope) {
|
|
1586
|
+
return true
|
|
1587
|
+
}
|
|
1588
|
+
} else if unicornPreventAbbreviationsIsAncestor(existing.scope, binding.scope) {
|
|
1589
|
+
if unicornPreventAbbreviationsBindingHasReferenceInScope(existing, binding.scope) {
|
|
1590
|
+
return true
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return false
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
func unicornPreventAbbreviationsBindingHasReferenceInScope(
|
|
1598
|
+
binding *unicornPreventAbbreviationsBinding,
|
|
1599
|
+
scope *shimast.Node,
|
|
1600
|
+
) bool {
|
|
1601
|
+
if binding == nil || scope == nil {
|
|
1602
|
+
return true
|
|
1603
|
+
}
|
|
1604
|
+
for _, reference := range binding.references {
|
|
1605
|
+
referenceScope := unicornPreventAbbreviationsReferenceScope(reference)
|
|
1606
|
+
if referenceScope == nil || referenceScope == scope ||
|
|
1607
|
+
unicornPreventAbbreviationsIsAncestor(scope, referenceScope) {
|
|
1608
|
+
return true
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
return false
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
func unicornPreventAbbreviationsIsAncestor(ancestor, node *shimast.Node) bool {
|
|
1615
|
+
for current := node.Parent; current != nil; current = current.Parent {
|
|
1616
|
+
if current == ancestor {
|
|
1617
|
+
return true
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return false
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
func reportUnicornPreventAbbreviationsProperty(
|
|
1624
|
+
ctx *Context,
|
|
1625
|
+
node *shimast.Node,
|
|
1626
|
+
options unicornPreventAbbreviationsOptions,
|
|
1627
|
+
) {
|
|
1628
|
+
if node == nil || node.Kind != shimast.KindIdentifier || identifierText(node) == "__proto__" ||
|
|
1629
|
+
!unicornPreventAbbreviationsIsProperty(node) {
|
|
1630
|
+
return
|
|
1631
|
+
}
|
|
92
1632
|
name := identifierText(node)
|
|
93
|
-
|
|
1633
|
+
replacements := getUnicornPreventAbbreviationsNameReplacements(name, options, 3)
|
|
1634
|
+
if replacements.total == 0 {
|
|
94
1635
|
return
|
|
95
1636
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
1637
|
+
message := unicornPreventAbbreviationsMessage(name, replacements, "property")
|
|
1638
|
+
suggestions := make([]Suggestion, 0, len(replacements.samples))
|
|
1639
|
+
if replacements.total > 1 && (node.Parent == nil || node.Parent.Kind != shimast.KindExportSpecifier) {
|
|
1640
|
+
pos, end := tokenRange(ctx.File, node)
|
|
1641
|
+
if pos >= 0 {
|
|
1642
|
+
for _, replacement := range replacements.samples {
|
|
1643
|
+
// Property IdentifierNames may use reserved words; only lexical
|
|
1644
|
+
// identifier validity matters here.
|
|
1645
|
+
if !shimscanner.IsValidIdentifier(replacement) {
|
|
1646
|
+
continue
|
|
1647
|
+
}
|
|
1648
|
+
suggestions = append(suggestions, Suggestion{
|
|
1649
|
+
Title: fmt.Sprintf("Rename to `%s`.", replacement),
|
|
1650
|
+
Edits: []TextEdit{{Pos: pos, End: end, Text: replacement}},
|
|
1651
|
+
})
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
103
1654
|
}
|
|
104
|
-
|
|
1655
|
+
ctx.ReportFixSuggestions(node, message, nil, suggestions...)
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
func unicornPreventAbbreviationsIsProperty(node *shimast.Node) bool {
|
|
1659
|
+
if node == nil || node.Parent == nil {
|
|
1660
|
+
return false
|
|
1661
|
+
}
|
|
1662
|
+
parent := node.Parent
|
|
1663
|
+
switch parent.Kind {
|
|
1664
|
+
case shimast.KindPropertyAccessExpression:
|
|
1665
|
+
access := parent.AsPropertyAccessExpression()
|
|
1666
|
+
if access == nil || access.Name() != node || parent.Parent == nil {
|
|
1667
|
+
return false
|
|
1668
|
+
}
|
|
1669
|
+
switch parent.Parent.Kind {
|
|
1670
|
+
case shimast.KindBinaryExpression:
|
|
1671
|
+
expression := parent.Parent.AsBinaryExpression()
|
|
1672
|
+
return expression != nil && expression.Left == parent && expression.OperatorToken != nil &&
|
|
1673
|
+
isAssignmentOperator(expression.OperatorToken.Kind)
|
|
1674
|
+
case shimast.KindPrefixUnaryExpression:
|
|
1675
|
+
expression := parent.Parent.AsPrefixUnaryExpression()
|
|
1676
|
+
return expression != nil && expression.Operand == parent &&
|
|
1677
|
+
(expression.Operator == shimast.KindPlusPlusToken || expression.Operator == shimast.KindMinusMinusToken)
|
|
1678
|
+
case shimast.KindPostfixUnaryExpression:
|
|
1679
|
+
expression := parent.Parent.AsPostfixUnaryExpression()
|
|
1680
|
+
return expression != nil && expression.Operand == parent &&
|
|
1681
|
+
(expression.Operator == shimast.KindPlusPlusToken || expression.Operator == shimast.KindMinusMinusToken)
|
|
1682
|
+
}
|
|
1683
|
+
case shimast.KindPropertyAssignment:
|
|
1684
|
+
assignment := parent.AsPropertyAssignment()
|
|
1685
|
+
return assignment != nil && assignment.Name() == node &&
|
|
1686
|
+
!isDestructuringAssignmentTarget(parent)
|
|
1687
|
+
case shimast.KindMethodDeclaration,
|
|
1688
|
+
shimast.KindPropertyDeclaration,
|
|
1689
|
+
shimast.KindGetAccessor,
|
|
1690
|
+
shimast.KindSetAccessor,
|
|
1691
|
+
shimast.KindMethodSignature,
|
|
1692
|
+
shimast.KindPropertySignature:
|
|
1693
|
+
return parent.Name() == node
|
|
1694
|
+
case shimast.KindExportSpecifier:
|
|
1695
|
+
specifier := parent.AsExportSpecifier()
|
|
1696
|
+
return specifier != nil && specifier.PropertyName != nil && specifier.Name() == node
|
|
1697
|
+
}
|
|
1698
|
+
return false
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
func reportUnicornPreventAbbreviationsFilename(
|
|
1702
|
+
ctx *Context,
|
|
1703
|
+
node *shimast.Node,
|
|
1704
|
+
options unicornPreventAbbreviationsOptions,
|
|
1705
|
+
) {
|
|
1706
|
+
filenameWithPath := ctx.File.FileName()
|
|
1707
|
+
if filenameWithPath == "" || unicornPreventAbbreviationsIsVirtualFilename(filenameWithPath) {
|
|
1708
|
+
return
|
|
1709
|
+
}
|
|
1710
|
+
filename := filepath.Base(filenameWithPath)
|
|
1711
|
+
extension := unicornPreventAbbreviationsFilenameExtension(filename)
|
|
1712
|
+
basename := strings.TrimSuffix(filename, extension)
|
|
1713
|
+
replacements := getUnicornPreventAbbreviationsNameReplacements(basename, options, 3)
|
|
1714
|
+
if replacements.total == 0 {
|
|
105
1715
|
return
|
|
106
1716
|
}
|
|
107
|
-
|
|
1717
|
+
for index := range replacements.samples {
|
|
1718
|
+
replacements.samples[index] += extension
|
|
1719
|
+
}
|
|
1720
|
+
ctx.Report(node, unicornPreventAbbreviationsMessage(filename, replacements, "filename"))
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
func unicornPreventAbbreviationsIsVirtualFilename(filename string) bool {
|
|
1724
|
+
return filename == "<input>" || filename == "<text>"
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// Node's path.extname treats a leading-dot basename such as `.err` as having
|
|
1728
|
+
// no extension, while filepath.Ext returns the whole basename. Preserve the
|
|
1729
|
+
// upstream filename contract without changing platform-specific separators.
|
|
1730
|
+
func unicornPreventAbbreviationsFilenameExtension(filename string) string {
|
|
1731
|
+
extension := filepath.Ext(filename)
|
|
1732
|
+
if extension == filename {
|
|
1733
|
+
return ""
|
|
1734
|
+
}
|
|
1735
|
+
return extension
|
|
108
1736
|
}
|
|
109
1737
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1738
|
+
func getUnicornPreventAbbreviationsNameReplacements(
|
|
1739
|
+
name string,
|
|
1740
|
+
options unicornPreventAbbreviationsOptions,
|
|
1741
|
+
limit int,
|
|
1742
|
+
) unicornPreventAbbreviationsNameReplacements {
|
|
1743
|
+
if name == "" || unicornPreventAbbreviationsUppercase(name) == name || options.allowList[name] {
|
|
1744
|
+
return unicornPreventAbbreviationsNameReplacements{}
|
|
1745
|
+
}
|
|
1746
|
+
for _, pattern := range options.ignore {
|
|
1747
|
+
if pattern.MatchString(name) {
|
|
1748
|
+
return unicornPreventAbbreviationsNameReplacements{}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
exact := getUnicornPreventAbbreviationsWordReplacements(name, options)
|
|
1753
|
+
if len(exact) > 0 {
|
|
1754
|
+
total := len(exact)
|
|
1755
|
+
if len(exact) > limit {
|
|
1756
|
+
exact = exact[:limit]
|
|
1757
|
+
}
|
|
1758
|
+
return unicornPreventAbbreviationsNameReplacements{total: total, samples: exact}
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
words := splitUnicornPreventAbbreviationsWords(name)
|
|
1762
|
+
combinations := make([][]string, 0, len(words))
|
|
1763
|
+
total := 1
|
|
1764
|
+
changed := false
|
|
1765
|
+
for _, word := range words {
|
|
1766
|
+
replacements := getUnicornPreventAbbreviationsWordReplacements(word, options)
|
|
1767
|
+
if len(replacements) == 0 {
|
|
1768
|
+
replacements = []string{word}
|
|
1769
|
+
} else {
|
|
1770
|
+
changed = true
|
|
1771
|
+
}
|
|
1772
|
+
combinations = append(combinations, replacements)
|
|
1773
|
+
if total > math.MaxInt/len(replacements) {
|
|
1774
|
+
total = math.MaxInt
|
|
1775
|
+
} else {
|
|
1776
|
+
total *= len(replacements)
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
if !changed {
|
|
1780
|
+
return unicornPreventAbbreviationsNameReplacements{}
|
|
1781
|
+
}
|
|
1782
|
+
samples := cartesianUnicornPreventAbbreviationsSamples(combinations, limit)
|
|
1783
|
+
for index, parts := range samples {
|
|
1784
|
+
for part := len(parts) - 1; part > 0; part-- {
|
|
1785
|
+
if unicornPreventAbbreviationsASCIIWord(parts[part]) && strings.HasSuffix(parts[part-1], parts[part]) {
|
|
1786
|
+
parts = append(parts[:part], parts[part+1:]...)
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
samples[index] = parts
|
|
1790
|
+
}
|
|
1791
|
+
joined := make([]string, 0, len(samples))
|
|
1792
|
+
for _, parts := range samples {
|
|
1793
|
+
joined = append(joined, strings.Join(parts, ""))
|
|
1794
|
+
}
|
|
1795
|
+
return unicornPreventAbbreviationsNameReplacements{total: total, samples: joined}
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
func getUnicornPreventAbbreviationsWordReplacements(
|
|
1799
|
+
word string,
|
|
1800
|
+
options unicornPreventAbbreviationsOptions,
|
|
1801
|
+
) []string {
|
|
1802
|
+
if word == "" || unicornPreventAbbreviationsUppercase(word) == word || options.allowList[word] {
|
|
1803
|
+
return nil
|
|
1804
|
+
}
|
|
1805
|
+
keys := []string{
|
|
1806
|
+
lowerUnicornPreventAbbreviationsFirst(word),
|
|
1807
|
+
word,
|
|
1808
|
+
upperUnicornPreventAbbreviationsFirst(word),
|
|
1809
|
+
}
|
|
1810
|
+
var replacements []string
|
|
1811
|
+
for _, key := range keys {
|
|
1812
|
+
if configured, ok := options.replacements[key]; ok {
|
|
1813
|
+
replacements = configured
|
|
1814
|
+
break
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
if len(replacements) == 0 {
|
|
1818
|
+
return nil
|
|
1819
|
+
}
|
|
1820
|
+
transformed := make([]string, 0, len(replacements))
|
|
1821
|
+
upperFirst := unicornPreventAbbreviationsStartsUpper(word)
|
|
1822
|
+
for _, replacement := range replacements {
|
|
1823
|
+
if upperFirst {
|
|
1824
|
+
transformed = append(transformed, upperUnicornPreventAbbreviationsFirst(replacement))
|
|
1825
|
+
} else {
|
|
1826
|
+
transformed = append(transformed, lowerUnicornPreventAbbreviationsFirst(replacement))
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
sort.Strings(transformed)
|
|
1830
|
+
return transformed
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
func splitUnicornPreventAbbreviationsWords(name string) []string {
|
|
1834
|
+
runes := []rune(name)
|
|
1835
|
+
if len(runes) == 0 {
|
|
1836
|
+
return nil
|
|
1837
|
+
}
|
|
1838
|
+
words := make([]string, 0, len(runes))
|
|
1839
|
+
start := 0
|
|
1840
|
+
flush := func(end int) {
|
|
1841
|
+
if end > start {
|
|
1842
|
+
words = append(words, string(runes[start:end]))
|
|
1843
|
+
}
|
|
1844
|
+
start = end
|
|
1845
|
+
}
|
|
1846
|
+
for index, current := range runes {
|
|
1847
|
+
if index == 0 {
|
|
1848
|
+
continue
|
|
1849
|
+
}
|
|
1850
|
+
// This is the rune-level equivalent of upstream's
|
|
1851
|
+
// /(?=\P{Lowercase_Letter})|(?<=\P{Letter})/u split expression.
|
|
1852
|
+
if !unicode.Is(unicode.Ll, current) || !unicode.IsLetter(runes[index-1]) {
|
|
1853
|
+
flush(index)
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
flush(len(runes))
|
|
1857
|
+
return words
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
func cartesianUnicornPreventAbbreviationsSamples(parts [][]string, limit int) [][]string {
|
|
1861
|
+
if len(parts) == 0 || limit <= 0 {
|
|
1862
|
+
return nil
|
|
1863
|
+
}
|
|
1864
|
+
samples := make([][]string, 0, limit)
|
|
1865
|
+
current := make([]string, len(parts))
|
|
1866
|
+
var visit func(int)
|
|
1867
|
+
visit = func(index int) {
|
|
1868
|
+
if len(samples) >= limit {
|
|
1869
|
+
return
|
|
1870
|
+
}
|
|
1871
|
+
if index == len(parts) {
|
|
1872
|
+
samples = append(samples, append([]string(nil), current...))
|
|
1873
|
+
return
|
|
1874
|
+
}
|
|
1875
|
+
for _, value := range parts[index] {
|
|
1876
|
+
current[index] = value
|
|
1877
|
+
visit(index + 1)
|
|
1878
|
+
if len(samples) >= limit {
|
|
1879
|
+
return
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
visit(0)
|
|
1884
|
+
return samples
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
func unicornPreventAbbreviationsMessage(
|
|
1888
|
+
discouragedName string,
|
|
1889
|
+
replacements unicornPreventAbbreviationsNameReplacements,
|
|
1890
|
+
nameType string,
|
|
1891
|
+
) string {
|
|
1892
|
+
const suffix = " A more descriptive name will do too."
|
|
1893
|
+
if replacements.total == 1 && len(replacements.samples) == 1 {
|
|
1894
|
+
return fmt.Sprintf(
|
|
1895
|
+
"The %s `%s` should be named `%s`.%s",
|
|
1896
|
+
nameType,
|
|
1897
|
+
discouragedName,
|
|
1898
|
+
replacements.samples[0],
|
|
1899
|
+
suffix,
|
|
1900
|
+
)
|
|
1901
|
+
}
|
|
1902
|
+
names := make([]string, 0, len(replacements.samples))
|
|
1903
|
+
for _, replacement := range replacements.samples {
|
|
1904
|
+
names = append(names, "`"+replacement+"`")
|
|
1905
|
+
}
|
|
1906
|
+
rendered := strings.Join(names, ", ")
|
|
1907
|
+
omitted := replacements.total - len(replacements.samples)
|
|
1908
|
+
if omitted > 0 {
|
|
1909
|
+
count := fmt.Sprintf("%d", omitted)
|
|
1910
|
+
if omitted > 99 {
|
|
1911
|
+
count = "99+"
|
|
1912
|
+
}
|
|
1913
|
+
rendered += fmt.Sprintf(", ... (%s more omitted)", count)
|
|
1914
|
+
}
|
|
1915
|
+
return fmt.Sprintf(
|
|
1916
|
+
"Please rename the %s `%s`. Suggested names are: %s.%s",
|
|
1917
|
+
nameType,
|
|
1918
|
+
discouragedName,
|
|
1919
|
+
rendered,
|
|
1920
|
+
suffix,
|
|
1921
|
+
)
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
func unicornPreventAbbreviationsValidIdentifier(name string) bool {
|
|
1925
|
+
if !shimscanner.IsValidIdentifier(name) {
|
|
1926
|
+
return false
|
|
1927
|
+
}
|
|
1928
|
+
if _, reserved := unicornPreventAbbreviationsReservedWords[name]; reserved {
|
|
1929
|
+
return false
|
|
1930
|
+
}
|
|
1931
|
+
return true
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
func unicornPreventAbbreviationsASCIIWord(value string) bool {
|
|
1935
|
+
if value == "" {
|
|
1936
|
+
return false
|
|
1937
|
+
}
|
|
1938
|
+
for _, character := range value {
|
|
1939
|
+
if character > unicode.MaxASCII || !unicode.IsLetter(character) {
|
|
119
1940
|
return false
|
|
120
1941
|
}
|
|
121
1942
|
}
|
|
122
1943
|
return true
|
|
123
1944
|
}
|
|
124
1945
|
|
|
1946
|
+
func unicornPreventAbbreviationsStartsUpper(value string) bool {
|
|
1947
|
+
first, size := utf8.DecodeRuneInString(value)
|
|
1948
|
+
// Upstream compares the first UTF-16 code unit with its upper-case form.
|
|
1949
|
+
// Consequently uncased ASCII characters (`$`, `_`, digits) and either half
|
|
1950
|
+
// of an astral rune count as upper-first too.
|
|
1951
|
+
return first > 0xFFFF || unicornPreventAbbreviationsUppercase(value[:size]) == value[:size]
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
func lowerUnicornPreventAbbreviationsFirst(value string) string {
|
|
1955
|
+
first, size := utf8.DecodeRuneInString(value)
|
|
1956
|
+
if size == 0 {
|
|
1957
|
+
return value
|
|
1958
|
+
}
|
|
1959
|
+
// JavaScript's charAt(0) sees only the high surrogate of an astral rune, so
|
|
1960
|
+
// the final upstream implementation leaves that first rune unchanged.
|
|
1961
|
+
if first > 0xFFFF {
|
|
1962
|
+
return value
|
|
1963
|
+
}
|
|
1964
|
+
if first <= unicode.MaxASCII {
|
|
1965
|
+
return string(unicode.ToLower(first)) + value[size:]
|
|
1966
|
+
}
|
|
1967
|
+
return cases.Lower(language.Und).String(value[:size]) + value[size:]
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1970
|
+
func upperUnicornPreventAbbreviationsFirst(value string) string {
|
|
1971
|
+
first, size := utf8.DecodeRuneInString(value)
|
|
1972
|
+
if size == 0 {
|
|
1973
|
+
return value
|
|
1974
|
+
}
|
|
1975
|
+
if first > 0xFFFF {
|
|
1976
|
+
return value
|
|
1977
|
+
}
|
|
1978
|
+
if first <= unicode.MaxASCII {
|
|
1979
|
+
return string(unicode.ToUpper(first)) + value[size:]
|
|
1980
|
+
}
|
|
1981
|
+
return unicornPreventAbbreviationsUppercase(value[:size]) + value[size:]
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
func unicornPreventAbbreviationsUppercase(value string) string {
|
|
1985
|
+
for index := 0; index < len(value); index++ {
|
|
1986
|
+
if value[index] >= utf8.RuneSelf {
|
|
1987
|
+
return cases.Upper(language.Und).String(value)
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
return strings.ToUpper(value)
|
|
1991
|
+
}
|
|
1992
|
+
|
|
125
1993
|
func init() {
|
|
126
1994
|
Register(unicornPreventAbbreviations{})
|
|
127
1995
|
}
|