aboutsummaryrefslogtreecommitdiff
path: root/lib/Parse/ParseStmt.cpp
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2011-04-24 05:37:28 +0000
committerDouglas Gregor <dgregor@apple.com>2011-04-24 05:37:28 +0000
commit312eadb832cab4497a069409954500d8192b8f0d (patch)
treef919e5f5b0f8f0e2092ee948b86f92a7171697a2 /lib/Parse/ParseStmt.cpp
parent1b6005285e234bc30698917b2d3abb2f1f98bc77 (diff)
Implement a new identifier-classification scheme where Sema
performs name lookup for an identifier and resolves it to a type/expression/template/etc. in the same step. This scheme is intended to improve both performance (by reducing the number of redundant name lookups for a given identifier token) and error recovery (by giving Sema a chance to correct type names before the parser has decided that the identifier isn't a type name). For example, this allows us to properly typo-correct type names at the beginning of a statement: t.c:6:3: error: use of undeclared identifier 'integer'; did you mean 'Integer'? integer *i = 0; ^~~~~~~ Integer t.c:1:13: note: 'Integer' declared here typedef int Integer; ^ Previously, we wouldn't give a Fix-It because the typo correction occurred after the parser had checked whether "integer" was a type name (via Sema::getTypeName(), which isn't allowed to typo-correct) and therefore decided to parse "integer * i = 0" as an expression. By typo-correcting earlier, we typo-correct to the type name Integer and parse this as a declaration. Moreover, in this context, we can also typo-correct identifiers to keywords, e.g., t.c:7:3: error: use of undeclared identifier 'vid'; did you mean 'void'? vid *p = i; ^~~ void and recover appropriately. Note that this is very much a work-in-progress. The new Sema::ClassifyName is only used for expression-or-declaration disambiguation in C at the statement level. The next steps will be to make this work for the same disambiguation in C++ (where functional-style casts make some trouble), then push it further into the parser to eliminate more redundant name lookups. Fixes <rdar://problem/7963833> for C and starts us down the path of <rdar://problem/8172000>. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130082 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Parse/ParseStmt.cpp')
-rw-r--r--lib/Parse/ParseStmt.cpp201
1 files changed, 167 insertions, 34 deletions
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 74c2472578..8a03864ad6 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -22,6 +22,10 @@
#include "clang/Basic/SourceManager.h"
using namespace clang;
+static bool isColonOrRSquareBracket(const Token &Tok) {
+ return Tok.is(tok::colon) || Tok.is(tok::r_square);
+}
+
//===----------------------------------------------------------------------===//
// C99 6.8: Statements and Blocks.
//===----------------------------------------------------------------------===//
@@ -87,6 +91,7 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement) {
// Cases in this switch statement should fall through if the parser expects
// the token to end in a semicolon (in which case SemiError should be set),
// or they directly 'return;' if not.
+Retry:
tok::TokenKind Kind = Tok.getKind();
SourceLocation AtLoc;
switch (Kind) {
@@ -101,13 +106,134 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement) {
ConsumeCodeCompletionToken();
return ParseStatementOrDeclaration(Stmts, OnlyStatement);
- case tok::identifier:
- if (NextToken().is(tok::colon)) { // C99 6.8.1: labeled-statement
+ case tok::identifier: {
+ Token Next = NextToken();
+ if (Next.is(tok::colon)) { // C99 6.8.1: labeled-statement
// identifier ':' statement
return ParseLabeledStatement(attrs);
}
- // PASS THROUGH.
-
+
+ if (!getLang().CPlusPlus) {
+ // FIXME: Temporarily enable this code only for C.
+ CXXScopeSpec SS;
+ IdentifierInfo *Name = Tok.getIdentifierInfo();
+ SourceLocation NameLoc = Tok.getLocation();
+ Sema::NameClassification Classification
+ = Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next);
+ switch (Classification.getKind()) {
+ case Sema::NC_Keyword:
+ // The identifier was corrected to a keyword. Update the token
+ // to this keyword, and try again.
+ if (Name->getTokenID() != tok::identifier) {
+ Tok.setIdentifierInfo(Name);
+ Tok.setKind(Name->getTokenID());
+ goto Retry;
+ }
+
+ // Fall through via the normal error path.
+ // FIXME: This seems like it could only happen for context-sensitive
+ // keywords.
+
+ case Sema::NC_Error:
+ // Handle errors here by skipping up to the next semicolon or '}', and
+ // eat the semicolon if that's what stopped us.
+ SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return StmtError();
+
+ case Sema::NC_Unknown:
+ // Either we don't know anything about this identifier, or we know that
+ // we're in a syntactic context we haven't handled yet.
+ break;
+
+ case Sema::NC_Type:
+ // We have a type.
+ // We have a type. In C, this means that we have a declaration.
+ if (!getLang().CPlusPlus) {
+ ParsedType Type = Classification.getType();
+ const char *PrevSpec = 0;
+ unsigned DiagID;
+ ConsumeToken(); // the identifier
+ ParsingDeclSpec DS(*this);
+ DS.takeAttributesFrom(attrs);
+ DS.SetTypeSpecType(DeclSpec::TST_typename, NameLoc, PrevSpec, DiagID,
+ Type);
+ DS.SetRangeStart(NameLoc);
+ DS.SetRangeEnd(NameLoc);
+
+ // In Objective-C, check whether this is the start of a class message
+ // send that is missing an opening square bracket ('[').
+ if (getLang().ObjC1 && Tok.is(tok::identifier) &&
+ Type.get()->isObjCObjectOrInterfaceType() &&
+ isColonOrRSquareBracket(NextToken())) {
+ // Fake up a Declarator to use with ActOnTypeName.
+ Declarator DeclaratorInfo(DS, Declarator::TypeNameContext);
+ TypeResult Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+ if (Ty.isInvalid()) {
+ SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return StmtError();
+ }
+
+ ExprResult MsgExpr = ParseObjCMessageExpressionBody(SourceLocation(),
+ SourceLocation(),
+ Ty.get(), 0);
+ return ParseExprStatement(attrs, MsgExpr);
+ }
+
+ // Objective-C supports syntax of the form 'id<proto1,proto2>' where
+ // 'id' is a specific typedef and 'itf<proto1,proto2>' where 'itf' is
+ // an Objective-C interface.
+ if (Tok.is(tok::less) && getLang().ObjC1)
+ ParseObjCProtocolQualifiers(DS);
+
+ SourceLocation DeclStart = NameLoc, DeclEnd;
+ DeclGroupPtrTy Decl = ParseSimpleDeclaration(DS, Stmts,
+ Declarator::BlockContext,
+ DeclEnd, true);
+ return Actions.ActOnDeclStmt(Decl, DeclStart, DeclEnd);
+ }
+
+ // In C++, we might also have a functional-style cast.
+ // FIXME: Implement this!
+ break;
+
+ case Sema::NC_Expression:
+ ConsumeToken(); // the identifier
+ return ParseExprStatement(attrs, Classification.getExpression());
+
+ case Sema::NC_TypeTemplate:
+ case Sema::NC_FunctionTemplate: {
+ ConsumeToken(); // the identifier
+ UnqualifiedId Id;
+ Id.setIdentifier(Name, NameLoc);
+ if (AnnotateTemplateIdToken(
+ TemplateTy::make(Classification.getTemplateName()),
+ Classification.getTemplateNameKind(),
+ SS, Id)) {
+ // Handle errors here by skipping up to the next semicolon or '}', and
+ // eat the semicolon if that's what stopped us.
+ SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return StmtError();
+ }
+
+ // We've annotated a template-id, so try again now.
+ goto Retry;
+ }
+
+ case Sema::NC_NestedNameSpecifier:
+ // FIXME: Implement this!
+ break;
+ }
+ }
+
+ // Fall through
+ }
+
default: {
if ((getLang().CPlusPlus || !OnlyStatement) && isDeclarationStatement()) {
SourceLocation DeclStart = Tok.getLocation(), DeclEnd;
@@ -121,36 +247,7 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement) {
return StmtError();
}
- // If a case keyword is missing, this is where it should be inserted.
- Token OldToken = Tok;
-
- // FIXME: Use the attributes
- // expression[opt] ';'
- ExprResult Expr(ParseExpression());
- if (Expr.isInvalid()) {
- // If the expression is invalid, skip ahead to the next semicolon or '}'.
- // Not doing this opens us up to the possibility of infinite loops if
- // ParseExpression does not consume any tokens.
- SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true);
- if (Tok.is(tok::semi))
- ConsumeToken();
- return StmtError();
- }
-
- if (Tok.is(tok::colon) && getCurScope()->isSwitchScope() &&
- Actions.CheckCaseExpression(Expr.get())) {
- // If a constant expression is followed by a colon inside a switch block,
- // suggest a missing case keywork.
- Diag(OldToken, diag::err_expected_case_before_expression)
- << FixItHint::CreateInsertion(OldToken.getLocation(), "case ");
-
- // Recover parsing as a case statement.
- return ParseCaseStatement(attrs, /*MissingCase=*/true, Expr);
- }
-
- // Otherwise, eat the semicolon.
- ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
- return Actions.ActOnExprStmt(Actions.MakeFullExpr(Expr.get()));
+ return ParseExprStatement(attrs, ExprResult());
}
case tok::kw_case: // C99 6.8.1: labeled-statement
@@ -225,6 +322,42 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts, bool OnlyStatement) {
return move(Res);
}
+/// \brief Parse an expression statement.
+StmtResult Parser::ParseExprStatement(ParsedAttributes &Attrs,
+ ExprResult Primary) {
+ // If a case keyword is missing, this is where it should be inserted.
+ Token OldToken = Tok;
+
+ // FIXME: Use the attributes
+ // expression[opt] ';'
+ ExprResult Expr(ParseExpression(Primary));
+ if (Expr.isInvalid()) {
+ // If the expression is invalid, skip ahead to the next semicolon or '}'.
+ // Not doing this opens us up to the possibility of infinite loops if
+ // ParseExpression does not consume any tokens.
+ SkipUntil(tok::r_brace, /*StopAtSemi=*/true, /*DontConsume=*/true);
+ if (Tok.is(tok::semi))
+ ConsumeToken();
+ return StmtError();
+ }
+
+ if (Tok.is(tok::colon) && getCurScope()->isSwitchScope() &&
+ Actions.CheckCaseExpression(Expr.get())) {
+ // If a constant expression is followed by a colon inside a switch block,
+ // suggest a missing case keyword.
+ Diag(OldToken, diag::err_expected_case_before_expression)
+ << FixItHint::CreateInsertion(OldToken.getLocation(), "case ");
+
+ // Recover parsing as a case statement.
+ return ParseCaseStatement(Attrs, /*MissingCase=*/true, Expr);
+ }
+
+ // Otherwise, eat the semicolon.
+ ExpectAndConsumeSemi(diag::err_expected_semi_after_expr);
+ return Actions.ActOnExprStmt(Actions.MakeFullExpr(Expr.get()));
+
+}
+
/// ParseLabeledStatement - We have an identifier and a ':' after it.
///
/// labeled-statement: