Skip to content

Commit

Permalink
feat: improve error messages from linker (#1272)
Browse files Browse the repository at this point in the history
Closes #1268

### Summary of Changes

* Improve default error message of the linker.
* Add a default resolution to the message that might solve the issue
(check spelling and imports).
* Add special resolutions, if users likely used Python keywords instead
of Safe-DS keywords by accident (e.g. `True` instead of `true`).
  • Loading branch information
lars-reimann authored Nov 24, 2024
1 parent 9b5e488 commit eddd868
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/safe-ds-cli/tests/cli/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Expand Down Expand Up @@ -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);
});
Expand Down
2 changes: 2 additions & 0 deletions packages/safe-ds-lang/src/language/safe-ds-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -184,6 +185,7 @@ export const SafeDsModule: Module<SafeDsServices, PartialLangiumServices & SafeD
PurityComputer: (services) => new SafeDsPurityComputer(services),
},
references: {
Linker: (services) => new SafeDsLinker(services),
ScopeComputation: (services) => new SafeDsScopeComputation(services),
ScopeProvider: (services) => new SafeDsScopeProvider(services),
},
Expand Down
34 changes: 34 additions & 0 deletions packages/safe-ds-lang/src/language/scoping/safe-ds-linker.ts
Original file line number Diff line number Diff line change
@@ -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}`,
};
}
}
Original file line number Diff line number Diff line change
@@ -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«;
}
Original file line number Diff line number Diff line change
@@ -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«;
}

0 comments on commit eddd868

Please sign in to comment.