mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 08:46:06 +00:00
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:
parent
edee47891c
commit
4f605aff7f
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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 ')'.
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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}}
|
||||
}
|
||||
|
@ -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}} \
|
||||
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
@ -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}}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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'}}
|
||||
|
@ -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;
|
||||
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user