diff options
Diffstat (limited to 'clang/lib/Format/ContinuationIndenter.cpp')
-rw-r--r-- | clang/lib/Format/ContinuationIndenter.cpp | 174 |
1 files changed, 150 insertions, 24 deletions
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 2ff6e5ec23446..b1497651a8fef 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -329,6 +329,11 @@ bool ContinuationIndenter::canBreak(const LineState &State) { bool ContinuationIndenter::mustBreak(const LineState &State) { const FormatToken &Current = *State.NextToken; const FormatToken &Previous = *Current.Previous; + if (Style.BraceWrapping.BeforeLambdaBody && Current.CanBreakBefore && + Current.is(TT_LambdaLBrace) && Previous.isNot(TT_LineComment)) { + auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack); + return (LambdaBodyLength > getColumnLimit(State)); + } if (Current.MustBreakBefore || Current.is(TT_InlineASMColon)) return true; if (State.Stack.back().BreakBeforeClosingBrace && @@ -337,10 +342,16 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { if (Previous.is(tok::semi) && State.LineContainsContinuedForLoopSection) return true; if (Style.Language == FormatStyle::LK_ObjC && + Style.ObjCBreakBeforeNestedBlockParam && Current.ObjCSelectorNameParts > 1 && Current.startsSequence(TT_SelectorName, tok::colon, tok::caret)) { return true; } + // Avoid producing inconsistent states by requiring breaks where they are not + // permitted for C# generic type constraints. + if (State.Stack.back().IsCSharpGenericTypeConstraint && + Previous.isNot(TT_CSharpGenericTypeConstraintComma)) + return false; if ((startsNextParameter(Current, Style) || Previous.is(tok::semi) || (Previous.is(TT_TemplateCloser) && Current.is(TT_StartOfName) && Style.isCpp() && @@ -356,6 +367,12 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { State.Stack.back().BreakBeforeParameter && !Current.isTrailingComment() && !Current.isOneOf(tok::r_paren, tok::r_brace)) return true; + if (State.Stack.back().IsChainedConditional && + ((Style.BreakBeforeTernaryOperators && Current.is(TT_ConditionalExpr) && + Current.is(tok::colon)) || + (!Style.BreakBeforeTernaryOperators && Previous.is(TT_ConditionalExpr) && + Previous.is(tok::colon)))) + return true; if (((Previous.is(TT_DictLiteral) && Previous.is(tok::l_brace)) || (Previous.is(TT_ArrayInitializerLSquare) && Previous.ParameterCount > 1) || @@ -412,7 +429,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { State.Stack.back().BreakBeforeParameter && Current.CanBreakBefore) return true; - if (State.Column <= NewLineColumn) + if (!State.Line->First->is(tok::kw_enum) && State.Column <= NewLineColumn) return false; if (Style.AlwaysBreakBeforeMultilineStrings && @@ -629,9 +646,12 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, State.Stack.back().NoLineBreak = true; if (Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign && + !State.Stack.back().IsCSharpGenericTypeConstraint && Previous.opensScope() && Previous.isNot(TT_ObjCMethodExpr) && - (Current.isNot(TT_LineComment) || Previous.BlockKind == BK_BracedInit)) + (Current.isNot(TT_LineComment) || Previous.BlockKind == BK_BracedInit)) { State.Stack.back().Indent = State.Column + Spaces; + State.Stack.back().IsAligned = true; + } if (State.Stack.back().AvoidBinPacking && startsNextParameter(Current, Style)) State.Stack.back().NoLineBreak = true; if (startsSegmentOfBuilderTypeCall(Current) && @@ -673,7 +693,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, // does not help. bool HasTwoOperands = P->OperatorIndex == 0 && !P->NextOperator && !P->is(TT_ConditionalExpr); - if ((!BreakBeforeOperator && !(HasTwoOperands && Style.AlignOperands)) || + if ((!BreakBeforeOperator && + !(HasTwoOperands && + Style.AlignOperands != FormatStyle::OAS_DontAlign)) || (!State.Stack.back().LastOperatorWrapped && BreakBeforeOperator)) State.Stack.back().NoLineBreakInOperand = true; } @@ -710,6 +732,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, } else if (Previous.is(TT_InheritanceColon)) { State.Stack.back().Indent = State.Column; State.Stack.back().LastSpace = State.Column; + } else if (Current.is(TT_CSharpGenericTypeConstraintColon)) { + State.Stack.back().ColonPos = State.Column; } else if (Previous.opensScope()) { // If a function has a trailing call, indent all parameters from the // opening parenthesis. This avoids confusing indents like: @@ -844,6 +868,7 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, bool ContinuePPDirective = State.Line->InPPDirective && State.Line->Type != LT_ImportStatement; Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column, + State.Stack.back().IsAligned, ContinuePPDirective); } @@ -861,8 +886,10 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, // Any break on this level means that the parent level has been broken // and we need to avoid bin packing there. bool NestedBlockSpecialCase = - !Style.isCpp() && Current.is(tok::r_brace) && State.Stack.size() > 1 && - State.Stack[State.Stack.size() - 2].NestedBlockInlined; + (!Style.isCpp() && Current.is(tok::r_brace) && State.Stack.size() > 1 && + State.Stack[State.Stack.size() - 2].NestedBlockInlined) || + (Style.Language == FormatStyle::LK_ObjC && Current.is(tok::r_brace) && + State.Stack.size() > 1 && !Style.ObjCBreakBeforeNestedBlockParam); if (!NestedBlockSpecialCase) for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i) State.Stack[i].BreakBeforeParameter = true; @@ -917,7 +944,13 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (!State.NextToken || !State.NextToken->Previous) return 0; + FormatToken &Current = *State.NextToken; + + if (State.Stack.back().IsCSharpGenericTypeConstraint && + Current.isNot(TT_CSharpGenericTypeConstraint)) + return State.Stack.back().ColonPos + 2; + const FormatToken &Previous = *Current.Previous; // If we are continuing an expression, we want to use the continuation indent. unsigned ContinuationIndent = @@ -997,8 +1030,28 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (State.Stack.back().QuestionColumn != 0 && ((NextNonComment->is(tok::colon) && NextNonComment->is(TT_ConditionalExpr)) || - Previous.is(TT_ConditionalExpr))) + Previous.is(TT_ConditionalExpr))) { + if (((NextNonComment->is(tok::colon) && NextNonComment->Next && + !NextNonComment->Next->FakeLParens.empty() && + NextNonComment->Next->FakeLParens.back() == prec::Conditional) || + (Previous.is(tok::colon) && !Current.FakeLParens.empty() && + Current.FakeLParens.back() == prec::Conditional)) && + !State.Stack.back().IsWrappedConditional) { + // NOTE: we may tweak this slightly: + // * not remove the 'lead' ContinuationIndentWidth + // * always un-indent by the operator when + // BreakBeforeTernaryOperators=true + unsigned Indent = State.Stack.back().Indent; + if (Style.AlignOperands != FormatStyle::OAS_DontAlign) { + Indent -= Style.ContinuationIndentWidth; + } + if (Style.BreakBeforeTernaryOperators && + State.Stack.back().UnindentOperator) + Indent -= 2; + return Indent; + } return State.Stack.back().QuestionColumn; + } if (Previous.is(tok::comma) && State.Stack.back().VariablePos != 0) return State.Stack.back().VariablePos; if ((PreviousNonComment && @@ -1040,6 +1093,9 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (NextNonComment->is(TT_ArraySubscriptLSquare)) { if (State.Stack.back().StartOfArraySubscripts != 0) return State.Stack.back().StartOfArraySubscripts; + else if (Style.isCSharp()) // C# allows `["key"] = value` inside object + // initializers. + return State.Stack.back().Indent; return ContinuationIndent; } @@ -1071,6 +1127,13 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return ContinuationIndent; if (Current.is(TT_ProtoExtensionLSquare)) return State.Stack.back().Indent; + if (Current.isBinaryOperator() && State.Stack.back().UnindentOperator) + return State.Stack.back().Indent - Current.Tok.getLength() - + Current.SpacesRequiredBefore; + if (Current.isOneOf(tok::comment, TT_BlockComment, TT_LineComment) && + NextNonComment->isBinaryOperator() && State.Stack.back().UnindentOperator) + return State.Stack.back().Indent - NextNonComment->Tok.getLength() - + NextNonComment->SpacesRequiredBefore; if (State.Stack.back().Indent == State.FirstIndent && PreviousNonComment && !PreviousNonComment->isOneOf(tok::r_brace, TT_CtorInitializerComma)) // Ensure that we fall back to the continuation indent width instead of @@ -1079,14 +1142,28 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { return State.Stack.back().Indent; } +static bool hasNestedBlockInlined(const FormatToken *Previous, + const FormatToken &Current, + const FormatStyle &Style) { + if (Previous->isNot(tok::l_paren)) + return true; + if (Previous->ParameterCount > 1) + return true; + + // Also a nested block if contains a lambda inside function with 1 parameter + return (Style.BraceWrapping.BeforeLambdaBody && Current.is(TT_LambdaLSquare)); +} + unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, bool DryRun, bool Newline) { assert(State.Stack.size()); const FormatToken &Current = *State.NextToken; + if (Current.is(TT_CSharpGenericTypeConstraint)) + State.Stack.back().IsCSharpGenericTypeConstraint = true; if (Current.isOneOf(tok::comma, TT_BinaryOperator)) State.Stack.back().NoLineBreakInOperand = false; - if (Current.is(TT_InheritanceColon)) + if (Current.isOneOf(TT_InheritanceColon, TT_CSharpGenericTypeConstraintColon)) State.Stack.back().AvoidBinPacking = true; if (Current.is(tok::lessless) && Current.isNot(TT_OverloadedOperator)) { if (State.Stack.back().FirstLessLess == 0) @@ -1102,6 +1179,11 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, if (Current.is(TT_ArraySubscriptLSquare) && State.Stack.back().StartOfArraySubscripts == 0) State.Stack.back().StartOfArraySubscripts = State.Column; + if (Current.is(TT_ConditionalExpr) && Current.is(tok::question) && + ((Current.MustBreakBefore) || + (Current.getNextNonComment() && + Current.getNextNonComment()->MustBreakBefore))) + State.Stack.back().IsWrappedConditional = true; if (Style.BreakBeforeTernaryOperators && Current.is(tok::question)) State.Stack.back().QuestionColumn = State.Column; if (!Style.BreakBeforeTernaryOperators && Current.isNot(tok::colon)) { @@ -1181,8 +1263,7 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, Previous->isOneOf(TT_BinaryOperator, TT_ConditionalExpr)) && !Previous->isOneOf(TT_DictLiteral, TT_ObjCMethodExpr)) { State.Stack.back().NestedBlockInlined = - !Newline && - (Previous->isNot(tok::l_paren) || Previous->ParameterCount > 1); + !Newline && hasNestedBlockInlined(Previous, Current, Style); } moveStatePastFakeLParens(State, Newline); @@ -1233,7 +1314,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, (Previous && (Previous->opensScope() || Previous->isOneOf(tok::semi, tok::kw_return) || (Previous->getPrecedence() == prec::Assignment && - Style.AlignOperands) || + Style.AlignOperands != FormatStyle::OAS_DontAlign) || Previous->is(TT_ObjCMethodExpr))); for (SmallVectorImpl<prec::Level>::const_reverse_iterator I = Current.FakeLParens.rbegin(), @@ -1243,6 +1324,9 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, NewParenState.Tok = nullptr; NewParenState.ContainsLineBreak = false; NewParenState.LastOperatorWrapped = true; + NewParenState.IsChainedConditional = false; + NewParenState.IsWrappedConditional = false; + NewParenState.UnindentOperator = false; NewParenState.NoLineBreak = NewParenState.NoLineBreak || State.Stack.back().NoLineBreakInOperand; @@ -1254,14 +1338,27 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, // a builder type call after 'return' or, if the alignment after opening // brackets is disabled. if (!Current.isTrailingComment() && - (Style.AlignOperands || *I < prec::Assignment) && + (Style.AlignOperands != FormatStyle::OAS_DontAlign || + *I < prec::Assignment) && (!Previous || Previous->isNot(tok::kw_return) || (Style.Language != FormatStyle::LK_Java && *I > 0)) && (Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign || - *I != prec::Comma || Current.NestingLevel == 0)) + *I != prec::Comma || Current.NestingLevel == 0)) { NewParenState.Indent = std::max(std::max(State.Column, NewParenState.Indent), State.Stack.back().LastSpace); + } + + // If BreakBeforeBinaryOperators is set, un-indent a bit to account for + // the operator and keep the operands aligned + if (Style.AlignOperands == FormatStyle::OAS_AlignAfterOperator && + Previous && + (Previous->getPrecedence() == prec::Assignment || + Previous->is(tok::kw_return) || + (*I == prec::Conditional && Previous->is(tok::question) && + Previous->is(TT_ConditionalExpr))) && + !Newline) + NewParenState.UnindentOperator = true; // Do not indent relative to the fake parentheses inserted for "." or "->". // This is a special case to make the following to statements consistent: @@ -1275,14 +1372,21 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State, Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign) NewParenState.StartOfFunctionCall = State.Column; - // Always indent conditional expressions. Never indent expression where - // the 'operator' is ',', ';' or an assignment (i.e. *I <= - // prec::Assignment) as those have different indentation rules. Indent - // other expression, unless the indentation needs to be skipped. - if (*I == prec::Conditional || - (!SkipFirstExtraIndent && *I > prec::Assignment && - !Current.isTrailingComment())) + // Indent conditional expressions, unless they are chained "else-if" + // conditionals. Never indent expression where the 'operator' is ',', ';' or + // an assignment (i.e. *I <= prec::Assignment) as those have different + // indentation rules. Indent other expression, unless the indentation needs + // to be skipped. + if (*I == prec::Conditional && Previous && Previous->is(tok::colon) && + Previous->is(TT_ConditionalExpr) && I == Current.FakeLParens.rbegin() && + !State.Stack.back().IsWrappedConditional) { + NewParenState.IsChainedConditional = true; + NewParenState.UnindentOperator = State.Stack.back().UnindentOperator; + } else if (*I == prec::Conditional || + (!SkipFirstExtraIndent && *I > prec::Assignment && + !Current.isTrailingComment())) { NewParenState.Indent += Style.ContinuationIndentWidth; + } if ((Previous && !Previous->opensScope()) || *I != prec::Comma) NewParenState.BreakBeforeParameter = false; State.Stack.push_back(NewParenState); @@ -1308,6 +1412,11 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, if (!Current.opensScope()) return; + // Don't allow '<' or '(' in C# generic type constraints to start new scopes. + if (Current.isOneOf(tok::less, tok::l_paren) && + State.Stack.back().IsCSharpGenericTypeConstraint) + return; + if (Current.MatchingParen && Current.BlockKind == BK_Block) { moveStateToNewBlock(State); return; @@ -1372,6 +1481,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, (State.Line->Type == LT_ObjCDecl && ObjCBinPackProtocolList); AvoidBinPacking = + (State.Stack.back().IsCSharpGenericTypeConstraint) || (Style.Language == FormatStyle::LK_JavaScript && EndsInComma) || (State.Line->MustBeDeclaration && !BinPackDeclaration) || (!State.Line->MustBeDeclaration && !Style.BinPackArguments) || @@ -1380,7 +1490,8 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, (!BinPackInconclusiveFunctions && Current.PackingKind == PPK_Inconclusive))); - if (Current.is(TT_ObjCMethodExpr) && Current.MatchingParen) { + if (Current.is(TT_ObjCMethodExpr) && Current.MatchingParen && + Style.ObjCBreakBeforeNestedBlockParam) { if (Style.ColumnLimit) { // If this '[' opens an ObjC call, determine whether all parameters fit // into one line and put one per line if they don't. @@ -1418,7 +1529,22 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, ParenState(&Current, NewIndent, LastSpace, AvoidBinPacking, NoLineBreak)); State.Stack.back().NestedBlockIndent = NestedBlockIndent; State.Stack.back().BreakBeforeParameter = BreakBeforeParameter; - State.Stack.back().HasMultipleNestedBlocks = Current.BlockParameterCount > 1; + State.Stack.back().HasMultipleNestedBlocks = + (Current.BlockParameterCount > 1); + + if (Style.BraceWrapping.BeforeLambdaBody && Current.Next != nullptr && + Current.Tok.is(tok::l_paren)) { + // Search for any parameter that is a lambda + FormatToken const *next = Current.Next; + while (next != nullptr) { + if (next->is(TT_LambdaLSquare)) { + State.Stack.back().HasMultipleNestedBlocks = true; + break; + } + next = next->Next; + } + } + State.Stack.back().IsInsideObjCArrayLiteral = Current.is(TT_ArrayInitializerLSquare) && Current.Previous && Current.Previous->is(tok::at); @@ -1513,8 +1639,8 @@ unsigned ContinuationIndenter::reformatRawStringLiteral( unsigned OldSuffixSize = 2 + OldDelimiter.size(); // We create a virtual text environment which expects a null-terminated // string, so we cannot use StringRef. - std::string RawText = - Current.TokenText.substr(OldPrefixSize).drop_back(OldSuffixSize); + std::string RawText = std::string( + Current.TokenText.substr(OldPrefixSize).drop_back(OldSuffixSize)); if (NewDelimiter != OldDelimiter) { // Don't update to the canonical delimiter 'deli' if ')deli"' occurs in the // raw string. @@ -1760,7 +1886,7 @@ ContinuationIndenter::createBreakableToken(const FormatToken &Current, LineState &State, bool AllowBreak) { unsigned StartColumn = State.Column - Current.ColumnWidth; if (Current.isStringLiteral()) { - // FIXME: String literal breaking is currently disabled for C#,Java and + // FIXME: String literal breaking is currently disabled for C#, Java and // JavaScript, as it requires strings to be merged using "+" which we // don't support. if (Style.Language == FormatStyle::LK_Java || |