diff --git a/packages/pyright-internal/src/common/console.ts b/packages/pyright-internal/src/common/console.ts index 2078cc842..3a7ac4e2f 100644 --- a/packages/pyright-internal/src/common/console.ts +++ b/packages/pyright-internal/src/common/console.ts @@ -84,7 +84,7 @@ export class NullConsole implements ConsoleInterface { export class StandardConsole implements ConsoleInterface { /** useful for determining whether to exit with a non-zero exit code */ - errors = new Array(); + errorWasLogged = false; constructor(private _maxLevel: LogLevel = LogLevel.Log) {} get level(): LogLevel { @@ -110,7 +110,9 @@ export class StandardConsole implements ConsoleInterface { } error(message: string) { - this.errors.push(message); + if (this.errorWasLogged) { + this.errorWasLogged = true; + } if (getLevelNumber(this._maxLevel) >= getLevelNumber(LogLevel.Error)) { console.error(message); } diff --git a/packages/pyright-internal/src/pyright.ts b/packages/pyright-internal/src/pyright.ts index 253ae290e..6a43e89d0 100644 --- a/packages/pyright-internal/src/pyright.ts +++ b/packages/pyright-internal/src/pyright.ts @@ -548,8 +548,8 @@ const checkForErrors = ( configParseErrorOccurred: boolean, console: ConsoleInterface ): boolean => { - if (console instanceof StandardConsole && console.errors.length > 0) { - console.errors = []; + if (console instanceof StandardConsole && console.errorWasLogged) { + console.errorWasLogged = false; exitStatus.resolve(ExitStatus.ConfigFileParseError); return true; } diff --git a/packages/pyright-internal/src/tests/config.test.ts b/packages/pyright-internal/src/tests/config.test.ts index 82ebbab1e..b991faf29 100644 --- a/packages/pyright-internal/src/tests/config.test.ts +++ b/packages/pyright-internal/src/tests/config.test.ts @@ -27,9 +27,18 @@ import { AnalysisResults } from '../analyzer/analysis'; import { existsSync } from 'fs'; import { NoAccessHost } from '../common/host'; +class ErrorTrackingNullConsole extends NullConsole { + errors = new Array(); + + override error(message: string) { + this.errors.push(message); + super.error(message); + } +} + function createAnalyzer(console?: ConsoleInterface) { const tempFile = new RealTempFile(); - const cons = console ?? new NullConsole(); + const cons = console ?? new ErrorTrackingNullConsole(); const fs = createFromRealFileSystem(tempFile, cons); const serviceProvider = createServiceProvider(fs, cons, tempFile); return new AnalyzerService('', serviceProvider, { console: cons }); @@ -227,12 +236,12 @@ describe('invalid config', () => { const json = { asdf: 1 }; const fs = new TestFileSystem(/* ignoreCase */ false); - const nullConsole = new NullConsole(); + const console = new ErrorTrackingNullConsole(); - const sp = createServiceProvider(fs, nullConsole); - const errors = configOptions.initializeFromJson(json, cwd, sp, new NoAccessHost()); + const sp = createServiceProvider(fs, console); + configOptions.initializeFromJson(json, cwd, sp, new NoAccessHost()); - assert.deepStrictEqual(errors, ['unknown config option: asdf']); + assert.deepStrictEqual(console.errors, ['unknown config option: asdf']); }); test('unknown value for top-level option', () => { const cwd = UriEx.file(normalizePath(process.cwd())); @@ -242,22 +251,20 @@ describe('invalid config', () => { const json = { typeCheckingMode: 'asdf' }; const fs = new TestFileSystem(/* ignoreCase */ false); - const nullConsole = new NullConsole(); + const console = new ErrorTrackingNullConsole(); - const sp = createServiceProvider(fs, nullConsole); - const errors = configOptions.initializeFromJson(json, cwd, sp, new NoAccessHost()); + const sp = createServiceProvider(fs, console); + configOptions.initializeFromJson(json, cwd, sp, new NoAccessHost()); - assert.deepStrictEqual(errors, [ + assert.deepStrictEqual(console.errors, [ 'invalid "typeCheckingMode" value: "asdf". expected: "off", "basic", "standard", "strict", "recommended", or "all"', ]); }); test('unknown value in execution environments', () => { - const { analysisResult } = setupPyprojectToml( + const { consoleErrors } = setupPyprojectToml( 'src/tests/samples/project_with_invalid_option_in_execution_environments' ); - assert.deepStrictEqual(analysisResult?.configParseErrorOccurred, [ - `unknown config option in execution environment "foo": asdf`, - ]); + assert.deepStrictEqual(consoleErrors, [`unknown config option in execution environment "foo": asdf`]); }); }); @@ -348,10 +355,15 @@ test('AutoSearchPathsOnAndExtraPaths', () => { const setupPyprojectToml = ( projectPath: string -): { configOptions: ConfigOptions; analysisResult: AnalysisResults | undefined } => { +): { + configOptions: ConfigOptions; + analysisResult: AnalysisResults | undefined; + consoleErrors: string[]; +} => { const cwd = normalizePath(combinePaths(process.cwd(), projectPath)); assert(existsSync(cwd)); - const service = createAnalyzer(); + const console = new ErrorTrackingNullConsole(); + const service = createAnalyzer(console); let analysisResult = undefined as AnalysisResults | undefined; service.setCompletionCallback((result) => (analysisResult = result)); const commandLineOptions = new CommandLineOptions(cwd, /* fromLanguageServer */ true); @@ -359,7 +371,8 @@ const setupPyprojectToml = ( service.setOptions(commandLineOptions); return { - configOptions: service.test_getConfigOptions(commandLineOptions), + configOptions: service.getConfigOptions(), + consoleErrors: console.errors, analysisResult, }; };