From 5a6f22f0f3797a2ee4949273bd124d7ed321f3a1 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 28 Mar 2024 23:53:57 +0100 Subject: [PATCH] feat!: Throw errors on missing inputs (#7969) Previously generators could generate code from inputs that didn't exist and get back the empty string. This silent failure was causing problems for diagnosing issues. This PR changes the behaviour so that an error is thrown. This will break generators which rely on the previous behaviour. Several of our demo blocks needed editing to accomodate this change. Resolves #7665 --- core/generator.ts | 6 ++++++ generators/dart/procedures.ts | 13 +++++++++++-- generators/dart/text.ts | 10 ++++++---- generators/javascript/procedures.ts | 13 +++++++++++-- generators/lua/procedures.ts | 13 +++++++++++-- generators/lua/text.ts | 4 ++-- generators/php/procedures.ts | 13 +++++++++++-- generators/python/procedures.ts | 13 +++++++++++-- 8 files changed, 69 insertions(+), 16 deletions(-) diff --git a/core/generator.ts b/core/generator.ts index c7c26f15f..f948924d9 100644 --- a/core/generator.ts +++ b/core/generator.ts @@ -313,6 +313,9 @@ export class CodeGenerator { throw TypeError('Expecting valid order from block: ' + block.type); } const targetBlock = block.getInputTargetBlock(name); + if (!targetBlock && !block.getInput(name)) { + throw ReferenceError(`Input "${name}" doesn't exist on "${block.type}"`); + } if (!targetBlock) { return ''; } @@ -391,6 +394,9 @@ export class CodeGenerator { */ statementToCode(block: Block, name: string): string { const targetBlock = block.getInputTargetBlock(name); + if (!targetBlock && !block.getInput(name)) { + throw ReferenceError(`Input "${name}" doesn't exist on "${block.type}"`); + } let code = this.blockToCode(targetBlock); // Value blocks must return code and order of operations info. // Statement blocks must only return code. diff --git a/generators/dart/procedures.ts b/generators/dart/procedures.ts index 5102432ad..0ecf6d210 100644 --- a/generators/dart/procedures.ts +++ b/generators/dart/procedures.ts @@ -35,8 +35,17 @@ export function procedures_defreturn(block: Block, generator: DartGenerator) { generator.INDENT, ); } - const branch = generator.statementToCode(block, 'STACK'); - let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + let branch = ''; + if (block.getInput('STACK')) { + // The 'procedures_defreturn' block might not have a STACK input. + branch = generator.statementToCode(block, 'STACK'); + } + let returnValue = ''; + if (block.getInput('RETURN')) { + // The 'procedures_defnoreturn' block (which shares this code) + // does not have a RETURN input. + returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + } let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. diff --git a/generators/dart/text.ts b/generators/dart/text.ts index 501619e03..6bb2bea82 100644 --- a/generators/dart/text.ts +++ b/generators/dart/text.ts @@ -128,10 +128,12 @@ export function text_charAt( return [code, Order.UNARY_POSTFIX]; } case 'LAST': - at = 1; - // Fall through. case 'FROM_END': { - at = generator.getAdjusted(block, 'AT', 1); + if (where === 'LAST') { + at = 1; + } else { + at = generator.getAdjusted(block, 'AT', 1); + } const functionName = generator.provideFunction_( 'text_get_from_end', ` @@ -140,7 +142,7 @@ String ${generator.FUNCTION_NAME_PLACEHOLDER_}(String text, num x) { } `, ); - const code = functionName + '(' + text + ', ' + at + ')'; + const code = `${functionName}(${text}, ${at})`; return [code, Order.UNARY_POSTFIX]; } case 'RANDOM': { diff --git a/generators/javascript/procedures.ts b/generators/javascript/procedures.ts index a835271e7..e0a055217 100644 --- a/generators/javascript/procedures.ts +++ b/generators/javascript/procedures.ts @@ -38,8 +38,17 @@ export function procedures_defreturn( generator.INDENT, ); } - const branch = generator.statementToCode(block, 'STACK'); - let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + let branch = ''; + if (block.getInput('STACK')) { + // The 'procedures_defreturn' block might not have a STACK input. + branch = generator.statementToCode(block, 'STACK'); + } + let returnValue = ''; + if (block.getInput('RETURN')) { + // The 'procedures_defnoreturn' block (which shares this code) + // does not have a RETURN input. + returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + } let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. diff --git a/generators/lua/procedures.ts b/generators/lua/procedures.ts index 07003aee9..79dc58aa2 100644 --- a/generators/lua/procedures.ts +++ b/generators/lua/procedures.ts @@ -38,8 +38,17 @@ export function procedures_defreturn( generator.INDENT, ); } - let branch = generator.statementToCode(block, 'STACK'); - let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + let branch = ''; + if (block.getInput('STACK')) { + // The 'procedures_defreturn' block might not have a STACK input. + branch = generator.statementToCode(block, 'STACK'); + } + let returnValue = ''; + if (block.getInput('RETURN')) { + // The 'procedures_defnoreturn' block (which shares this code) + // does not have a RETURN input. + returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + } let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. diff --git a/generators/lua/text.ts b/generators/lua/text.ts index defbbaf99..350b86313 100644 --- a/generators/lua/text.ts +++ b/generators/lua/text.ts @@ -132,8 +132,6 @@ export function text_charAt( // Get letter at index. // Note: Until January 2013 this block did not have the WHERE input. const where = block.getFieldValue('WHERE') || 'FROM_START'; - const atOrder = where === 'FROM_END' ? Order.UNARY : Order.NONE; - const at = generator.valueToCode(block, 'AT', atOrder) || '1'; const text = generator.valueToCode(block, 'VALUE', Order.NONE) || "''"; let code; if (where === 'RANDOM') { @@ -154,6 +152,8 @@ end } else if (where === 'LAST') { start = '-1'; } else { + const atOrder = where === 'FROM_END' ? Order.UNARY : Order.NONE; + const at = generator.valueToCode(block, 'AT', atOrder) || '1'; if (where === 'FROM_START') { start = at; } else if (where === 'FROM_END') { diff --git a/generators/php/procedures.ts b/generators/php/procedures.ts index bd114cd0d..acf84aea6 100644 --- a/generators/php/procedures.ts +++ b/generators/php/procedures.ts @@ -60,8 +60,17 @@ export function procedures_defreturn(block: Block, generator: PhpGenerator) { generator.INDENT, ); } - const branch = generator.statementToCode(block, 'STACK'); - let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + let branch = ''; + if (block.getInput('STACK')) { + // The 'procedures_defreturn' block might not have a STACK input. + branch = generator.statementToCode(block, 'STACK'); + } + let returnValue = ''; + if (block.getInput('RETURN')) { + // The 'procedures_defnoreturn' block (which shares this code) + // does not have a RETURN input. + returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + } let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return. diff --git a/generators/python/procedures.ts b/generators/python/procedures.ts index 798d6d55a..51d2ee9a3 100644 --- a/generators/python/procedures.ts +++ b/generators/python/procedures.ts @@ -60,8 +60,17 @@ export function procedures_defreturn(block: Block, generator: PythonGenerator) { generator.INDENT, ); } - let branch = generator.statementToCode(block, 'STACK'); - let returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + let branch = ''; + if (block.getInput('STACK')) { + // The 'procedures_defreturn' block might not have a STACK input. + branch = generator.statementToCode(block, 'STACK'); + } + let returnValue = ''; + if (block.getInput('RETURN')) { + // The 'procedures_defnoreturn' block (which shares this code) + // does not have a RETURN input. + returnValue = generator.valueToCode(block, 'RETURN', Order.NONE) || ''; + } let xfix2 = ''; if (branch && returnValue) { // After executing the function body, revisit this block for the return.