From 06e63fb8ed10bf89e698eea1777baa8b6f2db810 Mon Sep 17 00:00:00 2001 From: Ben Hamilton Date: Thu, 8 Feb 2018 16:07:25 +0000 Subject: [PATCH] [clang-format] Do not break Objective-C string literals inside array literals Summary: Concatenating Objective-C string literals inside an array literal raises the warning -Wobjc-string-concatenation (which is enabled by default). clang-format currently splits and concatenates string literals like the following: NSArray *myArray = @[ @"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ]; into: NSArray *myArray = @[ @"aaaaaaaaaaaaaaaaaaaaaaaaaaaa" @"aaaaaaaaa" ]; which raises the warning. This is https://bugs.llvm.org/show_bug.cgi?id=36153 . The options I can think of to fix this are: 1) Have clang-format disable Wobjc-string-concatenation by emitting pragmas around the formatted code 2) Have clang-format wrap the string literals in a macro (which disables the warning) 3) Disable string splitting for Objective-C string literals inside array literals I think 1) has no precedent, and I couldn't find a good identity() macro for 2). So, this diff implements 3). Test Plan: make -j12 FormatTests && ./tools/clang/unittests/Format/FormatTests Reviewers: jolesiak, stephanemoore, djasper Reviewed By: jolesiak Subscribers: klimek, cfe-commits Differential Revision: https://reviews.llvm.org/D42704 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@324618 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/ContinuationIndenter.cpp | 8 ++++++++ lib/Format/ContinuationIndenter.h | 7 ++++++- unittests/Format/FormatTestObjC.cpp | 6 ++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index 6ee299566e..14f8f05645 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -1285,6 +1285,9 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, State.Stack.back().NestedBlockIndent = NestedBlockIndent; State.Stack.back().BreakBeforeParameter = BreakBeforeParameter; State.Stack.back().HasMultipleNestedBlocks = Current.BlockParameterCount > 1; + State.Stack.back().IsInsideObjCArrayLiteral = + Current.is(TT_ArrayInitializerLSquare) && Current.Previous && + Current.Previous->is(tok::at); } void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) { @@ -1578,6 +1581,11 @@ std::unique_ptr ContinuationIndenter::createBreakableToken( // likely want to terminate the string before any line breaking is done. if (Current.IsUnterminatedLiteral) return nullptr; + // Don't break string literals inside Objective-C array literals (doing so + // raises the warning -Wobjc-string-concatenation). + if (State.Stack.back().IsInsideObjCArrayLiteral) { + return nullptr; + } StringRef Text = Current.TokenText; StringRef Prefix; diff --git a/lib/Format/ContinuationIndenter.h b/lib/Format/ContinuationIndenter.h index bb2b7320fd..64b3a3eba9 100644 --- a/lib/Format/ContinuationIndenter.h +++ b/lib/Format/ContinuationIndenter.h @@ -208,7 +208,8 @@ struct ParenState { NoLineBreakInOperand(false), LastOperatorWrapped(true), ContainsLineBreak(false), ContainsUnwrappedBuilder(false), AlignColons(true), ObjCSelectorNameFound(false), - HasMultipleNestedBlocks(false), NestedBlockInlined(false) {} + HasMultipleNestedBlocks(false), NestedBlockInlined(false), + IsInsideObjCArrayLiteral(false) {} /// \brief The position to which a specific parenthesis level needs to be /// indented. @@ -318,6 +319,10 @@ struct ParenState { /// "function" in JavaScript) is not wrapped to a new line. bool NestedBlockInlined : 1; + /// \brief \c true if the current \c ParenState represents an Objective-C + /// array literal. + bool IsInsideObjCArrayLiteral : 1; + bool operator<(const ParenState &Other) const { if (Indent != Other.Indent) return Indent < Other.Indent; diff --git a/unittests/Format/FormatTestObjC.cpp b/unittests/Format/FormatTestObjC.cpp index d270aabea5..df2e067a30 100644 --- a/unittests/Format/FormatTestObjC.cpp +++ b/unittests/Format/FormatTestObjC.cpp @@ -975,6 +975,12 @@ TEST_F(FormatTestObjC, ObjCArrayLiterals) { verifyFormat("[someFunction someLooooooooooooongParameter:@[\n" " NSBundle.mainBundle.infoDictionary[@\"a\"]\n" "]];"); + Style.ColumnLimit = 20; + // We can't break string literals inside NSArray literals + // (that raises -Wobjc-string-concatenation). + verifyFormat("NSArray *foo = @[\n" + " @\"aaaaaaaaaaaaaaaaaaaaaaaaaa\"\n" + "];\n"); } } // end namespace } // end namespace format -- 2.40.0