@@ -201,137 +201,6 @@ func shouldConvertToDotNotation(ctx rule.RuleContext, node *ast.Node, opts DotNo
201201 return true
202202}
203203
204- // computeDotNotationDiagnostic computes a single diagnostic for a bracket access if it should be converted
205- // to dot notation. Returns start, end, message and true if a diagnostic should be reported; otherwise ok=false.
206- func computeDotNotationDiagnostic (ctx rule.RuleContext , node * ast.Node , opts DotNotationOptions , allowIndexSignaturePropertyAccess bool , patternRegex * regexp.Regexp ) (int , int , rule.RuleMessage , bool ) {
207- if ! ast .IsElementAccessExpression (node ) {
208- return 0 , 0 , rule.RuleMessage {}, false
209- }
210-
211- elementAccess := node .AsElementAccessExpression ()
212- if elementAccess == nil {
213- return 0 , 0 , rule.RuleMessage {}, false
214- }
215-
216- argument := elementAccess .ArgumentExpression
217- if argument == nil {
218- return 0 , 0 , rule.RuleMessage {}, false
219- }
220-
221- // Only handle string literals, numeric literals, and identifiers that evaluate to strings
222- var propertyName string
223- isValidProperty := false
224-
225- switch argument .Kind {
226- case ast .KindStringLiteral :
227- stringLiteral := argument .AsStringLiteral ()
228- if stringLiteral == nil {
229- return 0 , 0 , rule.RuleMessage {}, false
230- }
231- // Remove quotes from string literal text
232- text := stringLiteral .Text
233- if len (text ) >= 2 && ((text [0 ] == '"' && text [len (text )- 1 ] == '"' ) || (text [0 ] == '\'' && text [len (text )- 1 ] == '\'' )) {
234- text = text [1 : len (text )- 1 ]
235- }
236- propertyName = text
237- isValidProperty = true
238- case ast .KindNoSubstitutionTemplateLiteral :
239- // Handle `obj[`foo`]` (no expressions)
240- propertyName = argument .AsNoSubstitutionTemplateLiteral ().Text
241- isValidProperty = true
242- case ast .KindNumericLiteral :
243- // Numeric properties should use bracket notation
244- return 0 , 0 , rule.RuleMessage {}, false
245- case ast .KindNullKeyword , ast .KindTrueKeyword , ast .KindFalseKeyword :
246- // These are allowed as dot notation
247- propertyName = getKeywordText (argument )
248- isValidProperty = true
249- default :
250- // Other cases (template literals, identifiers, etc.) should keep bracket notation
251- return 0 , 0 , rule.RuleMessage {}, false
252- }
253-
254- if ! isValidProperty || propertyName == "" {
255- return 0 , 0 , rule.RuleMessage {}, false
256- }
257-
258- // Check if it's a valid identifier
259- if ! isValidIdentifierName (propertyName ) {
260- return 0 , 0 , rule.RuleMessage {}, false
261- }
262-
263- // Check pattern allowlist
264- if patternRegex != nil && patternRegex .MatchString (propertyName ) {
265- return 0 , 0 , rule.RuleMessage {}, false
266- }
267-
268- // Check for keywords
269- if ! opts .AllowKeywords && isReservedWord (propertyName ) {
270- return 0 , 0 , rule.RuleMessage {}, false
271- }
272-
273- // Check for private/protected/index signature access
274- if shouldAllowBracketNotation (ctx , node , propertyName , opts , allowIndexSignaturePropertyAccess ) {
275- return 0 , 0 , rule.RuleMessage {}, false
276- }
277-
278- // Determine range start with hybrid logic to match typescript-eslint tests:
279- // - If '[' begins a new visual access (only whitespace before on the line), start at '[' column
280- // - If '[' follows an identifier/dot/closing bracket/paren on the same line (e.g., x['a']), start at the beginning of the line
281- start := node .Pos ()
282- if text := ctx .SourceFile .Text (); node .End () <= len (text ) {
283- // Prefer computing '[' from the argument position to avoid capturing prior '[' in chained expressions
284- bracketPos := - 1
285- if elementAccess .ArgumentExpression != nil {
286- candidate := elementAccess .ArgumentExpression .Pos () - 1
287- if candidate >= node .Pos () && candidate < node .End () && candidate >= 0 && candidate < len (text ) && text [candidate ] == '[' {
288- bracketPos = candidate
289- }
290- }
291- // Fallback: scan within node span
292- if bracketPos == - 1 {
293- slice := text [node .Pos ():node .End ()]
294- for i := 0 ; i < len (slice ); i ++ {
295- if slice [i ] == '[' {
296- bracketPos = node .Pos () + i
297- break
298- }
299- }
300- }
301- if bracketPos != - 1 {
302- // Compute start-of-line using scanner helpers for exact column mapping
303- lineIndex , _ := scanner .GetLineAndCharacterOfPosition (ctx .SourceFile , bracketPos )
304- lineStart := scanner .GetPositionOfLineAndCharacter (ctx .SourceFile , lineIndex , 0 )
305- prev := bracketPos - 1
306- prevNonSpace := byte ('\n' )
307- for prev >= lineStart {
308- if text [prev ] != ' ' && text [prev ] != '\t' {
309- prevNonSpace = text [prev ]
310- break
311- }
312- prev --
313- }
314- // If previous non-space is identifier/dot/closing bracket/paren, use line start;
315- // otherwise align to one column after the leading indentation to match TS snapshots
316- if (prev >= lineStart ) && ((prevNonSpace >= 'a' && prevNonSpace <= 'z' ) || (prevNonSpace >= 'A' && prevNonSpace <= 'Z' ) || (prevNonSpace >= '0' && prevNonSpace <= '9' ) || prevNonSpace == '_' || prevNonSpace == '$' || prevNonSpace == '.' || prevNonSpace == ')' || prevNonSpace == ']' ) {
317- start = lineStart
318- } else {
319- // bracketPos points at '[' which snapshots expect at column 4 in multiline case; offset by 1
320- start = bracketPos + 1
321- if start > node .End () {
322- start = bracketPos
323- }
324- }
325- }
326- }
327- msg := rule.RuleMessage {
328- Id : "useDot" ,
329- Description : fmt .Sprintf ("['%s'] is better written in dot notation." , propertyName ),
330- }
331- // return computed range to be flushed later in source order
332- return start , node .End (), msg , true
333- }
334-
335204func checkPropertyAccessKeywords (ctx rule.RuleContext , node * ast.Node ) {
336205 if ! ast .IsPropertyAccessExpression (node ) {
337206 return
@@ -517,7 +386,7 @@ func createFix(ctx rule.RuleContext, node *ast.Node, propertyName string) rule.R
517386 text := ctx .SourceFile .Text ()
518387 if node .End () <= len (text ) {
519388 slice := text [node .Pos ():node .End ()]
520- for i := 0 ; i < len ( slice ); i ++ {
389+ for i := range slice {
521390 if slice [i ] == '[' {
522391 start = node .Pos () + i
523392 break
0 commit comments