fix: resolve type errors in javascript validator

- Add None guards for node.text before calling .decode() throughout _extract_defined_identifiers and _extract_referenced_identifiers
- Add set[str] type annotations for defined/referenced sets in _check_undefined_references
- Fix test assertions using "in" on str | None by adding explicit None checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
claude[bot] 2026-04-02 11:59:55 +00:00
parent c8284c4dcd
commit ff3423f13f
2 changed files with 25 additions and 21 deletions

View file

@ -75,7 +75,8 @@ def _extract_defined_identifiers(node: Node, defined: set[str]) -> None:
if node.type in ("variable_declarator", "lexical_declaration"):
for child in node.children:
if child.type == "identifier":
defined.add(child.text.decode("utf8"))
if child.text is not None:
defined.add(child.text.decode("utf8"))
elif child.type == "variable_declarator":
_extract_defined_identifiers(child, defined)
elif child.type == "object_pattern":
@ -86,10 +87,11 @@ def _extract_defined_identifiers(node: Node, defined: set[str]) -> None:
elif node.type == "object_pattern":
for child in node.children:
if child.type == "shorthand_property_identifier_pattern":
defined.add(child.text.decode("utf8"))
if child.text is not None:
defined.add(child.text.decode("utf8"))
elif child.type == "pair_pattern":
for subchild in child.children:
if subchild.type == "identifier":
if subchild.type == "identifier" and subchild.text is not None:
defined.add(subchild.text.decode("utf8"))
else:
_extract_defined_identifiers(child, defined)
@ -98,7 +100,8 @@ def _extract_defined_identifiers(node: Node, defined: set[str]) -> None:
elif node.type == "array_pattern":
for child in node.children:
if child.type == "identifier":
defined.add(child.text.decode("utf8"))
if child.text is not None:
defined.add(child.text.decode("utf8"))
else:
_extract_defined_identifiers(child, defined)
@ -107,34 +110,37 @@ def _extract_defined_identifiers(node: Node, defined: set[str]) -> None:
for child in node.children:
if child.type == "identifier" and node.type == "function_declaration":
# Function name
defined.add(child.text.decode("utf8"))
if child.text is not None:
defined.add(child.text.decode("utf8"))
elif child.type == "formal_parameters":
# Function parameters - extract all identifiers within
for param_child in child.children:
if param_child.type == "identifier":
defined.add(param_child.text.decode("utf8"))
if param_child.text is not None:
defined.add(param_child.text.decode("utf8"))
elif param_child.type in ("required_parameter", "optional_parameter"):
for subchild in param_child.children:
if subchild.type == "identifier":
if subchild.type == "identifier" and subchild.text is not None:
defined.add(subchild.text.decode("utf8"))
elif subchild.type == "rest_pattern":
# Rest parameters: ...args
for rest_child in subchild.children:
if rest_child.type == "identifier":
if rest_child.type == "identifier" and rest_child.text is not None:
defined.add(rest_child.text.decode("utf8"))
# Import statements
elif node.type in ("import_statement", "import_specifier", "namespace_import", "import_clause"):
for child in node.children:
if child.type == "identifier":
defined.add(child.text.decode("utf8"))
if child.text is not None:
defined.add(child.text.decode("utf8"))
else:
_extract_defined_identifiers(child, defined)
# Class declarations
elif node.type == "class_declaration":
for child in node.children:
if child.type == "type_identifier" or child.type == "identifier":
if (child.type == "type_identifier" or child.type == "identifier") and child.text is not None:
defined.add(child.text.decode("utf8"))
# For-in and for-of loop iteration variables
@ -147,7 +153,8 @@ def _extract_defined_identifiers(node: Node, defined: set[str]) -> None:
found_keyword = True
elif found_keyword and child.type == "identifier":
# This is the iteration variable
defined.add(child.text.decode("utf8"))
if child.text is not None:
defined.add(child.text.decode("utf8"))
found_keyword = False # Only get the first identifier after the keyword
elif child.type in ("of", "in"):
# Stop looking after we hit of/in
@ -184,11 +191,11 @@ def _extract_referenced_identifiers(node: Node, referenced: set[str], defined_in
# Skip property access (e.g., obj.property - we only care about 'obj', not 'property')
if parent.type == "member_expression":
# Only count the object, not the property
if parent.children and parent.children[0] == node:
if parent.children and parent.children[0] == node and node.text is not None:
ident = node.text.decode("utf8")
if ident not in defined_in_scope:
referenced.add(ident)
else:
elif node.text is not None:
ident = node.text.decode("utf8")
if ident not in defined_in_scope:
referenced.add(ident)
@ -203,13 +210,13 @@ def _check_undefined_references(code: str, lang: str) -> tuple[bool, str | None]
parser = js_parser if lang == "js" else ts_parser
tree = parser.parse(code.encode("utf8"))
defined = set()
defined: set[str] = set()
_extract_defined_identifiers(tree.root_node, defined)
# Add built-in globals
defined.update(JAVASCRIPT_GLOBALS)
referenced = set()
referenced: set[str] = set()
_extract_referenced_identifiers(tree.root_node, referenced, defined)
# Find undefined references

View file

@ -2,10 +2,7 @@
import pytest
from aiservice.validators.javascript_validator import (
validate_javascript_syntax,
validate_typescript_syntax,
)
from aiservice.validators.javascript_validator import validate_javascript_syntax, validate_typescript_syntax
class TestJavaScriptValidator:
@ -231,7 +228,7 @@ type TarEntryInfo = { path: string; type: string; size: number };
# BUG: Validating source_code alone fails
is_valid_source_only, error_source_only = validate_typescript_syntax(source_code)
assert not is_valid_source_only, "This demonstrates the bug - validation fails without context"
assert "BLOCKED_TAR_ENTRY_TYPES" in error_source_only
assert error_source_only is not None and "BLOCKED_TAR_ENTRY_TYPES" in error_source_only
# FIX: Validating source_code + dependency_code together should pass
combined_code = dependency_code + "\n\n" + source_code
@ -343,7 +340,7 @@ type TarEntryInfo = { type: string; path: string };
# Validating source alone should fail
is_valid_alone, error_alone = validate_typescript_syntax(source_code)
assert not is_valid_alone, "Source alone should fail validation (missing constant)"
assert "BLOCKED_TAR_ENTRY_TYPES" in error_alone
assert error_alone is not None and "BLOCKED_TAR_ENTRY_TYPES" in error_alone
# Validating combined should pass
combined = dependency_code + "\n\n" + source_code