Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -422,11 +422,12 @@ return false; // Colons from ?: are handled in parseConditional(). if (Style.Language == FormatStyle::LK_JavaScript) { - if (Contexts.back().ColonIsForRangeExpr || - (Contexts.size() == 1 && + if (Contexts.back().ColonIsForRangeExpr || // colon in for loop + (Contexts.size() == 1 && // switch/case labels !Line.First->isOneOf(tok::kw_enum, tok::kw_case)) || - Contexts.back().ContextKind == tok::l_paren || - Contexts.back().ContextKind == tok::l_square) { + Contexts.back().ContextKind == tok::l_paren || // function params + Contexts.back().ContextKind == tok::l_square || // array type + Line.MustBeDeclaration) { // method/property declaration Tok->Type = TT_JsTypeColon; break; } @@ -541,6 +542,11 @@ Tok->Type = TT_JsTypeOptionalQuestion; break; } + // Declarations cannot be conditional expressions, this can only be part + // of a type declaration. + if (Line.MustBeDeclaration && + Style.Language == FormatStyle::LK_JavaScript) + break; parseConditional(); break; case tok::kw_template: @@ -872,7 +878,14 @@ } else if (Current.isOneOf(tok::exclaim, tok::tilde)) { Current.Type = TT_UnaryOperator; } else if (Current.is(tok::question)) { - Current.Type = TT_ConditionalExpr; + if (Style.Language == FormatStyle::LK_JavaScript && + Line.MustBeDeclaration) { + // In JavaScript, `interface X { foo?(): bar; }` is an optional method + // on the interface, not a ternary expression. + Current.Type = TT_JsTypeOptionalQuestion; + } else { + Current.Type = TT_ConditionalExpr; + } } else if (Current.isBinaryOperator() && (!Current.Previous || Current.Previous->isNot(tok::l_square))) { Current.Type = TT_BinaryOperator; @@ -1854,12 +1867,21 @@ return Right.is(tok::coloncolon); if (Right.is(TT_OverloadedOperatorLParen)) return false; - if (Right.is(tok::colon)) - return !Line.First->isOneOf(tok::kw_case, tok::kw_default) && - Right.getNextNonComment() && Right.isNot(TT_ObjCMethodExpr) && - !Left.is(tok::question) && - !(Right.is(TT_InlineASMColon) && Left.is(tok::coloncolon)) && - (Right.isNot(TT_DictLiteral) || Style.SpacesInContainerLiterals); + if (Right.is(tok::colon)) { + if (Line.First->isOneOf(tok::kw_case, tok::kw_default)) + return false; + if (!Right.getNextNonComment()) + return false; + if (Right.is(TT_ObjCMethodExpr)) + return false; + if (Left.is(tok::question)) + return false; + if (Right.is(TT_InlineASMColon) && Left.is(tok::coloncolon)) + return false; + if (Right.is(TT_DictLiteral)) + return Style.SpacesInContainerLiterals; + return true; + } if (Left.is(TT_UnaryOperator)) return Right.is(TT_BinaryOperator); if (Left.is(TT_CastRParen)) Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -262,9 +262,12 @@ } void UnwrappedLineParser::parseFile() { - ScopedDeclarationState DeclarationState( - *Line, DeclarationScopeStack, - /*MustBeDeclaration=*/!Line->InPPDirective); + // The top-level context in a file always has declarations, except for pre- + // processor directives and JavaScript files. + bool MustBeDeclaration = + !(Line->InPPDirective || Style.Language == FormatStyle::LK_JavaScript); + ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack, + MustBeDeclaration); parseLevel(/*HasOpeningBrace=*/false); // Make sure to format the remaining tokens. flushComments(true); @@ -845,6 +848,13 @@ tryToParseJSFunction(); break; } + if ((Style.Language == FormatStyle::LK_JavaScript || + Style.Language == FormatStyle::LK_Java) && + FormatTok->is(Keywords.kw_interface)) { + parseRecord(); + break; + } + nextToken(); if (Line->Tokens.size() == 1 && // JS doesn't have macros, and within classes colons indicate fields, @@ -1577,7 +1587,7 @@ // We fall through to parsing a structural element afterwards, so // class A {} n, m; // will end up in one unwrapped line. - // This does not apply for Java. + // This does not apply for Java and JavaScript. if (Style.Language == FormatStyle::LK_Java || Style.Language == FormatStyle::LK_JavaScript) addUnwrappedLine(); Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -703,6 +703,8 @@ " y?: z;\n" " z?;\n" "}"); + verifyFormat("interface X { y?(): z; }"); + verifyFormat("x ? 1 : 2;"); } TEST_F(FormatTestJS, IndexSignature) {