diff options
| -rw-r--r-- | clang/lib/Format/ContinuationIndenter.cpp | 63 | ||||
| -rw-r--r-- | clang/lib/Format/ContinuationIndenter.h | 17 | ||||
| -rw-r--r-- | clang/lib/Format/FormatToken.h | 3 | ||||
| -rw-r--r-- | clang/lib/Format/TokenAnnotator.cpp | 31 | ||||
| -rw-r--r-- | clang/lib/Format/UnwrappedLineParser.cpp | 48 | ||||
| -rw-r--r-- | clang/lib/Format/UnwrappedLineParser.h | 3 | ||||
| -rw-r--r-- | clang/unittests/Format/FormatTest.cpp | 43 |
7 files changed, 177 insertions, 31 deletions
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index c3eeb8fd678..7b3f5cb67fe 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -177,10 +177,8 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { State.Stack.back().FirstLessLess == 0) return true; - // FIXME: Comparing LongestObjCSelectorName to 0 is a hacky way of finding - // out whether it is the first parameter. Clean this up. if (Current.Type == TT_ObjCSelectorName && - Current.LongestObjCSelectorName == 0 && + State.Stack.back().ObjCSelectorNameFound && State.Stack.back().BreakBeforeParameter) return true; if (Current.Type == TT_CtorInitializerColon && @@ -258,9 +256,12 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, /*IndentLevel=*/0, Spaces, State.Column + Spaces); - if (Current.Type == TT_ObjCSelectorName && State.Stack.back().ColonPos == 0) { - if (State.Stack.back().Indent + Current.LongestObjCSelectorName > - State.Column + Spaces + Current.ColumnWidth) + if (Current.Type == TT_ObjCSelectorName && + !State.Stack.back().ObjCSelectorNameFound) { + if (Current.LongestObjCSelectorName == 0) + State.Stack.back().AlignColons = false; + else if (State.Stack.back().Indent + Current.LongestObjCSelectorName > + State.Column + Spaces + Current.ColumnWidth) State.Stack.back().ColonPos = State.Stack.back().Indent + Current.LongestObjCSelectorName; else @@ -280,7 +281,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, // Treat the condition inside an if as if it was a second function // parameter, i.e. let nested calls have a continuation indent. State.Stack.back().LastSpace = State.Column + 1; // 1 is length of "(". - else if (Previous.is(tok::comma) || Previous.Type == TT_ObjCMethodExpr) + else if (Current.isNot(tok::comment) && + (Previous.is(tok::comma) || + (Previous.is(tok::colon) && Previous.Type == TT_ObjCMethodExpr))) State.Stack.back().LastSpace = State.Column; else if ((Previous.Type == TT_BinaryOperator || Previous.Type == TT_ConditionalExpr || @@ -381,10 +384,17 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, State.Column = std::max(State.Stack.back().LastSpace, State.Stack.back().Indent); } else if (Current.Type == TT_ObjCSelectorName) { - if (State.Stack.back().ColonPos == 0) { - State.Stack.back().ColonPos = - State.Stack.back().Indent + Current.LongestObjCSelectorName; - State.Column = State.Stack.back().ColonPos - Current.ColumnWidth; + if (!State.Stack.back().ObjCSelectorNameFound) { + if (Current.LongestObjCSelectorName == 0) { + State.Column = State.Stack.back().Indent; + State.Stack.back().AlignColons = false; + } else { + State.Stack.back().ColonPos = + State.Stack.back().Indent + Current.LongestObjCSelectorName; + State.Column = State.Stack.back().ColonPos - Current.ColumnWidth; + } + } else if (!State.Stack.back().AlignColons) { + State.Column = State.Stack.back().Indent; } else if (State.Stack.back().ColonPos > Current.ColumnWidth) { State.Column = State.Stack.back().ColonPos - Current.ColumnWidth; } else { @@ -397,9 +407,21 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, else State.Column = ContinuationIndent; } else if (Current.Type == TT_StartOfName || - Previous.isOneOf(tok::coloncolon, tok::equal) || - Previous.Type == TT_ObjCMethodExpr) { + Previous.isOneOf(tok::coloncolon, tok::equal)) { + State.Column = ContinuationIndent; + } else if (PreviousNonComment && + PreviousNonComment->Type == TT_ObjCMethodExpr) { State.Column = ContinuationIndent; + // FIXME: This is hacky, find a better way. The problem is that in an ObjC + // method expression, the block should be aligned to the line starting it, + // e.g.: + // [aaaaaaaaaaaaaaa aaaaaaaaa: \\ break for some reason + // ^(int *i) { + // // ... + // }]; + // Thus, we set LastSpace of the next higher ParenLevel, to which we move + // when we consume all of the "}"'s FakeRParens at the "{". + State.Stack[State.Stack.size() - 2].LastSpace = ContinuationIndent; } else if (Current.Type == TT_CtorInitializerColon) { State.Column = State.FirstIndent + Style.ConstructorInitializerIndentWidth; } else if (Current.Type == TT_CtorInitializerComma) { @@ -449,7 +471,7 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, !PreviousNonComment->isOneOf(tok::comma, tok::semi) && PreviousNonComment->Type != TT_TemplateCloser && PreviousNonComment->Type != TT_BinaryOperator && - Current.Type != TT_BinaryOperator && + Current.Type != TT_BinaryOperator && !PreviousNonComment->opensScope()) State.Stack.back().BreakBeforeParameter = true; @@ -494,6 +516,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, if (Current.isMemberAccess()) State.Stack.back().StartOfFunctionCall = Current.LastInChainOfCalls ? 0 : State.Column + Current.ColumnWidth; + if (Current.Type == TT_ObjCSelectorName) + State.Stack.back().ObjCSelectorNameFound = true; if (Current.Type == TT_CtorInitializerColon) { // Indent 2 from the column, so: // SomeClass::SomeClass() @@ -588,7 +612,16 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, // }); for (unsigned i = 0; i != Current.MatchingParen->FakeRParens; ++i) State.Stack.pop_back(); - NewIndent = State.Stack.back().LastSpace + Style.IndentWidth; + bool IsObjCBlock = + Previous && + (Previous->is(tok::caret) || + (Previous->is(tok::r_paren) && Previous->MatchingParen && + Previous->MatchingParen->Previous && + Previous->MatchingParen->Previous->is(tok::caret))); + // For some reason, ObjC blocks are indented like continuations. + NewIndent = + State.Stack.back().LastSpace + + (IsObjCBlock ? Style.ContinuationIndentWidth : Style.IndentWidth); ++NewIndentLevel; BreakBeforeParameter = true; } else { diff --git a/clang/lib/Format/ContinuationIndenter.h b/clang/lib/Format/ContinuationIndenter.h index 7eab6d5456f..41f3f4b8808 100644 --- a/clang/lib/Format/ContinuationIndenter.h +++ b/clang/lib/Format/ContinuationIndenter.h @@ -133,7 +133,8 @@ struct ParenState { NoLineBreak(NoLineBreak), ColonPos(0), StartOfFunctionCall(0), StartOfArraySubscripts(0), NestedNameSpecifierContinuation(0), CallContinuation(0), VariablePos(0), ContainsLineBreak(false), - ContainsUnwrappedBuilder(0) {} + ContainsUnwrappedBuilder(0), AlignColons(true), + ObjCSelectorNameFound(false) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. @@ -210,6 +211,20 @@ struct ParenState { /// builder-type call on one line. bool ContainsUnwrappedBuilder; + /// \brief \c true if the colons of the curren ObjC method expression should + /// be aligned. + /// + /// Not considered for memoization as it will always have the same value at + /// the same token. + bool AlignColons; + + /// \brief \c true if at least one selector name was found in the current + /// ObjC method expression. + /// + /// Not considered for memoization as it will always have the same value at + /// the same token. + bool ObjCSelectorNameFound; + bool operator<(const ParenState &Other) const { if (Indent != Other.Indent) return Indent < Other.Indent; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 0c7634314e7..391f9ee8000 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -217,6 +217,9 @@ struct FormatToken { /// \brief If this is the first ObjC selector name in an ObjC method /// definition or call, this contains the length of the longest name. + /// + /// This being set to 0 means that the selectors should not be colon-aligned, + /// e.g. because several of them are block-type. unsigned LongestObjCSelectorName; /// \brief Stores the number of required fake parentheses and the diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index c2b9521a590..0cc46a8cad5 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -84,7 +84,7 @@ private: bool StartsObjCMethodExpr = false; FormatToken *Left = CurrentToken->Previous; if (CurrentToken->is(tok::caret)) { - // ^( starts a block. + // (^ can start a block type. Left->Type = TT_ObjCBlockLParen; } else if (FormatToken *MaybeSel = Left->Previous) { // @selector( starts a selector. @@ -103,6 +103,10 @@ private: Left->Previous->MatchingParen->Type == TT_LambdaLSquare) { // This is a parameter list of a lambda expression. Contexts.back().IsExpression = false; + } else if (Left->Previous && Left->Previous->is(tok::caret) && + Left->Previous->Type == TT_UnaryOperator) { + // This is the parameter list of an ObjC block. + Contexts.back().IsExpression = false; } if (StartsObjCMethodExpr) { @@ -226,9 +230,12 @@ private: } Left->MatchingParen = CurrentToken; CurrentToken->MatchingParen = Left; - if (Contexts.back().FirstObjCSelectorName != NULL) + if (Contexts.back().FirstObjCSelectorName != NULL) { Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = Contexts.back().LongestObjCSelectorName; + if (Contexts.back().NumBlockParameters > 1) + Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName = 0; + } next(); return true; } @@ -555,15 +562,16 @@ private: Context(tok::TokenKind ContextKind, unsigned BindingStrength, bool IsExpression) : ContextKind(ContextKind), BindingStrength(BindingStrength), - LongestObjCSelectorName(0), ColonIsForRangeExpr(false), - ColonIsDictLiteral(false), ColonIsObjCMethodExpr(false), - FirstObjCSelectorName(NULL), FirstStartOfName(NULL), - IsExpression(IsExpression), CanBeExpression(true), - InCtorInitializer(false) {} + LongestObjCSelectorName(0), NumBlockParameters(0), + ColonIsForRangeExpr(false), ColonIsDictLiteral(false), + ColonIsObjCMethodExpr(false), FirstObjCSelectorName(NULL), + FirstStartOfName(NULL), IsExpression(IsExpression), + CanBeExpression(true), InCtorInitializer(false) {} tok::TokenKind ContextKind; unsigned BindingStrength; unsigned LongestObjCSelectorName; + unsigned NumBlockParameters; bool ColonIsForRangeExpr; bool ColonIsDictLiteral; bool ColonIsObjCMethodExpr; @@ -650,6 +658,8 @@ private: Contexts.back().IsExpression); } else if (Current.isOneOf(tok::minus, tok::plus, tok::caret)) { Current.Type = determinePlusMinusCaretUsage(Current); + if (Current.Type == TT_UnaryOperator) + ++Contexts.back().NumBlockParameters; } else if (Current.isOneOf(tok::minusminus, tok::plusplus)) { Current.Type = determineIncrementUsage(Current); } else if (Current.is(tok::exclaim)) { @@ -916,8 +926,11 @@ public: int CurrentPrecedence = getCurrentPrecedence(); if (Current && Current->Type == TT_ObjCSelectorName && - Precedence == CurrentPrecedence) + Precedence == CurrentPrecedence) { + if (LatestOperator) + addFakeParenthesis(Start, prec::Level(Precedence)); Start = Current; + } // At the end of the line or when an operator with higher precedence is // found, insert fake parenthesis and return. @@ -1077,7 +1090,7 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) { Current->SpacesRequiredBefore = Style.Cpp11BracedListStyle ? 0 : 1; else Current->SpacesRequiredBefore = Style.SpacesBeforeTrailingComments; - } else if (Current->SpacesRequiredBefore == 0 && + } else if (Current->SpacesRequiredBefore == 0 && spaceRequiredBefore(Line, *Current)) { Current->SpacesRequiredBefore = 1; } diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 089bd570b5c..90fe3bd7860 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -740,7 +740,7 @@ void UnwrappedLineParser::parseStructuralElement() { } break; case tok::l_square: - tryToParseLambda(); + parseSquare(); break; default: nextToken(); @@ -749,18 +749,18 @@ void UnwrappedLineParser::parseStructuralElement() { } while (!eof()); } -void UnwrappedLineParser::tryToParseLambda() { +bool UnwrappedLineParser::tryToParseLambda() { // FIXME: This is a dirty way to access the previous token. Find a better // solution. if (!Line->Tokens.empty() && Line->Tokens.back().Tok->isOneOf(tok::identifier, tok::kw_operator)) { nextToken(); - return; + return false; } assert(FormatTok->is(tok::l_square)); FormatToken &LSquare = *FormatTok; if (!tryToParseLambdaIntroducer()) - return; + return false; while (FormatTok->isNot(tok::l_brace)) { switch (FormatTok->Tok.getKind()) { @@ -774,11 +774,12 @@ void UnwrappedLineParser::tryToParseLambda() { nextToken(); break; default: - return; + return true; } } LSquare.Type = TT_LambdaLSquare; parseChildBlock(); + return true; } bool UnwrappedLineParser::tryToParseLambdaIntroducer() { @@ -950,6 +951,43 @@ void UnwrappedLineParser::parseParens() { } while (!eof()); } +void UnwrappedLineParser::parseSquare() { + assert(FormatTok->Tok.is(tok::l_square) && "'[' expected."); + if (tryToParseLambda()) + return; + do { + // llvm::errs() << FormatTok->Tok.getName() << "\n"; + switch (FormatTok->Tok.getKind()) { + case tok::l_paren: + parseParens(); + break; + case tok::r_square: + nextToken(); + return; + case tok::r_brace: + // A "}" inside parenthesis is an error if there wasn't a matching "{". + return; + case tok::l_square: + parseSquare(); + break; + case tok::l_brace: { + if (!tryToParseBracedList()) { + parseChildBlock(); + } + break; + } + case tok::at: + nextToken(); + if (FormatTok->Tok.is(tok::l_brace)) + parseBracedList(); + break; + default: + nextToken(); + break; + } + } while (!eof()); +} + void UnwrappedLineParser::parseIfThenElse() { assert(FormatTok->Tok.is(tok::kw_if) && "'if' expected"); nextToken(); diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 71caed8505c..6e61bb8c640 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -84,6 +84,7 @@ private: bool parseBracedList(bool ContinueOnSemicolons = false); void parseReturn(); void parseParens(); + void parseSquare(); void parseIfThenElse(); void parseForOrWhileLoop(); void parseDoWhile(); @@ -98,7 +99,7 @@ private: void parseObjCUntilAtEnd(); void parseObjCInterfaceOrImplementation(); void parseObjCProtocol(); - void tryToParseLambda(); + bool tryToParseLambda(); bool tryToParseLambdaIntroducer(); void addUnwrappedLine(); bool eof() const; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 97cc2614e89..65124a80575 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -7816,6 +7816,49 @@ TEST_F(FormatTest, FormatsBlocks) { verifyFormat("[operation setCompletionBlock:^{ [self onOperationDone]; }];"); verifyFormat("int i = {[operation setCompletionBlock : ^{ [self " "onOperationDone]; }] };"); + verifyFormat("[operation setCompletionBlock:^(int *i) { f(); }];"); + + verifyFormat("[operation setCompletionBlock:^{\n" + " [self.delegate newDataAvailable];\n" + "}];", + getLLVMStyleWithColumns(60)); + verifyFormat("dispatch_async(_fileIOQueue, ^{\n" + " NSString *path = [self sessionFilePath];\n" + " if (path) {\n" + " // ...\n" + " }\n" + "});"); + verifyFormat("[[SessionService sharedService]\n" + " loadWindowWithCompletionBlock:^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + " }];"); + verifyFormat("void (^largeBlock)(void) = ^{\n" + " // ...\n" + "};\n", + getLLVMStyleWithColumns(40)); + verifyFormat("[[SessionService sharedService]\n" + " loadWindowWithCompletionBlock: //\n" + " ^(SessionWindow *window) {\n" + " if (window) {\n" + " [self windowDidLoad:window];\n" + " } else {\n" + " [self errorLoadingWindow];\n" + " }\n" + " }];", + getLLVMStyleWithColumns(60)); + verifyFormat("[myObject doSomethingWith:arg1\n" + " firstBlock:^(Foo *a) {\n" + " // ...\n" + " int i;\n" + " }\n" + " secondBlock:^(Bar *b) {\n" + " // ...\n" + " int i;\n" + " }];"); } TEST_F(FormatTest, SupportsCRLF) { |

