benhamilton created this revision. benhamilton added reviewers: krasimir, jolesiak.
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. Depends On https://reviews.llvm.org/D43904 Test Plan: New tests added. Ran tests with: make -j12 FormatTests && ./tools/clang/unittests/Format/FormatTests Repository: rC Clang https://reviews.llvm.org/D43905 Files: lib/Format/TokenAnnotator.cpp unittests/Format/FormatTest.cpp Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -11999,6 +11999,31 @@ EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]")); } +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<Bar>& baz = in.value(); !baz.at_end(); ++baz) {}")); +} + } // end namespace } // end namespace format } // end namespace clang Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -285,6 +285,16 @@ else Left->PackingKind = PPK_OnePerLine; + if (MightBeObjCForRangeLoop) { + FormatToken *ForInToken = Left; + while (ForInToken && ForInToken != CurrentToken) { + if (ForInToken->is(Keywords.kw_in)) { + ForInToken->Type = TT_ObjCForIn; + break; + } + ForInToken = ForInToken->Next; + } + } next(); return true; } @@ -303,8 +313,6 @@ Contexts.back().IsExpression = false; if (CurrentToken->isOneOf(tok::semi, tok::colon)) MightBeObjCForRangeLoop = false; - if (MightBeObjCForRangeLoop && CurrentToken->is(Keywords.kw_in)) - CurrentToken->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))
Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -11999,6 +11999,31 @@ EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]")); } +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<Bar>& baz = in.value(); !baz.at_end(); ++baz) {}")); +} + } // end namespace } // end namespace format } // end namespace clang Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -285,6 +285,16 @@ else Left->PackingKind = PPK_OnePerLine; + if (MightBeObjCForRangeLoop) { + FormatToken *ForInToken = Left; + while (ForInToken && ForInToken != CurrentToken) { + if (ForInToken->is(Keywords.kw_in)) { + ForInToken->Type = TT_ObjCForIn; + break; + } + ForInToken = ForInToken->Next; + } + } next(); return true; } @@ -303,8 +313,6 @@ Contexts.back().IsExpression = false; if (CurrentToken->isOneOf(tok::semi, tok::colon)) MightBeObjCForRangeLoop = false; - if (MightBeObjCForRangeLoop && CurrentToken->is(Keywords.kw_in)) - CurrentToken->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))
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits