Skip to content

Commit f127821

Browse files
committedApr 11, 2017
[Parser][ObjC++] Improve diagnostics and recovery when C++ keywords are used
as identifiers in Objective-C++ This commit improves the 'expected identifier' errors that are presented when a C++ keyword is used as an identifier in Objective-C++ by mentioning that this is a C++ keyword in the diagnostic message. It also improves the error recovery: the parser will now treat the C++ keywords as identifiers to prevent unrelated parsing errors. rdar://20626062 Differential Revision: https://reviews.llvm.org/D26503 llvm-svn: 299950
1 parent 59a2d7b commit f127821

File tree

8 files changed

+150
-53
lines changed

8 files changed

+150
-53
lines changed
 

‎clang/include/clang/Basic/DiagnosticParseKinds.td

+7
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,13 @@ def err_declaration_does_not_declare_param : Error<
436436
"declaration does not declare a parameter">;
437437
def err_no_matching_param : Error<"parameter named %0 is missing">;
438438

439+
/// Objective-C++ parser diagnostics
440+
def err_expected_token_instead_of_objcxx_keyword : Error<
441+
"expected %0; %1 is a keyword in Objective-C++">;
442+
def err_expected_member_name_or_semi_objcxx_keyword : Error<
443+
"expected member name or ';' after declaration specifiers; "
444+
"%0 is a keyword in Objective-C++">;
445+
439446
/// C++ parser diagnostics
440447
def err_invalid_operator_on_type : Error<
441448
"cannot use %select{dot|arrow}0 operator on a type">;

‎clang/include/clang/Basic/IdentifierTable.h

