Among other things, this eliminates at least one redundant error case
and allows recovery in several cases where it wasn't possible before
(e.g. correcting a mistyped static_cast<>).
---
include/clang/Parse/Parser.h | 9 +++
include/clang/Sema/Sema.h | 4 +-
lib/Parse/ParseCXXInlineMethods.cpp | 1 +
lib/Parse/ParseDecl.cpp | 4 +-
lib/Parse/ParseDeclCXX.cpp | 2 +-
lib/Parse/ParseExpr.cpp | 28 ++++++---
lib/Parse/ParseExprCXX.cpp | 13 ++++-
lib/Parse/ParseObjc.cpp | 5 +-
lib/Parse/ParseOpenMP.cpp | 3 +-
lib/Parse/ParseStmt.cpp | 8 ++-
lib/Sema/SemaExpr.cpp | 110 ++++++++++++++++++++++++++++++++---
lib/Sema/SemaStmt.cpp | 19 ++++++
test/FixIt/fixit-unrecoverable.cpp | 4 +-
test/SemaCXX/typo-correction.cpp | 7 ++-
test/SemaTemplate/crash-10438657.cpp | 2 +-
15 files changed, 188 insertions(+), 31 deletions(-)
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index ce4db22..a10e828 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -351,6 +351,15 @@ private:
/// For typos, give a fixit to '='
bool isTokenEqualOrEqualTypo();
+ /// \brief Return the current token to the token stream and make the given
+ /// token the current token.
+ void UnconsumeToken(Token &Consumed) {
+ Token Next = Tok;
+ PP.EnterToken(Consumed);
+ ConsumeToken();
+ PP.EnterToken(Next);
+ }
+
/// ConsumeAnyToken - Dispatch to the right Consume* method based on the
/// current token type. This should only be used in cases where the type of
/// the token really isn't known, e.g. in error recovery.
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index fa017bc..350d193 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -3442,7 +3442,7 @@ public:
Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
UnqualifiedId &Id, bool HasTrailingLParen, bool IsAddressOfOperand,
std::unique_ptr<CorrectionCandidateCallback> CCC = nullptr,
- bool IsInlineAsmIdentifier = false);
+ bool IsInlineAsmIdentifier = false, Token *KeywordReplacement = nullptr);
void DecomposeUnqualifiedId(const UnqualifiedId &Id,
TemplateArgumentListInfo &Buffer,
@@ -3453,7 +3453,7 @@ public:
DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
std::unique_ptr<CorrectionCandidateCallback> CCC,
TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr,
- ArrayRef<Expr *> Args = None);
+ ArrayRef<Expr *> Args = None, TypoExpr **Out = nullptr);
ExprResult LookupInObjCMethod(LookupResult &LookUp, Scope *S,
IdentifierInfo *II,
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index 55caa5e..5cfdb24 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -336,6 +336,7 @@ void Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) {
DefArgResult = ParseBraceInitializer();
} else
DefArgResult = ParseAssignmentExpression();
+ DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult);
if (DefArgResult.isInvalid())
Actions.ActOnParamDefaultArgumentError(LM.DefaultArgs[I].Param,
EqualLoc);
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 7132b4a..47f7188 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -302,7 +302,8 @@ unsigned Parser::ParseAttributeArgsCommon(
Unevaluated.reset(
new EnterExpressionEvaluationContext(Actions, Sema::Unevaluated));
- ExprResult ArgExpr(ParseAssignmentExpression());
+ ExprResult ArgExpr(
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression()));
if (ArgExpr.isInvalid()) {
SkipUntil(tok::r_paren, StopAtSemi);
return 0;
@@ -5486,6 +5487,7 @@ void Parser::ParseParameterDeclarationClause(
DefArgResult = ParseBraceInitializer();
} else
DefArgResult = ParseAssignmentExpression();
+ DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult);
if (DefArgResult.isInvalid()) {
Actions.ActOnParamDefaultArgumentError(Param, EqualLoc);
SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 497f7e0..d0229e1 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -780,7 +780,7 @@ SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
// The operand of the decltype specifier is an unevaluated operand.
EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated,
nullptr,/*IsDecltype=*/true);
- Result = ParseExpression();
+ Result = Actions.CorrectDelayedTyposInExpr(ParseExpression());
if (Result.isInvalid()) {
DS.SetTypeSpecError();
if (SkipUntil(tok::r_paren, StopAtSemi | StopBeforeMatch)) {
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 3c8a454..7cfa4d3 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -260,6 +260,7 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
// 'logical-OR-expression' as we might expect.
TernaryMiddle = ParseExpression();
if (TernaryMiddle.isInvalid()) {
+ Actions.CorrectDelayedTyposInExpr(LHS);
LHS = ExprError();
TernaryMiddle = nullptr;
}
@@ -328,9 +329,11 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
else
RHS = ParseCastExpression(false);
- if (RHS.isInvalid())
+ if (RHS.isInvalid()) {
+ Actions.CorrectDelayedTyposInExpr(LHS);
LHS = ExprError();
-
+ }
+
// Remember the precedence of this operator and get the precedence of the
// operator immediately to the right of the RHS.
prec::Level ThisPrec = NextTokPrec;
@@ -359,8 +362,10 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
static_cast<prec::Level>(ThisPrec + !isRightAssoc));
RHSIsInitList = false;
- if (RHS.isInvalid())
+ if (RHS.isInvalid()) {
+ Actions.CorrectDelayedTyposInExpr(LHS);
LHS = ExprError();
+ }
NextTokPrec = getBinOpPrecedence(Tok.getKind(), GreaterThanIsOperator,
getLangOpts().CPlusPlus11);
@@ -397,7 +402,9 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) {
LHS = Actions.ActOnConditionalOp(OpToken.getLocation(), ColonLoc,
LHS.get(), TernaryMiddle.get(),
RHS.get());
- }
+ } else
+ // Ensure potential typos in the RHS aren't left undiagnosed.
+ Actions.CorrectDelayedTyposInExpr(RHS);
}
}
@@ -812,13 +819,20 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
UnqualifiedId Name;
CXXScopeSpec ScopeSpec;
SourceLocation TemplateKWLoc;
+ Token Replacement;
auto Validator = llvm::make_unique<CastExpressionIdValidator>(
isTypeCast != NotTypeCast, isTypeCast != IsTypeCast);
Validator->IsAddressOfOperand = isAddressOfOperand;
Name.setIdentifier(&II, ILoc);
- Res = Actions.ActOnIdExpression(getCurScope(), ScopeSpec, TemplateKWLoc,
- Name, Tok.is(tok::l_paren),
- isAddressOfOperand, std::move(Validator));
+ Res = Actions.ActOnIdExpression(
+ getCurScope(), ScopeSpec, TemplateKWLoc, Name, Tok.is(tok::l_paren),
+ isAddressOfOperand, std::move(Validator),
+ /*IsInlineAsmIdentifier=*/false, &Replacement);
+ if (!Res.isInvalid() && !Res.get()) {
+ UnconsumeToken(Replacement);
+ return ParseCastExpression(isUnaryExpression, isAddressOfOperand,
+ NotCastExpr, isTypeCast);
+ }
break;
}
case tok::char_constant: // constant: character-constant
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 27109ab..36dbf83 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -608,6 +608,7 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
+retry:
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/false,
@@ -622,8 +623,16 @@ ExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) {
if (isAddressOfOperand && isPostfixExpressionSuffixStart())
isAddressOfOperand = false;
- return Actions.ActOnIdExpression(getCurScope(), SS, TemplateKWLoc, Name,
- Tok.is(tok::l_paren), isAddressOfOperand);
+ Token Replacement;
+ ExprResult Result = Actions.ActOnIdExpression(
+ getCurScope(), SS, TemplateKWLoc, Name, Tok.is(tok::l_paren),
+ isAddressOfOperand, nullptr, /*IsInlineAsmIdentifier=*/false,
+ &Replacement);
+ if (!Result.isInvalid() && !Result.get()) {
+ UnconsumeToken(Replacement);
+ goto retry;
+ }
+ return Result;
}
/// ParseLambdaExpression - Parse a C++11 lambda expression.
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index 7fe72ec..f9234db 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -2217,7 +2217,10 @@ bool Parser::ParseObjCXXMessageReceiver(bool &IsExpr, void *&TypeOrExpr) {
if (!Actions.isSimpleTypeSpecifier(Tok.getKind())) {
// objc-receiver:
// expression
- ExprResult Receiver = ParseExpression();
+ // Make sure any typos in the receiver are corrected or diagnosed, so that
+ // proper recovery can happen. FIXME: Perhaps filter the corrected expr to
+ // only the things that are valid ObjC receivers?
+ ExprResult Receiver = Actions.CorrectDelayedTyposInExpr(ParseExpression());
if (Receiver.isInvalid())
return true;
diff --git a/lib/Parse/ParseOpenMP.cpp b/lib/Parse/ParseOpenMP.cpp
index 4044b11..26d3b0c 100644
--- a/lib/Parse/ParseOpenMP.cpp
+++ b/lib/Parse/ParseOpenMP.cpp
@@ -724,7 +724,8 @@ OMPClause *Parser::ParseOpenMPVarListClause(OpenMPClauseKind Kind) {
if (MustHaveTail) {
ColonLoc = Tok.getLocation();
ConsumeToken();
- ExprResult Tail = ParseAssignmentExpression();
+ ExprResult Tail =
+ Actions.CorrectDelayedTyposInExpr(ParseAssignmentExpression());
if (Tail.isUsable())
TailExpr = Tail.get();
else
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 4ba75f8..6478adc 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -642,6 +642,12 @@ StmtResult Parser::ParseCaseStatement(bool MissingCase, ExprResult Expr) {
ExprResult LHS;
if (!MissingCase) {
LHS = ParseConstantExpression();
+ if (!getLangOpts().CPlusPlus11) {
+ LHS = Actions.CorrectDelayedTyposInExpr(
+ LHS, [this](class Expr *E) -> ExprResult {
+ return this->Actions.VerifyIntegerConstantExpression(E);
+ });
+ }
if (LHS.isInvalid()) {
// If constant-expression is parsed unsuccessfully, recover by skipping
// current case statement (moving to the colon that ends it).
@@ -1564,7 +1570,7 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
}
} else {
ProhibitAttributes(attrs);
- Value = ParseExpression();
+ Value = Actions.CorrectDelayedTyposInExpr(ParseExpression());
ForEach = isTokIdentifier_in();
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 8ad1d4f..c07fd4c 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -1719,6 +1719,61 @@ Sema::DecomposeUnqualifiedId(const UnqualifiedId &Id,
}
}
+namespace {
+class EmptyLookupTypoDiags : public TypoDiagnosticGenerator {
+ Sema &SemaRef;
+ DeclContext *Ctx;
+ SourceRange ScopeSpecLoc;
+ DeclarationName Typo;
+ SourceLocation TypoLoc;
+ TemplateArgumentListInfo *ExplicitTemplateArgs;
+ ArrayRef<Expr *> Args;
+ unsigned DiagnosticID, DiagnosticSuggestID;
+
+public:
+ EmptyLookupTypoDiags(Sema &SemaRef, CXXScopeSpec &SS, DeclarationName Typo,
+ SourceLocation TypoLoc,
+ TemplateArgumentListInfo *ExplicitTemplateArgs,
+ ArrayRef<Expr *> Args, unsigned DiagnosticID,
+ unsigned DiagnosticSuggestID)
+ : SemaRef(SemaRef),
+ Ctx(SS.isEmpty() ? nullptr : SemaRef.computeDeclContext(SS, false)),
+ ScopeSpecLoc(SS.getRange()), Typo(Typo), TypoLoc(TypoLoc),
+ ExplicitTemplateArgs(ExplicitTemplateArgs), Args(Args),
+ DiagnosticID(DiagnosticID), DiagnosticSuggestID(DiagnosticSuggestID) {}
+
+ void operator()(const TypoCorrection &TC) override {
+ assert(!ExplicitTemplateArgs && "Diagnosing an empty lookup with explicit template args!");
+ if (TC) {
+ std::string CorrectedStr(TC.getAsString(SemaRef.getLangOpts()));
+ bool DroppedSpecifier =
+ TC.WillReplaceSpecifier() && Typo.getAsString() == CorrectedStr;
+ unsigned NoteID = (TC.getCorrectionDecl() &&
+ isa<ImplicitParamDecl>(TC.getCorrectionDecl()))
+ ? diag::note_implicit_param_decl
+ : diag::note_previous_decl;
+ if (!Ctx)
+ SemaRef.diagnoseTypo(TC, SemaRef.PDiag(DiagnosticSuggestID) << Typo,
+ SemaRef.PDiag(NoteID));
+ else
+ SemaRef.diagnoseTypo(TC, SemaRef.PDiag(diag::err_no_member_suggest)
+ << Typo << Ctx << DroppedSpecifier
+ << ScopeSpecLoc,
+ SemaRef.PDiag(NoteID));
+ return;
+ }
+ // Emit a special diagnostic for failed member lookups.
+ // FIXME: computing the declaration context might fail here (?)
+ if (Ctx) {
+ SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << Ctx
+ << ScopeSpecLoc;
+ } else {
+ SemaRef.Diag(TypoLoc, DiagnosticID) << Typo;
+ }
+ }
+};
+}
+
/// Diagnose an empty lookup.
///
/// \return false if new lookup candidates were found
@@ -1726,7 +1781,7 @@ bool
Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
std::unique_ptr<CorrectionCandidateCallback> CCC,
TemplateArgumentListInfo *ExplicitTemplateArgs,
- ArrayRef<Expr *> Args) {
+ ArrayRef<Expr *> Args, TypoExpr **Out) {
DeclarationName Name = R.getLookupName();
unsigned diagnostic = diag::err_undeclared_var_use;
@@ -1843,8 +1898,18 @@ Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
// We didn't find anything, so try to correct for a typo.
TypoCorrection Corrected;
- if (S && (Corrected = CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(),
- S, &SS, std::move(CCC), CTK_ErrorRecovery))) {
+ if (S && Out) {
+ *Out = CorrectTypoDelayed(
+ R.getLookupNameInfo(), R.getLookupKind(), S, &SS, std::move(CCC),
+ llvm::make_unique<EmptyLookupTypoDiags>(*this, SS, Name, R.getNameLoc(),
+ ExplicitTemplateArgs, Args,
+ diagnostic, diagnostic_suggest),
+ nullptr, CTK_ErrorRecovery);
+ if (*Out)
+ return true;
+ } else if (S && (Corrected =
+ CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), S,
+ &SS, std::move(CCC), CTK_ErrorRecovery))) {
std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
bool DroppedSpecifier =
Corrected.WillReplaceSpecifier() && Name.getAsString() == CorrectedStr;
@@ -1996,7 +2061,7 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
SourceLocation TemplateKWLoc, UnqualifiedId &Id,
bool HasTrailingLParen, bool IsAddressOfOperand,
std::unique_ptr<CorrectionCandidateCallback> CCC,
- bool IsInlineAsmIdentifier) {
+ bool IsInlineAsmIdentifier, Token *KeywordReplacement) {
assert(!(IsAddressOfOperand && HasTrailingLParen) &&
"cannot be direct & operand and have a trailing lparen");
if (SS.isInvalid())
@@ -2108,13 +2173,40 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
// If this name wasn't predeclared and if this is not a function
// call, diagnose the problem.
- auto DefaultValidator = llvm::make_unique<CorrectionCandidateCallback>();
+ TypoExpr *TE = nullptr;
+ auto DefaultValidator = llvm::make_unique<CorrectionCandidateCallback>(
+ II, SS.isValid() ? SS.getScopeRep() : nullptr);
DefaultValidator->IsAddressOfOperand = IsAddressOfOperand;
assert((!CCC || CCC->IsAddressOfOperand == IsAddressOfOperand) &&
"Typo correction callback misconfigured");
- if (DiagnoseEmptyLookup(S, SS, R,
- CCC ? std::move(CCC) : std::move(DefaultValidator)))
- return ExprError();
+ if (CCC) {
+ // Make sure the callback knows what the typo being diagnosed is.
+ CCC->setTypoName(II);
+ if (SS.isValid())
+ CCC->setTypoNNS(SS.getScopeRep());
+ }
+ if (DiagnoseEmptyLookup(
+ S, SS, R, CCC ? std::move(CCC) : std::move(DefaultValidator),
+ nullptr, None, getLangOpts().CPlusPlus ? &TE : nullptr)) {
+ if (TE && KeywordReplacement) {
+ auto &State = getTypoExprState(TE);
+ auto BestTC = State.Consumer->getNextCorrection();
+ if (BestTC.isKeyword()) {
+ auto *II = BestTC.getCorrectionAsIdentifierInfo();
+ if (State.DiagHandler)
+ (*State.DiagHandler)(BestTC);
+ KeywordReplacement->startToken();
+ KeywordReplacement->setKind(II->getTokenID());
+ KeywordReplacement->setIdentifierInfo(II);
+ KeywordReplacement->setLocation(BestTC.getCorrectionRange().getBegin());
+ // Signal that a correction to a keyword was performed by returning a
+ // valid-but-null ExprResult.
+ return (Expr*)nullptr;
+ }
+ State.Consumer->resetCorrectionStream();
+ }
+ return TE ? TE : ExprError();
+ }
assert(!R.empty() &&
"DiagnoseEmptyLookup returned false but added no results");
@@ -12410,6 +12502,8 @@ void Sema::UpdateMarkingForLValueToRValue(Expr *E) {
}
ExprResult Sema::ActOnConstantExpression(ExprResult Res) {
+ Res = CorrectDelayedTyposInExpr(Res);
+
if (!Res.isUsable())
return Res;
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index ab0bfcd..b4e2113 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -364,6 +364,25 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, Expr *LHSVal,
return StmtError();
}
+ ExprResult LHS = CorrectDelayedTyposInExpr(
+ LHSVal, [this](class Expr *E) -> ExprResult {
+ if (this->getLangOpts().CPlusPlus11) {
+ if (Expr *CondExpr =
+ this->getCurFunction()->SwitchStack.back()->getCond()) {
+ QualType CondType = CondExpr->getType();
+ llvm::APSInt TempVal;
+ return this->CheckConvertedConstantExpression(E, CondType, TempVal,
+ CCEK_CaseValue);
+ }
+ return ExprError();
+ } else {
+ return this->VerifyIntegerConstantExpression(E);
+ }
+ });
+ if (LHS.isInvalid())
+ return StmtError();
+ LHSVal = LHS.get();
+
if (!getLangOpts().CPlusPlus11) {
// C99 6.8.4.2p3: The expression shall be an integer constant.
// However, GCC allows any evaluatable integer expression.
diff --git a/test/FixIt/fixit-unrecoverable.cpp b/test/FixIt/fixit-unrecoverable.cpp
index 1e1f1b8..f555792 100644
--- a/test/FixIt/fixit-unrecoverable.cpp
+++ b/test/FixIt/fixit-unrecoverable.cpp
@@ -6,7 +6,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
float f(int y) {
- return static_cst<float>(y); // expected-error{{use of undeclared identifier 'static_cst'; did you mean 'static_cast'?}} \
- // expected-error{{for function-style cast or type construction}}
+ return static_cst<float>(y); // expected-error{{use of undeclared identifier 'static_cst'; did you mean 'static_cast'?}}
}
-
diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp
index a4f5174..4f19283 100644
--- a/test/SemaCXX/typo-correction.cpp
+++ b/test/SemaCXX/typo-correction.cpp
@@ -209,11 +209,12 @@ namespace PR13051 {
};
void foo(); // expected-note{{'foo' declared here}}
- void g(void(*)());
- void g(bool(S<int>::*)() const);
+ void g(void(*)()); // expected-note{{candidate function not viable}}
+ void g(bool(S<int>::*)() const); // expected-note{{candidate function not viable}}
void test() {
- g(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}}
+ g(&S<int>::tempalte f<int>); // expected-error{{did you mean 'template'?}} \
+ // expected-error{{no matching function for call to 'g'}}
g(&S<int>::opeartor bool); // expected-error{{did you mean 'operator'?}}
g(&S<int>::foo); // expected-error{{no member named 'foo' in 'PR13051::S<int>'; did you mean simply 'foo'?}}
}
diff --git a/test/SemaTemplate/crash-10438657.cpp b/test/SemaTemplate/crash-10438657.cpp
index 2ee64bd..3eaa8c1 100644
--- a/test/SemaTemplate/crash-10438657.cpp
+++ b/test/SemaTemplate/crash-10438657.cpp
@@ -1,6 +1,6 @@
// RUN: not %clang_cc1 -fsyntax-only %s 2> %t
// RUN: FileCheck %s < %t
-// CHECK: 10 errors
+// CHECK: 9 errors
template<typename _CharT>
class collate : public locale::facet {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits