Variables named ref cannot be used with method calls
Description
A local variable named ref (case-insensitive) fails to resolve method calls. The compiler's resolver incorrectly binds ref to the REF builtin function instead of the local variable when processing call operators, causing "Could not resolve reference" errors on any subsequent member access.
Minimal Reproduction
FUNCTION_BLOCK MyFb
METHOD foo
printf('MyFb::foo$N');
END_METHOD
END_FUNCTION_BLOCK
FUNCTION main
VAR
ref: MyFb;
END_VAR
ref.foo();
END_FUNCTION
Expected Output
Actual Output
error[E048]: Could not resolve reference to foo
┌─ test.st:12:9
│
12 │ ref.foo();
│ ^^^ Could not resolve reference to foo
Root Cause
REF is registered as a builtin function in src/builtins.rs. When the resolver processes a call statement like ref.foo(), it resolves the operator using call_operator_scopes, which is ordered as [FunctionsOnly, Variable, POU, DataType]. Since FunctionsOnly runs first and matches the REF builtin (case-insensitive), the resolver annotates ref as Function { qualified_name: "REF" } instead of Variable { resulting_type: "MyFb" }. The subsequent .foo member lookup then fails because the REF function has no foo member.
Non-call contexts (e.g. assignments like ref := instance) are unaffected because they use the default resolving strategy [Variable, POU, DataType], where Variable runs first and correctly finds the local.
Notes
- Renaming the variable to anything other than
ref (e.g. reference, myRef) works around the issue.
- The same problem would apply to any variable whose name collides with a builtin function name under case-insensitive matching.
Variables named
refcannot be used with method callsDescription
A local variable named
ref(case-insensitive) fails to resolve method calls. The compiler's resolver incorrectly bindsrefto theREFbuiltin function instead of the local variable when processing call operators, causing "Could not resolve reference" errors on any subsequent member access.Minimal Reproduction
Expected Output
Actual Output
Root Cause
REFis registered as a builtin function insrc/builtins.rs. When the resolver processes a call statement likeref.foo(), it resolves the operator usingcall_operator_scopes, which is ordered as[FunctionsOnly, Variable, POU, DataType]. SinceFunctionsOnlyruns first and matches theREFbuiltin (case-insensitive), the resolver annotatesrefasFunction { qualified_name: "REF" }instead ofVariable { resulting_type: "MyFb" }. The subsequent.foomember lookup then fails because theREFfunction has nofoomember.Non-call contexts (e.g. assignments like
ref := instance) are unaffected because they use the default resolving strategy[Variable, POU, DataType], whereVariableruns first and correctly finds the local.Notes
ref(e.g.reference,myRef) works around the issue.