From: Richard Smith Date: Sat, 18 Aug 2012 00:55:03 +0000 (+0000) Subject: PR41111, PR5925, PR13210: Teach tentative parsing to annotate identifiers and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0576681bac125be07f77f66b02a3dba2c3a24557;p=clang PR41111, PR5925, PR13210: Teach tentative parsing to annotate identifiers and nested names as id-expressions, using the annot_primary_expr annotation, where possible. This removes some redundant lookups, and also allows us to typo-correct within tentative parsing, and to carry on disambiguating past an identifier which we can determine will fail lookup as both a type and as a non-type, allowing us to disambiguate more declarations (and thus offer improved error recovery for such cases). This also introduces to the parser the notion of a tentatively-declared name, which is an identifier which we *might* have seen a declaration for in a tentative parse (but only if we end up disambiguating the tokens as a declaration). This is necessary to correctly disambiguate cases where a variable is used within its own initializer. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162159 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 02e3f1e7e4..ca2c59d9b3 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -724,6 +724,14 @@ public: CachedTokens[CachedLexPos-1] = Tok; } + /// TypoCorrectToken - Update the current token to represent the provided + /// identifier, in order to cache an action performed by typo correction. + void TypoCorrectToken(const Token &Tok) { + assert(Tok.getIdentifierInfo() && "Expected identifier token"); + if (CachedLexPos != 0 && isBacktrackEnabled()) + CachedTokens[CachedLexPos-1] = Tok; + } + /// \brief Recompute the current lexer kind based on the CurLexer/CurPTHLexer/ /// CurTokenLexer pointers. void recomputeCurLexerKind(); diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 4ef92f7dbc..5e80ca36a0 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -30,6 +30,7 @@ namespace clang { class PragmaHandler; class Scope; class BalancedDelimiterTracker; + class CorrectionCandidateCallback; class DeclGroupRef; class DiagnosticBuilder; class Parser; @@ -204,6 +205,9 @@ class Parser : public CodeCompletionHandler { /// top-level declaration is finished. SmallVector TemplateIds; + /// \brief Identifiers which have been declared within a tentative parse. + SmallVector TentativelyDeclaredIdentifiers; + IdentifierInfo *getSEHExceptKeyword(); /// True if we are within an Objective-C container while parsing C-like decls. @@ -482,7 +486,28 @@ private: // find a type name by attempting typo correction. bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false, bool NeedType = false); + bool TryAnnotateTypeOrScopeTokenAfterScopeSpec(bool EnteringContext, + bool NeedType, + CXXScopeSpec &SS, + bool IsNewScope); bool TryAnnotateCXXScopeToken(bool EnteringContext = false); + enum AnnotatedNameKind { + /// Annotation has failed and emitted an error. + ANK_Error, + /// The identifier is a tentatively-declared name. + ANK_TentativeDecl, + /// The identifier is a template name. FIXME: Add an annotation for that. + ANK_TemplateName, + /// The identifier can't be resolved. + ANK_Unresolved, + /// Annotation was successful. + ANK_Success + }; + AnnotatedNameKind TryAnnotateName(bool IsAddressOfOperand, + CorrectionCandidateCallback *CCC = 0); + + /// Push a tok::annot_cxxscope token onto the token stream. + void AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation); /// TryAltiVecToken - Check for context-sensitive AltiVec identifier tokens, /// replacing them with the non-context-sensitive keywords. This returns @@ -529,12 +554,15 @@ private: class TentativeParsingAction { Parser &P; Token PrevTok; + size_t PrevTentativelyDeclaredIdentifierCount; unsigned short PrevParenCount, PrevBracketCount, PrevBraceCount; bool isActive; public: explicit TentativeParsingAction(Parser& p) : P(p) { PrevTok = P.Tok; + PrevTentativelyDeclaredIdentifierCount = + P.TentativelyDeclaredIdentifiers.size(); PrevParenCount = P.ParenCount; PrevBracketCount = P.BracketCount; PrevBraceCount = P.BraceCount; @@ -543,6 +571,8 @@ private: } void Commit() { assert(isActive && "Parsing action was finished!"); + P.TentativelyDeclaredIdentifiers.resize( + PrevTentativelyDeclaredIdentifierCount); P.PP.CommitBacktrackedTokens(); isActive = false; } @@ -550,6 +580,8 @@ private: assert(isActive && "Parsing action was finished!"); P.PP.Backtrack(); P.Tok = PrevTok; + P.TentativelyDeclaredIdentifiers.resize( + PrevTentativelyDeclaredIdentifierCount); P.ParenCount = PrevParenCount; P.BracketCount = PrevBracketCount; P.BraceCount = PrevBraceCount; @@ -1707,6 +1739,11 @@ private: isCXXDeclarationSpecifier(TPResult BracedCastResult = TPResult::False(), bool *HasMissingTypename = 0); + /// \brief Determine whether an identifier has been tentatively declared as a + /// non-type. Such tentative declarations should not be found to name a type + /// during a tentative parse, but also should not be annotated as a non-type. + bool isTentativelyDeclared(IdentifierInfo *II); + // "Tentative parsing" functions, used for disambiguation. If a parsing error // is encountered they will return TPResult::Error(). // Returning TPResult::True()/False() indicates that the ambiguity was diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index acc88c0578..4f1b2f6aa9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1203,7 +1203,7 @@ public: 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. @@ -1223,11 +1223,19 @@ public: /// /// \param NextToken The token following the identifier. Used to help /// disambiguate the name. + /// + /// \param IsAddressOfOperand True if this name is the operand of a unary + /// address of ('&') expression, assuming it is classified as an + /// expression. + /// + /// \param CCC The correction callback, if typo correction is desired. NameClassification ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, SourceLocation NameLoc, - const Token &NextToken); + const Token &NextToken, + bool IsAddressOfOperand, + CorrectionCandidateCallback *CCC = 0); Decl *ActOnDeclarator(Scope *S, Declarator &D); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index cb865cc9c2..4d33106aa8 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3165,6 +3165,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // anything that's a simple-type-specifier followed by '(' as an // expression. This suffices because function types are not valid // underlying types anyway. + EnterExpressionEvaluationContext Unevaluated(Actions, + Sema::ConstantEvaluated); TPResult TPR = isExpressionOrTypeSpecifierSimple(NextToken().getKind()); // If the next token starts an expression, we know we're parsing a // bit-field. This is the common case. @@ -4374,9 +4376,15 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // In such a case, check if we actually have a function declarator; if it // is not, the declarator has been fully parsed. bool IsAmbiguous = false; - if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit() && - !isCXXFunctionDeclarator(&IsAmbiguous)) - break; + if (getLangOpts().CPlusPlus && D.mayBeFollowedByCXXDirectInit()) { + // The name of the declarator, if any, is tentatively declared within + // a possible direct initializer. + TentativelyDeclaredIdentifiers.push_back(D.getIdentifier()); + bool IsFunctionDecl = isCXXFunctionDeclarator(&IsAmbiguous); + TentativelyDeclaredIdentifiers.pop_back(); + if (!IsFunctionDecl) + break; + } ParsedAttributes attrs(AttrFactory); BalancedDelimiterTracker T(*this, tok::l_paren); T.consumeOpen(); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index afac25793a..f2ba4c6e11 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -988,6 +988,21 @@ ExprResult Parser::ParseCXXTypeid() { ExprResult Result; + // C++0x [expr.typeid]p3: + // When typeid is applied to an expression other than an lvalue of a + // polymorphic class type [...] The expression is an unevaluated + // operand (Clause 5). + // + // Note that we can't tell whether the expression is an lvalue of a + // polymorphic class type until after we've parsed the expression; we + // speculatively assume the subexpression is unevaluated, and fix it up + // later. + // + // We enter the unevaluated context before trying to determine whether we + // have a type-id, because the tentative parse logic will try to resolve + // names, and must treat them as unevaluated. + EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); + if (isTypeIdInParens()) { TypeResult Ty = ParseTypeName(); @@ -1000,16 +1015,6 @@ ExprResult Parser::ParseCXXTypeid() { Result = Actions.ActOnCXXTypeid(OpLoc, LParenLoc, /*isType=*/true, Ty.get().getAsOpaquePtr(), RParenLoc); } else { - // C++0x [expr.typeid]p3: - // When typeid is applied to an expression other than an lvalue of a - // polymorphic class type [...] The expression is an unevaluated - // operand (Clause 5). - // - // Note that we can't tell whether the expression is an lvalue of a - // polymorphic class type until after we've parsed the expression; we - // speculatively assume the subexpression is unevaluated, and fix it up - // later. - EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); Result = ParseExpression(); // Match the ')'. diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index df9b996aa3..0716f6f901 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -17,6 +17,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/PrettyDeclStackTrace.h" #include "clang/Sema/Scope.h" +#include "clang/Sema/TypoCorrection.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/PrettyStackTrace.h" #include "clang/Basic/SourceManager.h" @@ -130,96 +131,38 @@ Retry: return ParseLabeledStatement(Attrs); } + // Look up the identifier, and typo-correct it to a keyword if it's not + // found. if (Next.isNot(tok::coloncolon)) { - CXXScopeSpec SS; - IdentifierInfo *Name = Tok.getIdentifierInfo(); - SourceLocation NameLoc = Tok.getLocation(); - - if (getLangOpts().CPlusPlus) - CheckForTemplateAndDigraph(Next, ParsedType(), - /*EnteringContext=*/false, *Name, SS); - - 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: + // Try to limit which sets of keywords should be included in typo + // correction based on what the next token is. + // FIXME: Pass the next token into the CorrectionCandidateCallback and + // do this filtering in a more fine-grained manner. + CorrectionCandidateCallback DefaultValidator; + DefaultValidator.WantTypeSpecifiers = + Next.is(tok::l_paren) || Next.is(tok::less) || + Next.is(tok::identifier) || Next.is(tok::star) || + Next.is(tok::amp) || Next.is(tok::l_square); + DefaultValidator.WantExpressionKeywords = + Next.is(tok::l_paren) || Next.is(tok::identifier) || + Next.is(tok::arrow) || Next.is(tok::period); + DefaultValidator.WantRemainingKeywords = + Next.is(tok::l_paren) || Next.is(tok::semi) || + Next.is(tok::identifier) || Next.is(tok::l_brace); + DefaultValidator.WantCXXNamedCasts = false; + if (TryAnnotateName(/*IsAddressOfOperand*/false, &DefaultValidator) + == ANK_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: - Tok.setKind(tok::annot_typename); - setTypeAnnotation(Tok, Classification.getType()); - Tok.setAnnotationEndLoc(NameLoc); - PP.AnnotateCachedTokens(Tok); - break; - - case Sema::NC_Expression: - Tok.setKind(tok::annot_primary_expr); - setExprAnnotation(Tok, Classification.getExpression()); - Tok.setAnnotationEndLoc(NameLoc); - PP.AnnotateCachedTokens(Tok); - break; - - 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, SourceLocation(), Id, - /*AllowTypeAnnotation=*/false)) { - // 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(); - } - - // If the next token is '::', jump right into parsing a - // nested-name-specifier. We don't want to leave the template-id - // hanging. - if (NextToken().is(tok::coloncolon) && TryAnnotateCXXScopeToken(false)){ - // 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; - } + // If the identifier was typo-corrected, try again. + if (Tok.isNot(tok::identifier)) + goto Retry; } // Fall through diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 1a4df4791b..653f6c26ae 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -623,6 +623,8 @@ Parser::TPResult Parser::TryParseDeclarator(bool mayBeAbstract, // declarator-id if (Tok.is(tok::annot_cxxscope)) ConsumeToken(); + else + TentativelyDeclaredIdentifiers.push_back(Tok.getIdentifierInfo()); ConsumeToken(); } else if (Tok.is(tok::l_paren)) { ConsumeParen(); @@ -824,6 +826,12 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { return TPResult::Ambiguous(); } +bool Parser::isTentativelyDeclared(IdentifierInfo *II) { + return std::find(TentativelyDeclaredIdentifiers.begin(), + TentativelyDeclaredIdentifiers.end(), II) + != TentativelyDeclaredIdentifiers.end(); +} + /// isCXXDeclarationSpecifier - Returns TPResult::True() if it is a declaration /// specifier, TPResult::False() if it is not, TPResult::Ambiguous() if it could /// be either a decl-specifier or a function-style cast, and TPResult::Error() @@ -831,7 +839,10 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) { /// /// If HasMissingTypename is provided, a name with a dependent scope specifier /// will be treated as ambiguous if the 'typename' keyword is missing. If this -/// happens, *HasMissingTypename will be set to 'true'. +/// happens, *HasMissingTypename will be set to 'true'. This will also be used +/// as an indicator that undeclared identifiers (which will trigger a later +/// parse error) should be treated as types. Returns TPResult::Ambiguous() in +/// such cases. /// /// decl-specifier: /// storage-class-specifier @@ -927,22 +938,64 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, bool *HasMissingTypename) { switch (Tok.getKind()) { - case tok::identifier: // foo::bar + case tok::identifier: { // Check for need to substitute AltiVec __vector keyword // for "vector" identifier. if (TryAltiVecVectorToken()) return TPResult::True(); - // Fall through. + + const Token &Next = NextToken(); + // In 'foo bar', 'foo' is always a type name outside of Objective-C. + if (!getLangOpts().ObjC1 && Next.is(tok::identifier)) + return TPResult::True(); + + if (Next.isNot(tok::coloncolon) && Next.isNot(tok::less)) { + // Determine whether this is a valid expression. If not, we will hit + // a parse error one way or another. In that case, tell the caller that + // this is ambiguous. Typo-correct to type and expression keywords and + // to types and identifiers, in order to try to recover from errors. + CorrectionCandidateCallback TypoCorrection; + TypoCorrection.WantRemainingKeywords = false; + switch (TryAnnotateName(false /* no nested name specifier */, + &TypoCorrection)) { + case ANK_Error: + return TPResult::Error(); + case ANK_TentativeDecl: + return TPResult::False(); + case ANK_TemplateName: + // A bare type template-name which can't be a template template + // argument is an error, and was probably intended to be a type. + return GreaterThanIsOperator ? TPResult::True() : TPResult::False(); + case ANK_Unresolved: + return HasMissingTypename ? TPResult::Ambiguous() : TPResult::False(); + case ANK_Success: + break; + } + assert(Tok.isNot(tok::identifier) && + "TryAnnotateName succeeded without producing an annotation"); + } else { + // This might possibly be a type with a dependent scope specifier and + // a missing 'typename' keyword. Don't use TryAnnotateName in this case, + // since it will annotate as a primary expression, and we want to use the + // "missing 'typename'" logic. + if (TryAnnotateTypeOrScopeToken()) + return TPResult::Error(); + // If annotation failed, assume it's a non-type. + // FIXME: If this happens due to an undeclared identifier, treat it as + // ambiguous. + if (Tok.is(tok::identifier)) + return TPResult::False(); + } + + // We annotated this token as something. Recurse to handle whatever we got. + return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); + } + case tok::kw_typename: // typename T::type // Annotate typenames and C++ scope specifiers. If we get one, just // recurse to handle whatever we get. if (TryAnnotateTypeOrScopeToken()) return TPResult::Error(); - if (Tok.is(tok::identifier)) { - const Token &Next = NextToken(); - return (!getLangOpts().ObjC1 && Next.is(tok::identifier)) ? - TPResult::True() : TPResult::False(); - } return isCXXDeclarationSpecifier(BracedCastResult, HasMissingTypename); case tok::coloncolon: { // ::foo::bar @@ -1073,6 +1126,28 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult, *HasMissingTypename = true; return TPResult::Ambiguous(); } + } else { + // Try to resolve the name. If it doesn't exist, assume it was + // intended to name a type and keep disambiguating. + switch (TryAnnotateName(false /* SS is not dependent */)) { + case ANK_Error: + return TPResult::Error(); + case ANK_TentativeDecl: + return TPResult::False(); + case ANK_TemplateName: + // A bare type template-name which can't be a template template + // argument is an error, and was probably intended to be a type. + return GreaterThanIsOperator ? TPResult::True() : TPResult::False(); + case ANK_Unresolved: + return HasMissingTypename ? TPResult::Ambiguous() + : TPResult::False(); + case ANK_Success: + // Annotated it, check again. + assert(Tok.isNot(tok::annot_cxxscope) || + NextToken().isNot(tok::identifier)); + return isCXXDeclarationSpecifier(BracedCastResult, + HasMissingTypename); + } } } return TPResult::False(); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 3725e2b845..bee802faaa 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1301,6 +1301,143 @@ TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) { return Id; } +void Parser::AnnotateScopeToken(CXXScopeSpec &SS, bool IsNewAnnotation) { + // Push the current token back into the token stream (or revert it if it is + // cached) and use an annotation scope token for current token. + if (PP.isBacktrackEnabled()) + PP.RevertCachedTokens(1); + else + PP.EnterToken(Tok); + Tok.setKind(tok::annot_cxxscope); + Tok.setAnnotationValue(Actions.SaveNestedNameSpecifierAnnotation(SS)); + Tok.setAnnotationRange(SS.getRange()); + + // In case the tokens were cached, have Preprocessor replace them + // with the annotation token. We don't need to do this if we've + // just reverted back to a prior state. + if (IsNewAnnotation) + PP.AnnotateCachedTokens(Tok); +} + +/// \brief Attempt to classify the name at the current token position. This may +/// form a type, scope or primary expression annotation, or replace the token +/// with a typo-corrected keyword. This is only appropriate when the current +/// name must refer to an entity which has already been declared. +/// +/// \param IsAddressOfOperand Must be \c true if the name is preceded by an '&' +/// and might possibly have a dependent nested name specifier. +/// \param CCC Indicates how to perform typo-correction for this name. If NULL, +/// no typo correction will be performed. +Parser::AnnotatedNameKind +Parser::TryAnnotateName(bool IsAddressOfOperand, + CorrectionCandidateCallback *CCC) { + assert(Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope)); + + const bool EnteringContext = false; + const bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); + + CXXScopeSpec SS; + if (getLangOpts().CPlusPlus && + ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext)) + return ANK_Error; + + if (Tok.isNot(tok::identifier) || SS.isInvalid()) { + if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(EnteringContext, false, SS, + !WasScopeAnnotation)) + return ANK_Error; + return ANK_Unresolved; + } + + IdentifierInfo *Name = Tok.getIdentifierInfo(); + SourceLocation NameLoc = Tok.getLocation(); + + // FIXME: Move the tentative declaration logic into ClassifyName so we can + // typo-correct to tentatively-declared identifiers. + if (isTentativelyDeclared(Name)) { + // Identifier has been tentatively declared, and thus cannot be resolved as + // an expression. Fall back to annotating it as a type. + if (TryAnnotateTypeOrScopeTokenAfterScopeSpec(EnteringContext, false, SS, + !WasScopeAnnotation)) + return ANK_Error; + return Tok.is(tok::annot_typename) ? ANK_Success : ANK_TentativeDecl; + } + + Token Next = NextToken(); + + // Look up and classify the identifier. We don't perform any typo-correction + // after a scope specifier, because in general we can't recover from typos + // there (eg, after correcting 'A::tempalte B::C', we would need to jump + // back into scope specifier parsing). + Sema::NameClassification Classification + = Actions.ClassifyName(getCurScope(), SS, Name, NameLoc, Next, + IsAddressOfOperand, SS.isEmpty() ? CCC : 0); + + switch (Classification.getKind()) { + case Sema::NC_Error: + return ANK_Error; + + case Sema::NC_Keyword: + // The identifier was typo-corrected to a keyword. + Tok.setIdentifierInfo(Name); + Tok.setKind(Name->getTokenID()); + PP.TypoCorrectToken(Tok); + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + // We've "annotated" this as a keyword. + return ANK_Success; + + case Sema::NC_Unknown: + // It's not something we know about. Leave it unannotated. + break; + + case Sema::NC_Type: + Tok.setKind(tok::annot_typename); + setTypeAnnotation(Tok, Classification.getType()); + Tok.setAnnotationEndLoc(NameLoc); + if (SS.isNotEmpty()) + Tok.setLocation(SS.getBeginLoc()); + PP.AnnotateCachedTokens(Tok); + return ANK_Success; + + case Sema::NC_Expression: + Tok.setKind(tok::annot_primary_expr); + setExprAnnotation(Tok, Classification.getExpression()); + Tok.setAnnotationEndLoc(NameLoc); + if (SS.isNotEmpty()) + Tok.setLocation(SS.getBeginLoc()); + PP.AnnotateCachedTokens(Tok); + return ANK_Success; + + case Sema::NC_TypeTemplate: + if (Next.isNot(tok::less)) { + // This may be a type template being used as a template template argument. + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return ANK_TemplateName; + } + // Fall through. + case Sema::NC_FunctionTemplate: { + // We have a type or function template followed by '<'. + ConsumeToken(); + UnqualifiedId Id; + Id.setIdentifier(Name, NameLoc); + if (AnnotateTemplateIdToken( + TemplateTy::make(Classification.getTemplateName()), + Classification.getTemplateNameKind(), SS, SourceLocation(), Id)) + return ANK_Error; + return ANK_Success; + } + + case Sema::NC_NestedNameSpecifier: + llvm_unreachable("already parsed nested name specifier"); + } + + // Unable to classify the name, but maybe we can annotate a scope specifier. + if (SS.isNotEmpty()) + AnnotateScopeToken(SS, !WasScopeAnnotation); + return ANK_Unresolved; +} + /// TryAnnotateTypeOrScopeToken - If the current token position is on a /// typename (possibly qualified in C++) or a C++ scope specifier not followed /// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens @@ -1404,13 +1541,24 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) { } // Remembers whether the token was originally a scope annotation. - bool wasScopeAnnotation = Tok.is(tok::annot_cxxscope); + bool WasScopeAnnotation = Tok.is(tok::annot_cxxscope); CXXScopeSpec SS; if (getLangOpts().CPlusPlus) if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext)) return true; + return TryAnnotateTypeOrScopeTokenAfterScopeSpec(EnteringContext, NeedType, + SS, !WasScopeAnnotation); +} + +/// \brief Try to annotate a type or scope token, having already parsed an +/// optional scope specifier. \p IsNewScope should be \c true unless the scope +/// specifier was extracted from an existing tok::annot_cxxscope annotation. +bool Parser::TryAnnotateTypeOrScopeTokenAfterScopeSpec(bool EnteringContext, + bool NeedType, + CXXScopeSpec &SS, + bool IsNewScope) { if (Tok.is(tok::identifier)) { IdentifierInfo *CorrectedII = 0; // Determine whether the identifier is a type name. @@ -1492,21 +1640,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) { return false; // A C++ scope specifier that isn't followed by a typename. - // Push the current token back into the token stream (or revert it if it is - // cached) and use an annotation scope token for current token. - if (PP.isBacktrackEnabled()) - PP.RevertCachedTokens(1); - else - PP.EnterToken(Tok); - Tok.setKind(tok::annot_cxxscope); - Tok.setAnnotationValue(Actions.SaveNestedNameSpecifierAnnotation(SS)); - Tok.setAnnotationRange(SS.getRange()); - - // In case the tokens were cached, have Preprocessor replace them - // with the annotation token. We don't need to do this if we've - // just reverted back to the state we were in before being called. - if (!wasScopeAnnotation) - PP.AnnotateCachedTokens(Tok); + AnnotateScopeToken(SS, IsNewScope); return false; } @@ -1529,19 +1663,7 @@ bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) { if (SS.isEmpty()) return false; - // Push the current token back into the token stream (or revert it if it is - // cached) and use an annotation scope token for current token. - if (PP.isBacktrackEnabled()) - PP.RevertCachedTokens(1); - else - PP.EnterToken(Tok); - Tok.setKind(tok::annot_cxxscope); - Tok.setAnnotationValue(Actions.SaveNestedNameSpecifierAnnotation(SS)); - Tok.setAnnotationRange(SS.getRange()); - - // In case the tokens were cached, have Preprocessor replace them with the - // annotation token. - PP.AnnotateCachedTokens(Tok); + AnnotateScopeToken(SS, true); return false; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index e1b146d9b4..84ddf36ecc 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -562,11 +562,28 @@ static bool isTagTypeWithMissingTag(Sema &SemaRef, LookupResult &Result, return false; } +/// Build a ParsedType for a simple-type-specifier with a nested-name-specifier. +static ParsedType buildNestedType(Sema &S, CXXScopeSpec &SS, + QualType T, SourceLocation NameLoc) { + ASTContext &Context = S.Context; + + TypeLocBuilder Builder; + Builder.pushTypeSpec(T).setNameLoc(NameLoc); + + T = S.getElaboratedType(ETK_None, SS, T); + ElaboratedTypeLoc ElabTL = Builder.push(T); + ElabTL.setElaboratedKeywordLoc(SourceLocation()); + ElabTL.setQualifierLoc(SS.getWithLocInContext(Context)); + return S.CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); +} + Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, SourceLocation NameLoc, - const Token &NextToken) { + const Token &NextToken, + bool IsAddressOfOperand, + CorrectionCandidateCallback *CCC) { DeclarationNameInfo NameInfo(Name, NameLoc); ObjCMethodDecl *CurMethod = getCurMethodDecl(); @@ -632,25 +649,11 @@ Corrected: // Perform typo correction to determine if there is another name that is // close to this name. - if (!SecondTry) { + if (!SecondTry && CCC) { SecondTry = true; - CorrectionCandidateCallback DefaultValidator; - // Try to limit which sets of keywords should be included in typo - // correction based on what the next token is. - DefaultValidator.WantTypeSpecifiers = - NextToken.is(tok::l_paren) || NextToken.is(tok::less) || - NextToken.is(tok::identifier) || NextToken.is(tok::star) || - NextToken.is(tok::amp) || NextToken.is(tok::l_square); - DefaultValidator.WantExpressionKeywords = - NextToken.is(tok::l_paren) || NextToken.is(tok::identifier) || - NextToken.is(tok::arrow) || NextToken.is(tok::period); - DefaultValidator.WantRemainingKeywords = - NextToken.is(tok::l_paren) || NextToken.is(tok::semi) || - NextToken.is(tok::identifier) || NextToken.is(tok::l_brace); - DefaultValidator.WantCXXNamedCasts = false; if (TypoCorrection Corrected = CorrectTypo(Result.getLookupNameInfo(), Result.getLookupKind(), S, - &SS, DefaultValidator)) { + &SS, *CCC)) { unsigned UnqualifiedDiag = diag::err_undeclared_var_use_suggest; unsigned QualifiedDiag = diag::err_no_member_suggest; std::string CorrectedStr(Corrected.getAsString(getLangOpts())); @@ -731,8 +734,9 @@ Corrected: // perform some heroics to see if we actually have a // template-argument-list, which would indicate a missing 'template' // keyword here. - return BuildDependentDeclRefExpr(SS, /*TemplateKWLoc=*/SourceLocation(), - NameInfo, /*TemplateArgs=*/0); + return ActOnDependentIdExpression(SS, /*TemplateKWLoc=*/SourceLocation(), + NameInfo, IsAddressOfOperand, + /*TemplateArgs=*/0); } case LookupResult::Found: @@ -808,14 +812,16 @@ Corrected: return NameClassification::TypeTemplate(Template); } } - + NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl(); if (TypeDecl *Type = dyn_cast(FirstDecl)) { DiagnoseUseOfDecl(Type, NameLoc); QualType T = Context.getTypeDeclType(Type); + if (SS.isNotEmpty()) + return buildNestedType(*this, SS, T, NameLoc); 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. @@ -838,10 +844,14 @@ Corrected: return ParsedType::make(T); } + // We can have a type template here if we're classifying a template argument. + if (isa(FirstDecl) && !isa(FirstDecl)) + return NameClassification::TypeTemplate( + TemplateName(cast(FirstDecl))); + // Check for a tag type hidden by a non-type decl in a few cases where it // seems likely a type is wanted instead of the non-type that was found. - if (!getLangOpts().ObjC1 && FirstDecl && !isa(FirstDecl) && - !isa(FirstDecl)) { + if (!getLangOpts().ObjC1) { bool NextIsOp = NextToken.is(tok::amp) || NextToken.is(tok::star); if ((NextToken.is(tok::identifier) || (NextIsOp && FirstDecl->isFunctionOrFunctionTemplate())) && @@ -850,12 +860,14 @@ Corrected: if (TypeDecl *Type = dyn_cast(FirstDecl)) { DiagnoseUseOfDecl(Type, NameLoc); QualType T = Context.getTypeDeclType(Type); + if (SS.isNotEmpty()) + return buildNestedType(*this, SS, T, NameLoc); return ParsedType::make(T); } } } - if (!Result.empty() && (*Result.begin())->isCXXClassMember()) + if (FirstDecl->isCXXClassMember()) return BuildPossibleImplicitMemberExpr(SS, SourceLocation(), Result, 0); bool ADL = UseArgumentDependentLookup(SS, Result, NextToken.is(tok::l_paren)); diff --git a/test/Parser/cxx-casting.cpp b/test/Parser/cxx-casting.cpp index 42ad12ee94..e7d05b4047 100644 --- a/test/Parser/cxx-casting.cpp +++ b/test/Parser/cxx-casting.cpp @@ -58,9 +58,9 @@ void test2(char x, struct B * b) { expected-error {{expected ']'}} #define LC <: #define C : - test1::A LC:B> c; // expected-error {{cannot refer to class template 'A' without a template argument list}} expected-error 2{{}} expected-note{{}} + test1::A LC:B> c; // expected-error {{class template test1::A requires template arguments}} expected-error 2{{}} (void)static_cast LC:c>(&x); // expected-error {{expected '<' after 'static_cast'}} expected-error 2{{}} expected-note{{}} - test1::A<:C B> d; // expected-error {{cannot refer to class template 'A' without a template argument list}} expected-error 2{{}} expected-note{{}} + test1::A<:C B> d; // expected-error {{class template test1::A requires template arguments}} expected-error 2{{}} (void)static_cast<:C c>(&x); // expected-error {{expected '<' after 'static_cast'}} expected-error 2{{}} expected-note{{}} #define LCC <:: @@ -85,8 +85,7 @@ void test3() { E< ::F>(); // Make sure that parser doesn't expand '[:' to '< ::' - ::D[:F> A5; // expected-error {{cannot refer to class template 'D' without a template argument list}} \ + ::D[:F> A5; // expected-error {{class template ::D requires template arguments}} \ // expected-error {{expected expression}} \ - // expected-error {{expected ']'}} \ - // expected-note {{to match this '['}} + // expected-error {{expected unqualified-id}} } diff --git a/test/Parser/cxx-decl.cpp b/test/Parser/cxx-decl.cpp index 951cd3dfd1..30ac2794ab 100644 --- a/test/Parser/cxx-decl.cpp +++ b/test/Parser/cxx-decl.cpp @@ -119,6 +119,9 @@ void CodeCompleteConsumer::() { // expected-error {{xpected unqualified-id}} ; +// PR4111 +void f(sqrgl); // expected-error {{unknown type name 'sqrgl'}} + // PR8380 extern "" // expected-error {{unknown linkage language}} test6a { ;// expected-error {{C++ requires a type specifier for all declarations}} \ diff --git a/test/Parser/cxx-template-argument.cpp b/test/Parser/cxx-template-argument.cpp index 5479961d56..afe318df7a 100644 --- a/test/Parser/cxx-template-argument.cpp +++ b/test/Parser/cxx-template-argument.cpp @@ -25,3 +25,20 @@ namespace greatergreater { (void)(&t>==p); // expected-error {{use '> >'}} expected-error {{use '> ='}} } } + +namespace PR5925 { + template + class foo { // expected-note {{here}} + }; + void bar(foo *X) { // expected-error {{requires template arguments}} + } +} + +namespace PR13210 { + template + class C {}; // expected-note {{here}} + + void f() { + new C(); // expected-error {{requires template arguments}} + } +} diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index 4e1abc5e5b..b35e382286 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -93,8 +93,7 @@ void f3() { } // make sure the following doesn't hit any asserts -void f4(undef::C); // expected-error {{use of undeclared identifier 'undef'}} \ - expected-error {{variable has incomplete type 'void'}} +void f4(undef::C); // expected-error {{use of undeclared identifier 'undef'}} typedef void C2::f5(int); // expected-error{{typedef declarator cannot be qualified}} diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp index 893f08a422..919edca831 100644 --- a/test/SemaCXX/typo-correction.cpp +++ b/test/SemaCXX/typo-correction.cpp @@ -116,14 +116,14 @@ void TestRedecl::add_in(int i) {} // expected-error{{out-of-line definition of ' // Test the improved typo correction for the Parser::ParseCastExpr => // Sema::ActOnIdExpression => Sema::DiagnoseEmptyLookup call path. -class SomeNetMessage; +class SomeNetMessage; // expected-note 2{{'SomeNetMessage'}} class Message {}; void foo(Message&); void foo(SomeNetMessage&); void doit(void *data) { Message somenetmsg; // expected-note{{'somenetmsg' declared here}} foo(somenetmessage); // expected-error{{use of undeclared identifier 'somenetmessage'; did you mean 'somenetmsg'?}} - foo((somenetmessage)data); // expected-error{{use of undeclared identifier 'somenetmessage'; did you mean 'SomeNetMessage'?}} + foo((somenetmessage)data); // expected-error{{unknown type name 'somenetmessage'; did you mean 'SomeNetMessage'?}} expected-error{{incomplete type}} } // Test the typo-correction callback in BuildRecoveryCallExpr. @@ -172,7 +172,7 @@ void Child::add_types(int value) {} // expected-error{{out-of-line definition of // Sema::ActOnIdExpression by Parser::ParseCastExpression to allow type names as // potential corrections for template arguments. namespace clash { -class ConstructExpr {}; // expected-note{{'clash::ConstructExpr' declared here}} +class ConstructExpr {}; // expected-note 2{{'clash::ConstructExpr' declared here}} } class ClashTool { bool HaveConstructExpr(); @@ -180,7 +180,7 @@ class ClashTool { void test() { ConstructExpr *expr = // expected-error{{unknown type name 'ConstructExpr'; did you mean 'clash::ConstructExpr'?}} - getExprAs(); // expected-error{{use of undeclared identifier 'ConstructExpr'; did you mean 'clash::ConstructExpr'?}} + getExprAs(); // expected-error{{unknown type name 'ConstructExpr'; did you mean 'clash::ConstructExpr'?}} } }; @@ -220,6 +220,8 @@ namespace PR13051 { } } +inf f(doulbe); // expected-error{{'int'}} expected-error{{'double'}} + namespace PR6325 { class foo { }; // expected-note{{'foo' declared here}} // Note that for this example (pulled from the PR), if keywords are not excluded diff --git a/test/SemaCXX/unknown-type-name.cpp b/test/SemaCXX/unknown-type-name.cpp index 893e0cc5dc..ce5972bf2d 100644 --- a/test/SemaCXX/unknown-type-name.cpp +++ b/test/SemaCXX/unknown-type-name.cpp @@ -6,6 +6,8 @@ namespace N { }; typedef Wibble foo; + + int zeppelin; // expected-note{{declared here}} } using namespace N; @@ -15,6 +17,13 @@ void f() { foo::bar = 4; // expected-error{{no member named 'bar' in 'N::Wibble'}} } +int f(foo::bar); // expected-error{{no type named 'bar' in 'N::Wibble'}} + +int f(doulbe); // expected-error{{did you mean 'double'?}} + +int fun(zapotron); // expected-error{{unknown type name 'zapotron'}} +int var(zepelin); // expected-error{{did you mean 'zeppelin'?}} + template struct A { typedef T type; @@ -59,6 +68,20 @@ void f(int, T::type, int) { } // expected-error{{missing 'typename'}} template void f(int, T::type x, char) { } // expected-error{{missing 'typename'}} +int *p; + +// FIXME: We should assume that 'undeclared' is a type, not a parameter name +// here, and produce an 'unknown type name' diagnostic instead. +int f1(undeclared, int); // expected-error{{requires a type specifier}} + +int f2(undeclared, 0); // expected-error{{undeclared identifier}} + +int f3(undeclared *p, int); // expected-error{{unknown type name 'undeclared'}} + +int f4(undeclared *p, 0); // expected-error{{undeclared identifier}} + +int *test(UnknownType *fool) { return 0; } // expected-error{{unknown type name 'UnknownType'}} + template int A::n(T::value); // ok template A::type // expected-error{{missing 'typename'}} diff --git a/test/SemaTemplate/class-template-id.cpp b/test/SemaTemplate/class-template-id.cpp index 3b02778724..b674537ea7 100644 --- a/test/SemaTemplate/class-template-id.cpp +++ b/test/SemaTemplate/class-template-id.cpp @@ -40,7 +40,7 @@ typedef N::C c2; // PR5655 template struct Foo { }; // expected-note{{template is declared here}} -void f(void) { Foo bar; } // expected-error{{without a template argument list}} +void f(void) { Foo bar; } // expected-error{{use of class template Foo requires template arguments}} // rdar://problem/8254267 template class Party; diff --git a/test/SemaTemplate/member-access-ambig.cpp b/test/SemaTemplate/member-access-ambig.cpp index f8a01d5fff..5c2d761703 100644 --- a/test/SemaTemplate/member-access-ambig.cpp +++ b/test/SemaTemplate/member-access-ambig.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-comparison %s // PR8439 class A @@ -43,3 +43,22 @@ namespace PR11134 { }; } +namespace AddrOfMember { + struct A { int X; }; + typedef int (A::*P); + template struct S : T { + void f() { + P(&T::X) // expected-error {{cannot cast from type 'int *' to member pointer type 'P'}} + == &A::X; + } + }; + + void g() { + S().f(); // ok, &T::X is 'int (A::*)', not 'int *', even though T is a base class + } + + struct B : A { static int X; }; + void h() { + S().f(); // expected-note {{here}} + } +}