-
Notifications
You must be signed in to change notification settings - Fork 2
fix: resolve all false positives (issues #2, #6, #7, #8) #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
3c24150
e324ee5
eb0fee1
0330a69
5aadb9d
8459346
5333ebd
4cb1636
55f09ef
fd9cf4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -270,7 +270,7 @@ class AISlopDetector { | |
| }, | ||
| { | ||
| id: 'missing_error_handling', | ||
| pattern: /(fetch|axios|http)\s*\(/g, | ||
| pattern: /\b(fetch|axios|http)\s*\(/g, | ||
| message: "Potential missing error handling for promise. Consider adding try/catch or .catch().", | ||
| severity: 'medium', | ||
| description: 'Detects calls that might need error handling', | ||
|
|
@@ -289,7 +289,7 @@ class AISlopDetector { | |
| }, | ||
| { | ||
| id: 'todo_comment', | ||
| pattern: /(TODO|FIXME|HACK|XXX|BUG)\b/g, | ||
| pattern: /\b(TODO|FIXME|HACK|XXX|BUG)\b/g, | ||
| message: "Found TODO/FIXME/HACK comment indicating incomplete implementation.", | ||
| severity: 'medium', | ||
| description: 'Detects incomplete implementation markers' | ||
|
|
@@ -495,7 +495,8 @@ class AISlopDetector { | |
| '**/lib/**', // Generated library files | ||
| 'scripts/ai-slop-detector.ts', // Exclude the detector script itself to avoid false positives | ||
| 'ai-slop-detector.ts', // Also exclude when in root directory | ||
| 'improved-ai-slop-detector.ts' // Exclude the improved detector script to avoid false positives | ||
| 'improved-ai-slop-detector.ts', // Exclude the improved detector script to avoid false positives | ||
| ...this.customIgnorePaths | ||
| ] | ||
| }); | ||
|
|
||
|
|
@@ -729,21 +730,36 @@ class AISlopDetector { | |
|
|
||
| // Special handling for missing error handling - look for properly handled fetch calls | ||
| if (pattern.id === 'missing_error_handling') { | ||
| const fullLine = line.trim(); | ||
| // Skip matches inside comment lines (single-line, JSDoc, block) | ||
| if (fullLine.startsWith('//') || fullLine.startsWith('*') || fullLine.startsWith('/*')) { | ||
| continue; | ||
| } | ||
| // Check if this fetch call is part of a properly handled async function | ||
| const isProperlyHandled = this.isFetchCallProperlyHandled(lines, i, match.index); | ||
| if (isProperlyHandled) { | ||
| continue; // Skip this fetch call as it's properly handled | ||
| continue; | ||
| } | ||
| } | ||
|
|
||
| // Special handling for unsafe_double_type_assertion - skip legitimate UI library patterns | ||
| if (pattern.id === 'unsafe_double_type_assertion') { | ||
| // Check the full line context to identify potentially legitimate patterns | ||
| const fullLine = line.trim(); | ||
| // Skip patterns that are actually safe (as unknown as Type) since we changed the regex | ||
| // but double-check to be extra sure | ||
| // Skip patterns that are actually safe (as unknown as Type) | ||
| if (fullLine.includes('as unknown as')) { | ||
| continue; // This is actually safe - skip it | ||
| continue; | ||
| } | ||
| // Skip matches inside comment lines (e.g., "as soon as React") | ||
| if (fullLine.startsWith('//') || fullLine.startsWith('*') || fullLine.startsWith('/*')) { | ||
| continue; | ||
| } | ||
| // Skip matches where the first word after "as" is a common English word | ||
| // indicating natural language rather than a type assertion | ||
| // e.g., "as soon as React hydrates" — "soon" is English, not a type | ||
| const firstWord = match[0].match(/^as\s+(\w+)/i)?.[1]?.toLowerCase(); | ||
| const englishWords = ['soon', 'quick', 'quickly', 'fast', 'smooth', 'long', 'much', 'little', 'well', 'good', 'bad', 'easy', 'hard', 'simple', 'clear', 'many', 'few', 'close', 'far', 'near']; | ||
| if (firstWord && englishWords.includes(firstWord)) { | ||
| continue; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -756,6 +772,20 @@ class AISlopDetector { | |
| continue; | ||
| } | ||
|
|
||
| // Skip console calls guarded by a conditional on the same line | ||
| // e.g., if (isDev) console.log('debug'); | ||
| if (/^if\s*\(/.test(fullLine)) { | ||
| continue; | ||
| } | ||
|
|
||
| // Skip console calls inside a conditional block opened on a prior line | ||
| if (i > 0) { | ||
| const prevLine = lines[i - 1].trim(); | ||
| if (/^if\s*\(/.test(prevLine) && (prevLine.includes('{') || fullLine.startsWith('{') === false)) { | ||
| continue; | ||
| } | ||
| } | ||
|
Comment on lines
+782
to
+787
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Conditional guard logic has an inverted condition that may cause false negatives. The condition
This effectively skips almost all console calls when the previous line is an if (condition) { doSomething(); }
console.log('not guarded'); // Would be incorrectly skippedThe heuristic is intentionally loose to avoid false positives, but it may now cause false negatives (missing real issues). 🤖 Prompt for AI Agents |
||
|
|
||
| // Skip general debugging logs that might be intentional in development | ||
| if (fullLine.includes('console.log(') && | ||
| (fullLine.includes('Debug') || fullLine.includes('debug') || fullLine.includes('debug:'))) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: The previous-line guard condition is effectively always true for most
console.*lines becausefullLine.startsWith('{') === falseusually holds, so any log directly after anif (...)line is skipped even when it is not actually inside a dev guard. Tighten this condition to only skip explicit dev/debug guard lines. [logic error]Severity Level: Major⚠️
Steps of Reproduction ✅
This mirrors a realistic production pattern where an unconditional log line follows an
if (...)statement on the preceding line.Run the CLI by executing
node ai-slop-detector.tsfrom the repo root, which callsrunIfMain()atai-slop-detector.ts:1307, constructsnew AISlopDetector(rootDir), andinvokes
detect()atai-slop-detector.ts:443.Inside
detect(),findAllFiles()atai-slop-detector.ts:473-521will includeexample-console.ts, andanalyzeFile(filePath)atai-slop-detector.ts:631will readand split the file into
lines, then iterate each line and eachdetectionPatternsentryincluding
production_console_logdefined atai-slop-detector.ts:282-288.When processing the
console.log('User data', user);line inexample-console.ts, theproduction_console_logregex matches, and the special-handling block atai-slop-detector.ts:765-799runs:prevLineis the precedingif (userLoaded) handleUser(user);line, so/^if\s*\(/.test(prevLine)is true, and becauseprevLinedoes not contain
{whilefullLine.startsWith('{') === falseis also true, thecondition
if (/^if\s*\(/.test(prevLine) && (prevLine.includes('{') || fullLine.startsWith('{') === false)) { continue; }atai-slop-detector.ts:783-784evaluates to true and thelog is skipped. This demonstrates that any
console.*line immediately following anif (...)line without a brace is treated as "guarded" and ignored, even though thelog is not actually inside a conditional block. Note that the current repository's
fixtures (e.g.
tests/fixtures/false-positives.ts:129-145) intentionally markconditionally-guarded logs as non-issues, so this overly-broad guard does not currently
break any existing test fixture but can cause false negatives for consumer projects
using this pattern.