From 2dceacbe22d2637b0c4ec6b02befe036b6dc7dbc Mon Sep 17 00:00:00 2001 From: Maximiliano Diaz Date: Wed, 11 Feb 2026 17:06:42 -0800 Subject: [PATCH] fix: avoid false positive error/warning detection from echoed source code The `grepWarningsAndErrors` regex matched any line containing `error:` or `warning:`, which produced false positives when xcodebuild echoes Swift source lines during compilation (e.g. `var authError: Error?`, `private(set) var error: WalletError?`). Tighten the regex to require `error:`/`warning:` at the start of the line or after a file:line:col location prefix (`:: `), matching only real xcodebuild diagnostic output. Co-Authored-By: Claude Opus 4.6 --- .../build-utils-suppress-warnings.test.ts | 92 +++++++++++++++++++ src/utils/build-utils.ts | 9 +- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/src/utils/__tests__/build-utils-suppress-warnings.test.ts b/src/utils/__tests__/build-utils-suppress-warnings.test.ts index 61dd69a8..1eac6002 100644 --- a/src/utils/__tests__/build-utils-suppress-warnings.test.ts +++ b/src/utils/__tests__/build-utils-suppress-warnings.test.ts @@ -75,4 +75,96 @@ describe('executeXcodeBuildCommand - suppressWarnings', () => { expect(textContent).not.toContain('⚠️ Warning:'); expect(textContent).toContain('❌ Error:'); }); + + it('should not flag source code lines containing "error" or "warning" as diagnostics', async () => { + sessionStore.setDefaults({ suppressWarnings: false }); + + // Swift source lines echoed during compilation that contain "error"/"warning" as substrings + const buildOutput = [ + ' var authError: Error?', + ' private(set) var error: WalletError?', + ' private(set) var lastError: Error?', + ' var loadError: Error?', + ' private(set) var error: String?', + ' var warningCount: Int = 0', + ' let isWarning: Bool', + ' fatalError("unexpected state")', + ].join('\n'); + + const mockExecutor = createMockExecutor({ + success: true, + output: buildOutput, + error: '', + exitCode: 0, + }); + + const result = await executeXcodeBuildCommand( + { + projectPath: '/test/project.xcodeproj', + scheme: 'TestScheme', + configuration: 'Debug', + }, + { + platform: XcodePlatform.macOS, + logPrefix: 'Test', + }, + false, + 'build', + mockExecutor, + ); + + expect(result.content).toBeDefined(); + const textContent = result.content + ?.filter((c) => c.type === 'text') + .map((c) => (c as { text: string }).text) + .join('\n'); + expect(textContent).not.toContain('❌ Error:'); + expect(textContent).not.toContain('⚠️ Warning:'); + }); + + it('should match real xcodebuild diagnostic lines', async () => { + sessionStore.setDefaults({ suppressWarnings: false }); + + const buildOutput = [ + "/path/to/File.swift:42:10: error: cannot find 'foo' in scope", + "/path/to/File.swift:15:5: warning: unused variable 'bar'", + 'error: build failed', + 'warning: deprecated API usage', + 'ld: warning: directory not found for option', + 'clang: error: linker command failed', + 'xcode-select: error: tool xcodebuild requires Xcode', + 'fatal error: too many errors emitted', + "/path/to/header.h:1:9: fatal error: 'Header.h' file not found", + ].join('\n'); + + const mockExecutor = createMockExecutor({ + success: true, + output: buildOutput, + error: '', + exitCode: 0, + }); + + const result = await executeXcodeBuildCommand( + { + projectPath: '/test/project.xcodeproj', + scheme: 'TestScheme', + configuration: 'Debug', + }, + { + platform: XcodePlatform.macOS, + logPrefix: 'Test', + }, + false, + 'build', + mockExecutor, + ); + + expect(result.content).toBeDefined(); + const textContent = result.content + ?.filter((c) => c.type === 'text') + .map((c) => (c as { text: string }).text) + .join('\n'); + expect(textContent).toContain('❌ Error:'); + expect(textContent).toContain('⚠️ Warning:'); + }); }); diff --git a/src/utils/build-utils.ts b/src/utils/build-utils.ts index 34f480ce..255f6207 100644 --- a/src/utils/build-utils.ts +++ b/src/utils/build-utils.ts @@ -60,11 +60,16 @@ export async function executeXcodeBuildCommand( // Collect warnings, errors, and stderr messages from the build output const buildMessages: { type: 'text'; text: string }[] = []; function grepWarningsAndErrors(text: string): { type: 'warning' | 'error'; content: string }[] { + // Require "error:"/"warning:" at line start (with optional tool prefix like "ld: ") + // or after a file:line:col location prefix, to avoid false positives from source + // code like "var authError: Error?" echoed during compilation. return text .split('\n') .map((content) => { - if (/warning:/i.test(content)) return { type: 'warning', content }; - if (/error:/i.test(content)) return { type: 'error', content }; + if (/(?:^(?:[\w-]+:\s+)?|:\d+:\s+)warning:\s/i.test(content)) + return { type: 'warning', content }; + if (/(?:^(?:[\w-]+:\s+)?|:\d+:\s+)(?:fatal )?error:\s/i.test(content)) + return { type: 'error', content }; return null; }) .filter(Boolean) as { type: 'warning' | 'error'; content: string }[];