-
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: check template expressions inside
@PythonCall
- Loading branch information
1 parent
d22c446
commit 9095618
Showing
3 changed files
with
84 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { hasContainerOfType, ValidationAcceptor } from 'langium'; | ||
import { isSdsClass, SdsFunction } from '../../generated/ast.js'; | ||
import { SafeDsServices } from '../../safe-ds-module.js'; | ||
import { findFirstAnnotationCallOf, getParameters } from '../../helpers/nodeProperties.js'; | ||
import { pluralize } from '../../../helpers/stringUtils.js'; | ||
|
||
export const CODE_PYTHON_CALL_INVALID_TEMPLATE_EXPRESSION = 'python-call/invalid-template-expression'; | ||
|
||
export const pythonCallMustOnlyContainValidTemplateExpressions = (services: SafeDsServices) => { | ||
const builtinAnnotations = services.builtins.Annotations; | ||
|
||
return (node: SdsFunction, accept: ValidationAcceptor) => { | ||
const pythonCall = builtinAnnotations.getPythonCall(node); | ||
if (!pythonCall) { | ||
return; | ||
} | ||
|
||
// Get actual template expressions | ||
const match = pythonCall.matchAll(/\$[_a-zA-Z][_a-zA-Z0-9]*/gu); | ||
const actualTemplateExpressions = [...match].map((it) => it[0]); | ||
|
||
// Compute valid template expressions | ||
const validTemplateExpressions = new Set(getParameters(node).map((it) => `\$${it.name}`)); | ||
if (hasContainerOfType(node, isSdsClass)) { | ||
validTemplateExpressions.add('$this'); | ||
} | ||
|
||
// Compute invalid template expressions | ||
const invalidTemplateExpressions = actualTemplateExpressions.filter((it) => !validTemplateExpressions.has(it)); | ||
|
||
// Report invalid template expressions | ||
if (invalidTemplateExpressions.length > 0) { | ||
const kind = pluralize(invalidTemplateExpressions.length, 'template expression'); | ||
const invalidTemplateExpressionsString = invalidTemplateExpressions.map((it) => `'${it}'`).join(', '); | ||
|
||
accept('error', `The ${kind} ${invalidTemplateExpressionsString} cannot be interpreted.`, { | ||
node: findFirstAnnotationCallOf(node, builtinAnnotations.PythonCall)!, | ||
property: 'annotation', | ||
code: CODE_PYTHON_CALL_INVALID_TEMPLATE_EXPRESSION, | ||
}); | ||
} | ||
}; | ||
}; |
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
39 changes: 39 additions & 0 deletions
39
tests/resources/validation/builtins/annotations/pythonCall/main.sdstest
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,39 @@ | ||
package tests.validation.builtins.pythonCall | ||
|
||
class MyClass { | ||
// $TEST$ no error r"The template expressions? .* cannot be interpreted." | ||
@»PythonCall«("myMethod1($param)") | ||
fun myMethod1(param: Int) | ||
|
||
// $TEST$ no error r"The template expressions? .* cannot be interpreted." | ||
@»PythonCall«("myMethod2($this)") | ||
fun myMethod2(this: Int) | ||
|
||
// $TEST$ no error "The template expression '$this' cannot be interpreted." | ||
@»PythonCall«("myMethod3($this)") | ||
fun myMethod3() | ||
|
||
// $TEST$ error "The template expressions '$param1', '$param2' cannot be interpreted." | ||
@»PythonCall«("myMethod4($param1, $param2)") | ||
fun myMethod4() | ||
} | ||
|
||
// $TEST$ no error r"The template expressions? .* cannot be interpreted." | ||
@»PythonCall«("myFunction1($param)") | ||
fun myFunction1(param: Int) | ||
|
||
// $TEST$ no error r"The template expressions? .* cannot be interpreted." | ||
@»PythonCall«("myFunction2($this)") | ||
fun myFunction2(this: Int) | ||
|
||
// $TEST$ error "The template expression '$this' cannot be interpreted." | ||
@»PythonCall«("myFunction3($this)") | ||
fun myFunction3() | ||
|
||
// $TEST$ error "The template expressions '$param1', '$param2' cannot be interpreted." | ||
@»PythonCall«("myFunction4($param1, $param2)") | ||
fun myFunction4() | ||
|
||
// $TEST$ no error "An expert parameter must be optional." | ||
@»PythonCall«("$this") | ||
annotation MyAnnotation() |