Closed jamiebuilds closed 2 years ago
Luckily, Rust allows us to make it so that those indexes are not a public API:
This is true but I don't think it adds much value because authors can always fall back to bogus.syntax().children()
and then continue to depend on offsets.
The question would then also be why we even need the function in the first place because it would then only be visible to the parser crate (or the future, an AST crate) but the parser has no interest in ever iterating over the AST. I assume what you wanted is to make this method "public to rome only". I don't know of a way to do that in Rust that has no "friendly assemblies" that are allowed to poke into internals.
FunctionBody::parent() could return any of:
The AST currently doesn't expose a parent()
method. The only way to get a node's parent is to use body.syntax().parent()
and it's then to you to cast it to the appropriate type (it returns a SyntaxNode<JsLangauge>
). I don't see a reason for not adding it though.
Result<AnyJsNode, AnyBogusJsNode>
AnyBogusJsNode
should be part of the AnyJsNode
. These are possible JsNode
s. It also breaks with the rest of our API where we carefully removed the need for Result
to express that a node can either be a valid
or a bogus
node.
I'm not sure which is the best choice, but the last two of those would require us to codegen .parent() methods everywhere.
or write them by hand... The main challenge I see is that every union needs its dedicated name. What would you call AnyJsFunction | AnyBogusJsNode
and how to defer the name inside of the codegen? What about syntaxes that have many possible different parents:
class_declaration.parent() -> ExportDeclarationClause | BlockStatement | FunctionBody
We would probably need to have a JsAnyClassDeclarationParent
but that will lead to many additional unions.
For @jamiebuilds
My first question is: what's the different between an Unknown
node and a Bogus
node? Unless they are the same, then I didn't understand the whole proposal.
While I think you should be able to access the BogusJsStatement, I don't think it should give you access to its children.
How would that work with linters? If we decorate the whole statement - like in this example - in a bogus, analyzers won't be able see anything. Unless we will have analyzers with different access rights.
For @MichaReiser
or write them by hand...
I believe .parent()
will end up to be a combination of manual code and generated code. A child that doesn't have a union as a parent, can be predicted without issues. For unions, probably we should provide a cast
method, similar to what we do when we want to get a certain child.
I believe .parent() will end up to be a combination of manual code and generated code. A child that doesn't have a union as a parent, can be predicted without issues. For unions, probably we should provide a cast method, similar to what we do when we want to get a certain child.
My main worry is about the number of types this will introduce. We also need to consider what parents a node can have if a AST is invalid. For example, a FunctionBody
can be a child of an UnknownStatement
if the function uses typescript syntax (or is invalid for other reasons).
Description
CST nodes have a lot of qualities that make it nice to represent both errors and error recovery.
Take the following code:
It should look something like this in CST form:
We can tell a few things from this:
JsFunctionStatement
even though it contains errors.foo
is the identifier.Just as easily we could have created this CST:
Or this one:
Or this one:
It that sense, the CST is a very clear representation of how our error recovery works, and it provides a lot of flexibility for us to improve.
But we throw all that out as soon as we're using the AST.
In our current design we have a limited set of "Bogus" (Unknown) nodes where we can say an error exists. But it doesn't provide us the flexibility to recover inside of the node. It also forces us to pretend an error is more wide-spread than it really should be.
But what if we made these bogus nodes more CST-like?
This provides us with all of the flexibility of the CST:
AST navigation
Let's take this AST:
There are a number of different operations an analyzer could run on this:
To summarize:
BogusJsStatement
.While I think you should be able to access the
BogusJsStatement
, I don't think it should give you access to its children.My biggest concern is that analyzers, particularly third-party ones, would eventually end up depending on indexes of the
BogusJsStatement
and it would make every change to our error recovery a breaking change.Luckily, Rust allows us to make it so that those indexes are not a public API:
However, we should consider what types we return for child nodes trying to reach back up the tree into the bogus node.
FunctionBody::parent()
could return any of:AnyJsNode
Result<AnyJsNode, AnyBogusJsNode>
AnyJsFunction | AnyBogusJsNode
Some(AnyJsFunction)
I'm not sure which is the best choice, but the last two of those would require us to codegen
.parent()
methods everywhere.