From: Ben Hamilton Date: Tue, 6 Mar 2018 17:21:42 +0000 (+0000) Subject: [clang-format] Improve detection of ObjC for-in statements X-Git-Tag: llvmorg-7.0.0-rc1~11304 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1d6c6ee1cf98d260b486242b288a9d8fa0e4e7e1;p=platform%2Fupstream%2Fllvm.git [clang-format] Improve detection of ObjC for-in statements Summary: Previously, clang-format would detect the following as an Objective-C for-in statement: for (int x = in.value(); ...) {} because the logic only decided a for-loop was definitely *not* an Objective-C for-in loop after it saw a semicolon or a colon. To fix this, I delayed the decision of whether this was a for-in statement until after we found the matching right-paren, at which point we know if we've seen a semicolon or not. Test Plan: New tests added. Ran tests with: make -j12 FormatTests && ./tools/clang/unittests/Format/FormatTests Reviewers: krasimir, jolesiak Reviewed By: jolesiak Subscribers: djasper, cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D43904 llvm-svn: 326815 --- diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d083cc7..013a77b 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -216,6 +216,7 @@ private: bool HasMultipleParametersOnALine = false; bool MightBeObjCForRangeLoop = Left->Previous && Left->Previous->is(tok::kw_for); + FormatToken *PossibleObjCForInToken = nullptr; while (CurrentToken) { // LookForDecls is set when "if (" has been seen. Check for // 'identifier' '*' 'identifier' followed by not '=' -- this @@ -301,10 +302,17 @@ private: CurrentToken->Previous->isSimpleTypeSpecifier()) && !CurrentToken->is(tok::l_brace)) Contexts.back().IsExpression = false; - if (CurrentToken->isOneOf(tok::semi, tok::colon)) + if (CurrentToken->isOneOf(tok::semi, tok::colon)) { MightBeObjCForRangeLoop = false; - if (MightBeObjCForRangeLoop && CurrentToken->is(Keywords.kw_in)) - CurrentToken->Type = TT_ObjCForIn; + if (PossibleObjCForInToken) { + PossibleObjCForInToken->Type = TT_Unknown; + PossibleObjCForInToken = nullptr; + } + } + if (MightBeObjCForRangeLoop && CurrentToken->is(Keywords.kw_in)) { + PossibleObjCForInToken = CurrentToken; + PossibleObjCForInToken->Type = TT_ObjCForIn; + } // When we discover a 'new', we set CanBeExpression to 'false' in order to // parse the type correctly. Reset that after a comma. if (CurrentToken->is(tok::comma)) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index c011721..75667d5 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -742,6 +742,12 @@ TEST_F(FormatTest, FormatsForLoop) { " aaaaaaaaaaaaaaaaaaaaaaaaaaa != bbbbbbbbbbbbbbbbbbbbbbb;\n" " ++aaaaaaaaaaaaaaaaaaaaaaaaaaa) {"); + // These should not be formatted as Objective-C for-in loops. + verifyFormat("for (Foo *x = 0; x != in; x++) {\n}"); + verifyFormat("Foo *x;\nfor (x = 0; x != in; x++) {\n}"); + verifyFormat("Foo *x;\nfor (x in y) {\n}"); + verifyFormat("for (const Foo &baz = in.value(); !baz.at_end(); ++baz) {\n}"); + FormatStyle NoBinPacking = getLLVMStyle(); NoBinPacking.BinPackParameters = false; verifyFormat("for (int aaaaaaaaaaa = 1;\n" @@ -12082,6 +12088,31 @@ TEST_F(FormatTest, FileAndCode) { EXPECT_EQ(FormatStyle::LK_ObjC, guessLanguage("foo", "@interface Foo\n@end\n")); } +TEST_F(FormatTest, GuessLanguageWithForIn) { + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "for (Foo *x = 0; x != in; x++) {}")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "for (Foo *x in bar) {}")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "for (Foo *x in [bar baz]) {}")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "for (Foo *x in [bar baz:blech]) {}")); + EXPECT_EQ( + FormatStyle::LK_ObjC, + guessLanguage("foo.h", "for (Foo *x in [bar baz:blech, 1, 2, 3, 0]) {}")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "for (Foo *x in [bar baz:^{[uh oh];}]) {}")); + EXPECT_EQ(FormatStyle::LK_Cpp, + guessLanguage("foo.h", "Foo *x; for (x = 0; x != in; x++) {}")); + EXPECT_EQ(FormatStyle::LK_ObjC, + guessLanguage("foo.h", "Foo *x; for (x in y) {}")); + EXPECT_EQ( + FormatStyle::LK_Cpp, + guessLanguage( + "foo.h", + "for (const Foo& baz = in.value(); !baz.at_end(); ++baz) {}")); +} + } // end namespace } // end namespace format } // end namespace clang diff --git a/clang/unittests/Format/FormatTestObjC.cpp b/clang/unittests/Format/FormatTestObjC.cpp index 15c3d76..8773088 100644 --- a/clang/unittests/Format/FormatTestObjC.cpp +++ b/clang/unittests/Format/FormatTestObjC.cpp @@ -893,6 +893,13 @@ TEST_F(FormatTestObjC, ObjCForIn) { " foo(n);\n" " }\n" "}"); + verifyFormat("for (Foo *x in bar) {\n}"); + verifyFormat("for (Foo *x in [bar baz]) {\n}"); + verifyFormat("for (Foo *x in [bar baz:blech]) {\n}"); + verifyFormat("for (Foo *x in [bar baz:blech, 1, 2, 3, 0]) {\n}"); + verifyFormat("for (Foo *x in [bar baz:^{\n" + " [uh oh];\n" + " }]) {\n}"); } TEST_F(FormatTestObjC, ObjCLiterals) {