From c6d83ff4292cecb4b002b569e75f8cc35b17fe22 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Tue, 12 May 2026 11:52:24 +0200 Subject: [PATCH] c: Fix segfault when one placeholder is a prefix of another --- c/src/compiler.c | 5 ++- .../scenario_outline_replacements.feature | 35 ++++++++++++++++++ ...io_outline_replacements.feature.ast.ndjson | 1 + ...utline_replacements.feature.pickles.ndjson | 4 +++ ...outline_replacements.feature.source.ndjson | 1 + ...enario_outline_replacements.feature.tokens | 36 +++++++++++++++++++ 6 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 testdata/good/scenario_outline_replacements.feature create mode 100644 testdata/good/scenario_outline_replacements.feature.ast.ndjson create mode 100644 testdata/good/scenario_outline_replacements.feature.pickles.ndjson create mode 100644 testdata/good/scenario_outline_replacements.feature.source.ndjson create mode 100644 testdata/good/scenario_outline_replacements.feature.tokens diff --git a/c/src/compiler.c b/c/src/compiler.c index 3e366e048..29912664d 100644 --- a/c/src/compiler.c +++ b/c/src/compiler.c @@ -325,13 +325,16 @@ static const wchar_t* create_expanded_text(const wchar_t* original_text, const T for (j = 0; j < example_header->table_cells->cell_count; ++j) { int cell_text_length = wcslen(example_header->table_cells->table_cells[j].value); if (cell_text_length < length - i - 1 && - wcsncmp(original_text + i + 1, example_header->table_cells->table_cells[j].value, cell_text_length) == 0) { + wcsncmp(original_text + i + 1, example_header->table_cells->table_cells[j].value, cell_text_length) == 0 && + original_text[i + cell_text_length + 1] == L'>') { ReplacementItem* item = (ReplacementItem*)malloc(sizeof(ReplacementItem)); item->item_delete = (item_delete_function)ReplacementItem_delete; item->start_position = i; item->old_length = cell_text_length + 2; item->new_text = body_row->table_cells->table_cells[j].value; ItemQueue_add(replacement_list, (Item*)item); + i = i + cell_text_length + 1; + break; } } } diff --git a/testdata/good/scenario_outline_replacements.feature b/testdata/good/scenario_outline_replacements.feature new file mode 100644 index 000000000..1d4ab2842 --- /dev/null +++ b/testdata/good/scenario_outline_replacements.feature @@ -0,0 +1,35 @@ +Feature: Scenario Outline replacements + + Variables can be used in scenario outlines. These are replaced by going through + the examples table from left to right, replacing the `<` + header + `>` with the + value from the first matching column. + + Scenario Outline: the variable must match the exact column name + Given + When + Then + + Examples: + | a | ax | + | pickle | gherkin | + + Scenario Outline: the variables are replaced by going through from left to right through the examples table + Given > and + + Examples: + | a | b | + | pickle | gherkin | + + Scenario Outline: the first matching column is used + Given + + Examples: + | a | a | + | pickle | gherkin | + + Scenario Outline: variables are replaced once + Given and + + Examples: + | a | b | + | | pickle | diff --git a/testdata/good/scenario_outline_replacements.feature.ast.ndjson b/testdata/good/scenario_outline_replacements.feature.ast.ndjson new file mode 100644 index 000000000..2ef3839e4 --- /dev/null +++ b/testdata/good/scenario_outline_replacements.feature.ast.ndjson @@ -0,0 +1 @@ +{"gherkinDocument":{"comments":[],"feature":{"children":[{"scenario":{"description":"","examples":[{"description":"","id":"5","keyword":"Examples","location":{"column":5,"line":12},"name":"","tableBody":[{"cells":[{"location":{"column":9,"line":14},"value":"pickle"},{"location":{"column":18,"line":14},"value":"gherkin"}],"id":"4","location":{"column":7,"line":14}}],"tableHeader":{"cells":[{"location":{"column":9,"line":13},"value":"a"},{"location":{"column":18,"line":13},"value":"ax"}],"id":"3","location":{"column":7,"line":13}},"tags":[]}],"id":"6","keyword":"Scenario Outline","location":{"column":3,"line":7},"name":"the variable must match the exact column name","steps":[{"id":"0","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":8},"text":""},{"id":"1","keyword":"When ","keywordType":"Action","location":{"column":5,"line":9},"text":""},{"id":"2","keyword":"Then ","keywordType":"Outcome","location":{"column":5,"line":10},"text":""}],"tags":[]}},{"scenario":{"description":"","examples":[{"description":"","id":"10","keyword":"Examples","location":{"column":5,"line":19},"name":"","tableBody":[{"cells":[{"location":{"column":9,"line":21},"value":"pickle"},{"location":{"column":18,"line":21},"value":"gherkin"}],"id":"9","location":{"column":7,"line":21}}],"tableHeader":{"cells":[{"location":{"column":9,"line":20},"value":"a"},{"location":{"column":18,"line":20},"value":"b"}],"id":"8","location":{"column":7,"line":20}},"tags":[]}],"id":"11","keyword":"Scenario Outline","location":{"column":3,"line":16},"name":"the variables are replaced by going through from left to right through the examples table","steps":[{"id":"7","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":17},"text":"> and "}],"tags":[]}},{"scenario":{"description":"","examples":[{"description":"","id":"15","keyword":"Examples","location":{"column":5,"line":26},"name":"","tableBody":[{"cells":[{"location":{"column":9,"line":28},"value":"pickle"},{"location":{"column":18,"line":28},"value":"gherkin"}],"id":"14","location":{"column":7,"line":28}}],"tableHeader":{"cells":[{"location":{"column":9,"line":27},"value":"a"},{"location":{"column":18,"line":27},"value":"a"}],"id":"13","location":{"column":7,"line":27}},"tags":[]}],"id":"16","keyword":"Scenario Outline","location":{"column":3,"line":23},"name":"the first matching column is used","steps":[{"id":"12","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":24},"text":""}],"tags":[]}},{"scenario":{"description":"","examples":[{"description":"","id":"20","keyword":"Examples","location":{"column":5,"line":33},"name":"","tableBody":[{"cells":[{"location":{"column":9,"line":35},"value":""},{"location":{"column":15,"line":35},"value":"pickle"}],"id":"19","location":{"column":7,"line":35}}],"tableHeader":{"cells":[{"location":{"column":9,"line":34},"value":"a"},{"location":{"column":15,"line":34},"value":"b"}],"id":"18","location":{"column":7,"line":34}},"tags":[]}],"id":"21","keyword":"Scenario Outline","location":{"column":3,"line":30},"name":"variables are replaced once","steps":[{"id":"17","keyword":"Given ","keywordType":"Context","location":{"column":5,"line":31},"text":" and "}],"tags":[]}}],"description":" Variables can be used in scenario outlines. These are replaced by going through\n the examples table from left to right, replacing the `<` + header + `>` with the\n value from the first matching column.","keyword":"Feature","language":"en","location":{"column":1,"line":1},"name":"Scenario Outline replacements","tags":[]},"uri":"../testdata/good/scenario_outline_replacements.feature"}} diff --git a/testdata/good/scenario_outline_replacements.feature.pickles.ndjson b/testdata/good/scenario_outline_replacements.feature.pickles.ndjson new file mode 100644 index 000000000..922e1faa2 --- /dev/null +++ b/testdata/good/scenario_outline_replacements.feature.pickles.ndjson @@ -0,0 +1,4 @@ +{"pickle":{"astNodeIds":["6","4"],"id":"25","language":"en","location":{"column":7,"line":14},"name":"the variable must match the exact column name","steps":[{"astNodeIds":["0","4"],"id":"22","text":"pickle","type":"Context"},{"astNodeIds":["1","4"],"id":"23","text":"gherkin","type":"Action"},{"astNodeIds":["2","4"],"id":"24","text":"","type":"Outcome"}],"tags":[],"uri":"../testdata/good/scenario_outline_replacements.feature"}} +{"pickle":{"astNodeIds":["11","9"],"id":"27","language":"en","location":{"column":7,"line":21},"name":"the variables are replaced by going through from left to right through the examples table","steps":[{"astNodeIds":["7","9"],"id":"26","text":"gherkin and pickle","type":"Context"}],"tags":[],"uri":"../testdata/good/scenario_outline_replacements.feature"}} +{"pickle":{"astNodeIds":["16","14"],"id":"29","language":"en","location":{"column":7,"line":28},"name":"the first matching column is used","steps":[{"astNodeIds":["12","14"],"id":"28","text":"pickle","type":"Context"}],"tags":[],"uri":"../testdata/good/scenario_outline_replacements.feature"}} +{"pickle":{"astNodeIds":["21","19"],"id":"31","language":"en","location":{"column":7,"line":35},"name":"variables are replaced once","steps":[{"astNodeIds":["17","19"],"id":"30","text":" and pickle","type":"Context"}],"tags":[],"uri":"../testdata/good/scenario_outline_replacements.feature"}} diff --git a/testdata/good/scenario_outline_replacements.feature.source.ndjson b/testdata/good/scenario_outline_replacements.feature.source.ndjson new file mode 100644 index 000000000..97dd5f44f --- /dev/null +++ b/testdata/good/scenario_outline_replacements.feature.source.ndjson @@ -0,0 +1 @@ +{"source":{"data":"Feature: Scenario Outline replacements\n\n Variables can be used in scenario outlines. These are replaced by going through\n the examples table from left to right, replacing the `<` + header + `>` with the\n value from the first matching column.\n\n Scenario Outline: the variable must match the exact column name\n Given \n When \n Then \n\n Examples:\n | a | ax |\n | pickle | gherkin |\n\n Scenario Outline: the variables are replaced by going through from left to right through the examples table\n Given > and \n\n Examples:\n | a | b |\n | pickle | gherkin |\n\n Scenario Outline: the first matching column is used\n Given \n\n Examples:\n | a | a |\n | pickle | gherkin |\n\n Scenario Outline: variables are replaced once\n Given and \n\n Examples:\n | a | b |\n | | pickle |\n","mediaType":"text/x.cucumber.gherkin+plain","uri":"../testdata/good/scenario_outline_replacements.feature"}} diff --git a/testdata/good/scenario_outline_replacements.feature.tokens b/testdata/good/scenario_outline_replacements.feature.tokens new file mode 100644 index 000000000..63addf668 --- /dev/null +++ b/testdata/good/scenario_outline_replacements.feature.tokens @@ -0,0 +1,36 @@ +(1:1)FeatureLine:()Feature/Scenario Outline replacements/ +(2:1)Empty:// +(3:1)Other:/ Variables can be used in scenario outlines. These are replaced by going through/ +(4:1)Other:/ the examples table from left to right, replacing the `<` + header + `>` with the/ +(5:1)Other:/ value from the first matching column./ +(6:1)Other:// +(7:3)ScenarioLine:()Scenario Outline/the variable must match the exact column name/ +(8:5)StepLine:(Context)Given // +(9:5)StepLine:(Action)When // +(10:5)StepLine:(Outcome)Then // +(11:1)Empty:// +(12:5)ExamplesLine:()Examples// +(13:7)TableRow://9:a,18:ax +(14:7)TableRow://9:pickle,18:gherkin +(15:1)Empty:// +(16:3)ScenarioLine:()Scenario Outline/the variables are replaced by going through from left to right through the examples table/ +(17:5)StepLine:(Context)Given /> and / +(18:1)Empty:// +(19:5)ExamplesLine:()Examples// +(20:7)TableRow://9:a,18:b +(21:7)TableRow://9:pickle,18:gherkin +(22:1)Empty:// +(23:3)ScenarioLine:()Scenario Outline/the first matching column is used/ +(24:5)StepLine:(Context)Given // +(25:1)Empty:// +(26:5)ExamplesLine:()Examples// +(27:7)TableRow://9:a,18:a +(28:7)TableRow://9:pickle,18:gherkin +(29:1)Empty:// +(30:3)ScenarioLine:()Scenario Outline/variables are replaced once/ +(31:5)StepLine:(Context)Given / and / +(32:1)Empty:// +(33:5)ExamplesLine:()Examples// +(34:7)TableRow://9:a,15:b +(35:7)TableRow://9:,15:pickle +EOF