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.

llvm-svn: 162159
This commit is contained in:
Richard Smith 2012-08-18 00:55:03 +00:00
parent edee47891c
commit 4f605aff7f
17 changed files with 450 additions and 170 deletions

View File

@ -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();

View File

@ -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<TemplateIdAnnotation *, 16> TemplateIds;
/// \brief Identifiers which have been declared within a tentative parse.
SmallVector<IdentifierInfo *, 8> 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

View File

@ -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);

View File

@ -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();

View File

@ -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 ')'.

View File

@ -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.
// If the identifier was typo-corrected, try again.
if (Tok.isNot(tok::identifier))
goto Retry;
}
case Sema::NC_NestedNameSpecifier:
// FIXME: Implement this!
break;
}
}
// Fall through

View File

@ -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();

View File

@ -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<X>::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;
}

View File

@ -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<ElaboratedTypeLoc>(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<TypeDecl>(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<ObjCInterfaceDecl>(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<TemplateDecl>(FirstDecl) && !isa<FunctionTemplateDecl>(FirstDecl))
return NameClassification::TypeTemplate(
TemplateName(cast<TemplateDecl>(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<ClassTemplateDecl>(FirstDecl) &&
!isa<TypeAliasTemplateDecl>(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<TypeDecl>(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));

View File

@ -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}}
}

View File

@ -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}} \

View File

@ -25,3 +25,20 @@ namespace greatergreater {
(void)(&t<S<int>>==p); // expected-error {{use '> >'}} expected-error {{use '> ='}}
}
}
namespace PR5925 {
template <typename x>
class foo { // expected-note {{here}}
};
void bar(foo *X) { // expected-error {{requires template arguments}}
}
}
namespace PR13210 {
template <class T>
class C {}; // expected-note {{here}}
void f() {
new C(); // expected-error {{requires template arguments}}
}
}

View File

@ -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}}

View File

@ -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<ConstructExpr>(); // expected-error{{use of undeclared identifier 'ConstructExpr'; did you mean 'clash::ConstructExpr'?}}
getExprAs<ConstructExpr>(); // 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

View File

@ -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<typename T>
struct A {
typedef T type;
@ -59,6 +68,20 @@ void f(int, T::type, int) { } // expected-error{{missing 'typename'}}
template<typename T>
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<typename T> int A<T>::n(T::value); // ok
template<typename T>
A<T>::type // expected-error{{missing 'typename'}}

View File

@ -40,7 +40,7 @@ typedef N::C<float> c2;
// PR5655
template<typename T> 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 <typename T> class Party;

View File

@ -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<typename T> 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<A>().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<B>().f(); // expected-note {{here}}
}
}