Skip to content

Commit

Permalink
Fix union type unary operations and Array.sort(by) optional params (#897
Browse files Browse the repository at this point in the history
)

* Fix unary operator validation on union types

* fixed array.sort flags as being optional... re-ran scraper

* added test for array.sort optional types
  • Loading branch information
markwpearce authored Sep 19, 2023
1 parent 7cafaf1 commit 9d8940f
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 12 deletions.
2 changes: 1 addition & 1 deletion scripts/scrape-roku-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class Runner {
let paramType = 'dynamic';
let paramName = defaultParamName;

const isOptional = foundParam.endsWith(']');
const isOptional = foundParam.endsWith(']') || foundParam.includes('=');

if (paramTypeIndex >= 0) {
// if we found a word that looks like a type, use it for the type, and remove it from the array
Expand Down
23 changes: 23 additions & 0 deletions src/bscPlugin/validation/ScopeValidator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1320,5 +1320,28 @@ describe('ScopeValidator', () => {
]);
});

it('allows unary on dynamic and union types', () => {
program.setFile('source/util.bs', `
sub doStuff(x)
y = -x
print y
end sub
sub doOtherStuff(x as float or integer)
y = -x
print y
end sub
sub doEventMoreStuff(x as boolean or dynamic)
if not x
print "ok"
end if
end sub
`);
program.validate();
//should have no errors
expectZeroDiagnostics(program);
});

});
});
9 changes: 6 additions & 3 deletions src/bscPlugin/validation/ScopeValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,14 @@ export class ScopeValidator {
if (isEnumMemberType(rightType)) {
rightTypeToTest = rightType.underlyingType;
}
if (isDynamicType(rightTypeToTest) || isObjectType(rightTypeToTest)) {
if (isUnionType(rightTypeToTest)) {
// TODO: it is possible to validate based on innerTypes, but more complicated
// Because you need to verify each combination of types
return;
} else if (isDynamicType(rightTypeToTest) || isObjectType(rightTypeToTest)) {
// operand is basically "any" type... ignore;
return;
}
if (isPrimitiveType(rightType)) {
} else if (isPrimitiveType(rightType)) {
const opResult = util.unaryOperatorResultType(unaryExpr.operator, rightTypeToTest);
if (isDynamicType(opResult)) {
this.addMultiScopeDiagnostic({
Expand Down
38 changes: 31 additions & 7 deletions src/roku-types/data.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"generatedDate": "2023-07-27T11:52:31.001Z",
"generatedDate": "2023-09-19T15:05:44.139Z",
"nodes": {
"animation": {
"description": "Extends [**AnimationBase**](https://developer.roku.com/docs/references/scenegraph/abstract-nodes/animationbase.md\n\nThe Animation node class provides animations of renderable nodes, by applying interpolator functions to the values in specified renderable node fields. For an animation to take effect, an Animation node definition must include a child field interpolator node ([FloatFieldInterpolator](https://developer.roku.com/docs/references/scenegraph/animation-nodes/floatfieldinterpolator.md\"FloatFieldInterpolator\"), [Vector2DFieldInterpolator](https://developer.roku.com/docs/references/scenegraph/animation-nodes/vector2dfieldinterpolator.md\"Vector2DFieldInterpolator\"), [ColorFieldInterpolator](https://developer.roku.com/docs/references/scenegraph/animation-nodes/colorfieldinterpolator.md\"ColorFieldInterpolator\")) definition for each renderable node field that is animated.\n\nThe Animation node class provides a simple linear interpolator function, where the animation takes place smoothly and simply from beginning to end. The Animation node class also provides several more complex interpolator functions to allow custom animation effects. For example, you can move a graphic image around the screen at differing speeds and curved trajectories at different times in the animation by specifying the appropriate function in the easeFunction field (quadratic and exponential are two examples of functions that can be specified). The interpolator functions are divided into two parts: the beginning of the animation (ease-in), and the end of the animation (ease-out). You can apply a specified interpolator function to either or both ease-in and ease-out, or specify no function for either or both (which is the linear function). You can also change the portion of the animation that is ease-in and ease-out to arbitrary fractional values for a quadratic interpolator function applied to both ease-in and ease-out.",
Expand Down Expand Up @@ -5948,14 +5948,16 @@
{
"params": [
{
"default": null,
"isRequired": true,
"name": "param1",
"type": "dynamic"
"name": "size",
"type": "Integer"
},
{
"default": null,
"isRequired": true,
"name": "param2",
"type": "dynamic"
"name": "resize",
"type": "Boolean"
}
],
"returnType": "roArray"
Expand Down Expand Up @@ -6116,6 +6118,17 @@
},
"robitmap": {
"constructors": [
{
"params": [
{
"default": null,
"isRequired": true,
"name": "bitmapProps",
"type": "Object"
}
],
"returnType": "roBitmap"
},
{
"params": [
{
Expand Down Expand Up @@ -6934,6 +6947,17 @@
},
"roregistrysection": {
"constructors": [
{
"params": [
{
"default": null,
"isRequired": true,
"name": "section",
"type": "String"
}
],
"returnType": "roRegistrySection"
},
{
"params": [
{
Expand Down Expand Up @@ -7969,7 +7993,7 @@
{
"default": null,
"description": "Items are arbitrarily grouped by comparable type of number or string, and are sorted within the group with a logical comparison. If \"r\" is included in flags, a reverse sort is performed. If \"i\" is included in flags, a case-insensitive sort is performed. If invalid flags are specified, the sort is not performed.",
"isRequired": true,
"isRequired": false,
"name": "flags",
"type": "String"
}
Expand All @@ -7990,7 +8014,7 @@
{
"default": null,
"description": "Items are arbitrarily grouped by comparable type of number or string, and are sorted within the group with a logical comparison. If \"r\" is included in flags, a reverse sort is performed. If \"i\" is included in flags, a case-insensitive sort is performed. If invalid flags are specified, the sort is not performed.",
"isRequired": true,
"isRequired": false,
"name": "flags",
"type": "String"
}
Expand Down
12 changes: 12 additions & 0 deletions src/types/ArrayType.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,17 @@ describe('ArrayType', () => {
const pushType = myArray.getMemberType('push', { flags: SymbolTypeFlag.runtime }) as TypedFunctionType;
expectTypeToBe(pushType.params[0].type, IntegerType);
});

it('correctly gets optional params for sorts', () => {
const myArray = new ArrayType();
const sortType = myArray.getMemberType('sort', { flags: SymbolTypeFlag.runtime });
expectTypeToBe((sortType as TypedFunctionType).params[0].type, StringType);
expect((sortType as TypedFunctionType).params[0].isOptional).to.be.true;
const sortByType = myArray.getMemberType('sortBy', { flags: SymbolTypeFlag.runtime });
expectTypeToBe((sortByType as TypedFunctionType).params[0].type, StringType);
expectTypeToBe((sortByType as TypedFunctionType).params[1].type, StringType);
expect((sortByType as TypedFunctionType).params[1].isOptional).to.be.true;

});
});
});
2 changes: 1 addition & 1 deletion src/util.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,7 @@ describe('util', () => {
expect(result.range).to.eql(util.createRange(3, 3, 4, 4));
});

it('respects the seperatorToken', () => {
it('respects the separatorToken', () => {
const chain = [
new TypeChainEntry('roSGNodeCustom', new ComponentType('Custom'), util.createRange(1, 1, 2, 2)),
new TypeChainEntry('someCallFunc', new TypedFunctionType(VoidType.instance), util.createRange(2, 2, 3, 3), createToken(TokenKind.Callfunc))
Expand Down

0 comments on commit 9d8940f

Please sign in to comment.