Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import (
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/array_type"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/await_thenable"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/ban_ts_comment"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/ban_tslint_comment"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/ban_types"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/class_literal_property_style"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/class_methods_use_this"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_generic_constructors"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_indexed_object_style"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_return"
Expand All @@ -24,6 +26,7 @@ import (
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_type_exports"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/consistent_type_imports"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/default_param_last"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/explicit_function_return_type"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_array_delete"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_base_to_string"
"github.com/web-infra-dev/rslint/internal/plugins/typescript/rules/no_confusing_void_expression"
Expand Down Expand Up @@ -365,8 +368,10 @@ func registerAllTypeScriptEslintPluginRules() {
GlobalRuleRegistry.Register("@typescript-eslint/array-type", array_type.ArrayTypeRule)
GlobalRuleRegistry.Register("@typescript-eslint/await-thenable", await_thenable.AwaitThenableRule)
GlobalRuleRegistry.Register("@typescript-eslint/ban-ts-comment", ban_ts_comment.BanTsCommentRule)
GlobalRuleRegistry.Register("@typescript-eslint/ban-tslint-comment", ban_tslint_comment.BanTslintCommentRule)
GlobalRuleRegistry.Register("@typescript-eslint/ban-types", ban_types.BanTypesRule)
GlobalRuleRegistry.Register("@typescript-eslint/class-literal-property-style", class_literal_property_style.ClassLiteralPropertyStyleRule)
GlobalRuleRegistry.Register("@typescript-eslint/class-methods-use-this", class_methods_use_this.ClassMethodsUseThisRule)
GlobalRuleRegistry.Register("@typescript-eslint/consistent-generic-constructors", consistent_generic_constructors.ConsistentGenericConstructorsRule)
GlobalRuleRegistry.Register("@typescript-eslint/consistent-indexed-object-style", consistent_indexed_object_style.ConsistentIndexedObjectStyleRule)
GlobalRuleRegistry.Register("@typescript-eslint/consistent-return", consistent_return.ConsistentReturnRule)
Expand All @@ -376,6 +381,7 @@ func registerAllTypeScriptEslintPluginRules() {
GlobalRuleRegistry.Register("@typescript-eslint/consistent-type-imports", consistent_type_imports.ConsistentTypeImportsRule)
GlobalRuleRegistry.Register("@typescript-eslint/default-param-last", default_param_last.DefaultParamLastRule)
GlobalRuleRegistry.Register("@typescript-eslint/dot-notation", dot_notation.DotNotationRule)
GlobalRuleRegistry.Register("@typescript-eslint/explicit-function-return-type", explicit_function_return_type.ExplicitFunctionReturnTypeRule)
GlobalRuleRegistry.Register("@typescript-eslint/no-array-delete", no_array_delete.NoArrayDeleteRule)
GlobalRuleRegistry.Register("@typescript-eslint/no-base-to-string", no_base_to_string.NoBaseToStringRule)
GlobalRuleRegistry.Register("@typescript-eslint/no-confusing-void-expression", no_confusing_void_expression.NoConfusingVoidExpressionRule)
Expand Down
14 changes: 14 additions & 0 deletions internal/linter/linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ func RunLinterInProgram(program *compiler.Program, allowFiles []string, skipFile
Severity: r.Severity,
})
},
ReportRangeWithFixes: func(textRange core.TextRange, msg rule.RuleMessage, fixes ...rule.RuleFix) {
// Check if rule is disabled at this position
if disableManager.IsRuleDisabled(r.Name, textRange.Pos()) {
return
}
onDiagnostic(rule.RuleDiagnostic{
RuleName: r.Name,
Range: textRange,
Message: msg,
FixesPtr: &fixes,
SourceFile: file,
Severity: r.Severity,
})
},
ReportNode: func(node *ast.Node, msg rule.RuleMessage) {
// Check if rule is disabled at this position
if disableManager.IsRuleDisabled(r.Name, node.Pos()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
package ban_tslint_comment

import (
"regexp"
"strings"

"github.com/microsoft/typescript-go/shim/core"
"github.com/web-infra-dev/rslint/internal/rule"
)

// Regular expressions for matching TSLint directives
var (
// Matches single-line comments: // tslint:disable or // tslint:enable
singleLineTslintRegex = regexp.MustCompile(`^//\s*tslint:(disable|enable)`)

// Matches multi-line comments: /* tslint:disable */ or /* tslint:enable */
multiLineTslintRegex = regexp.MustCompile(`^/\*\s*tslint:(disable|enable)`)
)

// BanTslintCommentRule implements the ban-tslint-comment rule
// Bans // tslint:<rule-flag> comments
var BanTslintCommentRule = rule.CreateRule(rule.Rule{
Name: "ban-tslint-comment",
Run: run,
})

func run(ctx rule.RuleContext, options any) rule.RuleListeners {
// Get the full text of the source file
text := ctx.SourceFile.Text()

// Process the text to find tslint comments
processComments(ctx, text)

return rule.RuleListeners{}
}

// processComments scans the source text for tslint comments
func processComments(ctx rule.RuleContext, text string) {
pos := 0
length := len(text)
lineStarts := calculateLineStarts(text)

for pos < length {
// Skip to next potential comment
if pos+1 < length {
if text[pos] == '/' && text[pos+1] == '/' {
// Single-line comment
commentStart := pos
pos += 2
lineEnd := pos
for lineEnd < length && text[lineEnd] != '\n' && text[lineEnd] != '\r' {
lineEnd++
}
commentText := text[commentStart:lineEnd]

// Check if this is a tslint comment
if singleLineTslintRegex.MatchString(commentText) {
reportTslintComment(ctx, commentText, commentStart, lineEnd, lineStarts, text)
}

pos = lineEnd
} else if text[pos] == '/' && text[pos+1] == '*' {
// Multi-line comment
commentStart := pos
pos += 2
commentEnd := pos
for commentEnd+1 < length {
if text[commentEnd] == '*' && text[commentEnd+1] == '/' {
commentEnd += 2
break
}
commentEnd++
}
commentText := text[commentStart:commentEnd]

// Check if this is a tslint comment
if multiLineTslintRegex.MatchString(commentText) {
reportTslintComment(ctx, commentText, commentStart, commentEnd, lineStarts, text)
}

pos = commentEnd
} else {
pos++
}
} else {
pos++
}
}
}

// calculateLineStarts returns the starting positions of each line
func calculateLineStarts(text string) []int {
lineStarts := []int{0}
for i := 0; i < len(text); i++ {

Check failure on line 94 in internal/plugins/typescript/rules/ban_tslint_comment/ban_tslint_comment.go

View workflow job for this annotation

GitHub Actions / Lint&Check

for loop can be changed to use an integer range (Go 1.22+) (intrange)
if text[i] == '\n' {
lineStarts = append(lineStarts, i+1)
}
}
return lineStarts
}

// getLineAndColumn returns the line and column numbers for a given position
func getLineAndColumn(pos int, lineStarts []int) (line, column int) {
for i := len(lineStarts) - 1; i >= 0; i-- {
if pos >= lineStarts[i] {
line = i + 1
column = pos - lineStarts[i] + 1
return
}
}
return 1, 1
}

// reportTslintComment reports a tslint comment with autofix
func reportTslintComment(ctx rule.RuleContext, commentText string, start, end int, lineStarts []int, fullText string) {
line, column := getLineAndColumn(start, lineStarts)

// Create the fix
fix := createFix(start, end, fullText)

ctx.ReportRangeWithFixes(
core.NewTextRange(start, end),
rule.RuleMessage{
Id: "commentDetected",
Description: "tslint is deprecated and you should stop using it",
},
*fix,
)

_ = line
_ = column
}

// createFix creates a fix that removes the tslint comment
func createFix(start, end int, fullText string) *rule.RuleFix {
// Check if we need to remove the entire line or just the comment

// Look backwards to see if there's any non-whitespace before the comment
hasContentBefore := false
lineStart := start
for lineStart > 0 && fullText[lineStart-1] != '\n' && fullText[lineStart-1] != '\r' {
lineStart--
if !isWhitespace(fullText[lineStart]) {
hasContentBefore = true
}
}

// Look forwards to see if there's any non-whitespace after the comment (on the same line)
hasContentAfter := false
lineEnd := end
for lineEnd < len(fullText) && fullText[lineEnd] != '\n' && fullText[lineEnd] != '\r' {
if !isWhitespace(fullText[lineEnd]) {
hasContentAfter = true
break
}
lineEnd++
}

// Skip the newline characters if removing the entire line
if !hasContentBefore && !hasContentAfter {
// Include the newline in the removal
if lineEnd < len(fullText) && fullText[lineEnd] == '\r' {
lineEnd++
}
if lineEnd < len(fullText) && fullText[lineEnd] == '\n' {
lineEnd++
}

return &rule.RuleFix{
Range: core.NewTextRange(lineStart, lineEnd),
Text: "",
}
}

// If there's content before the comment (e.g., "someCode(); // tslint:disable-line")
if hasContentBefore {
// Remove just the comment, preserving whitespace before it but removing the comment
// Find where the actual code ends
codeEnd := start
for codeEnd > lineStart && isWhitespace(fullText[codeEnd-1]) {
codeEnd--
}

return &rule.RuleFix{
Range: core.NewTextRange(codeEnd, end),
Text: "",
}
}

// Otherwise, just remove the comment
return &rule.RuleFix{
Range: core.NewTextRange(start, end),
Text: "",
}
}

// isWhitespace checks if a character is whitespace
func isWhitespace(ch byte) bool {
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'
}

// trimTrailingWhitespace removes trailing whitespace and newlines
func trimTrailingWhitespace(s string) string {

Check failure on line 203 in internal/plugins/typescript/rules/ban_tslint_comment/ban_tslint_comment.go

View workflow job for this annotation

GitHub Actions / Lint&Check

func trimTrailingWhitespace is unused (unused)
return strings.TrimRight(s, " \t\r\n")
}
Loading
Loading