Description
Using user-defined classes as type annotations can raise a NameError due to incorrect constant resolution during evaluation.
Reproduction
class PaymentMethod; end
class Order
include LowType
def process(method: PaymentMethod)
"Processing via #{method.class}"
end
end
Order.new.process(method: PaymentMethod.new)
Actual Behavior
Raises:
NameError: Unknown type 'PaymentMethod'
Expected Behavior
"Processing via PaymentMethod"
Root Cause
In Evaluator#instance_evaluate, expressions are evaluated using:
eval(proxy.value, binding, ...)
The binding belongs to Low::Evaluator, not the class that includes LowType. As a result, user-defined constants are not visible during evaluation, leading to NameError.
There is also an existing TODO in the code highlighting this:
Evaluate in the binding of the class that included LowType if not a type managed by LowType.
Proposed Direction
Possible approaches to resolve this:
- Pass the including class context into the evaluator
- Resolve constants using
const_get within the correct namespace
- Use
class_eval on the including class to evaluate expressions in the right scope
Impact
- Breaks support for user-defined types
- Fails for namespaced constants (e.g.,
MyModule::MyClass)
- Likely affects return type evaluation as well (same evaluation path)
Description
Using user-defined classes as type annotations can raise a
NameErrordue to incorrect constant resolution during evaluation.Reproduction
Actual Behavior
Raises:
Expected Behavior
Root Cause
In
Evaluator#instance_evaluate, expressions are evaluated using:The
bindingbelongs toLow::Evaluator, not the class that includesLowType. As a result, user-defined constants are not visible during evaluation, leading toNameError.There is also an existing TODO in the code highlighting this:
Proposed Direction
Possible approaches to resolve this:
const_getwithin the correct namespaceclass_evalon the including class to evaluate expressions in the right scopeImpact
MyModule::MyClass)