Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -204,6 +204,9 @@ def err_expected_semi_after_static_assert : Error< "expected ';' after static_assert">; def err_expected_semi_for : Error<"expected ';' in 'for' statement specifier">; +def warn_single_decl_assign_in_for_range : Warning< + "range based for statement requires ':' after range declaration">, + InGroup; 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 @@ -1722,11 +1722,15 @@ bool AllowFunctionDefinitions, SourceLocation *DeclEnd = 0, ForRangeInit *FRI = 0); - Decl *ParseDeclarationAfterDeclarator(Declarator &D, - const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); + Decl *ParseDeclarationAfterDeclarator( + Declarator &D, + const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), + bool IsFirstDeclInGroup = false); bool ParseAsmAttributesAfterDeclarator(Declarator &D); - Decl *ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, - const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo()); + Decl *ParseDeclarationAfterDeclaratorAndAttributes( + Declarator &D, + const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), + bool IsFirstDeclInGroup = false); 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 @@ -1607,7 +1607,9 @@ } SmallVector DeclsInGroup; - Decl *FirstDecl = ParseDeclarationAfterDeclaratorAndAttributes(D); + Decl *FirstDecl = + ParseDeclarationAfterDeclaratorAndAttributes(D, ParsedTemplateInfo(), + /*IsFirstDeclInGroup*/ true); if (LateParsedAttrs.size() > 0) ParseLexedAttributeList(LateParsedAttrs, FirstDecl, true, false); D.complete(FirstDecl); @@ -1645,7 +1647,8 @@ ParseDeclarator(D); if (!D.isInvalidType()) { - Decl *ThisDecl = ParseDeclarationAfterDeclarator(D); + Decl *ThisDecl = ParseDeclarationAfterDeclarator(D, ParsedTemplateInfo(), + DeclsInGroup.empty()); D.complete(ThisDecl); if (ThisDecl) DeclsInGroup.push_back(ThisDecl); @@ -1713,16 +1716,20 @@ /// 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, + bool IsFirstDeclInGroup) { if (ParseAsmAttributesAfterDeclarator(D)) return 0; - return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo); + return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo, + IsFirstDeclInGroup); } -Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, - const ParsedTemplateInfo &TemplateInfo) { +Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( + Declarator &D, const ParsedTemplateInfo &TemplateInfo, + bool IsFirstDeclInGroup) { // Inform the current actions module that we just parsed this declarator. Decl *ThisDecl = 0; switch (TemplateInfo.Kind) { @@ -1787,7 +1794,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()) @@ -1816,6 +1823,16 @@ ExprResult Init(ParseInitializer()); + // If this is the only decl in (possibly) range based for statement, and + // it's initializing a variable with placeholder (auto) type, our best + // guess is that the user meant ':' instead of '='. + if (Tok.is(tok::r_paren) && getLangOpts().CPlusPlus11 && + D.getContext() == Declarator::ForContext && TypeContainsAuto && + IsFirstDeclInGroup) { + Diag(EqualLoc, diag::warn_single_decl_assign_in_for_range) + << FixItHint::CreateReplacement(EqualLoc, ":"); + } + if (getLangOpts().CPlusPlus && D.getCXXScopeSpec().isSet()) { Actions.ActOnCXXExitDeclInitializer(getCurScope(), ThisDecl); ExitScope(); 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,33 @@ 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 {{expected ';' in 'for' statement specifier}} + // expected-warning@-1 {{range based for statement requires ':' after range declaration}} + // expected-error@-2 {{expected ';' in 'for' statement specifier}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:19-[[@LINE-3]]:20}:":" + (void)foo; + for (auto i + = + v) // expected-error {{expected ';' in 'for' statement specifier}} + // expected-warning@-1 {{range based for statement requires ':' after range declaration}} + // expected-error@-2 {{expected ';' in 'for' statement specifier}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:7-[[@LINE-4]]:8}:":" + (void)i; +} +}