Skip to content

Conversation

@kazcw
Copy link
Contributor

@kazcw kazcw commented Oct 30, 2025

Pull Request Description

Add a PropertyAccess node to the Rust AST, representing use of the dot . operator. This operator was previously treated as a special case both in the GUI and in the backend.

The PropertyAccess node only represents syntactically-valid applications of the operator. Its RHS is non-optional, and is an identifier token, not an expression.

This eliminates the largest difference between AST and Tree, and so is a major step toward #10754.

Important Notes

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • Screenshots/screencasts have been attached, if there are any visual changes. For interactive or animated visual changes, a screencast is preferred.
  • All code follows the
    Scala,
    Java,
    TypeScript,
    and
    Rust
    style guides. In case you are using a language not listed above, follow the Rust style guide.
  • Unit tests have been written where possible.
  • If meaningful changes were made to logic or tests affecting Enso Cloud integration in the libraries,
    or the Snowflake database integration, a run of the Extra Tests has been scheduled.
    • If applicable, it is suggested to paste a link to a successful run of the Extra Tests.

@kazcw kazcw added the CI: No changelog needed Do not require a changelog entry for this PR. label Oct 30, 2025
Comment on lines -42 to -78
@Test
public void dotUnderscore() throws Exception {
var ir =
parse(
"""
run op =
op._
""");

assertSingleSyntaxError(ir, Syntax.InvalidUnderscore$.MODULE$, "Invalid use of _", 14, 15);
}

@Test
public void spaceDotUnderscore() throws Exception {
var ir =
parse(
"""
run op =
op ._
""");

assertSingleSyntaxError(
ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 14, 16);
}

@Test
public void dotUnderscore2() throws Exception {
var ir =
parse(
"""
run op =
op._.something
""");

assertSingleSyntaxError(ir, Syntax.InvalidUnderscore$.MODULE$, "Invalid use of _", 14, 15);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed some tests that only check for detection of syntax errors, as these are now covered by the parser's tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Shouldn't you just update the error message?
  • There should still be assertSingleSyntaxError in such test, right?
  • Why not keep those tests?

Comment on lines 1872 to 1883
if (t instanceof Tree.Wildcard wild) {
return join(new Name.Blank(getIdentifiedLocation(wild.getToken(), generateId), meta()), nil());
}
List<Name> names = nil();
while (t instanceof Tree.PropertyAccess app) {
names = join(sanitizeName(buildName(app.getRhs(), generateId)), names);
t = app.getLhs();
}
if (t instanceof Tree.Ident id) {
names = join(sanitizeName(buildName(id, generateId)), names);
} else {
throw translateEntity(t, "qualifiedNameSegment");
Copy link
Contributor Author

@kazcw kazcw Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit stricter than the original logic: A blank (_) on its own is (still) allowed, but a blank as the first-segment of a multi-segment name is no longer allowed. I don't think the later case makes sense and I expect that attempting to use it would have previously yielded semantic errors; it is now caught here.

test.each([
{ target: 'a.b', pattern: '__', extracted: ['a.b'] },
{ target: 'a.b', pattern: 'a.__', extracted: ['b'] },
{ target: 'a.b', pattern: 'a.__', extracted: [] },
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer expected to match, as the Pattern API only matches expressions, and the RHS of the operator is now just a token. I have checked all usages of the Pattern API to make sure we were not relying on the previous behaviour in this type of case.

&& isDotOperator(oprApp.getOpr().getRight())
&& oprApp.getRhs() instanceof Tree.Ident) {
func = translateExpression(oprApp.getRhs(), true);
if (tree instanceof Tree.PropertyAccess oprApp) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like simplification.

/**
* Substitute `pattern` inside `expression` with `to`.
* Will only replace the first item in the property acccess chain.
* Substitute `pattern` in any `Ident` nodes inside `expression` with `to`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a warning/remainder that right side of PropertyAccess is not Ident node.

get operator(): Token {
return this.module.getToken(this.fields.get('operator').node)
}
/** TODO: Add docs */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add one or two docs on this occasion.

Comment on lines +424 to 432
} else {
match self.pop_macro_stack_if_reserved(repr) {
Some(popped) => {
self.resolve(popped);
return Step::MacroStackPop(token.into());
}
_ => {}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel it is better now (if let expresses the thing more concisely), but perhaps it's a matter of taste new edition?

test.each([
{ target: 'a.b', pattern: '__', extracted: ['a.b'] },
{ target: 'a.b', pattern: 'a.__', extracted: ['b'] },
{ target: 'a.b', pattern: 'a.__' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the AST patterns are able to catch/substitute AST nodes only, not tokens inside a node? I would either add comment here or just remove this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI: No changelog needed Do not require a changelog entry for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants