Skip to content

[libSyntax] Make parsing of attributes more structured #16117

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

Merged
merged 1 commit into from
Apr 25, 2018

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Apr 24, 2018

Add significantly more structure to the way the attributes are parsed in libSyntax by no longer parsing the arguments as a TokenList. This way we can also parse @_specialize attributes correctly which can have a GenericWhereClause as one of its arguments.

@ahoppen ahoppen requested review from rintaro and nkcsgexi April 24, 2018 00:08
@ahoppen
Copy link
Member Author

ahoppen commented Apr 24, 2018

@swift-ci Please smoke test

@rintaro rintaro self-assigned this Apr 24, 2018
@ahoppen
Copy link
Member Author

ahoppen commented Apr 24, 2018

@swift-ci Please smoke test

If the attribute takes arguments, the closing parenthesis.
'''),
# TokenList to gather remaining tokens of invalid attributes
Child('TokenList', kind='TokenList', is_optional=True),
Copy link
Member

Choose a reason for hiding this comment

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

Is it possible to make TokenList be a choice of Child('Argument') ?
I mean, invalid argument could be constructed as '@' identifier ('(' token-list ')')?.

Copy link
Member Author

Choose a reason for hiding this comment

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

I would actually like to get rid of TokenList completely. It is only used here and for closure capture items. Maybe I should add a FIXME instead.

node_choices=[
Child('Identifier', kind='IdentifierToken'),
Child('String', kind='StringLiteralToken'),
Child('Integer', kind='IntegerLiteralToken'),
Copy link
Member

Choose a reason for hiding this comment

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

Which attribute uses IntegerLiteralToken as the argument?

Copy link
Member Author

Choose a reason for hiding this comment

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

@_alignment

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! Could you add a test case?

@@ -189,6 +190,8 @@ void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind) {
case SyntaxKind::MemberTypeIdentifier:
case SyntaxKind::FunctionCallExpr:
case SyntaxKind::SubscriptExpr:
case SyntaxKind::AvailabilitySpecList:
case SyntaxKind::AvailabilityArgument:
Copy link
Member

Choose a reason for hiding this comment

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

I don't think these are used. Am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

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

AvailabilityArgument are the children of AvailabilitySpecList and AvailabilitySpecList is one of the options for Attribute.Arguments or AvailabilityCondition.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, sorry. I meant createNodeInPlace(SyntaxKind::AvailabilitySpecList) is not used, nor AvailabilityArgument.
This switch is not supposed to be exhaustive.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, OK. I added them while trying to fix it and will remove them again.

StringRef AttrName = "_specialize";
AvailableAttr *Parser::parseExtendedAvailabilitySpecList(
SourceLoc AtLoc, SourceLoc AttrStartLoc, StringRef Platform,
StringRef AttrName, SourceLoc AttrLoc, bool &DiscardAttribute) {
Copy link
Member

@rintaro rintaro Apr 24, 2018

Choose a reason for hiding this comment

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

  • This function returns the attribute itself, naming parseExtendedAvailabilityAttribute might be more relevant IMO.
  • Return ParserResult<AvailableAttr> without bool &DiscardAttribute like parseImplementsAttribute.
  • AttrLoc and AttrStartLoc is the same. Please consolidate them.
  • Platform can be retrieved with Tok.getText() inside the function.

Copy link
Member Author

Choose a reason for hiding this comment

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

The motivation for factoring out this function was that it could create a SyntaxParsingContext with kind AvailabilitySpecList for its entire scope so we don't have to create a code block that for that context that spans nearly 200 lines of code. That's why it also doesn't parse the trailing ).
I will implement 2, 3, and 4. Thanks.

Copy link
Member

@rintaro rintaro Apr 24, 2018

Choose a reason for hiding this comment

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

Have you tried collectNodesInPlace(SyntaxKind::AvailabilitySpecList) without creating SyntaxParsingContext in parseExtendedAvailabilitySpecList?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, AtLoc andAttrLoc are different, AtLoc refers to where the @ is, AttrLoc where the name of the attribute starts.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that doesn't work because the opening ( is still on the stack of items to be parsed and doesn't fit into the AvailabilitySpecList

Copy link
Member

Choose a reason for hiding this comment

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

AtLoc != AttrLoc but AttrStartLoc == AttrLoc, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. Sorry, misread your comment. You're right of course

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that doesn't work because the opening ( is still on the stack of items

It should work. For example, in parseDeclEnumCase(), collectNodesInPlace(SyntaxKind::EnumCaseElementList) turns
{ 'case', element, element, element ...} into { 'case', element-list }.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, it does seem to work. I'm pretty sure I tried before and it didn't but maybe I'm mixing things up. It's not needed though since I keep the parsing logic for ) outside of the refactored function.


AttrRange = SourceRange(Loc, Tok.getLoc());
auto AvailabilityAttr = parseExtendedAvailabilitySpecList(
AtLoc, Loc, Platform, AttrName, Loc, DiscardAttribute);

if (!consumeIf(tok::r_paren)) {
Copy link
Member

@rintaro rintaro Apr 24, 2018

Choose a reason for hiding this comment

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

IMO, ) should be parsed in the refactored function.
I guess you leave it here because ( is parsed in this function, but still... What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

Combining comments above, the call-site can be something like:

ParserResult<AvailableAttr> Attr =
    parseExtendedAvailabilityAttribute(AtLoc, Loc, AttrName);
if (Attr.isNonNull()) {
  Attributes.add(Attr.get());
}
break;

Copy link
Member Author

Choose a reason for hiding this comment

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

See above

Copy link
Member Author

Choose a reason for hiding this comment

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

Thinking about it, I would leave it as it is. The parsing of @available attributes is slightly weird because there are the two variants @available(*, …) and @available(iOS 10, *) that are handled inside the switch. I don't want to parse ) inside the refactored function and ( outside and everything else would require ripping apart the entire parsing logic and I don't think it's worth it.

@ahoppen ahoppen force-pushed the attributes branch 2 times, most recently from a5ac617 to 7a49737 Compare April 24, 2018 16:14
@ahoppen
Copy link
Member Author

ahoppen commented Apr 24, 2018

@swift-ci Please smoke test

This also fixes several issues where attribute arguments could not be
parsed as a TokenList since some of its arguments already had structure
and were not tokens
@ahoppen
Copy link
Member Author

ahoppen commented Apr 24, 2018

@swift-ci Please smoke test

@ahoppen
Copy link
Member Author

ahoppen commented Apr 24, 2018

@rintaro Unless you have any further comments, I'll be merging this tonight.

Copy link
Member

@rintaro rintaro left a comment

Choose a reason for hiding this comment

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

Looks good!

@rintaro rintaro removed their assignment Apr 25, 2018
@ahoppen ahoppen merged commit a5314af into swiftlang:master Apr 25, 2018
@ahoppen ahoppen deleted the attributes branch April 26, 2018 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants