diff --git a/python-dsl/codepathfinder/matchers.py b/python-dsl/codepathfinder/matchers.py index 64b4a177..476affc8 100644 --- a/python-dsl/codepathfinder/matchers.py +++ b/python-dsl/codepathfinder/matchers.py @@ -70,6 +70,9 @@ def _make_constraint(self, value: ArgumentValue) -> Dict[str, Any]: Dictionary with 'value' and 'wildcard' keys """ # Check if wildcard characters are present in string values + # NOTE: Argument wildcard is independent of pattern wildcard (self.wildcard) + # Pattern wildcard applies to function name matching (e.g., "*.bind") + # Argument wildcard applies to argument value matching (e.g., "192.168.*") has_wildcard = False if isinstance(value, str) and ("*" in value or "?" in value): has_wildcard = True @@ -78,7 +81,7 @@ def _make_constraint(self, value: ArgumentValue) -> Dict[str, Any]: isinstance(v, str) and ("*" in v or "?" in v) for v in value ) - return {"value": value, "wildcard": has_wildcard or self.wildcard} + return {"value": value, "wildcard": has_wildcard} def to_ir(self) -> dict: """ diff --git a/sourcecode-parser/dsl/call_matcher.go b/sourcecode-parser/dsl/call_matcher.go index 67a32b1f..16cc65c4 100644 --- a/sourcecode-parser/dsl/call_matcher.go +++ b/sourcecode-parser/dsl/call_matcher.go @@ -152,7 +152,14 @@ func (e *CallMatcherExecutor) ExecuteWithContext() []CallMatchResult { } // getMatchedPattern returns which pattern matched (or empty string if no match). +// Also checks argument constraints to ensure full matching logic is applied. func (e *CallMatcherExecutor) getMatchedPattern(cs *core.CallSite) string { + // Check if the callsite matches (both pattern AND arguments) + if !e.matchesCallSite(cs) { + return "" // Doesn't match pattern or arguments don't satisfy constraints + } + + // Find which pattern matched for _, pattern := range e.IR.Patterns { if e.matchesPattern(cs.Target, pattern) { return pattern @@ -293,10 +300,12 @@ func parseTupleIndex(posStr string) (int, int, bool, bool) { // 5. Clean up quotes and whitespace // // Examples: -// - extractTupleElement("(\"0.0.0.0\", 8080)", 0) → "0.0.0.0" -// - extractTupleElement("(\"0.0.0.0\", 8080)", 1) → "8080" -// - extractTupleElement("(\"a\", \"b\", \"c\")", 1) → "b" -// - extractTupleElement("not_a_tuple", 0) → "not_a_tuple" +// - extractTupleElement("(\"0.0.0.0\", 8080)", 0) → ("0.0.0.0", true) +// - extractTupleElement("(\"0.0.0.0\", 8080)", 1) → ("8080", true) +// - extractTupleElement("(\"a\", \"b\", \"c\")", 1) → ("b", true) +// - extractTupleElement("(\"a\", \"b\")", 5) → ("", false) // out of bounds +// - extractTupleElement("(\"\", 8080)", 0) → ("", true) // empty string is valid +// - extractTupleElement("not_a_tuple", 0) → ("not_a_tuple", true) // // Limitations: // - Does not handle nested tuples/lists @@ -307,8 +316,9 @@ func parseTupleIndex(posStr string) (int, int, bool, bool) { // - index: 0-indexed position of element to extract // // Returns: -// - Extracted element as string, or empty string if index out of bounds -func extractTupleElement(tupleStr string, index int) string { +// - value: Extracted element as string (can be empty string if that's the actual value) +// - ok: true if extraction succeeded, false if index out of bounds +func extractTupleElement(tupleStr string, index int) (string, bool) { tupleStr = strings.TrimSpace(tupleStr) // Check if it's a tuple or list @@ -317,13 +327,19 @@ func extractTupleElement(tupleStr string, index int) string { if index == 0 { // Remove quotes from plain strings too result := strings.Trim(tupleStr, `"'`) - return result + return result, true } - return "" // Index out of bounds for non-tuple + return "", false // Index out of bounds for non-tuple } // Strip outer parentheses or brackets inner := tupleStr[1 : len(tupleStr)-1] + inner = strings.TrimSpace(inner) + + // Handle empty tuple/list + if inner == "" { + return "", false // Empty tuple has no elements + } // Split by comma // Note: This is a simple implementation that doesn't handle nested structures @@ -331,7 +347,7 @@ func extractTupleElement(tupleStr string, index int) string { elements := strings.Split(inner, ",") if index >= len(elements) { - return "" // Index out of bounds + return "", false // Index out of bounds } element := strings.TrimSpace(elements[index]) @@ -339,7 +355,7 @@ func extractTupleElement(tupleStr string, index int) string { // Remove quotes if present (handles both single and double quotes) element = strings.Trim(element, `"'`) - return element + return element, true } // matchesPositionalArguments checks positional argument constraints. @@ -381,10 +397,12 @@ func (e *CallMatcherExecutor) matchesPositionalArguments(args []core.Argument) b // Extract tuple element if tuple indexing used if isTupleIndex { - actualValue = extractTupleElement(actualValue, tupleIdx) - if actualValue == "" { + var ok bool + actualValue, ok = extractTupleElement(actualValue, tupleIdx) + if !ok { return false // Tuple index out of bounds } + // Note: actualValue can be empty string if that's the actual tuple element value } // Match against constraint diff --git a/sourcecode-parser/dsl/call_matcher_test.go b/sourcecode-parser/dsl/call_matcher_test.go index b7dce4bc..4b8d01db 100644 --- a/sourcecode-parser/dsl/call_matcher_test.go +++ b/sourcecode-parser/dsl/call_matcher_test.go @@ -1564,83 +1564,103 @@ func TestParseTupleIndex(t *testing.T) { // TestExtractTupleElement tests extraction of elements from tuple strings. func TestExtractTupleElement(t *testing.T) { tests := []struct { - name string - input string - index int - expected string + name string + input string + index int + expected string + expectedOk bool }{ { - name: "extract first element from string tuple", - input: `("0.0.0.0", 8080)`, - index: 0, - expected: "0.0.0.0", + name: "extract first element from string tuple", + input: `("0.0.0.0", 8080)`, + index: 0, + expected: "0.0.0.0", + expectedOk: true, + }, + { + name: "extract second element from string tuple", + input: `("0.0.0.0", 8080)`, + index: 1, + expected: "8080", + expectedOk: true, }, { - name: "extract second element from string tuple", - input: `("0.0.0.0", 8080)`, - index: 1, - expected: "8080", + name: "extract from single-quoted string", + input: `('0.0.0.0', 8080)`, + index: 0, + expected: "0.0.0.0", + expectedOk: true, }, { - name: "extract from single-quoted string", - input: `('0.0.0.0', 8080)`, - index: 0, - expected: "0.0.0.0", + name: "extract from tuple with spaces", + input: `( "0.0.0.0" , 8080 )`, + index: 0, + expected: "0.0.0.0", + expectedOk: true, }, { - name: "extract from tuple with spaces", - input: `( "0.0.0.0" , 8080 )`, - index: 0, - expected: "0.0.0.0", + name: "extract from three-element tuple", + input: `("a", "b", "c")`, + index: 1, + expected: "b", + expectedOk: true, }, { - name: "extract from three-element tuple", - input: `("a", "b", "c")`, - index: 1, - expected: "b", + name: "extract from list syntax", + input: `["host", 8080]`, + index: 0, + expected: "host", + expectedOk: true, }, { - name: "extract from list syntax", - input: `["host", 8080]`, - index: 0, - expected: "host", + name: "index out of bounds", + input: `("0.0.0.0", 8080)`, + index: 5, + expected: "", + expectedOk: false, }, { - name: "index out of bounds", - input: `("0.0.0.0", 8080)`, - index: 5, - expected: "", + name: "not a tuple - return as is for index 0", + input: `"plain_string"`, + index: 0, + expected: "plain_string", + expectedOk: true, }, { - name: "not a tuple - return as is for index 0", - input: `"plain_string"`, - index: 0, - expected: "plain_string", + name: "not a tuple - empty for index > 0", + input: `"plain_string"`, + index: 1, + expected: "", + expectedOk: false, }, { - name: "not a tuple - empty for index > 0", - input: `"plain_string"`, - index: 1, - expected: "", + name: "empty tuple", + input: `()`, + index: 0, + expected: "", + expectedOk: false, }, { - name: "empty tuple", - input: `()`, - index: 0, - expected: "", + name: "tuple with numeric values", + input: `(80, 443, 8080)`, + index: 1, + expected: "443", + expectedOk: true, }, { - name: "tuple with numeric values", - input: `(80, 443, 8080)`, - index: 1, - expected: "443", + name: "tuple with empty string - should extract empty string successfully", + input: `("", 8080)`, + index: 0, + expected: "", + expectedOk: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := extractTupleElement(tt.input, tt.index) + result, ok := extractTupleElement(tt.input, tt.index) assert.Equal(t, tt.expected, result) + assert.Equal(t, tt.expectedOk, ok) }) } }