Skip to content

Commit 2a05904

Browse files
authored
[clang-format] Keep the ObjC selector name and @selector together (#160739)
Fixes #36459. after ```Objective-C - (void)test { if ([object respondsToSelector:@selector( selectorNameThatIsReallyLong:param1:param2:)]) return; } ``` before ```Objective-C - (void)test { if ([object respondsToSelector:@selector (selectorNameThatIsReallyLong:param1:param2:)]) return; } ``` Before this patch, the `ObjCMethodExpr` type was assigned to many kinds of tokens. The rule for allowing breaking the line before the colon on line TokenAnnotator.cpp:6289 was intended for method declarations and calls. It matched the parenthesis following `@selector` by mistake. To fix the problem, this patch adds a new type for `@selector`. Most of the special things in the code related to the old type is intended for other constructs. So most of the code related to the old type is not changed in this patch.
1 parent 36dc2a9 commit 2a05904

File tree

5 files changed

+62
-12
lines changed

5 files changed

+62
-12
lines changed

clang/lib/Format/Format.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3199,7 +3199,7 @@ class ObjCHeaderStyleGuesser : public TokenAnalyzer {
31993199
Keywords.kw_NS_OPTIONS, TT_ObjCBlockLBrace,
32003200
TT_ObjCBlockLParen, TT_ObjCDecl, TT_ObjCForIn,
32013201
TT_ObjCMethodExpr, TT_ObjCMethodSpecifier,
3202-
TT_ObjCProperty)) {
3202+
TT_ObjCProperty, TT_ObjCSelector)) {
32033203
LLVM_DEBUG(llvm::dbgs()
32043204
<< "Detected ObjC at location "
32053205
<< FormatTok->Tok.getLocation().printToString(

clang/lib/Format/FormatToken.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,17 @@ namespace format {
127127
TYPE(ObjCBlockLParen) \
128128
TYPE(ObjCDecl) \
129129
TYPE(ObjCForIn) \
130+
/* The square brackets surrounding a method call, the colon separating the \
131+
* method or parameter name and the argument inside the square brackets, and \
132+
* the colon separating the method or parameter name and the type inside the \
133+
* method declaration. */ \
130134
TYPE(ObjCMethodExpr) \
135+
/* The '+' or '-' at the start of the line. */ \
131136
TYPE(ObjCMethodSpecifier) \
132137
TYPE(ObjCProperty) \
138+
/* The parentheses following '@selector' and the colon following the method \
139+
* or parameter name inside the parentheses. */ \
140+
TYPE(ObjCSelector) \
133141
TYPE(ObjCStringLiteral) \
134142
TYPE(OverloadedOperator) \
135143
TYPE(OverloadedOperatorLParen) \
@@ -146,6 +154,9 @@ namespace format {
146154
TYPE(RequiresExpression) \
147155
TYPE(RequiresExpressionLBrace) \
148156
TYPE(RequiresExpressionLParen) \
157+
/* The hash key in languages that have hash literals, not including the \
158+
* field name in the C++ struct literal. Also the method or parameter name \
159+
* in the Objective-C method declaration or call. */ \
149160
TYPE(SelectorName) \
150161
TYPE(StartOfName) \
151162
TYPE(StatementAttributeLikeMacro) \

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,13 @@ class AnnotatingParser {
321321
return parseUntouchableParens();
322322
}
323323

324-
bool StartsObjCMethodExpr = false;
324+
bool StartsObjCSelector = false;
325325
if (!Style.isVerilog()) {
326326
if (FormatToken *MaybeSel = OpeningParen.Previous) {
327327
// @selector( starts a selector.
328328
if (MaybeSel->is(tok::objc_selector) && MaybeSel->Previous &&
329329
MaybeSel->Previous->is(tok::at)) {
330-
StartsObjCMethodExpr = true;
330+
StartsObjCSelector = true;
331331
}
332332
}
333333
}
@@ -451,10 +451,8 @@ class AnnotatingParser {
451451
}
452452
}
453453

454-
if (StartsObjCMethodExpr) {
455-
Contexts.back().ColonIsObjCMethodExpr = true;
456-
OpeningParen.setType(TT_ObjCMethodExpr);
457-
}
454+
if (StartsObjCSelector)
455+
OpeningParen.setType(TT_ObjCSelector);
458456

459457
// MightBeFunctionType and ProbablyFunctionType are used for
460458
// function pointer and reference types as well as Objective-C
@@ -513,8 +511,8 @@ class AnnotatingParser {
513511
}
514512
}
515513

516-
if (StartsObjCMethodExpr) {
517-
CurrentToken->setType(TT_ObjCMethodExpr);
514+
if (StartsObjCSelector) {
515+
CurrentToken->setType(TT_ObjCSelector);
518516
if (Contexts.back().FirstObjCSelectorName) {
519517
Contexts.back().FirstObjCSelectorName->LongestObjCSelectorName =
520518
Contexts.back().LongestObjCSelectorName;
@@ -1449,7 +1447,7 @@ class AnnotatingParser {
14491447
Next->Next->is(tok::colon)))) {
14501448
// This handles a special macro in ObjC code where selectors including
14511449
// the colon are passed as macro arguments.
1452-
Tok->setType(TT_ObjCMethodExpr);
1450+
Tok->setType(TT_ObjCSelector);
14531451
}
14541452
break;
14551453
case tok::pipe:
@@ -4608,7 +4606,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
46084606
return false;
46094607
}
46104608
if (Left.is(tok::colon))
4611-
return Left.isNot(TT_ObjCMethodExpr);
4609+
return Left.isNoneOf(TT_ObjCSelector, TT_ObjCMethodExpr);
46124610
if (Left.is(tok::coloncolon))
46134611
return false;
46144612
if (Left.is(tok::less) || Right.isOneOf(tok::greater, tok::less)) {
@@ -5464,7 +5462,7 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
54645462
// `private:` and `public:`.
54655463
if (!Right.getNextNonComment())
54665464
return false;
5467-
if (Right.is(TT_ObjCMethodExpr))
5465+
if (Right.isOneOf(TT_ObjCSelector, TT_ObjCMethodExpr))
54685466
return false;
54695467
if (Left.is(tok::question))
54705468
return false;
@@ -6288,6 +6286,7 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
62886286
return Style.BreakInheritanceList == FormatStyle::BILS_AfterColon;
62896287
if (Right.is(TT_InheritanceColon))
62906288
return Style.BreakInheritanceList != FormatStyle::BILS_AfterColon;
6289+
// When the method parameter has no name, allow breaking before the colon.
62916290
if (Right.is(TT_ObjCMethodExpr) && Right.isNot(tok::r_square) &&
62926291
Left.isNot(TT_SelectorName)) {
62936292
return true;

clang/unittests/Format/FormatTestObjC.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,15 @@ TEST_F(FormatTestObjC, FormatObjCMethodExpr) {
763763
" backing:NSBackingStoreBuffered\n"
764764
" defer:NO]);\n"
765765
"}");
766+
Style.ColumnLimit = 63;
767+
verifyFormat(
768+
"- (void)test {\n"
769+
" if ([object\n"
770+
" respondsToSelector:@selector(\n"
771+
" selectorName:param1:param2:)])\n"
772+
" return;\n"
773+
"}");
774+
Style.ColumnLimit = PreviousColumnLimit;
766775
verifyFormat("[contentsContainer replaceSubview:[subviews objectAtIndex:0]\n"
767776
" with:contentsNativeView];");
768777

clang/unittests/Format/TokenAnnotatorTest.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,6 +1929,37 @@ TEST_F(TokenAnnotatorTest, UnderstandsObjCMethodExpr) {
19291929
ASSERT_EQ(Tokens.size(), 20u) << Tokens;
19301930
EXPECT_TOKEN(Tokens[9], tok::l_square, TT_ObjCMethodExpr);
19311931
EXPECT_TOKEN(Tokens[15], tok::greater, TT_BinaryOperator);
1932+
1933+
Tokens = annotate("a = @selector(name:);");
1934+
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
1935+
EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_ObjCSelector);
1936+
EXPECT_TOKEN(Tokens[6], tok::colon, TT_ObjCSelector);
1937+
EXPECT_TOKEN(Tokens[7], tok::r_paren, TT_ObjCSelector);
1938+
1939+
Tokens =
1940+
annotate("[object respondsToSelector:@selector(name:param1:param2:)\n"
1941+
" respondsToSelector:@selector(name:param1:param2:)];");
1942+
ASSERT_EQ(Tokens.size(), 29u) << Tokens;
1943+
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_ObjCMethodExpr);
1944+
EXPECT_TOKEN(Tokens[3], tok::colon, TT_ObjCMethodExpr);
1945+
EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_ObjCSelector);
1946+
EXPECT_TOKEN(Tokens[8], tok::colon, TT_ObjCSelector);
1947+
EXPECT_TOKEN(Tokens[10], tok::colon, TT_ObjCSelector);
1948+
EXPECT_TOKEN(Tokens[12], tok::colon, TT_ObjCSelector);
1949+
EXPECT_TOKEN(Tokens[13], tok::r_paren, TT_ObjCSelector);
1950+
EXPECT_TOKEN(Tokens[15], tok::colon, TT_ObjCMethodExpr);
1951+
EXPECT_TOKEN(Tokens[18], tok::l_paren, TT_ObjCSelector);
1952+
EXPECT_TOKEN(Tokens[20], tok::colon, TT_ObjCSelector);
1953+
EXPECT_TOKEN(Tokens[22], tok::colon, TT_ObjCSelector);
1954+
EXPECT_TOKEN(Tokens[24], tok::colon, TT_ObjCSelector);
1955+
EXPECT_TOKEN(Tokens[25], tok::r_paren, TT_ObjCSelector);
1956+
EXPECT_TOKEN(Tokens[26], tok::r_square, TT_ObjCMethodExpr);
1957+
1958+
Tokens = annotate("[a b:c];");
1959+
ASSERT_EQ(Tokens.size(), 8u) << Tokens;
1960+
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_ObjCMethodExpr);
1961+
EXPECT_TOKEN(Tokens[3], tok::colon, TT_ObjCMethodExpr);
1962+
EXPECT_TOKEN(Tokens[5], tok::r_square, TT_ObjCMethodExpr);
19321963
}
19331964

19341965
TEST_F(TokenAnnotatorTest, UnderstandsObjCMethodDecl) {

0 commit comments

Comments
 (0)