From: Douglas Gregor Date: Sun, 24 Apr 2011 05:37:28 +0000 (+0000) Subject: Implement a new identifier-classification scheme where Sema X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=312eadb832cab4497a069409954500d8192b8f0d;p=clang 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 for C and starts us down the path of . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130082 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 1ab2fc6116..1c0d63b026 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1048,24 +1048,24 @@ private: //===--------------------------------------------------------------------===// // C99 6.5: Expressions. - ExprResult ParseExpression(); + ExprResult ParseExpression(ExprResult Primary = ExprResult()); ExprResult ParseConstantExpression(); // Expr that doesn't include commas. - ExprResult ParseAssignmentExpression(); + ExprResult ParseAssignmentExpression(ExprResult Primary = ExprResult()); ExprResult ParseExpressionWithLeadingAt(SourceLocation AtLoc); ExprResult ParseExpressionWithLeadingExtension(SourceLocation ExtLoc); ExprResult ParseRHSOfBinaryExpression(ExprResult LHS, - prec::Level MinPrec); + prec::Level MinPrec); ExprResult ParseCastExpression(bool isUnaryExpression, - bool isAddressOfOperand, - bool &NotCastExpr, - ParsedType TypeOfCast); + bool isAddressOfOperand, + bool &NotCastExpr, + ParsedType TypeOfCast); ExprResult ParseCastExpression(bool isUnaryExpression, - bool isAddressOfOperand = false, - ParsedType TypeOfCast = ParsedType()); + bool isAddressOfOperand = false, + ParsedType TypeOfCast = ParsedType()); /// Returns true if the next token would start a postfix-expression /// suffix. @@ -1257,13 +1257,14 @@ private: } StmtResult ParseStatementOrDeclaration(StmtVector& Stmts, bool OnlyStatement = false); + StmtResult ParseExprStatement(ParsedAttributes &Attrs, ExprResult Primary); StmtResult ParseLabeledStatement(ParsedAttributes &Attr); StmtResult ParseCaseStatement(ParsedAttributes &Attr, bool MissingCase = false, ExprResult Expr = ExprResult()); StmtResult ParseDefaultStatement(ParsedAttributes &Attr); StmtResult ParseCompoundStatement(ParsedAttributes &Attr, - bool isStmtExpr = false); + bool isStmtExpr = false); StmtResult ParseCompoundStatementBody(bool isStmtExpr = false); bool ParseParenExprOrCondition(ExprResult &ExprResult, Decl *&DeclResult, @@ -1330,6 +1331,12 @@ private: ParsedAttributes &attrs, bool RequireSemi, ForRangeInit *FRI = 0); + DeclGroupPtrTy ParseSimpleDeclaration(ParsingDeclSpec &DS, + StmtVector &Stmts, + unsigned Context, + SourceLocation &DeclEnd, + bool RequireSemi, + ForRangeInit *FRI = 0); DeclGroupPtrTy ParseDeclGroup(ParsingDeclSpec &DS, unsigned Context, bool AllowFunctionDefinitions, SourceLocation *DeclEnd = 0, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 940d213e57..1d82eb72e9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -818,6 +818,107 @@ public: CXXScopeSpec *SS, ParsedType &SuggestedType); + /// \brief Describes the result of the name lookup and resolution performed + /// by \c ClassifyName(). + enum NameClassificationKind { + NC_Unknown, + NC_Error, + NC_Keyword, + NC_Type, + NC_Expression, + NC_NestedNameSpecifier, + NC_TypeTemplate, + NC_FunctionTemplate + }; + + class NameClassification { + NameClassificationKind Kind; + ExprResult Expr; + TemplateName Template; + ParsedType Type; + const IdentifierInfo *Keyword; + + explicit NameClassification(NameClassificationKind Kind) : Kind(Kind) {} + + public: + NameClassification(ExprResult Expr) : Kind(NC_Expression), Expr(Expr) {} + + NameClassification(ParsedType Type) : Kind(NC_Type), Type(Type) {} + + NameClassification(const IdentifierInfo *Keyword) + : Kind(NC_Keyword), Keyword(Keyword) { } + + static NameClassification Error() { + return NameClassification(NC_Error); + } + + static NameClassification Unknown() { + return NameClassification(NC_Unknown); + } + + static NameClassification NestedNameSpecifier() { + return NameClassification(NC_NestedNameSpecifier); + } + + static NameClassification TypeTemplate(TemplateName Name) { + NameClassification Result(NC_TypeTemplate); + Result.Template = Name; + return Result; + } + + static NameClassification FunctionTemplate(TemplateName Name) { + NameClassification Result(NC_FunctionTemplate); + Result.Template = Name; + return Result; + } + + NameClassificationKind getKind() const { return Kind; } + + ParsedType getType() const { + assert(Kind == NC_Type); + return Type; + } + + ExprResult getExpression() const { + assert(Kind == NC_Expression); + return Expr; + } + + TemplateName getTemplateName() const { + assert(Kind == NC_TypeTemplate || Kind == NC_FunctionTemplate); + return Template; + } + + TemplateNameKind getTemplateNameKind() const { + assert(Kind == NC_TypeTemplate || Kind == NC_FunctionTemplate); + return Kind == NC_TypeTemplate? TNK_Type_template : TNK_Function_template; + } +}; + + /// \brief Perform name lookup on the given name, classifying it based on + /// the results of name lookup and the following token. + /// + /// This routine is used by the parser to resolve identifiers and help direct + /// parsing. When the identifier cannot be found, this routine will attempt + /// to correct the typo and classify based on the resulting name. + /// + /// \param S The scope in which we're performing name lookup. + /// + /// \param SS The nested-name-specifier that precedes the name. + /// + /// \param Name The identifier. If typo correction finds an alternative name, + /// this pointer parameter will be updated accordingly. + /// + /// \param NameLoc The location of the identifier. + /// + /// \param NextToken The token following the identifier. Used to help + /// disambiguate the name. + NameClassification ClassifyName(Scope *S, + CXXScopeSpec &SS, + IdentifierInfo *&Name, + SourceLocation NameLoc, + const Token &NextToken); + Decl *ActOnDeclarator(Scope *S, Declarator &D); Decl *HandleDeclarator(Scope *S, Declarator &D, @@ -1961,6 +2062,10 @@ public: // Primary Expressions. SourceRange getExprRange(Expr *E) const; + ObjCIvarDecl *SynthesizeProvisionalIvar(LookupResult &Lookup, + IdentifierInfo *II, + SourceLocation NameLoc); + ExprResult ActOnIdExpression(Scope *S, CXXScopeSpec &SS, UnqualifiedId &Name, bool HasTrailingLParen, bool IsAddressOfOperand); @@ -3174,18 +3279,21 @@ public: //===--------------------------------------------------------------------===// // C++ Templates [C++ 14] // + void FilterAcceptableTemplateNames(LookupResult &R); + bool hasAnyAcceptableTemplateNames(LookupResult &R); + void LookupTemplateName(LookupResult &R, Scope *S, CXXScopeSpec &SS, QualType ObjectType, bool EnteringContext, bool &MemberOfUnknownSpecialization); TemplateNameKind isTemplateName(Scope *S, - CXXScopeSpec &SS, - bool hasTemplateKeyword, - UnqualifiedId &Name, - ParsedType ObjectType, - bool EnteringContext, - TemplateTy &Template, - bool &MemberOfUnknownSpecialization); + CXXScopeSpec &SS, + bool hasTemplateKeyword, + UnqualifiedId &Name, + ParsedType ObjectType, + bool EnteringContext, + TemplateTy &Template, + bool &MemberOfUnknownSpecialization); bool DiagnoseUnknownTemplateName(const IdentifierInfo &II, SourceLocation IILoc, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 0b06d6a4fd..c09a93a84f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -724,23 +724,46 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(StmtVector &Stmts, // Parse the common declaration-specifiers piece. ParsingDeclSpec DS(*this); DS.takeAttributesFrom(attrs); + return ParseSimpleDeclaration(DS, Stmts, Context, DeclEnd, RequireSemi, FRI); +} + +/// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl] +/// declaration-specifiers init-declarator-list[opt] ';' +///[C90/C++]init-declarator-list ';' [TODO] +/// [OMP] threadprivate-directive [TODO] +/// +/// for-range-declaration: [C++0x 6.5p1: stmt.ranged] +/// attribute-specifier-seq[opt] type-specifier-seq declarator +/// +/// If RequireSemi is false, this does not check for a ';' at the end of the +/// declaration. If it is true, it checks for and eats it. +/// +/// If FRI is non-null, we might be parsing a for-range-declaration instead +/// of a simple-declaration. If we find that we are, we also parse the +/// for-range-initializer, and place it here. +Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(ParsingDeclSpec &DS, + StmtVector &Stmts, + unsigned Context, + SourceLocation &DeclEnd, + bool RequireSemi, + ForRangeInit *FRI) { ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS_none, getDeclSpecContextFromDeclaratorContext(Context)); StmtResult R = Actions.ActOnVlaStmt(DS); if (R.isUsable()) Stmts.push_back(R.release()); - + // C99 6.7.2.3p6: Handle "struct-or-union identifier;", "enum { X };" // declaration-specifiers init-declarator-list[opt] ';' if (Tok.is(tok::semi)) { if (RequireSemi) ConsumeToken(); Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec(getCurScope(), AS_none, - DS); + DS); DS.complete(TheDecl); return Actions.ConvertDeclToDeclGroup(TheDecl); } - - return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI); + + return ParseDeclGroup(DS, Context, /*FunctionDefs=*/ false, &DeclEnd, FRI); } /// ParseDeclGroup - Having concluded that this is either a function @@ -1254,9 +1277,12 @@ Parser::getDeclSpecContextFromDeclaratorContext(unsigned Context) { void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, const ParsedTemplateInfo &TemplateInfo, AccessSpecifier AS, - DeclSpecContext DSContext) { - DS.SetRangeStart(Tok.getLocation()); - DS.SetRangeEnd(Tok.getLocation()); + DeclSpecContext DSContext) { + if (DS.getSourceRange().isInvalid()) { + DS.SetRangeStart(Tok.getLocation()); + DS.SetRangeEnd(Tok.getLocation()); + } + while (1) { bool isInvalid = false; const char *PrevSpec = 0; diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 0ad153fc57..cd8f9c53f7 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -175,8 +175,10 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind, /// assignment-expression ...[opt] /// expression ',' assignment-expression ...[opt] /// -ExprResult Parser::ParseExpression() { - ExprResult LHS(ParseAssignmentExpression()); +/// \param Primary if non-empty, an already-parsed expression that will be used +/// as the first primary expression. +ExprResult Parser::ParseExpression(ExprResult Primary) { + ExprResult LHS(ParseAssignmentExpression(Primary)); return ParseRHSOfBinaryExpression(move(LHS), prec::Comma); } @@ -213,16 +215,26 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) { /// ParseAssignmentExpression - Parse an expr that doesn't include commas. /// -ExprResult Parser::ParseAssignmentExpression() { +/// \param Primary if non-empty, an already-parsed expression that will be used +/// as the first primary expression. +ExprResult Parser::ParseAssignmentExpression(ExprResult Primary) { if (Tok.is(tok::code_completion)) { - Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression); + if (Primary.isUsable()) + Actions.CodeCompletePostfixExpression(getCurScope(), Primary); + else + Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression); ConsumeCodeCompletionToken(); } - if (Tok.is(tok::kw_throw)) + if (!Primary.isUsable() && Tok.is(tok::kw_throw)) return ParseThrowExpression(); - ExprResult LHS(ParseCastExpression(false)); + ExprResult LHS; + if (Primary.get() || Primary.isInvalid()) + LHS = ParsePostfixExpressionSuffix(Primary); + else + LHS = ParseCastExpression(false, false, ParsedType()); + return ParseRHSOfBinaryExpression(move(LHS), prec::Assignment); } @@ -415,8 +427,8 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { /// due to member pointers. /// ExprResult Parser::ParseCastExpression(bool isUnaryExpression, - bool isAddressOfOperand, - ParsedType TypeOfCast) { + bool isAddressOfOperand, + ParsedType TypeOfCast) { bool NotCastExpr; ExprResult Res = ParseCastExpression(isUnaryExpression, isAddressOfOperand, 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' where + // 'id' is a specific typedef and 'itf' 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: diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index b1d9bc2c8e..05a077b38f 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -371,6 +371,312 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, return true; } +/// \brief Determine whether the given result set contains either a type name +/// or +static bool isResultTypeOrTemplate(LookupResult &R, const Token &NextToken) { + bool CheckTemplate = R.getSema().getLangOptions().CPlusPlus && + NextToken.is(tok::less); + + for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) { + if (isa(*I) || isa(*I)) + return true; + + if (CheckTemplate && isa(*I)) + return true; + } + + return false; +} + +Sema::NameClassification Sema::ClassifyName(Scope *S, + CXXScopeSpec &SS, + IdentifierInfo *&Name, + SourceLocation NameLoc, + const Token &NextToken) { + DeclarationNameInfo NameInfo(Name, NameLoc); + ObjCMethodDecl *CurMethod = getCurMethodDecl(); + + if (NextToken.is(tok::coloncolon)) { + BuildCXXNestedNameSpecifier(S, *Name, NameLoc, NextToken.getLocation(), + QualType(), false, SS, 0, false); + + } + + LookupResult Result(*this, Name, NameLoc, LookupOrdinaryName); + LookupParsedName(Result, S, &SS, !CurMethod); + + // Perform lookup for Objective-C instance variables (including automatically + // synthesized instance variables), if we're in an Objective-C method. + // FIXME: This lookup really, really needs to be folded in to the normal + // unqualified lookup mechanism. + if (!SS.isSet() && CurMethod && !isResultTypeOrTemplate(Result, NextToken)) { + ExprResult E = LookupInObjCMethod(Result, S, Name, true); + + if (E.isInvalid()) + return NameClassification::Error(); + + if (E.get()) + return E; + + // Synthesize ivars lazily. + if (getLangOptions().ObjCDefaultSynthProperties && + getLangOptions().ObjCNonFragileABI2) { + if (SynthesizeProvisionalIvar(Result, Name, NameLoc)) { + if (const ObjCPropertyDecl *Property = + canSynthesizeProvisionalIvar(Name)) { + Diag(NameLoc, diag::warn_synthesized_ivar_access) << Name; + Diag(Property->getLocation(), diag::note_property_declare); + } + + // FIXME: This is strange. Shouldn't we just take the ivar returned + // from SynthesizeProvisionalIvar and continue with that? + E = LookupInObjCMethod(Result, S, Name, true); + + if (E.isInvalid()) + return NameClassification::Error(); + + if (E.get()) + return E; + } + } + } + + bool SecondTry = false; + bool IsFilteredTemplateName = false; + +Corrected: + switch (Result.getResultKind()) { + case LookupResult::NotFound: + // If an unqualified-id is followed by a '(', then we have a function + // call. + if (!SS.isSet() && NextToken.is(tok::l_paren)) { + // In C++, this is an ADL-only call. + // FIXME: Reference? + if (getLangOptions().CPlusPlus) + return BuildDeclarationNameExpr(SS, Result, /*ADL=*/true); + + // C90 6.3.2.2: + // If the expression that precedes the parenthesized argument list in a + // function call consists solely of an identifier, and if no + // declaration is visible for this identifier, the identifier is + // implicitly declared exactly as if, in the innermost block containing + // the function call, the declaration + // + // extern int identifier (); + // + // appeared. + // + // We also allow this in C99 as an extension. + if (NamedDecl *D = ImplicitlyDefineFunction(NameLoc, *Name, S)) { + Result.addDecl(D); + Result.resolveKind(); + return BuildDeclarationNameExpr(SS, Result, /*ADL=*/false); + } + } + + // In C, we first see whether there is a tag type by the same name, in + // which case it's likely that the user just forget to write "enum", + // "struct", or "union". + if (!getLangOptions().CPlusPlus && !SecondTry) { + Result.clear(LookupTagName); + LookupParsedName(Result, S, &SS); + if (TagDecl *Tag = Result.getAsSingle()) { + const char *TagName = 0; + const char *FixItTagName = 0; + switch (Tag->getTagKind()) { + case TTK_Class: + TagName = "class"; + FixItTagName = "class "; + break; + + case TTK_Enum: + TagName = "enum"; + FixItTagName = "enum "; + break; + + case TTK_Struct: + TagName = "struct"; + FixItTagName = "struct "; + break; + + case TTK_Union: + TagName = "union"; + FixItTagName = "union "; + break; + } + + Diag(NameLoc, diag::err_use_of_tag_name_without_tag) + << Name << TagName << getLangOptions().CPlusPlus + << FixItHint::CreateInsertion(NameLoc, FixItTagName); + break; + } + + Result.clear(LookupOrdinaryName); + } + + // Perform typo correction to determine if there is another name that is + // close to this name. + if (!SecondTry) { + if (DeclarationName Corrected = CorrectTypo(Result, S, &SS)) { + if (SS.isEmpty()) + Diag(NameLoc, diag::err_undeclared_var_use_suggest) + << Name << Corrected + << FixItHint::CreateReplacement(NameLoc, Corrected.getAsString()); + else + Diag(NameLoc, diag::err_no_member_suggest) + << Name << computeDeclContext(SS, false) << Corrected + << SS.getRange() + << FixItHint::CreateReplacement(NameLoc, Corrected.getAsString()); + + // Update the name, so that the caller has the new name. + Name = Corrected.getAsIdentifierInfo(); + + // Typo correction corrected to a keyword. + if (Result.empty()) + return Corrected.getAsIdentifierInfo(); + + NamedDecl *FirstDecl = *Result.begin(); + Diag(FirstDecl->getLocation(), diag::note_previous_decl) + << FirstDecl->getDeclName(); + + // If we found an Objective-C instance variable, let + // LookupInObjCMethod build the appropriate expression to + // reference the ivar. + // FIXME: This is a gross hack. + if (ObjCIvarDecl *Ivar = Result.getAsSingle()) { + Result.clear(); + ExprResult E(LookupInObjCMethod(Result, S, Ivar->getIdentifier())); + return move(E); + } + + goto Corrected; + } + } + + // We failed to correct; just fall through and let the parser deal with it. + Result.suppressDiagnostics(); + return NameClassification::Unknown(); + + case LookupResult::NotFoundInCurrentInstantiation: + // We performed name lookup into the current instantiation, and there were + // dependent bases, so we treat this result the same way as any other + // dependent nested-name-specifier. + + // C++ [temp.res]p2: + // A name used in a template declaration or definition and that is + // dependent on a template-parameter is assumed not to name a type + // unless the applicable name lookup finds a type name or the name is + // qualified by the keyword typename. + // + // FIXME: If the next token is '<', we might want to ask the parser to + // perform some heroics to see if we actually have a + // template-argument-list, which would indicate a missing 'template' + // keyword here. + return BuildDependentDeclRefExpr(SS, NameInfo, /*TemplateArgs=*/0); + + case LookupResult::Found: + case LookupResult::FoundOverloaded: + case LookupResult::FoundUnresolvedValue: + break; + + case LookupResult::Ambiguous: + if (getLangOptions().CPlusPlus && NextToken.is(tok::less)) { + // C++ [temp.local]p3: + // A lookup that finds an injected-class-name (10.2) can result in an + // ambiguity in certain cases (for example, if it is found in more than + // one base class). If all of the injected-class-names that are found + // refer to specializations of the same class template, and if the name + // is followed by a template-argument-list, the reference refers to the + // class template itself and not a specialization thereof, and is not + // ambiguous. + // + // This filtering can make an ambiguous result into an unambiguous one, + // so try again after filtering out template names. + FilterAcceptableTemplateNames(Result); + if (!Result.isAmbiguous()) { + IsFilteredTemplateName = true; + break; + } + } + + // Diagnose the ambiguity and return an error. + return NameClassification::Error(); + } + + if (getLangOptions().CPlusPlus && NextToken.is(tok::less) && + (IsFilteredTemplateName || hasAnyAcceptableTemplateNames(Result))) { + // C++ [temp.names]p3: + // After name lookup (3.4) finds that a name is a template-name or that + // an operator-function-id or a literal- operator-id refers to a set of + // overloaded functions any member of which is a function template if + // this is followed by a <, the < is always taken as the delimiter of a + // template-argument-list and never as the less-than operator. + if (!IsFilteredTemplateName) + FilterAcceptableTemplateNames(Result); + + bool IsFunctionTemplate; + TemplateName Template; + if (Result.end() - Result.begin() > 1) { + IsFunctionTemplate = true; + Template = Context.getOverloadedTemplateName(Result.begin(), + Result.end()); + } else { + TemplateDecl *TD = cast(Result.getFoundDecl()); + IsFunctionTemplate = isa(TD); + + if (SS.isSet() && !SS.isInvalid()) + Template = Context.getQualifiedTemplateName(SS.getScopeRep(), + /*TemplateKeyword=*/false, + TD); + else + Template = TemplateName(TD); + } + + if (IsFunctionTemplate) { + // Function templates always go through overload resolution, at which + // point we'll perform the various checks (e.g., accessibility) we need + // to based on which function we selected. + Result.suppressDiagnostics(); + + return NameClassification::FunctionTemplate(Template); + } + + return NameClassification::TypeTemplate(Template); + } + + NamedDecl *FirstDecl = *Result.begin(); + if (TypeDecl *Type = dyn_cast(FirstDecl)) { + DiagnoseUseOfDecl(Type, NameLoc); + QualType T = Context.getTypeDeclType(Type); + return ParsedType::make(T); + } + + ObjCInterfaceDecl *Class = dyn_cast(FirstDecl); + if (!Class) { + // FIXME: It's unfortunate that we don't have a Type node for handling this. + if (ObjCCompatibleAliasDecl *Alias + = dyn_cast(FirstDecl)) + Class = Alias->getClassInterface(); + } + + if (Class) { + DiagnoseUseOfDecl(Class, NameLoc); + + if (NextToken.is(tok::period)) { + // Interface. is parsed as a property reference expression. + // Just return "unknown" as a fall-through for now. + Result.suppressDiagnostics(); + return NameClassification::Unknown(); + } + + QualType T = Context.getObjCInterfaceType(Class); + return ParsedType::make(T); + } + + bool ADL = UseArgumentDependentLookup(SS, Result, NextToken.is(tok::l_paren)); + return BuildDeclarationNameExpr(SS, Result, ADL); +} + // Determines the context to return to after temporarily entering a // context. This depends in an unnecessarily complicated way on the // exact ordering of callbacks from the parser. diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index da7619d0f4..1a8c4c9e04 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1708,11 +1708,10 @@ bool Sema::canSynthesizeProvisionalIvar(ObjCPropertyDecl *Property) { return true; } -static ObjCIvarDecl *SynthesizeProvisionalIvar(Sema &SemaRef, - LookupResult &Lookup, - IdentifierInfo *II, - SourceLocation NameLoc) { - ObjCMethodDecl *CurMeth = SemaRef.getCurMethodDecl(); +ObjCIvarDecl *Sema::SynthesizeProvisionalIvar(LookupResult &Lookup, + IdentifierInfo *II, + SourceLocation NameLoc) { + ObjCMethodDecl *CurMeth = getCurMethodDecl(); bool LookForIvars; if (Lookup.empty()) LookForIvars = true; @@ -1732,7 +1731,7 @@ static ObjCIvarDecl *SynthesizeProvisionalIvar(Sema &SemaRef, if (!ClassImpDecl) return 0; bool DynamicImplSeen = false; - ObjCPropertyDecl *property = SemaRef.LookupPropertyDecl(IDecl, II); + ObjCPropertyDecl *property = LookupPropertyDecl(IDecl, II); if (!property) return 0; if (ObjCPropertyImplDecl *PIDecl = ClassImpDecl->FindPropertyImplDecl(II)) { @@ -1744,8 +1743,8 @@ static ObjCIvarDecl *SynthesizeProvisionalIvar(Sema &SemaRef, return 0; } if (!DynamicImplSeen) { - QualType PropType = SemaRef.Context.getCanonicalType(property->getType()); - ObjCIvarDecl *Ivar = ObjCIvarDecl::Create(SemaRef.Context, ClassImpDecl, + QualType PropType = Context.getCanonicalType(property->getType()); + ObjCIvarDecl *Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl, NameLoc, NameLoc, II, PropType, /*Dinfo=*/0, ObjCIvarDecl::Private, @@ -1848,7 +1847,7 @@ ExprResult Sema::ActOnIdExpression(Scope *S, // Synthesize ivars lazily. if (getLangOptions().ObjCDefaultSynthProperties && getLangOptions().ObjCNonFragileABI2) { - if (SynthesizeProvisionalIvar(*this, R, II, NameLoc)) { + if (SynthesizeProvisionalIvar(R, II, NameLoc)) { if (const ObjCPropertyDecl *Property = canSynthesizeProvisionalIvar(II)) { Diag(NameLoc, diag::warn_synthesized_ivar_access) << II; diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 08eb654236..bc29a4cbdc 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -76,13 +76,13 @@ static NamedDecl *isAcceptableTemplateName(ASTContext &Context, return 0; } -static void FilterAcceptableTemplateNames(ASTContext &C, LookupResult &R) { +void Sema::FilterAcceptableTemplateNames(LookupResult &R) { // The set of class templates we've already seen. llvm::SmallPtrSet ClassTemplates; LookupResult::Filter filter = R.makeFilter(); while (filter.hasNext()) { NamedDecl *Orig = filter.next(); - NamedDecl *Repl = isAcceptableTemplateName(C, Orig); + NamedDecl *Repl = isAcceptableTemplateName(Context, Orig); if (!Repl) filter.erase(); else if (Repl != Orig) { @@ -114,6 +114,14 @@ static void FilterAcceptableTemplateNames(ASTContext &C, LookupResult &R) { filter.done(); } +bool Sema::hasAnyAcceptableTemplateNames(LookupResult &R) { + for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; ++I) + if (isAcceptableTemplateName(Context, *I)) + return true; + + return true; +} + TemplateNameKind Sema::isTemplateName(Scope *S, CXXScopeSpec &SS, bool hasTemplateKeyword, @@ -289,7 +297,7 @@ void Sema::LookupTemplateName(LookupResult &Found, DeclarationName Name = Found.getLookupName(); if (DeclarationName Corrected = CorrectTypo(Found, S, &SS, LookupCtx, false, CTC_CXXCasts)) { - FilterAcceptableTemplateNames(Context, Found); + FilterAcceptableTemplateNames(Found); if (!Found.empty()) { if (LookupCtx) Diag(Found.getNameLoc(), diag::err_no_member_template_suggest) @@ -311,7 +319,7 @@ void Sema::LookupTemplateName(LookupResult &Found, } } - FilterAcceptableTemplateNames(Context, Found); + FilterAcceptableTemplateNames(Found); if (Found.empty()) { if (isDependent) MemberOfUnknownSpecialization = true; @@ -327,7 +335,7 @@ void Sema::LookupTemplateName(LookupResult &Found, LookupResult FoundOuter(*this, Found.getLookupName(), Found.getNameLoc(), LookupOrdinaryName); LookupName(FoundOuter, S); - FilterAcceptableTemplateNames(Context, FoundOuter); + FilterAcceptableTemplateNames(FoundOuter); if (FoundOuter.empty()) { // - if the name is not found, the name found in the class of the diff --git a/test/FixIt/fixit-unrecoverable.c b/test/FixIt/fixit-unrecoverable.c index 8052beb559..fb6c4e2104 100644 --- a/test/FixIt/fixit-unrecoverable.c +++ b/test/FixIt/fixit-unrecoverable.c @@ -5,6 +5,4 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -// FIXME: Sadly, the following doesn't work within a function. - unsinged x = 17; // expected-error{{unknown type name 'unsinged'; did you mean 'unsigned'?}} diff --git a/test/FixIt/typo.c b/test/FixIt/typo.c index 01ff3a09cf..8a277c75f0 100644 --- a/test/FixIt/typo.c +++ b/test/FixIt/typo.c @@ -2,6 +2,7 @@ // RUN: cp %s %t // RUN: %clang_cc1 -fsyntax-only -fixit -x c %t || true // RUN: %clang_cc1 -fsyntax-only -pedantic -Werror -x c %t +// RUN: grep "Rectangle" %t struct Point { float x, y; }; @@ -23,3 +24,14 @@ struct Window window = { topleft.x = 3.14, // expected-error{{field designator 'topleft' does not refer to any field in type 'struct Rectangle'; did you mean 'top_left'?}} 2.71818, 5.0, 6.0, Red }; + +void test() { + Rectangle r1; // expected-error{{must use 'struct' tag to refer to type 'Rectangle'}} + r1.top_left.x = 0; + + typedef struct Rectangle Rectangle; // expected-note{{'Rectangle' declared here}} + rectangle *r2 = &r1; // expected-error{{use of undeclared identifier 'rectangle'; did you mean 'Rectangle'?}} + r2->top_left.y = 0; + unsinged *ptr = 0; // expected-error{{use of undeclared identifier 'unsinged'; did you mean 'unsigned'?}} + *ptr = 17; +} diff --git a/test/FixIt/typo.m b/test/FixIt/typo.m index 8a474ab168..28c0529af4 100644 --- a/test/FixIt/typo.m +++ b/test/FixIt/typo.m @@ -1,22 +1,16 @@ // RUN: %clang_cc1 -fsyntax-only -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -DNON_FIXITS -verify %s -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -x objective-c -E -P %s -o %t +// RUN: cp %s %t // RUN: %clang_cc1 -x objective-c -fsyntax-only -fobjc-nonfragile-abi -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fixit %t || true // RUN: %clang_cc1 -x objective-c -fsyntax-only -fobjc-nonfragile-abi -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -pedantic -Werror %t -// RUN: false -// XFAIL: * +// RUN: grep "@implementation Sub3" %t - -@interface NSString // expected-note{{'NSString' declared here}} +@interface NSString // expected-note 2{{'NSString' declared here}} + (int)method:(int)x; @end -#ifdef NON_FIXITS void test() { - // FIXME: not providing fix-its - NSstring *str = @"A string"; // expected-error{{use of undeclared identifier 'NSstring'; did you mean 'NSString'?}} \ - // expected-error{{use of undeclared identifier 'str'}} + NSstring *str = @"A string"; // expected-error{{use of undeclared identifier 'NSstring'; did you mean 'NSString'?}} } -#endif @protocol P1 @optional @@ -166,7 +160,7 @@ void f(A *a) { @implementation Sub3 - (int)method3 { - int x = super; // expected-note{{use of undeclared identifier 'super'}} + int x = super; // expected-error{{use of undeclared identifier 'super'}} return 0; } @end