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
5 changes: 4 additions & 1 deletion python-dsl/codepathfinder/matchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
"""
Expand Down
42 changes: 30 additions & 12 deletions sourcecode-parser/dsl/call_matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -317,29 +327,35 @@ 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
// For production, we'd need a proper parser
elements := strings.Split(inner, ",")

if index >= len(elements) {
return "" // Index out of bounds
return "", false // Index out of bounds
}

element := strings.TrimSpace(elements[index])

// Remove quotes if present (handles both single and double quotes)
element = strings.Trim(element, `"'`)

return element
return element, true
}

// matchesPositionalArguments checks positional argument constraints.
Expand Down Expand Up @@ -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
Expand Down
118 changes: 69 additions & 49 deletions sourcecode-parser/dsl/call_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
}
}
Expand Down
Loading