From 79a3833459c9d7f96d7942ed414cbb9032b282cd Mon Sep 17 00:00:00 2001 From: Joe Esquibel Date: Sun, 2 Aug 2026 20:07:01 -0400 Subject: [PATCH 1/4] Harden matlab extraction regexes (Issue #852) --- gitgalaxy/standards/language_standards.py | 4 +- tests/extraction/languages/test_matlab.py | 216 ++++++++++++++++++ .../test_dependency_extraction_strict.py | 9 +- .../test_function_extraction_strict.py | 15 +- 4 files changed, 220 insertions(+), 24 deletions(-) create mode 100644 tests/extraction/languages/test_matlab.py diff --git a/gitgalaxy/standards/language_standards.py b/gitgalaxy/standards/language_standards.py index 2c438964c..f9a0d377d 100644 --- a/gitgalaxy/standards/language_standards.py +++ b/gitgalaxy/standards/language_standards.py @@ -9507,7 +9507,7 @@ class PrismConfigSchema(TypedDict): # args: Captures standard function inputs and return signatures `function [out1, out2] = myFun(in1, in2)`. # CRITICAL GUARDRAIL: Safely bounds `\([^)]*\)` and `\[[^\]]*\]`. "args": re.compile( - r"\bfunction[ \t]+(?:\[[^\]]*\][ \t]*=[ \t]*|[a-zA-Z_]\w*[ \t]*=[ \t]*)?[a-zA-Z_]\w*[ \t]*\([^)]*\)|@[ \t]*\([^)]*\)" + r"\bfunction[ \t\n]+(?:\[[^\]]*\][ \t\n]*=[ \t\n]*|[a-zA-Z_]\w*[ \t\n]*=[ \t\n]*)?[a-zA-Z_]\w*[ \t\n]*\([^)]*\)|@[ \t\n]*\([^)]*\)" ), # linear: Structural boundaries defining straight-line execution. # CRITICAL GUARDRAIL: Access modifiers (private, protected) explicitly omitted. @@ -9532,7 +9532,7 @@ class PrismConfigSchema(TypedDict): # class_start: Defines an object-oriented boundary. # Safely steps over optional class attributes like `classdef (ConstructOnLoad) MyClass` "class_start": re.compile( - r"^[ \t]*classdef(?:[ \t]*\([^)]*\))?[ \t]+([a-zA-Z_]\w*)(?=[ \t\n]|$)", + r"^[ \t]*classdef(?:[ \t\n]*\([^)]*\))?[ \t\n]+([a-zA-Z_]\w*)(?=[ \t\n]|$)", re.M, ), # --- PHASE 2: RISK & STRUCTURAL INTEGRITY --- diff --git a/tests/extraction/languages/test_matlab.py b/tests/extraction/languages/test_matlab.py new file mode 100644 index 000000000..137034d83 --- /dev/null +++ b/tests/extraction/languages/test_matlab.py @@ -0,0 +1,216 @@ +""" +MATLAB extraction hardening (epic #852). See +tests/extraction/how_to_harden_extraction.md for the methodology. +""" + +import sys +from pathlib import Path + +import pytest + +from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS + +_EXTRACTION_DIR = str(Path(__file__).resolve().parent.parent) +if _EXTRACTION_DIR not in sys.path: + sys.path.insert(0, _EXTRACTION_DIR) + +from typing import Any # noqa: E402 + +from _extraction_harness import ( # noqa: E402 # type: ignore + assert_invalid_no_match, + assert_pathological_dependency_match, + assert_pathological_match, + assert_valid_dependency_match, + assert_valid_match, +) + +MATLAB_RULES = LANGUAGE_DEFINITIONS["matlab"]["rules"] + +# ============================================================================== +# FUNC_START (func_start) +# ============================================================================== +FUNCTION_CASES: dict[str, Any] = { + "valid": [ + # Modern idiom (carried forward) + ("function [out] = TargetFunc(in)", "TargetFunc"), + ("function TargetFunc()", "TargetFunc"), + # Other idioms + ("function TargetFunc", "TargetFunc"), + ("function [out1, out2] = TargetFunc(in1, in2)", "TargetFunc"), + ("function out = TargetFunc(in)", "TargetFunc"), + ("function [out] = TargetFunc ( in )", "TargetFunc"), # Spacing + ], + "invalid": [ + # Carried forward + "if TargetFunc()", + "classdef TargetFunc", + "TargetFunc = 5", + # Ghost Prevention + "disp('function TargetFunc()')", + "% function TargetFunc()", + ], + "pathological": [ + # Splitting output arrays across newlines (carried forward) + ( + "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc \n (", + "TargetFunc", + ), + ( + "function\nout\n=\nTargetFunc\n(", + "TargetFunc", + ), + ( + "function \n TargetFunc \n (", + "TargetFunc", + ), + ( + "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc", + "TargetFunc", + ), + ], +} + + +@pytest.mark.parametrize("payload,expected_name", FUNCTION_CASES["valid"]) +def test_matlab_func_start_valid(payload, expected_name): + assert_valid_match(MATLAB_RULES["func_start"], payload, expected_name, "matlab.func_start") + + +@pytest.mark.parametrize("payload", FUNCTION_CASES["invalid"]) +def test_matlab_func_start_invalid(payload): + assert_invalid_no_match(MATLAB_RULES["func_start"], payload, "matlab.func_start") + + +@pytest.mark.parametrize("payload,expected_name", FUNCTION_CASES["pathological"]) +def test_matlab_func_start_pathological(payload, expected_name): + assert_pathological_match(MATLAB_RULES["func_start"], payload, expected_name, "matlab.func_start") + + +# ============================================================================== +# ARGS (args) +# ============================================================================== +ARGS_CASES: dict[str, Any] = { + "valid": [ + ("function [out] = TargetFunc(in1, in2)", None), + ("function TargetFunc(in1)", None), + ("function TargetFunc()", None), + ("@(x, y)", None), # anonymous function + ], + "invalid": [ + "if TargetFunc(in1, in2)", + "disp(in1, in2)", + ], + "pathological": [ + ( + "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc \n (\n in1 \n , \n in2 \n )", + None, + ), + ( + "function\nout\n=\nTargetFunc\n(\n in1 \n )", + None, + ), + ( + "@\n(\nx\n,\ny\n)", + None, + ), + ], +} + + +@pytest.mark.parametrize("payload,expected_name", ARGS_CASES["valid"]) +def test_matlab_args_valid(payload, expected_name): + assert_valid_match(MATLAB_RULES["args"], payload, expected_name, "matlab.args") + + +@pytest.mark.parametrize("payload", ARGS_CASES["invalid"]) +def test_matlab_args_invalid(payload): + assert_invalid_no_match(MATLAB_RULES["args"], payload, "matlab.args") + + +@pytest.mark.parametrize("payload,expected_name", ARGS_CASES["pathological"]) +def test_matlab_args_pathological(payload, expected_name): + assert_pathological_match(MATLAB_RULES["args"], payload, expected_name, "matlab.args") + + +# ============================================================================== +# CLASS_START (class_start) +# ============================================================================== +CLASS_CASES: dict[str, Any] = { + "valid": [ + ("classdef TargetClass", "TargetClass"), + ("classdef (ConstructOnLoad) TargetClass", "TargetClass"), + ("classdef TargetClass < handle", "TargetClass"), + ("classdef (Sealed = true, Hidden = false) TargetClass < handle & matlab.mixin.Copyable", "TargetClass"), + ], + "invalid": [ + "if classdef TargetClass", + "% classdef TargetClass", + "disp('classdef TargetClass')", + ], + "pathological": [ + ( + "classdef \n ( \n ConstructOnLoad \n ) \n TargetClass \n < \n handle", + "TargetClass", + ), + ( + "classdef \n TargetClass", + "TargetClass", + ), + ], +} + + +@pytest.mark.parametrize("payload,expected_name", CLASS_CASES["valid"]) +def test_matlab_class_start_valid(payload, expected_name): + assert_valid_match(MATLAB_RULES["class_start"], payload, expected_name, "matlab.class_start") + + +@pytest.mark.parametrize("payload", CLASS_CASES["invalid"]) +def test_matlab_class_start_invalid(payload): + assert_invalid_no_match(MATLAB_RULES["class_start"], payload, "matlab.class_start") + + +@pytest.mark.parametrize("payload,expected_name", CLASS_CASES["pathological"]) +def test_matlab_class_start_pathological(payload, expected_name): + assert_pathological_match(MATLAB_RULES["class_start"], payload, expected_name, "matlab.class_start") + + +# ============================================================================== +# DEPENDENCY (_dependency_capture) +# ============================================================================== +DEPENDENCY_CASES: dict[str, Any] = { + "valid": [ + # Carried forward + ("import matlab.unittest.*", "matlab.unittest.*"), + ("import mypack.myclass", "mypack.myclass"), + ], + "invalid": [ + # Carried forward + "import_val = 1;", + "% import matlab.unittest.*", + "disp('import matlab.unittest.*')", + ], + "pathological": [ + # Carried forward + ("import \n parallel.Pool", "parallel.Pool"), + ], +} + + +@pytest.mark.parametrize("payload,expected_path", DEPENDENCY_CASES["valid"]) +def test_matlab_dependency_capture_valid(payload, expected_path): + assert_valid_dependency_match( + MATLAB_RULES["_dependency_capture"], payload, expected_path, "matlab._dependency_capture" + ) + + +@pytest.mark.parametrize("payload", DEPENDENCY_CASES["invalid"]) +def test_matlab_dependency_capture_invalid(payload): + assert_invalid_no_match(MATLAB_RULES["_dependency_capture"], payload, "matlab._dependency_capture") + + +@pytest.mark.parametrize("payload,expected_path", DEPENDENCY_CASES["pathological"]) +def test_matlab_dependency_capture_pathological(payload, expected_path): + assert_pathological_dependency_match( + MATLAB_RULES["_dependency_capture"], payload, expected_path, "matlab._dependency_capture" + ) diff --git a/tests/extraction/test_dependency_extraction_strict.py b/tests/extraction/test_dependency_extraction_strict.py index 4435e51a0..d109c0422 100644 --- a/tests/extraction/test_dependency_extraction_strict.py +++ b/tests/extraction/test_dependency_extraction_strict.py @@ -92,14 +92,7 @@ "invalid": ["ENV FROM_PATH=/app"], "pathological": [("FROM \n --platform=linux/amd64 \n alpine:3.18", "alpine:3.18")], }, - "matlab": { - "valid": [ - ("import matlab.unittest.*", "matlab.unittest.*"), - ("import mypack.myclass", "mypack.myclass"), - ], - "invalid": ["import_val = 1;"], - "pathological": [("import \n parallel.Pool", "parallel.Pool")], - }, + "livecode": { "valid": [ ('start using stack "lib"', "lib"), diff --git a/tests/extraction/test_function_extraction_strict.py b/tests/extraction/test_function_extraction_strict.py index 41530098b..1988f35fb 100644 --- a/tests/extraction/test_function_extraction_strict.py +++ b/tests/extraction/test_function_extraction_strict.py @@ -95,20 +95,7 @@ ) ], }, - "matlab": { - "valid": [ - ("function [out] = TargetFunc(in)", "TargetFunc"), - ("function TargetFunc()", "TargetFunc"), - ], - "invalid": ["if TargetFunc()", "classdef TargetFunc", "TargetFunc = 5"], - "pathological": [ - # Splitting output arrays across newlines - ( - "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc \n (", - "TargetFunc", - ) - ], - }, + "livecode": { "valid": [ ("on TargetFunc", "TargetFunc"), From 9b686d7d4cea420a89e90a1fc1f8893ad6c741a9 Mon Sep 17 00:00:00 2001 From: Joe Esquibel Date: Sun, 2 Aug 2026 20:12:44 -0400 Subject: [PATCH 2/4] Harden MATLAB extraction (Issue #852) - Added missing line continuation support for func_start, args, class_start, and _dependency_capture. - Fixed class_start generic missing space support. - Recorded unshielded Mode D execution for block comments and string literals as known limitation (Class 3). --- gitgalaxy/standards/language_standards.py | 8 ++++---- tests/extraction/languages/test_matlab.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/gitgalaxy/standards/language_standards.py b/gitgalaxy/standards/language_standards.py index f9a0d377d..68e6e7610 100644 --- a/gitgalaxy/standards/language_standards.py +++ b/gitgalaxy/standards/language_standards.py @@ -9507,7 +9507,7 @@ class PrismConfigSchema(TypedDict): # args: Captures standard function inputs and return signatures `function [out1, out2] = myFun(in1, in2)`. # CRITICAL GUARDRAIL: Safely bounds `\([^)]*\)` and `\[[^\]]*\]`. "args": re.compile( - r"\bfunction[ \t\n]+(?:\[[^\]]*\][ \t\n]*=[ \t\n]*|[a-zA-Z_]\w*[ \t\n]*=[ \t\n]*)?[a-zA-Z_]\w*[ \t\n]*\([^)]*\)|@[ \t\n]*\([^)]*\)" + r"\bfunction(?:[ \t\n]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*)?[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*\([^)]*\)|@(?:[ \t\n]|\.\.\.[^\n]*\n)*\([^)]*\)" ), # linear: Structural boundaries defining straight-line execution. # CRITICAL GUARDRAIL: Access modifiers (private, protected) explicitly omitted. @@ -9526,13 +9526,13 @@ class PrismConfigSchema(TypedDict): # the optional `(?:\[[^\]]*\]...)?` output array matcher, allowing the # regex to crawl down to the assignment operator `=` and map the name. # ===================================================================== - r"^[ \t]*(?!(?:if|for|while|switch|catch|classdef)\b)function[ \t\n]+(?:\[[^\]]*\][ \t\n]*=[ \t\n]*|[a-zA-Z_]\w*[ \t\n]*=[ \t\n]*)?([a-zA-Z_]\w*)(?=[ \t\n]*\(|$)", + r"^[ \t]*(?!(?:if|for|while|switch|catch|classdef)\b)function(?:[ \t\n]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*)?([a-zA-Z_]\w*)(?=(?:[ \t\n]|\.\.\.[^\n]*\n)*\(|$)", re.M, ), # class_start: Defines an object-oriented boundary. # Safely steps over optional class attributes like `classdef (ConstructOnLoad) MyClass` "class_start": re.compile( - r"^[ \t]*classdef(?:[ \t\n]*\([^)]*\))?[ \t\n]+([a-zA-Z_]\w*)(?=[ \t\n]|$)", + r"^[ \t]*classdef(?:(?:[ \t\n]|\.\.\.[^\n]*\n)*\([^)]*\))?(?:[ \t\n]|\.\.\.[^\n]*\n)+([a-zA-Z_]\w*)(?=(?:[ \t\n]|\.\.\.[^\n]*\n)|<|$)", re.M, ), # --- PHASE 2: RISK & STRUCTURAL INTEGRITY --- @@ -9630,7 +9630,7 @@ class PrismConfigSchema(TypedDict): ), # import: Namespace/Class loading. "import": re.compile(r"^[ \t]*import[ \t]+[a-zA-Z0-9_.*]+", re.M), - "_dependency_capture": re.compile(r"^[ \t]*import[ \t\n]+([a-zA-Z0-9_.*]+)", re.M), + "_dependency_capture": re.compile(r"^[ \t]*import(?:[ \t\n]|\.\.\.[^\n]*\n)+([a-zA-Z0-9_.*]+)", re.M), # ownership: Standard MATLAB comment authorship signatures. "ownership": re.compile(r"^[ \t]*%[ \t]*(?:Author|Created by|Copyright)[ \t]*:(.*)", re.M | re.I), # --- PHASE 4: SPECIALIZED SUB-SYSTEMS --- diff --git a/tests/extraction/languages/test_matlab.py b/tests/extraction/languages/test_matlab.py index 137034d83..e13d66ce5 100644 --- a/tests/extraction/languages/test_matlab.py +++ b/tests/extraction/languages/test_matlab.py @@ -39,6 +39,10 @@ ("function [out1, out2] = TargetFunc(in1, in2)", "TargetFunc"), ("function out = TargetFunc(in)", "TargetFunc"), ("function [out] = TargetFunc ( in )", "TargetFunc"), # Spacing + # Line continuation + ("function [out] = ...\n TargetFunc(in)", "TargetFunc"), + ("function ...\n TargetFunc(in)", "TargetFunc"), + # Block comments lookalike ], "invalid": [ # Carried forward @@ -48,6 +52,9 @@ # Ghost Prevention "disp('function TargetFunc()')", "% function TargetFunc()", + # KNOWN LIMITATION (Class 3): Block comments and string literals are unshielded in Mode D + # "%{ \n function TargetFunc() \n %}", + # "x = \"function TargetFunc()\"", ], "pathological": [ # Splitting output arrays across newlines (carried forward) @@ -95,6 +102,7 @@ def test_matlab_func_start_pathological(payload, expected_name): ("function TargetFunc(in1)", None), ("function TargetFunc()", None), ("@(x, y)", None), # anonymous function + ("function [out] = ...\n TargetFunc(in1, in2)", None), ], "invalid": [ "if TargetFunc(in1, in2)", @@ -141,6 +149,8 @@ def test_matlab_args_pathological(payload, expected_name): ("classdef (ConstructOnLoad) TargetClass", "TargetClass"), ("classdef TargetClass < handle", "TargetClass"), ("classdef (Sealed = true, Hidden = false) TargetClass < handle & matlab.mixin.Copyable", "TargetClass"), + ("classdef TargetClass Date: Sun, 2 Aug 2026 20:23:38 -0400 Subject: [PATCH 3/4] Fix matlab line continuation regex and impossible pathological test cases --- gitgalaxy/standards/language_standards.py | 8 ++++---- tests/extraction/languages/test_matlab.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/gitgalaxy/standards/language_standards.py b/gitgalaxy/standards/language_standards.py index 68e6e7610..ff16eeec6 100644 --- a/gitgalaxy/standards/language_standards.py +++ b/gitgalaxy/standards/language_standards.py @@ -9507,7 +9507,7 @@ class PrismConfigSchema(TypedDict): # args: Captures standard function inputs and return signatures `function [out1, out2] = myFun(in1, in2)`. # CRITICAL GUARDRAIL: Safely bounds `\([^)]*\)` and `\[[^\]]*\]`. "args": re.compile( - r"\bfunction(?:[ \t\n]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*)?[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*\([^)]*\)|@(?:[ \t\n]|\.\.\.[^\n]*\n)*\([^)]*\)" + r"\bfunction(?:[ \t]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t]|\.\.\.[^\n]*\n)*=(?:[ \t]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t]|\.\.\.[^\n]*\n)*=(?:[ \t]|\.\.\.[^\n]*\n)*)?[a-zA-Z_]\w*(?:[ \t]|\.\.\.[^\n]*\n)*\([^)]*\)|@(?:[ \t]|\.\.\.[^\n]*\n)*\([^)]*\)" ), # linear: Structural boundaries defining straight-line execution. # CRITICAL GUARDRAIL: Access modifiers (private, protected) explicitly omitted. @@ -9526,13 +9526,13 @@ class PrismConfigSchema(TypedDict): # the optional `(?:\[[^\]]*\]...)?` output array matcher, allowing the # regex to crawl down to the assignment operator `=` and map the name. # ===================================================================== - r"^[ \t]*(?!(?:if|for|while|switch|catch|classdef)\b)function(?:[ \t\n]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*)?([a-zA-Z_]\w*)(?=(?:[ \t\n]|\.\.\.[^\n]*\n)*\(|$)", + r"^[ \t]*(?!(?:if|for|while|switch|catch|classdef)\b)function(?:[ \t]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t]|\.\.\.[^\n]*\n)*=(?:[ \t]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t]|\.\.\.[^\n]*\n)*=(?:[ \t]|\.\.\.[^\n]*\n)*)?([a-zA-Z_]\w*)(?=(?:[ \t]|\.\.\.[^\n]*\n)*\(|$)", re.M, ), # class_start: Defines an object-oriented boundary. # Safely steps over optional class attributes like `classdef (ConstructOnLoad) MyClass` "class_start": re.compile( - r"^[ \t]*classdef(?:(?:[ \t\n]|\.\.\.[^\n]*\n)*\([^)]*\))?(?:[ \t\n]|\.\.\.[^\n]*\n)+([a-zA-Z_]\w*)(?=(?:[ \t\n]|\.\.\.[^\n]*\n)|<|$)", + r"^[ \t]*classdef(?:(?:[ \t]|\.\.\.[^\n]*\n)*\([^)]*\))?(?:[ \t]|\.\.\.[^\n]*\n)+([a-zA-Z_]\w*)(?=(?:[ \t]|\.\.\.[^\n]*\n)|<|$)", re.M, ), # --- PHASE 2: RISK & STRUCTURAL INTEGRITY --- @@ -9630,7 +9630,7 @@ class PrismConfigSchema(TypedDict): ), # import: Namespace/Class loading. "import": re.compile(r"^[ \t]*import[ \t]+[a-zA-Z0-9_.*]+", re.M), - "_dependency_capture": re.compile(r"^[ \t]*import(?:[ \t\n]|\.\.\.[^\n]*\n)+([a-zA-Z0-9_.*]+)", re.M), + "_dependency_capture": re.compile(r"^[ \t]*import(?:[ \t]|\.\.\.[^\n]*\n)+([a-zA-Z0-9_.*]+)", re.M), # ownership: Standard MATLAB comment authorship signatures. "ownership": re.compile(r"^[ \t]*%[ \t]*(?:Author|Created by|Copyright)[ \t]*:(.*)", re.M | re.I), # --- PHASE 4: SPECIALIZED SUB-SYSTEMS --- diff --git a/tests/extraction/languages/test_matlab.py b/tests/extraction/languages/test_matlab.py index e13d66ce5..609cdaa4b 100644 --- a/tests/extraction/languages/test_matlab.py +++ b/tests/extraction/languages/test_matlab.py @@ -59,19 +59,19 @@ "pathological": [ # Splitting output arrays across newlines (carried forward) ( - "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc \n (", + "function ...\n [ \n out1 \n , \n out2 \n ] ...\n = ...\n TargetFunc ...\n (", "TargetFunc", ), ( - "function\nout\n=\nTargetFunc\n(", + "function...\nout...\n=...\nTargetFunc...\n(", "TargetFunc", ), ( - "function \n TargetFunc \n (", + "function ...\n TargetFunc ...\n (", "TargetFunc", ), ( - "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc", + "function ...\n [ \n out1 \n , \n out2 \n ] ...\n = ...\n TargetFunc", "TargetFunc", ), ], @@ -110,15 +110,15 @@ def test_matlab_func_start_pathological(payload, expected_name): ], "pathological": [ ( - "function \n [ \n out1 \n , \n out2 \n ] \n = \n TargetFunc \n (\n in1 \n , \n in2 \n )", + "function ...\n [ \n out1 \n , \n out2 \n ] ...\n = ...\n TargetFunc ...\n (\n in1 \n , \n in2 \n )", None, ), ( - "function\nout\n=\nTargetFunc\n(\n in1 \n )", + "function...\nout...\n=...\nTargetFunc...\n(\n in1 \n )", None, ), ( - "@\n(\nx\n,\ny\n)", + "@...\n(\nx\n,\ny\n)", None, ), ], @@ -159,11 +159,11 @@ def test_matlab_args_pathological(payload, expected_name): ], "pathological": [ ( - "classdef \n ( \n ConstructOnLoad \n ) \n TargetClass \n < \n handle", + "classdef ...\n ( \n ConstructOnLoad \n ) ...\n TargetClass ...\n < ...\n handle", "TargetClass", ), ( - "classdef \n TargetClass", + "classdef ...\n TargetClass", "TargetClass", ), ], @@ -203,7 +203,7 @@ def test_matlab_class_start_pathological(payload, expected_name): ], "pathological": [ # Carried forward - ("import \n parallel.Pool", "parallel.Pool"), + ("import ...\n parallel.Pool", "parallel.Pool"), ], } From cc70f423fa78ad4f17798721a2fe3e90983c2931 Mon Sep 17 00:00:00 2001 From: Joe Esquibel Date: Sun, 2 Aug 2026 20:34:31 -0400 Subject: [PATCH 4/4] Fix matlab func_start missing vertical output array shield for tests --- gitgalaxy/standards/language_standards.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitgalaxy/standards/language_standards.py b/gitgalaxy/standards/language_standards.py index ff16eeec6..9797367c3 100644 --- a/gitgalaxy/standards/language_standards.py +++ b/gitgalaxy/standards/language_standards.py @@ -9526,7 +9526,7 @@ class PrismConfigSchema(TypedDict): # the optional `(?:\[[^\]]*\]...)?` output array matcher, allowing the # regex to crawl down to the assignment operator `=` and map the name. # ===================================================================== - r"^[ \t]*(?!(?:if|for|while|switch|catch|classdef)\b)function(?:[ \t]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t]|\.\.\.[^\n]*\n)*=(?:[ \t]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t]|\.\.\.[^\n]*\n)*=(?:[ \t]|\.\.\.[^\n]*\n)*)?([a-zA-Z_]\w*)(?=(?:[ \t]|\.\.\.[^\n]*\n)*\(|$)", + r"^[ \t]*(?!(?:if|for|while|switch|catch|classdef)\b)function(?:[ \t\n]|\.\.\.[^\n]*\n)+(?:\[[^\]]*\](?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*|[a-zA-Z_]\w*(?:[ \t\n]|\.\.\.[^\n]*\n)*=(?:[ \t\n]|\.\.\.[^\n]*\n)*)?([a-zA-Z_]\w*)(?=(?:[ \t\n]|\.\.\.[^\n]*\n)*\(|$)", re.M, ), # class_start: Defines an object-oriented boundary.