| |
| |
| |
| |
| |
|
|
|
|
| interface StaticAnalysisFinding {
|
| line?: number
|
| severity: 'error' | 'warning'
|
| message: string
|
| rule?: string
|
| }
|
|
|
| interface StaticAnalysisResult {
|
| filename: string
|
| findings: StaticAnalysisFinding[]
|
| }
|
|
|
| |
| |
|
|
| function analyzePythonBasic(content: string): StaticAnalysisFinding[] {
|
| const findings: StaticAnalysisFinding[] = []
|
| const lines = content.split('\n')
|
|
|
|
|
| lines.forEach((line, idx) => {
|
| const lineNum = idx + 1
|
|
|
|
|
| const openBrackets = (line.match(/\(/g) || []).length
|
| const closeBrackets = (line.match(/\)/g) || []).length
|
| if (openBrackets !== closeBrackets) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'error',
|
| message: 'Unmatched parentheses detected',
|
| rule: 'syntax-balance'
|
| })
|
| }
|
|
|
|
|
| const openBraces = (line.match(/\{/g) || []).length
|
| const closeBraces = (line.match(/\}/g) || []).length
|
| if (openBraces !== closeBraces) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'error',
|
| message: 'Unmatched braces detected',
|
| rule: 'syntax-balance'
|
| })
|
| }
|
|
|
|
|
| const openSquare = (line.match(/\[/g) || []).length
|
| const closeSquare = (line.match(/\]/g) || []).length
|
| if (openSquare !== closeSquare) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'error',
|
| message: 'Unmatched square brackets detected',
|
| rule: 'syntax-balance'
|
| })
|
| }
|
| })
|
|
|
| return findings
|
| }
|
|
|
| |
| |
|
|
| function analyzeJavaScriptBasic(_content: string): StaticAnalysisFinding[] {
|
| return []
|
| }
|
|
|
| function analyzeTypeScriptEnhanced(content: string): StaticAnalysisFinding[] {
|
| const findings: StaticAnalysisFinding[] = []
|
| const lines = content.split('\n')
|
|
|
| lines.forEach((line, idx) => {
|
| const lineNum = idx + 1
|
| const trimmed = line.trim()
|
|
|
|
|
| if (trimmed.includes('interface') && !trimmed.includes('{')) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'error',
|
| message: 'Interface declaration missing opening brace',
|
| rule: 'ts-syntax'
|
| })
|
| }
|
|
|
|
|
| if (trimmed.match(/<[^>]+>\s*[^=]/) && !trimmed.includes('extends')) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'warning',
|
| message: 'Potential type assertion - consider using as keyword',
|
| rule: 'ts-style'
|
| })
|
| }
|
|
|
|
|
| if (
|
| trimmed.includes('await') &&
|
| !trimmed.includes('try') &&
|
| !trimmed.includes('catch') &&
|
| !line.includes('.catch(') &&
|
| !line.includes('await Promise.')
|
| ) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'warning',
|
| message: 'Await without error handling',
|
| rule: 'async-error'
|
| })
|
| }
|
|
|
|
|
| if (
|
| trimmed.includes('console.log') ||
|
| trimmed.includes('console.warn') ||
|
| trimmed.includes('console.error')
|
| ) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'warning',
|
| message: 'Console statement detected',
|
| rule: 'no-console'
|
| })
|
| }
|
|
|
|
|
| if (
|
| trimmed.includes(': any') ||
|
| trimmed.includes('any[]') ||
|
| trimmed.includes('any>')
|
| ) {
|
| findings.push({
|
| line: lineNum,
|
| severity: 'warning',
|
| message: 'Using any type reduces type safety',
|
| rule: 'no-any'
|
| })
|
| }
|
| })
|
|
|
| return findings
|
| }
|
|
|
| |
| |
| |
|
|
| export async function analyzeStatic(
|
| content: string,
|
| filename: string
|
| ): Promise<StaticAnalysisResult> {
|
| const ext = filename.split('.').pop()?.toLowerCase()
|
| let findings: StaticAnalysisFinding[] = []
|
|
|
| switch (ext) {
|
| case 'py':
|
| findings = analyzePythonBasic(content)
|
| break
|
| case 'js':
|
| case 'jsx':
|
| case 'ts':
|
| case 'tsx':
|
| findings = analyzeTypeScriptEnhanced(content)
|
| break
|
| default:
|
|
|
| break
|
| }
|
|
|
| return {
|
| filename,
|
| findings
|
| }
|
| }
|
|
|
| |
| |
| |
|
|
| export function formatStaticAnalysisFindings(
|
| findings: StaticAnalysisFinding[]
|
| ): string {
|
| if (findings.length === 0) {
|
| return 'Static analysis: No syntax errors or common issues detected.'
|
| }
|
|
|
| const formatted = findings
|
| .map(f => {
|
| const line = f.line ? ` line ${f.line}` : ''
|
| const rule = f.rule ? ` [${f.rule}]` : ''
|
| return `- ${f.severity.toUpperCase()}${line}${rule}: ${f.message}`
|
| })
|
| .join('\n')
|
|
|
| return `Static analysis detected:\n${formatted}`
|
| }
|
|
|