r236488 - clang-format: [JS] support optional methods.
Daniel Jasper
djasper at google.com
Tue May 5 01:40:33 PDT 2015
Author: djasper
Date: Tue May 5 03:40:32 2015
New Revision: 236488
URL: http://llvm.org/viewvc/llvm-project?rev=236488&view=rev
Log:
clang-format: [JS] support optional methods.
Optional methods use ? tokens like this:
interface X { y?(): z; }
It seems easiest to detect and disambiguate these from ternary
expressions by checking if the code is in a declaration context. Turns
out that that didn't quite work properly for interfaces in Java and JS,
and for JS file root contexts.
Patch by Martin Probst, thank you.
Modified:
cfe/trunk/lib/Format/TokenAnnotator.cpp
cfe/trunk/lib/Format/UnwrappedLineParser.cpp
cfe/trunk/unittests/Format/FormatTestJS.cpp
Modified: cfe/trunk/lib/Format/TokenAnnotator.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/TokenAnnotator.cpp?rev=236488&r1=236487&r2=236488&view=diff
==============================================================================
--- cfe/trunk/lib/Format/TokenAnnotator.cpp (original)
+++ cfe/trunk/lib/Format/TokenAnnotator.cpp Tue May 5 03:40:32 2015
@@ -422,11 +422,12 @@ private:
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;
}
@@ -533,14 +534,20 @@ private:
break;
case tok::question:
if (Style.Language == FormatStyle::LK_JavaScript && Tok->Next &&
- Tok->Next->isOneOf(tok::colon, tok::semi, tok::r_paren,
+ Tok->Next->isOneOf(tok::semi, tok::colon, tok::r_paren,
tok::r_brace)) {
- // Question marks before semicolons, colons, commas, etc. indicate
- // optional types (fields, parameters), e.g.
- // `function(x?: string, y?) {...}` or `class X {y?;}`
+ // Question marks before semicolons, colons, etc. indicate optional
+ // types (fields, parameters), e.g.
+ // function(x?: string, y?) {...}
+ // class X { y?; }
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 +879,14 @@ private:
} 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 +1868,20 @@ bool TokenAnnotator::spaceRequiredBefore
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) ||
+ !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))
Modified: cfe/trunk/lib/Format/UnwrappedLineParser.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineParser.cpp?rev=236488&r1=236487&r2=236488&view=diff
==============================================================================
--- cfe/trunk/lib/Format/UnwrappedLineParser.cpp (original)
+++ cfe/trunk/lib/Format/UnwrappedLineParser.cpp Tue May 5 03:40:32 2015
@@ -262,9 +262,12 @@ bool UnwrappedLineParser::parse() {
}
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);
@@ -837,14 +840,21 @@ void UnwrappedLineParser::parseStructura
parseTryCatch();
return;
case tok::identifier: {
- StringRef Text = FormatTok->TokenText;
// 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 && Text == "function" &&
- Line->Tokens.size() > 0) {
+ if (Style.Language == FormatStyle::LK_JavaScript &&
+ FormatTok->is(Keywords.kw_function) && Line->Tokens.size() > 0) {
tryToParseJSFunction();
break;
}
+ if ((Style.Language == FormatStyle::LK_JavaScript ||
+ Style.Language == FormatStyle::LK_Java) &&
+ FormatTok->is(Keywords.kw_interface)) {
+ parseRecord();
+ break;
+ }
+
+ StringRef Text = FormatTok->TokenText;
nextToken();
if (Line->Tokens.size() == 1 &&
// JS doesn't have macros, and within classes colons indicate fields,
@@ -1577,7 +1587,7 @@ void UnwrappedLineParser::parseRecord()
// 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();
Modified: cfe/trunk/unittests/Format/FormatTestJS.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/FormatTestJS.cpp?rev=236488&r1=236487&r2=236488&view=diff
==============================================================================
--- cfe/trunk/unittests/Format/FormatTestJS.cpp (original)
+++ cfe/trunk/unittests/Format/FormatTestJS.cpp Tue May 5 03:40:32 2015
@@ -709,6 +709,10 @@ TEST_F(FormatTestJS, OptionalTypes) {
" y?: z;\n"
" z?;\n"
"}");
+ verifyFormat("interface X {\n"
+ " y?(): z;\n"
+ "}");
+ verifyFormat("x ? 1 : 2;");
}
TEST_F(FormatTestJS, IndexSignature) {
More information about the cfe-commits
mailing list