-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: error if purity of functions is not specified (#768)
Closes #731 ### Summary of Changes Show an error if * a function is marked as `@Impure` and as `@Pure`, * a function is neither marked as `@Impure` and `@Pure`, * a function is marked as `@Impure` but not reasons are given.
- Loading branch information
1 parent
bd08ec1
commit a15b0af
Showing
55 changed files
with
644 additions
and
305 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { isSdsParameter, type SdsParameter } from '../generated/ast.js'; | ||
import { getQualifiedName } from '../helpers/nodeProperties.js'; | ||
|
||
/** | ||
* A reason why a function is impure. | ||
*/ | ||
export abstract class ImpurityReason { | ||
/** | ||
* Whether the impurity reason is a side effect. | ||
*/ | ||
abstract isSideEffect: boolean; | ||
|
||
/** | ||
* Returns whether this impurity reason is equal to another object. | ||
*/ | ||
abstract equals(other: unknown): boolean; | ||
|
||
/** | ||
* Returns a string representation of this impurity reason. | ||
*/ | ||
abstract toString(): string; | ||
} | ||
|
||
/** | ||
* The function reads from a file. | ||
* | ||
* @param path The path of the read file. This can either be a parameter or a constant string. | ||
*/ | ||
export class FileRead extends ImpurityReason { | ||
override isSideEffect = false; | ||
|
||
constructor(readonly path: SdsParameter | string | undefined) { | ||
super(); | ||
} | ||
|
||
override equals(other: unknown): boolean { | ||
return other instanceof FileRead && this.path === other.path; | ||
} | ||
|
||
override toString(): string { | ||
if (isSdsParameter(this.path)) { | ||
return `File read from ${getQualifiedName(this.path)}`; | ||
} else if (typeof this.path === 'string') { | ||
return `File read from "${this.path}"`; | ||
} else { | ||
return 'File read from ?'; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* The function writes to a file. | ||
* | ||
* @param path The path of the written file. This can either be a parameter or a constant string. | ||
*/ | ||
export class FileWrite extends ImpurityReason { | ||
override isSideEffect = true; | ||
|
||
constructor(readonly path: SdsParameter | string | undefined) { | ||
super(); | ||
} | ||
|
||
override equals(other: unknown): boolean { | ||
return other instanceof FileWrite && this.path === other.path; | ||
} | ||
|
||
override toString(): string { | ||
if (isSdsParameter(this.path)) { | ||
return `File write to ${getQualifiedName(this.path)}`; | ||
} else if (typeof this.path === 'string') { | ||
return `File write to "${this.path}"`; | ||
} else { | ||
return 'File write to ?'; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* The function calls a callable that is given as a parameter and that might be impure. | ||
* | ||
* @param parameter The parameter that is called. | ||
*/ | ||
export class PotentiallyImpureParameterCall extends ImpurityReason { | ||
override isSideEffect = true; | ||
|
||
constructor(readonly parameter: SdsParameter | undefined) { | ||
super(); | ||
} | ||
|
||
override equals(other: unknown): boolean { | ||
return other instanceof PotentiallyImpureParameterCall && this.parameter === other.parameter; | ||
} | ||
|
||
override toString(): string { | ||
if (isSdsParameter(this.parameter)) { | ||
return `Potentially impure call of ${getQualifiedName(this.parameter)}`; | ||
} else { | ||
return 'Potentially impure call of ?'; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* A function is impure due to some reason that is not covered by the other impurity reasons. | ||
*/ | ||
class OtherImpurityReasonClass extends ImpurityReason { | ||
override isSideEffect = true; | ||
|
||
override equals(other: unknown): boolean { | ||
return other instanceof OtherImpurityReasonClass; | ||
} | ||
|
||
override toString(): string { | ||
return 'Other'; | ||
} | ||
} | ||
|
||
/** | ||
* A function is impure due to some reason that is not covered by the other impurity reasons. | ||
*/ | ||
export const OtherImpurityReason = new OtherImpurityReasonClass(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { type ValidationAcceptor } from 'langium'; | ||
import type { SdsFunction } from '../generated/ast.js'; | ||
import { findFirstAnnotationCallOf } from '../helpers/nodeProperties.js'; | ||
import type { SafeDsServices } from '../safe-ds-module.js'; | ||
|
||
const CODE_PURITY_IMPURE_AND_PURE = 'purity/impure-and-pure'; | ||
const CODE_PURITY_MUST_BE_SPECIFIED = 'purity/must-be-specified'; | ||
|
||
export const functionPurityMustBeSpecified = (services: SafeDsServices) => { | ||
const annotations = services.builtins.Annotations; | ||
|
||
return (node: SdsFunction, accept: ValidationAcceptor) => { | ||
if (annotations.isPure(node) && annotations.isImpure(node)) { | ||
return accept('error', "'@Impure' and '@Pure' are mutually exclusive.", { | ||
node, | ||
property: 'name', | ||
code: CODE_PURITY_IMPURE_AND_PURE, | ||
}); | ||
} else if (!annotations.isImpure(node) && !annotations.isPure(node)) { | ||
return accept( | ||
'error', | ||
"The purity of a function must be specified. Call the annotation '@Pure' or '@Impure'.", | ||
{ | ||
node, | ||
property: 'name', | ||
code: CODE_PURITY_MUST_BE_SPECIFIED, | ||
}, | ||
); | ||
} | ||
|
||
const impureAnnotationCall = findFirstAnnotationCallOf(node, annotations.Impure); | ||
if (impureAnnotationCall && annotations.streamImpurityReasons(node).isEmpty()) { | ||
accept('error', 'At least one impurity reason must be specified.', { | ||
node: impureAnnotationCall, | ||
property: 'annotation', | ||
code: CODE_PURITY_MUST_BE_SPECIFIED, | ||
}); | ||
} | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 9 additions & 9 deletions
18
packages/safe-ds-lang/tests/language/builtins/builtinFilesCorrectness.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.