Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions gitgalaxy/standards/language_standards/languages/ada.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@
"rules": {
# Epic #3264: Explicitly declare the structural invocation paradigm
"calls_out": CALLS_OUT_C_STYLE,
"_calls_out_ignore": frozenset(
{
"pragma",
"volatile",
"suppress",
"delay",
"accept",
"entry",
"raise",
"abs",
"mod",
"rem",
"others",
"aliased",
}
),
# --- PHASE 1: LOGIC TOPOLOGY & STRUCTURE ---
# branch: Ada's short-circuit forms are the two-word "and then"/
# "or else" (no && / || symbols exist in Ada).
Expand Down
14 changes: 14 additions & 0 deletions gitgalaxy/standards/language_standards/languages/lua.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@
"rules": {
# Epic #3264: Explicitly declare the structural invocation paradigm
"calls_out": CALLS_OUT_C_STYLE,
"_calls_out_ignore": frozenset(
{
"pairs",
"ipairs",
"tostring",
"tonumber",
"type",
"unpack",
"rawequal",
"rawlen",
"next",
"select",
}
),
# --- PHASE 1: LOGIC TOPOLOGY & STRUCTURE ---
# 1. branch: decisions that split flow. Includes standard loops and Lua 5.2+ goto.
"branch": re.compile(r"\b(if|elseif|else|for|while|repeat|and|or|not)\b"),
Expand Down
16 changes: 16 additions & 0 deletions gitgalaxy/standards/language_standards/languages/m4.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@
"rules": {
# Epic #3264: Explicitly declare the structural invocation paradigm
"calls_out": CALLS_OUT_C_STYLE,
"_calls_out_ignore": frozenset(
{
"ifelse",
"ifdef",
"define",
"m4_define",
"m4_if",
"m4_ifdef",
"dnl",
"divert",
"undivert",
"m4_provide",
"include",
"sinclude",
}
),
# --- PHASE 1: LOGIC TOPOLOGY & STRUCTURE ---
# 1. branch (Control Flow / Branching)
# M4 branching logic and Autoconf shell-generation branches.
Expand Down
17 changes: 17 additions & 0 deletions gitgalaxy/standards/language_standards/languages/matlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@
"rules": {
# Epic #3264: Explicitly declare the structural invocation paradigm
"calls_out": CALLS_OUT_C_STYLE,
"_calls_out_ignore": frozenset(
{
"zeros",
"ones",
"size",
"length",
"numel",
"disp",
"error",
"sprintf",
"fprintf",
"nargin",
"nargout",
"exist",
"strcmp",
}
),
# --- PHASE 1: LOGIC TOPOLOGY & STRUCTURE ---
# branch: MATLAB control flow. EXCLUDES 'error' and 'rethrow' (bailout_hits).
"branch": re.compile(r"\b(?:if|elseif|else|switch|case|otherwise|for|while)\b|&&|\|\||~="),
Expand Down
22 changes: 22 additions & 0 deletions gitgalaxy/standards/language_standards/languages/perl.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@
"rules": {
# Epic #3264: Explicitly declare the structural invocation paradigm
"calls_out": CALLS_OUT_C_STYLE,
"_calls_out_ignore": frozenset(
{
"my",
"our",
"local",
"unless",
"elsif",
"foreach",
"defined",
"wantarray",
"ref",
"bless",
"scalar",
"exists",
"delete",
"shift",
"unshift",
"chomp",
"die",
"warn",
}
),
# --- PHASE 1: LOGIC TOPOLOGY & STRUCTURE ---
# 1. branch: Decisions that split the flow. Includes modern try/catch/finally and defer.
"branch": re.compile(
Expand Down
15 changes: 15 additions & 0 deletions gitgalaxy/standards/language_standards/languages/ruby.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@
"rules": {
# Epic #3264: Explicitly declare the structural invocation paradigm
"calls_out": CALLS_OUT_C_STYLE,
"_calls_out_ignore": frozenset(
{
"elsif",
"module_function",
"attr_accessor",
"attr_reader",
"attr_writer",
"lambda",
"proc",
"puts",
"raise",
"loop",
"yield",
}
),
# 1. branch (Control Flow / Branching)
# Decisions and logical jumps. EXCLUDES raise/throw (bailout_hits).
"branch": re.compile(r"\b(if|unless|elsif|else|case|when|in|for|while|until)\b|&&|\|\||(?<!\w)\?"),
Expand Down
4 changes: 1 addition & 3 deletions tests/dead_key_audit_baseline.json
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
{
"_calls_out_ignore": "Read added by the #3282 foundation (Epic #3264 Phase 3) ahead of its writers by design: the per-language ignore sets land in #3284-#3287, and the foundation must merge first while staying output-neutral. Remove this entry in the first batch PR that ships a rules['_calls_out_ignore'] set."
}
{}
43 changes: 43 additions & 0 deletions tests/extraction/languages/test_ada_strict.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def test_ada_signature_positive_and_negative(signature, positive, negative):
# ==============================================================================
_BASELINE_KEYS = [
"calls_out", # Epic #3264
"_calls_out_ignore",
"branch", "args", "structural_boundaries", "func_start", "class_start",
"safety", "safety_bypasses", "high_risk_execution", "io", "api",
"state_mutation", "dead_code", "doc", "test",
Expand Down Expand Up @@ -505,3 +506,45 @@ def test_ada_api_contract_2730():
# Not declarations -- must not match.
assert not api.search(' procedure Helper (X : Integer) is'), 'nested (body-local) procedure'
assert not api.search('package body Foo is'), 'package body'

def test_ada_calls_out_strict():
"""Epic #3264 Phase 3: Ada calls_out extraction and ignore tuning."""
from _strict_harness import assert_redos_immune
from gitgalaxy.core.detector import StructuralExtractor
from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS

ada = LANGUAGE_DEFINITIONS["ada"]
calls_out = ada["rules"]["calls_out"]

assert calls_out.groups == 1

code = """
procedure test_ada is
begin
pragma Volatile (Region);
pragma Suppress (All_Checks);
delay(10);
raise(Error);

Probe_Branch(1);
Probe_Io(1);
Ada.Text_IO.Open (File);
Read(1);
end test_ada;
"""
extractor = StructuralExtractor("ada", {"ada": ada})
res = extractor.splice(code, "")
calls = res["functions"][0]["calls_out_to"]

assert "Probe_Branch" in calls
assert "Probe_Io" in calls
assert "Open" in calls
assert "Read" in calls

assert "Volatile" not in calls
assert "Suppress" not in calls
assert "delay" not in calls
assert "raise" not in calls

payload = "pragma" + (" " * 10000) + "("
assert_redos_immune(calls_out, payload)
45 changes: 45 additions & 0 deletions tests/extraction/languages/test_lua_strict.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,48 @@ def test_lua_api_contract_2730():
# Not declarations -- must not match.
assert not api.search("function ()"), "anonymous function"
assert not api.search("local function helper()"), "local function"

def test_lua_calls_out_strict():
"""Epic #3264 Phase 3: Lua calls_out extraction and ignore tuning."""
from _strict_harness import assert_redos_immune
from gitgalaxy.core.detector import StructuralExtractor
from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS

lua = LANGUAGE_DEFINITIONS["lua"]
calls_out = lua["rules"]["calls_out"]

assert calls_out.groups == 1

code = """
function test_lua()
for k, v in pairs(t) do end
ipairs(t)
tostring(x)
type(x)

probe_branch(1)
probe_io(1)
io.open("file")
read()
execute()
exit()
end
"""
extractor = StructuralExtractor("lua", {"lua": lua})
res = extractor.splice(code, "")
calls = res["functions"][0]["calls_out_to"]

assert "probe_branch" in calls
assert "probe_io" in calls
assert "open" in calls
assert "read" in calls
assert "execute" in calls
assert "exit" in calls

assert "pairs" not in calls
assert "ipairs" not in calls
assert "tostring" not in calls
assert "type" not in calls

payload = "pairs" + (" " * 10000) + "("
assert_redos_immune(calls_out, payload)
41 changes: 41 additions & 0 deletions tests/extraction/languages/test_m4_strict.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,44 @@ def test_m4_dependency_capture_does_not_claim_embedded_c_includes():
"""
embedded = "AC_CHECK_TYPES([sig_atomic_t], [], [], [[#include <signal.h>]])\n\t#include <gmp.h>\n"
assert not M4_RULES["_dependency_capture"].search(embedded)

def test_m4_calls_out_strict():
"""Epic #3264 Phase 3: m4 calls_out extraction and ignore tuning."""
from _strict_harness import assert_redos_immune
from gitgalaxy.core.detector import StructuralExtractor
from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS

m4 = LANGUAGE_DEFINITIONS["m4"]
calls_out = m4["rules"]["calls_out"]

assert calls_out.groups == 1

code = """
define(`test_m4', `
ifelse(A, B)
ifdef(C, D)
m4_define(E, F)

probe_dispatch(1)
probe_branch(1)
probe_io(1)
syscmd(ls)
esyscmd(ls)
')
"""
extractor = StructuralExtractor("m4", {"m4": m4})
res = extractor.splice(code, "")
calls = res["functions"][0]["calls_out_to"]

assert "probe_dispatch" in calls
assert "probe_branch" in calls
assert "probe_io" in calls
assert "syscmd" in calls
assert "esyscmd" in calls

assert "ifelse" not in calls
assert "ifdef" not in calls
assert "m4_define" not in calls

payload = "ifelse" + (" " * 10000) + "("
assert_redos_immune(calls_out, payload)
49 changes: 49 additions & 0 deletions tests/extraction/languages/test_matlab_strict.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,3 +698,52 @@ def test_matlab_api_contract_2730():

# Not declarations -- must not match.
assert not api.search(" function y = helper(x)"), "indented classdef method"

def test_matlab_calls_out_strict():
"""Epic #3264 Phase 3: Matlab calls_out extraction and ignore tuning."""
from _strict_harness import assert_redos_immune
from gitgalaxy.core.detector import StructuralExtractor
from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS

matlab = LANGUAGE_DEFINITIONS["matlab"]
calls_out = matlab["rules"]["calls_out"]

assert calls_out.groups == 1

code = """
function test_matlab()
zeros(10)
ones(5)
size(A)
disp('hello')
error('err')

probe_branch(1)
probe_io(1)
load('data')
save('data')
fopen('f')
system('ls')
dos('dir')
end
"""
extractor = StructuralExtractor("matlab", {"matlab": matlab})
res = extractor.splice(code, "")
calls = res["functions"][0]["calls_out_to"]

assert "probe_branch" in calls
assert "probe_io" in calls
assert "load" in calls
assert "save" in calls
assert "fopen" in calls
assert "system" in calls
assert "dos" in calls

assert "zeros" not in calls
assert "ones" not in calls
assert "size" not in calls
assert "disp" not in calls
assert "error" not in calls

payload = "zeros" + (" " * 10000) + "("
assert_redos_immune(calls_out, payload)
41 changes: 41 additions & 0 deletions tests/extraction/languages/test_perl_strict.py
Original file line number Diff line number Diff line change
Expand Up @@ -620,3 +620,44 @@ def test_perl_api_contract_2730():

# Not declarations -- must not match.
assert not api.search("sub _private_helper {"), "underscore-private sub"

def test_perl_calls_out_strict():
"""Epic #3264 Phase 3: Perl calls_out extraction and ignore tuning."""
from _strict_harness import assert_redos_immune
from gitgalaxy.core.detector import StructuralExtractor
from gitgalaxy.standards.language_standards import LANGUAGE_DEFINITIONS

perl = LANGUAGE_DEFINITIONS["perl"]
calls_out = perl["rules"]["calls_out"]

assert calls_out.groups == 1

code = """
sub my_func {
my ($x, $y) = @_;
elsif ($x == 1) { }
unless ($y) { }
foreach my $k (keys %h) { }
wantarray();

probe_branch($x);
probe_io($x);
open(my $fh, "<", "file.txt");
}
"""
extractor = StructuralExtractor("perl", {"perl": perl})
res = extractor.splice(code, "")
calls = res["functions"][0]["calls_out_to"]

assert "probe_branch" in calls
assert "probe_io" in calls
assert "open" in calls

assert "my" not in calls
assert "elsif" not in calls
assert "unless" not in calls
assert "foreach" not in calls
assert "wantarray" not in calls

payload = "my " + (" " * 10000) + "("
assert_redos_immune(calls_out, payload)
Loading
Loading