Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -297,6 +297,20 @@ } template bool isNot(T Kind) const { return !is(Kind); } + /// \c true if this token starts a sequence with the given tokens in order, + /// following the ``Next`` pointers, ignoring comments. + template + bool startsSequence(A K1, Ts... Tokens) const { + return startsSequenceInternal(K1, Tokens...); + } + + /// \c true if this token ends a sequence with the given tokens in order, + /// following the ``Previous`` pointers, ignoring comments. + template + bool endsSequence(A K1, Ts... Tokens) const { + return endsSequenceInternal(K1, Tokens...); + } + bool isStringLiteral() const { return tok::isStringLiteral(Tok.getKind()); } bool isObjCAtKeyword(tok::ObjCKeywordKind Kind) const { @@ -429,6 +443,34 @@ // Disallow copying. FormatToken(const FormatToken &) = delete; void operator=(const FormatToken &) = delete; + + template + bool startsSequenceInternal(A K1, Ts... Tokens) const { + if (is(tok::comment) && Next) + return Next->startsSequenceInternal(K1, Tokens...); + return is(K1) && Next && Next->startsSequenceInternal(Tokens...); + } + + template + bool startsSequenceInternal(A K1) const { + if (is(tok::comment) && Next) + return Next->startsSequenceInternal(K1); + return is(K1); + } + + template + bool endsSequenceInternal(A K1) const { + if (is(tok::comment) && Previous) + return Previous->endsSequenceInternal(K1); + return is(K1); + } + + template + bool endsSequenceInternal(A K1, Ts... Tokens) const { + if (is(tok::comment) && Previous) + return Previous->endsSequenceInternal(K1, Tokens...); + return is(K1) && Previous && Previous->endsSequenceInternal(Tokens...); + } }; class ContinuationIndenter; Index: lib/Format/TokenAnnotator.h =================================================================== --- lib/Format/TokenAnnotator.h +++ lib/Format/TokenAnnotator.h @@ -83,7 +83,7 @@ /// \c true if this line starts with the given tokens in order, ignoring /// comments. template bool startsWith(Ts... Tokens) const { - return startsWithInternal(First, Tokens...); + return First && First->startsSequence(Tokens...); } /// \c true if this line ends with the given tokens in reversed order, @@ -91,7 +91,7 @@ /// For example, given tokens [T1, T2, T3, ...], the function returns true if /// this line is like "... T3 T2 T1". template bool endsWith(Ts... Tokens) const { - return endsWithInternal(Last, Tokens...); + return Last && Last->endsSequence(Tokens...); } /// \c true if this line looks like a function definition instead of a @@ -130,44 +130,6 @@ // Disallow copying. AnnotatedLine(const AnnotatedLine &) = delete; void operator=(const AnnotatedLine &) = delete; - - template - bool startsWithInternal(const FormatToken *Tok, A K1) const { - // Even though we skip comments in the outer `startWithInternal` function, - // this loop is still necessary if it is invoked by the public interface - // `startsWith`. - while (Tok && Tok->is(tok::comment)) - Tok = Tok->Next; - return Tok && Tok->is(K1); - } - - template - bool startsWithInternal(const FormatToken *Tok, A K1, Ts... Tokens) const { - // Skip comments before calling `startsWithInternal(Tok, K1)` so that the - // second call to `startsWithInternal` takes the correct `Tok->Next`, which - // should be the next token of the token checked in the first call. - while (Tok && Tok->is(tok::comment)) - Tok = Tok->Next; - return Tok && startsWithInternal(Tok, K1) && - startsWithInternal(Tok->Next, Tokens...); - } - - template - bool endsWithInternal(const FormatToken *Tok, A K1) const { - // See the comments above in `startsWithInternal(Tok, K1)`. - while (Tok && Tok->is(tok::comment)) - Tok = Tok->Previous; - return Tok && Tok->is(K1); - } - - template - bool endsWithInternal(const FormatToken *Tok, A K1, Ts... Tokens) const { - // See the comments above in `startsWithInternal(Tok, K1, Tokens)`. - while (Tok && Tok->is(tok::comment)) - Tok = Tok->Previous; - return Tok && endsWithInternal(Tok, K1) && - endsWithInternal(Tok->Previous, Tokens...); - } }; /// \brief Determines extra information about the tokens comprising an Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -1013,7 +1013,9 @@ // Parse function literal unless 'function' is the first token in a line // in which case this should be treated as a free-standing function. if (Style.Language == FormatStyle::LK_JavaScript && - FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function) && + (FormatTok->is(Keywords.kw_function) || + FormatTok->startsSequence(Keywords.kw_async, + Keywords.kw_function)) && Line->Tokens.size() > 0) { tryToParseJSFunction(); break; @@ -1200,7 +1202,8 @@ } void UnwrappedLineParser::tryToParseJSFunction() { - assert(FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function)); + assert(FormatTok->is(Keywords.kw_function) || + FormatTok->startsSequence(Keywords.kw_async, Keywords.kw_function)); if (FormatTok->is(Keywords.kw_async)) nextToken(); // Consume "function". @@ -1254,7 +1257,8 @@ // replace this by using parseAssigmentExpression() inside. do { if (Style.Language == FormatStyle::LK_JavaScript) { - if (FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function)) { + if (FormatTok->is(Keywords.kw_function) || + FormatTok->startsSequence(Keywords.kw_async, Keywords.kw_function)) { tryToParseJSFunction(); continue; } @@ -1352,7 +1356,8 @@ break; case tok::identifier: if (Style.Language == FormatStyle::LK_JavaScript && - FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function)) + (FormatTok->is(Keywords.kw_function) || + FormatTok->startsSequence(Keywords.kw_async, Keywords.kw_function))) tryToParseJSFunction(); else nextToken(); Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ unittests/Format/FormatTestJS.cpp @@ -354,6 +354,10 @@ verifyFormat("class X {\n" " async asyncMethod() { return fetch(1); }\n" "}"); + verifyFormat("function initialize() {\n" + " // Comment.\n" + " return async.then();\n" + "}\n"); } TEST_F(FormatTestJS, ArrayLiterals) {