Skip to content

Commit d2753cd

Browse files
fix: resolve CI linting issues
- Remove unused functions (computeDotNotationDiagnostic, getMissingAccessibilityRange) - Fix staticcheck switch statement issues with tagged switches - Fix empty branch in api.go - Replace fmt.Errorf with errors.New for simple error messages - Update for loops to use integer range syntax for Go 1.22+ - Remove unused imports (strings, scanner) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a9cb08d commit d2753cd

File tree

5 files changed

+54
-288
lines changed

5 files changed

+54
-288
lines changed

cmd/rslint/api.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"errors"
45
"fmt"
56
"os"
67
"sync"
@@ -114,7 +115,7 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
114115
if req.LanguageOptions != nil && req.LanguageOptions.ParserOptions != nil && req.LanguageOptions.ParserOptions.Project != nil {
115116
// Use project from languageOptions
116117
configDirectory = currentDirectory
117-
118+
118119
var projectPaths []string
119120
switch project := req.LanguageOptions.ParserOptions.Project.(type) {
120121
case string:
@@ -128,11 +129,11 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
128129
}
129130
}
130131
}
131-
132+
132133
if len(projectPaths) == 0 {
133-
return nil, fmt.Errorf("no valid project paths found in languageOptions")
134+
return nil, errors.New("no valid project paths found in languageOptions")
134135
}
135-
136+
136137
// Resolve and validate all project paths
137138
for _, projectPath := range projectPaths {
138139
resolvedPath := tspath.ResolvePath(currentDirectory, projectPath)
@@ -217,10 +218,12 @@ func (h *IPCHandler) HandleLint(req api.LintRequest) (*api.LintResponse, error)
217218
var found bool
218219
if option, found = req.RuleOptions[r.Name]; found {
219220
// Found with short name (e.g., "member-ordering")
221+
// option and found are set correctly by the if condition
220222
} else if option, found = req.RuleOptions["@typescript-eslint/"+r.Name]; found {
221223
// Found with full name (e.g., "@typescript-eslint/member-ordering")
224+
// option and found are set correctly by the if condition
222225
}
223-
226+
224227
if found {
225228
rulesWithOptions = append(rulesWithOptions, RuleWithOption{
226229
rule: r,

internal/rules/dot_notation/dot_notation.go

Lines changed: 1 addition & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
335204
func 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

internal/rules/explicit_member_accessibility/explicit_member_accessibility.go

Lines changed: 0 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ package explicit_member_accessibility
33
import (
44
"fmt"
55

6-
"strings"
7-
86
"github.com/microsoft/typescript-go/shim/ast"
97
"github.com/microsoft/typescript-go/shim/core"
10-
"github.com/microsoft/typescript-go/shim/scanner"
118
"github.com/web-infra-dev/rslint/internal/rule"
129
"github.com/web-infra-dev/rslint/internal/utils"
1310
)
@@ -205,123 +202,6 @@ func getNodeType(node *ast.Node, memberKind string) string {
205202
// Removed getMemberHeadLoc and getParameterPropertyHeadLoc functions
206203
// Now using ReportNode directly which handles positioning correctly
207204

208-
func getMissingAccessibilityRange(ctx rule.RuleContext, node *ast.Node) core.TextRange {
209-
// Default to node's name range when available
210-
findAccessorKeywordStart := func(nameRange core.TextRange) int {
211-
// Search backwards from name for 'get' or 'set' keyword within the declaration span
212-
text := ctx.SourceFile.Text()
213-
startBound := node.Pos()
214-
endBound := nameRange.Pos()
215-
if startBound < 0 || endBound > len(text) || startBound >= endBound {
216-
return nameRange.Pos()
217-
}
218-
snippet := text[startBound:endBound]
219-
// look for last occurrence to get the actual keyword near the name
220-
idxGet := strings.LastIndex(snippet, "get")
221-
idxSet := strings.LastIndex(snippet, "set")
222-
idx := -1
223-
kw := ""
224-
if idxGet > idxSet {
225-
idx = idxGet
226-
kw = "get"
227-
} else {
228-
idx = idxSet
229-
kw = "set"
230-
}
231-
if idx >= 0 {
232-
// ensure simple word boundary (whitespace or start before; whitespace/paren after)
233-
abs := startBound + idx
234-
beforeOk := abs == startBound || (abs > 0 && (text[abs-1] == ' ' || text[abs-1] == '\t' || text[abs-1] == '\n'))
235-
afterPos := abs + len(kw)
236-
afterOk := afterPos < len(text) && (text[afterPos] == ' ' || text[afterPos] == '\t' || text[afterPos] == '\n' || text[afterPos] == '(')
237-
if beforeOk && afterOk {
238-
return abs
239-
}
240-
}
241-
return nameRange.Pos()
242-
}
243-
switch node.Kind {
244-
case ast.KindConstructor:
245-
// Highlight the 'constructor' keyword only
246-
return scanner.GetRangeOfTokenAtPosition(ctx.SourceFile, node.Pos())
247-
case ast.KindMethodDeclaration:
248-
m := node.AsMethodDeclaration()
249-
nameNode := m.Name()
250-
if nameNode != nil {
251-
nameRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode)
252-
start := nameRange.Pos()
253-
// If abstract, start from 'abstract'
254-
if m.Modifiers() != nil {
255-
for _, mod := range m.Modifiers().Nodes {
256-
if mod.Kind == ast.KindAbstractKeyword {
257-
start = mod.Pos()
258-
break
259-
}
260-
}
261-
}
262-
nameText, _ := utils.GetNameFromMember(ctx.SourceFile, nameNode)
263-
end := nameRange.Pos() + len(nameText)
264-
return core.NewTextRange(start, end)
265-
}
266-
return utils.TrimNodeTextRange(ctx.SourceFile, node)
267-
case ast.KindGetAccessor:
268-
g := node.AsGetAccessorDeclaration()
269-
nameNode := g.Name()
270-
if nameNode != nil {
271-
nameRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode)
272-
// Start at the 'get' keyword token by scanning between node start and name
273-
start := findAccessorKeywordStart(nameRange)
274-
nameText, _ := utils.GetNameFromMember(ctx.SourceFile, nameNode)
275-
end := nameRange.Pos() + len(nameText)
276-
return core.NewTextRange(start, end)
277-
}
278-
return utils.TrimNodeTextRange(ctx.SourceFile, node)
279-
case ast.KindSetAccessor:
280-
s := node.AsSetAccessorDeclaration()
281-
nameNode := s.Name()
282-
if nameNode != nil {
283-
nameRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode)
284-
// Start at the 'set' keyword token by scanning between node start and name
285-
start := findAccessorKeywordStart(nameRange)
286-
nameText, _ := utils.GetNameFromMember(ctx.SourceFile, nameNode)
287-
end := nameRange.Pos() + len(nameText)
288-
return core.NewTextRange(start, end)
289-
}
290-
return utils.TrimNodeTextRange(ctx.SourceFile, node)
291-
case ast.KindPropertyDeclaration:
292-
p := node.AsPropertyDeclaration()
293-
nameNode := p.Name()
294-
if nameNode != nil {
295-
nameRange := utils.TrimNodeTextRange(ctx.SourceFile, nameNode)
296-
start := nameRange.Pos()
297-
if p.Modifiers() != nil {
298-
// Prefer abstract start if present
299-
for _, mod := range p.Modifiers().Nodes {
300-
if mod.Kind == ast.KindAbstractKeyword {
301-
start = mod.Pos()
302-
break
303-
}
304-
}
305-
// Otherwise, if accessor keyword present, start there to include `accessor foo`
306-
if start == nameRange.Pos() {
307-
for _, mod := range p.Modifiers().Nodes {
308-
if mod.Kind == ast.KindAccessorKeyword {
309-
start = mod.Pos()
310-
break
311-
}
312-
}
313-
}
314-
}
315-
nameText, _ := utils.GetNameFromMember(ctx.SourceFile, nameNode)
316-
end := nameRange.Pos() + len(nameText)
317-
return core.NewTextRange(start, end)
318-
}
319-
return utils.TrimNodeTextRange(ctx.SourceFile, node)
320-
}
321-
// Fallback
322-
return utils.TrimNodeTextRange(ctx.SourceFile, node)
323-
}
324-
325205
var ExplicitMemberAccessibilityRule = rule.Rule{
326206

327207
Name: "explicit-member-accessibility",

internal/rules/member_ordering/member_ordering.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -357,18 +357,19 @@ func isFunctionExpression(node *ast.Node) bool {
357357
}
358358

359359
func getMemberName(node *ast.Node, sourceFile *ast.SourceFile) string {
360-
switch node.Kind {
360+
switch kind := node.Kind; kind {
361361
case ast.KindPropertySignature, ast.KindMethodSignature,
362362
ast.KindPropertyDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
363363
// For signatures, the node itself is the name node
364364
var nameNode *ast.Node
365-
if node.Kind == ast.KindPropertyDeclaration {
365+
switch kind {
366+
case ast.KindPropertyDeclaration:
366367
nameNode = node.AsPropertyDeclaration().Name()
367-
} else if node.Kind == ast.KindGetAccessor {
368+
case ast.KindGetAccessor:
368369
nameNode = node.AsGetAccessorDeclaration().Name()
369-
} else if node.Kind == ast.KindSetAccessor {
370+
case ast.KindSetAccessor:
370371
nameNode = node.AsSetAccessorDeclaration().Name()
371-
} else {
372+
default:
372373
nameNode = node
373374
}
374375
name, _ := utils.GetNameFromMember(sourceFile, nameNode)
@@ -402,20 +403,21 @@ func getMemberName(node *ast.Node, sourceFile *ast.SourceFile) string {
402403
// For quoted literal member names (e.g. "b.c"), this strips the surrounding quotes
403404
// so comparisons are based on the actual text content rather than quote characters.
404405
func getMemberSortName(node *ast.Node, sourceFile *ast.SourceFile) string {
405-
switch node.Kind {
406+
switch kind := node.Kind; kind {
406407
case ast.KindPropertySignature, ast.KindMethodSignature,
407408
ast.KindPropertyDeclaration, ast.KindGetAccessor, ast.KindSetAccessor,
408409
ast.KindMethodDeclaration:
409410
var nameNode *ast.Node
410-
if node.Kind == ast.KindPropertyDeclaration {
411+
switch kind {
412+
case ast.KindPropertyDeclaration:
411413
nameNode = node.AsPropertyDeclaration().Name()
412-
} else if node.Kind == ast.KindGetAccessor {
414+
case ast.KindGetAccessor:
413415
nameNode = node.AsGetAccessorDeclaration().Name()
414-
} else if node.Kind == ast.KindSetAccessor {
416+
case ast.KindSetAccessor:
415417
nameNode = node.AsSetAccessorDeclaration().Name()
416-
} else if node.Kind == ast.KindMethodDeclaration {
418+
case ast.KindMethodDeclaration:
417419
nameNode = node.AsMethodDeclaration().Name()
418-
} else {
420+
default:
419421
nameNode = node
420422
}
421423
name, nameType := utils.GetNameFromMember(sourceFile, nameNode)
@@ -444,7 +446,7 @@ func getMemberSnippet(sourceFile *ast.SourceFile, node *ast.Node) string {
444446
text := sourceFile.Text()[r.Pos():r.End()]
445447
// Take only the first line and trim
446448
end := len(text)
447-
for i := 0; i < len(text); i++ {
449+
for i := range text {
448450
if text[i] == '\n' || text[i] == '\r' {
449451
end = i
450452
break

0 commit comments

Comments
 (0)