Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -204,6 +204,8 @@ def err_expected_semi_after_static_assert : Error< "expected ';' after static_assert">; def err_expected_semi_for : Error<"expected ';' in 'for' statement specifier">; +def err_single_decl_assign_in_for_range : Error< + "range based for statement requires ':' after range declaration">; def warn_missing_selector_name : Warning< "%0 used as the name of the previous parameter rather than as part " "of the selector">, Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -1727,8 +1727,10 @@ Decl *ParseDeclarationAfterDeclarator(Declarator &D, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); bool ParseAsmAttributesAfterDeclarator(Declarator &D); - Decl *ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, - const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); + Decl *ParseDeclarationAfterDeclaratorAndAttributes( + Declarator &D, + const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), + ForRangeInit *FRI = nullptr); Decl *ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope); Decl *ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope); Index: lib/Parse/ParseDecl.cpp =================================================================== --- lib/Parse/ParseDecl.cpp +++ lib/Parse/ParseDecl.cpp @@ -1620,7 +1620,8 @@ } SmallVector DeclsInGroup; - Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(D); + Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes( + D, ParsedTemplateInfo(), FRI); if (LateParsedAttrs.size() > 0) ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false); D.complete(FirstDecl); @@ -1726,16 +1727,16 @@ /// According to the standard grammar, =default and =delete are function /// definitions, but that definitely doesn't fit with the parser here. /// -Decl *Parser::ParseDeclarationAfterDeclarator(Declarator &D, - const ParsedTemplateInfo &TemplateInfo) { +Decl *Parser::ParseDeclarationAfterDeclarator( + Declarator &D, const ParsedTemplateInfo &TemplateInfo) { if (ParseAsmAttributesAfterDeclarator(D)) return 0; return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo); } -Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, - const ParsedTemplateInfo &TemplateInfo) { +Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( + Declarator &D, const ParsedTemplateInfo &TemplateInfo, ForRangeInit *FRI) { // Inform the current actions module that we just parsed this declarator. Decl *ThisDecl = 0; switch (TemplateInfo.Kind) { @@ -1800,7 +1801,7 @@ // Parse declarator '=' initializer. // If a '==' or '+=' is found, suggest a fixit to '='. if (isTokenEqualOrEqualTypo()) { - ConsumeToken(); + SourceLocation EqualLoc = ConsumeToken(); if (Tok.is(tok::kw_delete)) { if (D.isFunctionDeclarator()) @@ -1829,13 +1830,30 @@ ExprResult Init(ParseInitializer()); + // If this is the only decl in (possibly) range based for statement, + // our best guess is that the user meant ':' instead of '='. + if (Tok.is(tok::r_paren) && getLangOpts().CPlusPlus11 && + D.getContext() == Declarator::ForContext && D.isFirstDeclarator()) { + Diag(EqualLoc, diag::err_single_decl_assign_in_for_range) + << FixItHint::CreateReplacement( + EqualLoc.isMacroID() + ? PP.getSourceManager().getSpellingLoc(EqualLoc) + : EqualLoc, + ":"); + // We are trying to stop parser from looking for ';' in this for + // statement, therefore preventing spurious errors to be issued. + FRI->ColonLoc = EqualLoc; + Init = ExprError(); + } + if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl); ExitScope(); } if (Init.isInvalid()) { - SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch); + if (!FRI || FRI->ColonLoc.isInvalid() || Tok.isNot(tok::r_paren)) + SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch); Actions.ActOnInitializerError(ThisDecl); } else Actions.AddInitializerToDecl(ThisDecl, Init.take(), Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -1479,12 +1479,10 @@ SourceLocation DeclStart = Tok.getLocation(), DeclEnd; StmtVector Stmts; - DeclGroupPtrTy DG = ParseSimpleDeclaration(Stmts, Declarator::ForContext, - DeclEnd, attrs, false, - MightBeForRangeStmt ? - &ForRangeInit : 0); + DeclGroupPtrTy DG = ParseSimpleDeclaration( + Stmts, Declarator::ForContext, DeclEnd, attrs, false, + MightBeForRangeStmt ? &ForRangeInit : nullptr); FirstPart = Actions.ActOnDeclStmt(DG, DeclStart, Tok.getLocation()); - if (ForRangeInit.ParsedForRangeDecl()) { Diag(ForRangeInit.ColonLoc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_for_range : diag::ext_for_range); Index: test/Parser/cxx0x-for-range.cpp =================================================================== --- test/Parser/cxx0x-for-range.cpp +++ test/Parser/cxx0x-for-range.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: not %clang_cc1 -fsyntax-only -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s template struct pair {}; @@ -28,3 +28,36 @@ return n; } + +namespace PR19176 { +struct Vector { + struct iterator { + int &operator*(); + iterator &operator++(); + iterator &operator++(int); + bool operator==(const iterator &) const; + }; + iterator begin(); + iterator end(); +}; + +void f() { + Vector v; + int a[] = {1, 2, 3, 4}; + for (auto foo = a) // expected-error {{range based for statement requires ':' after range declaration}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:19-[[@LINE-1]]:20}:":" + (void)foo; + for (auto i + = + v) // expected-error@-1 {{range based for statement requires ':' after range declaration}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:7-[[@LINE-2]]:8}:":" + (void)i; +#define FORRANGE(v, a) for (DECLVARWITHINIT(v) a) // expected-note {{expanded from macro}} +#define DECLAUTOVAR(v) auto v +#define DECLVARWITHINIT(v) DECLAUTOVAR(v) = // expected-note {{expanded from macro}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:43-[[@LINE-1]]:44}:":" + FORRANGE(i, a) { // expected-error {{range based for statement requires ':' after range declaration}} + + } +} +}