+5-1
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,11 @@ class IdentifierInfo {
280280
bool isCPlusPlusOperatorKeyword() const { return IsCPPOperatorKeyword; }
281281

282282
/// \brief Return true if this token is a keyword in the specified language.
283-
bool isKeyword(const LangOptions &LangOpts);
283+
bool isKeyword(const LangOptions &LangOpts) const;
284+
285+
/// \brief Return true if this token is a C++ keyword in the specified
286+
/// language.
287+
bool isCPlusPlusKeyword(const LangOptions &LangOpts) const;
284288

285289
/// getFETokenInfo/setFETokenInfo - The language front-end is allowed to
286290
/// associate arbitrary metadata with this token.

‎clang/include/clang/Parse/Parser.h

+8
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,14 @@ class Parser : public CodeCompletionHandler {
800800
/// \brief Consume any extra semi-colons until the end of the line.
801801
void ConsumeExtraSemi(ExtraSemiKind Kind, unsigned TST = TST_unspecified);
802802

803+
/// Return false if the next token is an identifier. An 'expected identifier'
804+
/// error is emitted otherwise.
805+
///
806+
/// The parser tries to recover from the error by checking if the next token
807+
/// is a C++ keyword when parsing Objective-C++. Return false if the recovery
808+
/// was successful.
809+
bool expectIdentifier();
810+
803811
public:
804812
//===--------------------------------------------------------------------===//
805813
// Scope manipulation

‎clang/lib/Basic/IdentifierTable.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ static KeywordStatus getTokenKwStatus(const LangOptions &LangOpts,
244244

245245
/// \brief Returns true if the identifier represents a keyword in the
246246
/// specified language.
247-
bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) {
247+
bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) const {
248248
switch (getTokenKwStatus(LangOpts, getTokenID())) {
249249
case KS_Enabled:
250250
case KS_Extension:
@@ -254,6 +254,19 @@ bool IdentifierInfo::isKeyword(const LangOptions &LangOpts) {
254254
}
255255
}
256256

257+
/// \brief Returns true if the identifier represents a C++ keyword in the
258+
/// specified language.
259+
bool IdentifierInfo::isCPlusPlusKeyword(const LangOptions &LangOpts) const {
260+
if (!LangOpts.CPlusPlus || !isKeyword(LangOpts))
261+
return false;
262+
// This is a C++ keyword if this identifier is not a keyword when checked
263+
// using LangOptions without C++ support.
264+
LangOptions LangOptsNoCPP = LangOpts;
265+
LangOptsNoCPP.CPlusPlus = false;
266+
LangOptsNoCPP.CPlusPlus11 = false;
267+
return !isKeyword(LangOptsNoCPP);
268+
}
269+
257270
tok::PPKeywordKind IdentifierInfo::getPPKeywordID() const {
258271
// We use a perfect hash function here involving the length of the keyword,
259272
// the first and third character. For preprocessor ID's there are no

‎clang/lib/Parse/ParseDecl.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -5580,6 +5580,21 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
55805580
if (Tok.is(tok::l_square))
55815581
return ParseMisplacedBracketDeclarator(D);
55825582
if (D.getContext() == Declarator::MemberContext) {
5583+
// Objective-C++: Detect C++ keywords and try to prevent further errors by
5584+
// treating these keyword as valid member names.
5585+
if (getLangOpts().ObjC1 && getLangOpts().CPlusPlus &&
5586+
Tok.getIdentifierInfo() &&
5587+
Tok.getIdentifierInfo()->isCPlusPlusKeyword(getLangOpts())) {
5588+
Diag(getMissingDeclaratorIdLoc(D, Tok.getLocation()),
5589+
diag::err_expected_member_name_or_semi_objcxx_keyword)
5590+
<< Tok.getIdentifierInfo()
5591+
<< (D.getDeclSpec().isEmpty() ? SourceRange()
5592+
: D.getDeclSpec().getSourceRange());
5593+
D.SetIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
5594+
D.SetRangeEnd(Tok.getLocation());
5595+
ConsumeToken();
5596+
goto PastIdentifier;
5597+
}
55835598
Diag(getMissingDeclaratorIdLoc(D, Tok.getLocation()),
55845599
diag::err_expected_member_name_or_semi)
55855600
<< (D.getDeclSpec().isEmpty() ? SourceRange()

‎clang/lib/Parse/ParseObjc.cpp

+24-51
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,7 @@ Parser::ParseObjCAtClassDeclaration(SourceLocation atLoc) {
137137

138138
while (1) {
139139
MaybeSkipAttributes(tok::objc_class);
140-
if (Tok.isNot(tok::identifier)) {
141-
Diag(Tok, diag::err_expected) << tok::identifier;
140+
if (expectIdentifier()) {
142141
SkipUntil(tok::semi);
143142
return Actions.ConvertDeclToDeclGroup(nullptr);
144143
}
@@ -229,11 +228,8 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
229228

230229
MaybeSkipAttributes(tok::objc_interface);
231230

232-
if (Tok.isNot(tok::identifier)) {
233-
Diag(Tok, diag::err_expected)
234-
<< tok::identifier; // missing class or category name.
235-
return nullptr;
236-
}
231+
if (expectIdentifier())
232+
return nullptr; // missing class or category name.
237233

238234
// We have a class or category name - consume it.
239235
IdentifierInfo *nameId = Tok.getIdentifierInfo();
@@ -319,11 +315,8 @@ Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
319315
return nullptr;
320316
}
321317

322-
if (Tok.isNot(tok::identifier)) {
323-
Diag(Tok, diag::err_expected)
324-
<< tok::identifier; // missing super class name.
325-
return nullptr;
326-
}
318+
if (expectIdentifier())
319+
return nullptr; // missing super class name.
327320
superClassId = Tok.getIdentifierInfo();
328321
superClassLoc = ConsumeToken();
329322

@@ -1438,12 +1431,9 @@ Decl *Parser::ParseObjCMethodDecl(SourceLocation mLoc,
14381431
cutOffParsing();
14391432
return nullptr;
14401433
}
1441-
1442-
if (Tok.isNot(tok::identifier)) {
1443-
Diag(Tok, diag::err_expected)
1444-
<< tok::identifier; // missing argument name.
1445-
break;
1446-
}
1434+
1435+
if (expectIdentifier())
1436+
break; // missing argument name.
14471437

14481438
ArgInfo.Name = Tok.getIdentifierInfo();
14491439
ArgInfo.NameLoc = Tok.getLocation();
@@ -1552,8 +1542,7 @@ ParseObjCProtocolReferences(SmallVectorImpl<Decl *> &Protocols,
15521542
return true;
15531543
}
15541544

1555-
if (Tok.isNot(tok::identifier)) {
1556-
Diag(Tok, diag::err_expected) << tok::identifier;
1545+
if (expectIdentifier()) {
15571546
SkipUntil(tok::greater, StopAtSemi);
15581547
return true;
15591548
}
@@ -2035,10 +2024,8 @@ Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc,
20352024

20362025
MaybeSkipAttributes(tok::objc_protocol);
20372026

2038-
if (Tok.isNot(tok::identifier)) {
2039-
Diag(Tok, diag::err_expected) << tok::identifier; // missing protocol name.
2040-
return nullptr;
2041-
}
2027+
if (expectIdentifier())
2028+
return nullptr; // missing protocol name.
20422029
// Save the protocol name, then consume it.
20432030
IdentifierInfo *protocolName = Tok.getIdentifierInfo();
20442031
SourceLocation nameLoc = ConsumeToken();
@@ -2058,8 +2045,7 @@ Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc,
20582045
// Parse the list of forward declarations.
20592046
while (1) {
20602047
ConsumeToken(); // the ','
2061-
if (Tok.isNot(tok::identifier)) {
2062-
Diag(Tok, diag::err_expected) << tok::identifier;
2048+
if (expectIdentifier()) {
20632049
SkipUntil(tok::semi);
20642050
return nullptr;
20652051
}
@@ -2126,11 +2112,8 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
21262112

21272113
MaybeSkipAttributes(tok::objc_implementation);
21282114

2129-
if (Tok.isNot(tok::identifier)) {
2130-
Diag(Tok, diag::err_expected)
2131-
<< tok::identifier; // missing class or category name.
2132-
return nullptr;
2133-
}
2115+
if (expectIdentifier())
2116+
return nullptr; // missing class or category name.
21342117
// We have a class or category name - consume it.
21352118
IdentifierInfo *nameId = Tok.getIdentifierInfo();
21362119
SourceLocation nameLoc = ConsumeToken(); // consume class or category name
@@ -2200,11 +2183,8 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
22002183
IdentifierInfo *superClassId = nullptr;
22012184
if (TryConsumeToken(tok::colon)) {
22022185
// We have a super class
2203-
if (Tok.isNot(tok::identifier)) {
2204-
Diag(Tok, diag::err_expected)
2205-
<< tok::identifier; // missing super class name.
2206-
return nullptr;
2207-
}
2186+
if (expectIdentifier())
2187+
return nullptr; // missing super class name.
22082188
superClassId = Tok.getIdentifierInfo();
22092189
superClassLoc = ConsumeToken(); // Consume super class name
22102190
}
@@ -2304,16 +2284,12 @@ Decl *Parser::ParseObjCAtAliasDeclaration(SourceLocation atLoc) {
23042284
assert(Tok.isObjCAtKeyword(tok::objc_compatibility_alias) &&
23052285
"ParseObjCAtAliasDeclaration(): Expected @compatibility_alias");
23062286
ConsumeToken(); // consume compatibility_alias
2307-
if (Tok.isNot(tok::identifier)) {
2308-
Diag(Tok, diag::err_expected) << tok::identifier;
2287+
if (expectIdentifier())
23092288
return nullptr;
2310-
}
23112289
IdentifierInfo *aliasId = Tok.getIdentifierInfo();
23122290
SourceLocation aliasLoc = ConsumeToken(); // consume alias-name
2313-
if (Tok.isNot(tok::identifier)) {
2314-
Diag(Tok, diag::err_expected) << tok::identifier;
2291+
if (expectIdentifier())
23152292
return nullptr;
2316-
}
23172293
IdentifierInfo *classId = Tok.getIdentifierInfo();
23182294
SourceLocation classLoc = ConsumeToken(); // consume class-name;
23192295
ExpectAndConsume(tok::semi, diag::err_expected_after, "@compatibility_alias");
@@ -2361,11 +2337,9 @@ Decl *Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) {
23612337
cutOffParsing();
23622338
return nullptr;
23632339
}
2364-
2365-
if (Tok.isNot(tok::identifier)) {
2366-
Diag(Tok, diag::err_expected) << tok::identifier;
2340+
2341+
if (expectIdentifier())
23672342
break;
2368-
}
23692343
propertyIvar = Tok.getIdentifierInfo();
23702344
propertyIvarLoc = ConsumeToken(); // consume ivar-name
23712345
}
@@ -2423,9 +2397,8 @@ Decl *Parser::ParseObjCPropertyDynamic(SourceLocation atLoc) {
24232397
cutOffParsing();
24242398
return nullptr;
24252399
}
2426-
2427-
if (Tok.isNot(tok::identifier)) {
2428-
Diag(Tok, diag::err_expected) << tok::identifier;
2400+
2401+
if (expectIdentifier()) {
24292402
SkipUntil(tok::semi);
24302403
return nullptr;
24312404
}
@@ -3561,8 +3534,8 @@ Parser::ParseObjCProtocolExpression(SourceLocation AtLoc) {
35613534
BalancedDelimiterTracker T(*this, tok::l_paren);
35623535
T.consumeOpen();
35633536

3564-
if (Tok.isNot(tok::identifier))
3565-
return ExprError(Diag(Tok, diag::err_expected) << tok::identifier);
3537+
if (expectIdentifier())
3538+
return ExprError();
35663539

35673540
IdentifierInfo *protocolId = Tok.getIdentifierInfo();
35683541
SourceLocation ProtoIdLoc = ConsumeToken();

‎clang/lib/Parse/Parser.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,21 @@ void Parser::ConsumeExtraSemi(ExtraSemiKind Kind, unsigned TST) {
211211
<< FixItHint::CreateRemoval(SourceRange(StartLoc, EndLoc));
212212
}
213213

214+
bool Parser::expectIdentifier() {
215+
if (Tok.is(tok::identifier))
216+
return false;
217+
if (const auto *II = Tok.getIdentifierInfo()) {
218+
if (II->isCPlusPlusKeyword(getLangOpts())) {
219+
Diag(Tok, diag::err_expected_token_instead_of_objcxx_keyword)
220+
<< tok::identifier << Tok.getIdentifierInfo();
221+
// Objective-C++: Recover by treating this keyword as a valid identifier.
222+
return false;
223+
}
224+
}
225+
Diag(Tok, diag::err_expected) << tok::identifier;
226+
return true;
227+
}
228+
214229
//===----------------------------------------------------------------------===//
215230
// Error recovery.
216231
//===----------------------------------------------------------------------===//
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -Wno-objc-root-class -Wno-incomplete-implementation -triple x86_64-apple-macosx10.10.0 -verify %s
2+
3+
// rdar://20626062
4+
5+
struct S {
6+
int throw; // expected-error {{expected member name or ';' after declaration specifiers; 'throw' is a keyword in Objective-C++}}
7+
};
8+
9+
@interface class // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
10+
@end
11+
12+
@interface Bar: class // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
13+
@end
14+
15+
@protocol P // ok
16+
@end
17+
18+
@protocol new // expected-error {{expected identifier; 'new' is a keyword in Objective-C++}}
19+
@end
20+
21+
@protocol P2, delete; // expected-error {{expected identifier; 'delete' is a keyword in Objective-C++}}
22+
23+
@class Foo, try; // expected-error {{expected identifier; 'try' is a keyword in Objective-C++}}
24+
25+
@interface Foo
26+
27+
@property (readwrite, nonatomic) int a, b, throw; // expected-error {{expected member name or ';' after declaration specifiers; 'throw' is a keyword in Objective-C++}}
28+
29+
-foo:(int)class; // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
30+
+foo:(int)constexpr; // expected-error {{expected identifier; 'constexpr' is a keyword in Objective-C++}}
31+
32+
@end
33+
34+
@interface Foo () <P, new> // expected-error {{expected identifier; 'new' is a keyword in Objective-C++}}
35+
@end
36+
37+
@implementation Foo
38+
39+
@synthesize a = _a; // ok
40+
@synthesize b = virtual; // expected-error {{expected identifier; 'virtual' is a keyword in Objective-C++}}
41+
42+
@dynamic throw; // expected-error {{expected identifier; 'throw' is a keyword in Objective-C++}}
43+
44+
-foo:(int)class { // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
45+
}
46+
47+
@end
48+
49+
@implementation class // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
50+
@end
51+
52+
@implementation Bar: class // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
53+
@end
54+
55+
@compatibility_alias C Foo; // ok
56+
@compatibility_alias const_cast Bar; // expected-error {{expected identifier; 'const_cast' is a keyword in Objective-C++}}
57+
@compatibility_alias C2 class; // expected-error {{expected identifier; 'class' is a keyword in Objective-C++}}
58+
59+
void func() {
60+
(void)@protocol(P); // ok
61+
(void)@protocol(delete); // expected-error {{expected identifier; 'delete' is a keyword in Objective-C++}}
62+
}

0 commit comments

Comments
 (0)
Please sign in to comment.