From 8cc4d4df1904a8a700fde82f9ddb4e563a80b7ae Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 24 Nov 2024 19:20:06 +0100 Subject: [PATCH 1/2] feat: improve error messages from linker --- .../src/language/safe-ds-module.ts | 2 ++ .../src/language/scoping/safe-ds-linker.ts | 34 +++++++++++++++++++ .../validation/linking/default error/main.sds | 6 ++++ .../linking/usage of python keywords/main.sds | 21 ++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 packages/safe-ds-lang/src/language/scoping/safe-ds-linker.ts create mode 100644 packages/safe-ds-lang/tests/resources/validation/linking/default error/main.sds create mode 100644 packages/safe-ds-lang/tests/resources/validation/linking/usage of python keywords/main.sds diff --git a/packages/safe-ds-lang/src/language/safe-ds-module.ts b/packages/safe-ds-lang/src/language/safe-ds-module.ts index 766bae884..c938c8b5a 100644 --- a/packages/safe-ds-lang/src/language/safe-ds-module.ts +++ b/packages/safe-ds-lang/src/language/safe-ds-module.ts @@ -57,6 +57,7 @@ import { SafeDsServiceRegistry } from './safe-ds-service-registry.js'; import { SafeDsPythonServer } from './runtime/safe-ds-python-server.js'; import { SafeDsSlicer } from './flow/safe-ds-slicer.js'; import { SafeDsSyntheticProperties } from './helpers/safe-ds-synthetic-properties.js'; +import { SafeDsLinker } from './scoping/safe-ds-linker.js'; /** * Declaration of custom services - add your own service classes here. @@ -184,6 +185,7 @@ export const SafeDsModule: Module new SafeDsPurityComputer(services), }, references: { + Linker: (services) => new SafeDsLinker(services), ScopeComputation: (services) => new SafeDsScopeComputation(services), ScopeProvider: (services) => new SafeDsScopeProvider(services), }, diff --git a/packages/safe-ds-lang/src/language/scoping/safe-ds-linker.ts b/packages/safe-ds-lang/src/language/scoping/safe-ds-linker.ts new file mode 100644 index 000000000..c14b51d0f --- /dev/null +++ b/packages/safe-ds-lang/src/language/scoping/safe-ds-linker.ts @@ -0,0 +1,34 @@ +import { AstNodeDescription, DefaultLinker, isLinkingError, LinkingError, ReferenceInfo } from 'langium'; +import { isSdsMemberAccess, isSdsReference } from '../generated/ast.js'; + +export class SafeDsLinker extends DefaultLinker { + override getCandidate(refInfo: ReferenceInfo): AstNodeDescription | LinkingError { + const superResult = super.getCandidate(refInfo); + + if (!isLinkingError(superResult)) { + return superResult; + } + + const node = refInfo.container; + + // Create a default error message + const message = `Could not find a declaration named '${refInfo.reference.$refText}' in this context.`; + let resolution = 'Did you spell the name correctly and add all needed imports?'; + + // Improve the error message if Python keywords False, True, or None are used as references + if (isSdsReference(node) && refInfo.property === 'target' && !isSdsMemberAccess(node.$container)) { + if (refInfo.reference.$refText === 'False') { + resolution = "Did you mean to write 'false'?"; + } else if (refInfo.reference.$refText === 'True') { + resolution = "Did you mean to write 'true'?"; + } else if (refInfo.reference.$refText === 'None') { + resolution = "Did you mean to write 'null'?"; + } + } + + return { + ...superResult, + message: `${message}\n${resolution}`, + }; + } +} diff --git a/packages/safe-ds-lang/tests/resources/validation/linking/default error/main.sds b/packages/safe-ds-lang/tests/resources/validation/linking/default error/main.sds new file mode 100644 index 000000000..b72410eff --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/validation/linking/default error/main.sds @@ -0,0 +1,6 @@ +package tests.validation.linking.defaultError + +pipeline test { + // $TEST$ error r"Could not find a declaration named 'Unknown' in this context\.\nDid you spell the name correctly and add all needed imports\?" + »Unknown«; +} diff --git a/packages/safe-ds-lang/tests/resources/validation/linking/usage of python keywords/main.sds b/packages/safe-ds-lang/tests/resources/validation/linking/usage of python keywords/main.sds new file mode 100644 index 000000000..71d6759de --- /dev/null +++ b/packages/safe-ds-lang/tests/resources/validation/linking/usage of python keywords/main.sds @@ -0,0 +1,21 @@ +package tests.validation.linking.usageOfPythonKeywords + +pipeline test { + // $TEST$ error r"Did you mean to write 'false'\?" + out »False«; + + // $TEST$ error r"Did you mean to write 'true'\?" + out »True«; + + // $TEST$ error r"Did you mean to write 'null'\?" + out »None«; + + // $TEST$ no error r"Did you mean to write 'false'\?" + out a.»False«; + + // $TEST$ no error r"Did you mean to write 'true'\?" + out a.»True«; + + // $TEST$ no error r"Did you mean to write 'null'\?" + out a».None«; +} From 171dfa37de09860a9f8204d04ca370186e2b00ac Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Sun, 24 Nov 2024 19:25:05 +0100 Subject: [PATCH 2/2] test: update CLI tests --- packages/safe-ds-cli/tests/cli/main.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/safe-ds-cli/tests/cli/main.test.ts b/packages/safe-ds-cli/tests/cli/main.test.ts index e84048c89..ffba4c6ce 100644 --- a/packages/safe-ds-cli/tests/cli/main.test.ts +++ b/packages/safe-ds-cli/tests/cli/main.test.ts @@ -150,7 +150,7 @@ describe('safe-ds', () => { it('should show an error if a Safe-DS file has errors', () => { const process = spawnDocumentProcess([], ['.']); expect(process.stderr.toString()).toContain( - "Could not resolve reference to SdsNamedTypeDeclaration named 'Unresolved'", + "Could not find a declaration named 'Unresolved' in this context.", ); expect(process.status).toBe(ExitCode.FileHasErrors); }); @@ -259,7 +259,7 @@ describe('safe-ds', () => { it('should show an error if a Safe-DS file has errors', () => { const process = spawnGenerateProcess([], ['.']); expect(process.stderr.toString()).toContain( - "Could not resolve reference to SdsNamedTypeDeclaration named 'Unresolved'", + "Could not find a declaration named 'Unresolved' in this context.", ); expect(process.status).toBe(ExitCode.FileHasErrors); });