static const tok::TokenKind JSShiftEqual[] = {tok::greater, tok::greater,
tok::greaterequal};
static const tok::TokenKind JSRightArrow[] = {tok::equal, tok::greater};
- // FIXME: We probably need to change token type to mimic operator with the
- // correct priority.
- if (tryMergeTokens(JSIdentity))
+ // FIXME: Investigate what token type gives the correct operator priority.
+ if (tryMergeTokens(JSIdentity, TT_BinaryOperator))
return;
- if (tryMergeTokens(JSNotIdentity))
+ if (tryMergeTokens(JSNotIdentity, TT_BinaryOperator))
return;
- if (tryMergeTokens(JSShiftEqual))
+ if (tryMergeTokens(JSShiftEqual, TT_BinaryOperator))
return;
- if (tryMergeTokens(JSRightArrow))
+ if (tryMergeTokens(JSRightArrow, TT_JsFatArrow))
return;
}
}
return true;
}
- bool tryMergeTokens(ArrayRef<tok::TokenKind> Kinds) {
+ bool tryMergeTokens(ArrayRef<tok::TokenKind> Kinds, TokenType NewType) {
if (Tokens.size() < Kinds.size())
return false;
First[0]->TokenText = StringRef(First[0]->TokenText.data(),
First[0]->TokenText.size() + AddLength);
First[0]->ColumnWidth += AddLength;
+ First[0]->Type = NewType;
return true;
}
TT_InlineASMBrace,
TT_InlineASMColon,
TT_JavaAnnotation,
+ TT_JsFatArrow,
TT_JsTypeColon,
TT_JsTypeOptionalQuestion,
TT_LambdaArrow,
break;
}
case tok::equal:
+ // Fat arrows (=>) have tok::TokenKind tok::equal but TokenType
+ // TT_JsFatArrow. The always start an expression or a child block if
+ // followed by a curly.
+ if (FormatTok->is(TT_JsFatArrow)) {
+ nextToken();
+ if (FormatTok->is(tok::l_brace)) {
+ parseChildBlock();
+ }
+ break;
+ }
+
nextToken();
if (FormatTok->Tok.is(tok::l_brace)) {
parseBracedList();
if (FormatTok->isNot(tok::l_paren))
return;
- nextToken();
- while (FormatTok->isNot(tok::l_brace)) {
- // Err on the side of caution in order to avoid consuming the full file in
- // case of incomplete code.
- if (!FormatTok->isOneOf(tok::identifier, tok::comma, tok::r_paren,
- tok::comment))
- return;
+
+ // Parse formal parameter list.
+ parseBalanced(tok::l_paren, tok::r_paren);
+
+ if (FormatTok->is(tok::colon)) {
+ // Parse a type definition.
nextToken();
+
+ // Eat the type declaration. For braced inline object types, balance braces,
+ // otherwise just parse until finding an l_brace for the function body.
+ if (FormatTok->is(tok::l_brace)) {
+ parseBalanced(tok::l_brace, tok::r_brace);
+ } else {
+ while(FormatTok->isNot(tok::l_brace) && !eof()) {
+ nextToken();
+ }
+ }
}
+
parseChildBlock();
}
+void UnwrappedLineParser::parseBalanced(tok::TokenKind OpenKind,
+ tok::TokenKind CloseKind) {
+ assert(FormatTok->is(OpenKind));
+ nextToken();
+ int Depth = 1;
+ while (Depth > 0 && !eof()) {
+ // Parse the formal parameter list.
+ if (FormatTok->is(OpenKind)) {
+ ++Depth;
+ } else if (FormatTok->is(CloseKind)) {
+ --Depth;
+ }
+ nextToken();
+ }
+}
+
bool UnwrappedLineParser::tryToParseBracedList() {
if (FormatTok->BlockKind == BK_Unknown)
calculateBraceTypes();
// FIXME: Once we have an expression parser in the UnwrappedLineParser,
// replace this by using parseAssigmentExpression() inside.
do {
- if (Style.Language == FormatStyle::LK_JavaScript &&
- FormatTok->is(Keywords.kw_function)) {
- tryToParseJSFunction();
- continue;
+ if (Style.Language == FormatStyle::LK_JavaScript) {
+ if (FormatTok->is(Keywords.kw_function)) {
+ tryToParseJSFunction();
+ continue;
+ } else if (FormatTok->is(TT_JsFatArrow)) {
+ nextToken();
+ // Fat arrows can be followed by simple expressions or by child blocks
+ // in curly braces.
+ if (FormatTok->is(tok::l_brace)){
+ parseChildBlock();
+ continue;
+ }
+ }
}
switch (FormatTok->Tok.getKind()) {
case tok::caret:
bool tryToParseLambda();
bool tryToParseLambdaIntroducer();
void tryToParseJSFunction();
+ /// \brief Parses tokens until encountering the CloseKind token, but balances
+ /// tokens when encountering more OpenKind tokens. Useful for e.g. parsing a
+ /// curly brace delimited block that can contain nested blocks.
+ /// The parser must be positioned on a token of OpenKind.
+ void parseBalanced(tok::TokenKind OpenKind, tok::TokenKind CloseKind);
void addUnwrappedLine();
bool eof() const;
void nextToken();
}
TEST_F(FormatTestJS, ContainerLiterals) {
+ verifyFormat("var x = {y: function(a) { return a; }};");
verifyFormat("return {\n"
" link: function() {\n"
" f(); //\n"
verifyFormat("X = {\n a: 123\n};");
verifyFormat("X.Y = {\n a: 123\n};");
verifyFormat("x = foo && {a: 123};");
+
+ // Arrow functions in object literals.
+ verifyFormat("var x = {y: (a) => { return a; }};");
+ verifyFormat("var x = {y: (a) => a};");
}
TEST_F(FormatTestJS, MethodsInObjectLiterals) {
" .thenCatch(function(error) { body(); });");
}
+TEST_F(FormatTestJS, ArrowFunctions) {
+ verifyFormat("var x = (a) => {\n"
+ " return a;\n"
+ "};");
+ verifyFormat("var x = (a) => {\n"
+ " function y() { return 42; }\n"
+ " return a;\n"
+ "};");
+ verifyFormat("var x = (a: type): {some: type} => {\n"
+ " return a;\n"
+ "};");
+ verifyFormat("var x = (a) => a;");
+ verifyFormat("var x = (a) => a;");
+}
+
TEST_F(FormatTestJS, ReturnStatements) {
verifyFormat("function() {\n"
" return [hello, world];\n"
"}");
}
-TEST_F(FormatTestJS, ClosureStyleComments) {
+TEST_F(FormatTestJS, ClosureStyleCasts) {
verifyFormat("var x = /** @type {foo} */ (bar);");
}
TEST_F(FormatTestJS, TypeAnnotations) {
verifyFormat("var x: string;");
verifyFormat("function x(): string {\n return 'x';\n}");
+ verifyFormat("function x(): {x: string} {\n return {x: 'x'};\n}");
verifyFormat("function x(y: string): string {\n return 'x';\n}");
verifyFormat("for (var y: string in x) {\n x();\n}");
verifyFormat("((a: string, b: number): string => a + b);");
verifyFormat("var x: (y: number) => string;");
verifyFormat("var x: P<string, (a: number) => string>;");
+ verifyFormat("var x = {y: function(): z { return 1; }};");
+ verifyFormat("var x = {y: function(): {a: number} { return 1; }};");
}
TEST_F(FormatTestJS, ClassDeclarations) {