-
Notifications
You must be signed in to change notification settings - Fork 8
fix: reorder memberDeclaration alternatives to prioritize class over field #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…face/enum over fields This fixes an issue where virtual inner classes were incorrectly parsed as fields, causing FieldModifierValidator to report 'Field cannot be declared as virtual' errors. The grammar now tries class/interface/enum declarations before field declarations, giving priority to specific keywords (CLASS, INTERFACE, ENUM) over the general typeRef pattern used in field declarations. Fixes: Virtual inner classes parsing issue (GUS W-18895431) Tests: All existing tests pass (72 NPM + 71 JVM) Verified: Virtual inner classes now parse correctly without errors
Build would most likely have passed - but another one of the sample repos has been deleted off github. It happens, not to worry. I don't think there are fancy rules like that in antlr so this would be the best way. In some ts visitor code I had to hand, I was struggling to repro this though - I got |
it's from the js. |
So I still can't repro it with this sample, on both v4 branch and main - I get class declarations. Quick test I was trying in test("Class declaration", () => {
const [parser, errorCounter] = createParser(`
public class FFLIB_MetadataService {
public virtual class GlobalPicklistValue extends Metadata {
public String color;
public Boolean default_x;
public String description;
public Boolean isActive;
}
public virtual class Metadata {
public String fullName;
}
}`);
const context = parser.typeDeclaration();
expect(errorCounter.getNumErrors()).toEqual(0);
const pick = context.classDeclaration().classBody().classBodyDeclaration(0);
expect(pick.memberDeclaration().children.length).toEqual(1);
expect(pick.memberDeclaration().children[0]).toBeInstanceOf(
ClassDeclarationContext
);
const meta = context.classDeclaration().classBody().classBodyDeclaration(1);
expect(meta.memberDeclaration().children.length).toEqual(1);
expect(meta.memberDeclaration().children[0]).toBeInstanceOf(
ClassDeclarationContext
);
}); |
good point; from what I've seen, I'll get a correct parse from doing the whole file via I think our code is doing visitor-pattern stuff, so that when the inner classed is parsed using
|
Ah, thanks for confirming and providing the example. This is happening because the sample text is in fact This is why I couldn't repro it either, because |
I'm speculating the issue it that we're not choosing what to parse it with, but as the parser visits the things at the top level of the outer class, it'll hit a memberDeclaration, incorrectly decide it's a field because that matches, and then call our Listener's We can create some crazy workaround code to say, "oh, if the text includes |
I'll accept this because it won't do any harm that I can see and if it fixes your problem, great (the best kind of test). I will also fix the system test (bear with) and release it to v4 just in case. I understand the original scenario too, makes sense that a broad rule could lead to unintended behaviour. Clarifying my previous comment, I was trying to point out that this:
would not be parseable with These lines:
would each be parseable with |
This fixes an issue where virtual inner classes were incorrectly parsed as fields.
The grammar now tries class/interface/enum declarations before field declarations, giving priority to specific keywords (CLASS, INTERFACE, ENUM) over the general typeRef pattern used in field declarations.
repro code from one of our test cases y'all might recognize;
am open to other approaches (ex: trying to make the selector for fieldDeclaration tighter so that it looks ahead and excludes
class
keyword but I'm not good enough at antlr to know how to do that.