https://github.com/osamakader updated https://github.com/llvm/llvm-project/pull/166004
>From b0d5dd5d4d76714dd359ceeec5d50e049a729dd0 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader <[email protected]> Date: Sat, 1 Nov 2025 19:53:55 +0200 Subject: [PATCH] [clang] Reject 'auto' storage class with type specifier in C++ Previously, clang allowed 'auto int x = 1;' in C++ as an extension (for C compatibility), emitting only a warning. This was confusing since 'auto' in C++11+ is a type specifier, not a storage class. This patch: - Adds a new error diagnostic 'err_auto_type_specifier' - Updates the parser to emit an error (instead of warning) when 'auto' is used as a storage class with a type specifier in C++ mode - Preserves C23 behavior where 'auto int' is valid - Adds comprehensive tests Fixes #164273 Signed-off-by: Osama Abdelkader <[email protected]> --- .../clang/Basic/DiagnosticParseKinds.td | 2 + clang/lib/Parse/ParseDecl.cpp | 492 +++++++++--------- .../test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp | 36 +- .../dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp | 2 +- .../dcl.spec/dcl.type/dcl.spec.auto/p3.cpp | 7 +- clang/test/CXX/drs/cwg3xx.cpp | 11 +- clang/test/Parser/cxx-auto-type-specifier.cpp | 22 + clang/test/SemaCXX/auto-cxx0x.cpp | 2 +- clang/test/SemaCXX/class.cpp | 7 +- clang/test/SemaCXX/static-data-member.cpp | 7 +- 10 files changed, 329 insertions(+), 259 deletions(-) create mode 100644 clang/test/Parser/cxx-auto-type-specifier.cpp diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index e5e071f43fa75..baf91b107b8c4 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -399,6 +399,8 @@ def err_requires_clause_on_declarator_not_declaring_a_function : Error< "trailing requires clause can only be used when declaring a function">; def err_requires_clause_inside_parens : Error< "trailing requires clause should be placed outside parentheses">; +def err_auto_type_specifier : Error< + "'auto' cannot be combined with a type specifier in C++">; def ext_auto_storage_class : ExtWarn< "'auto' storage class specifier is not permitted in C++11, and will not " "be supported in future releases">, InGroup<DiagGroup<"auto-storage-class">>; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 7e4a164e34eda..47b17ba7510ec 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -674,9 +674,9 @@ void Parser::ParseGNUAttributeArgs( // These may refer to the function arguments, but need to be parsed early to // participate in determining whether it's a redeclaration. std::optional<ParseScope> PrototypeScope; - if (normalizeAttrName(AttrName->getName()) == "enable_if" && - D && D->isFunctionDeclarator()) { - const DeclaratorChunk::FunctionTypeInfo& FTI = D->getFunctionTypeInfo(); + if (normalizeAttrName(AttrName->getName()) == "enable_if" && D && + D->isFunctionDeclarator()) { + const DeclaratorChunk::FunctionTypeInfo &FTI = D->getFunctionTypeInfo(); PrototypeScope.emplace(this, Scope::FunctionPrototypeScope | Scope::FunctionDeclarationScope | Scope::DeclScope); @@ -1090,8 +1090,7 @@ void Parser::ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs) { IdentifierInfo *AttrName = Tok.getIdentifierInfo(); SourceLocation AttrNameLoc = ConsumeToken(); if (!getLangOpts().ObjC) - Diag(AttrNameLoc, diag::ext_nullability) - << AttrName; + Diag(AttrNameLoc, diag::ext_nullability) << AttrName; attrs.addNew(AttrName, AttrNameLoc, AttributeScopeInfo(), nullptr, 0, Kind); break; @@ -1121,7 +1120,7 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { // lexer, which is that it handles something like 1.2.3 as a single // numeric constant, rather than two separate tokens. SmallString<512> Buffer; - Buffer.resize(Tok.getLength()+1); + Buffer.resize(Tok.getLength() + 1); const char *ThisTokBegin = &Buffer[0]; // Get the spelling of the token, which eliminates trigraphs, etc. @@ -1158,8 +1157,8 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { } const char AfterMajorSeparator = ThisTokBegin[AfterMajor]; - if (!VersionNumberSeparator(AfterMajorSeparator) - || (AfterMajor + 1 == ActualLength)) { + if (!VersionNumberSeparator(AfterMajorSeparator) || + (AfterMajor + 1 == ActualLength)) { Diag(Tok, diag::err_expected_version); SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch | StopAtCodeCompletion); @@ -1291,7 +1290,7 @@ void Parser::ParseAvailabilityAttribute( if (Keyword == Ident_strict) { if (StrictLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) - << Keyword << SourceRange(StrictLoc); + << Keyword << SourceRange(StrictLoc); } StrictLoc = KeywordLoc; continue; @@ -1300,7 +1299,7 @@ void Parser::ParseAvailabilityAttribute( if (Keyword == Ident_unavailable) { if (UnavailableLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) - << Keyword << SourceRange(UnavailableLoc); + << Keyword << SourceRange(UnavailableLoc); } UnavailableLoc = KeywordLoc; continue; @@ -1311,8 +1310,7 @@ void Parser::ParseAvailabilityAttribute( // For swift, we deprecate for all versions. if (Changes[Deprecated].KeywordLoc.isValid()) { Diag(KeywordLoc, diag::err_availability_redundant) - << Keyword - << SourceRange(Changes[Deprecated].KeywordLoc); + << Keyword << SourceRange(Changes[Deprecated].KeywordLoc); } Changes[Deprecated].KeywordLoc = KeywordLoc; @@ -1337,7 +1335,7 @@ void Parser::ParseAvailabilityAttribute( if (Keyword == Ident_message || Keyword == Ident_replacement) { if (!isTokenStringLiteral()) { Diag(Tok, diag::err_expected_string_literal) - << /*Source='availability attribute'*/2; + << /*Source='availability attribute'*/ 2; SkipUntil(tok::r_paren, StopAtSemi); return; } @@ -1393,9 +1391,9 @@ void Parser::ParseAvailabilityAttribute( if (Index < Unknown) { if (!Changes[Index].KeywordLoc.isInvalid()) { Diag(KeywordLoc, diag::err_availability_redundant) - << Keyword - << SourceRange(Changes[Index].KeywordLoc, - Changes[Index].VersionRange.getEnd()); + << Keyword + << SourceRange(Changes[Index].KeywordLoc, + Changes[Index].VersionRange.getEnd()); } Changes[Index].KeywordLoc = KeywordLoc; @@ -1403,7 +1401,7 @@ void Parser::ParseAvailabilityAttribute( Changes[Index].VersionRange = VersionRange; } else { Diag(KeywordLoc, diag::err_availability_unknown_change) - << Keyword << VersionRange; + << Keyword << VersionRange; } } while (TryConsumeToken(tok::comma)); @@ -1423,8 +1421,8 @@ void Parser::ParseAvailabilityAttribute( if (Changes[Index].KeywordLoc.isValid()) { if (!Complained) { Diag(UnavailableLoc, diag::warn_availability_and_unavailable) - << SourceRange(Changes[Index].KeywordLoc, - Changes[Index].VersionRange.getEnd()); + << SourceRange(Changes[Index].KeywordLoc, + Changes[Index].VersionRange.getEnd()); Complained = true; } @@ -1735,7 +1733,7 @@ void Parser::ParseTypeTagForDatatypeAttribute( bool Parser::DiagnoseProhibitedCXX11Attribute() { assert(Tok.is(tok::l_square) && NextToken().is(tok::l_square)); - switch (isCXX11AttributeSpecifier(/*Disambiguate*/true)) { + switch (isCXX11AttributeSpecifier(/*Disambiguate*/ true)) { case CXX11AttributeKind::NotAttributeSpecifier: // No diagnostic: we're in Obj-C++11 and this is not actually an attribute. return false; @@ -1752,7 +1750,7 @@ bool Parser::DiagnoseProhibitedCXX11Attribute() { assert(Tok.is(tok::r_square) && "isCXX11AttributeSpecifier lied"); SourceLocation EndLoc = ConsumeBracket(); Diag(BeginLoc, diag::err_attributes_not_allowed) - << SourceRange(BeginLoc, EndLoc); + << SourceRange(BeginLoc, EndLoc); return true; } llvm_unreachable("All cases handled above."); @@ -1859,8 +1857,7 @@ void Parser::stripTypeAttributesOffDeclSpec(ParsedAttributes &Attrs, llvm::SmallVector<ParsedAttr *, 1> ToBeMoved; for (ParsedAttr &AL : DS.getAttributes()) { - if ((AL.getKind() == ParsedAttr::AT_Aligned && - AL.isDeclspecAttribute()) || + if ((AL.getKind() == ParsedAttr::AT_Aligned && AL.isDeclspecAttribute()) || AL.isMicrosoftAttribute()) ToBeMoved.push_back(&AL); } @@ -1956,14 +1953,15 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration( if (Tok.is(tok::semi)) { ProhibitAttributes(DeclAttrs); DeclEnd = Tok.getLocation(); - if (RequireSemi) ConsumeToken(); + if (RequireSemi) + ConsumeToken(); RecordDecl *AnonRecord = nullptr; Decl *TheDecl = Actions.ParsedFreeStandingDeclSpec( getCurScope(), AS_none, DS, ParsedAttributesView::none(), AnonRecord); Actions.ActOnDefinedDeclarationSpecifier(TheDecl); DS.complete(TheDecl); if (AnonRecord) { - Decl* decls[] = {AnonRecord, TheDecl}; + Decl *decls[] = {AnonRecord, TheDecl}; return Actions.BuildDeclaratorGroup(decls); } return Actions.ConvertDeclToDeclGroup(TheDecl); @@ -2097,8 +2095,7 @@ void Parser::SkipMalformedDecl() { case tok::at: // @end is very much like } in Objective-C contexts. - if (NextToken().isObjCAtKeyword(tok::objc_end) && - ParsingInObjCContainer) + if (NextToken().isObjCAtKeyword(tok::objc_end) && ParsingInObjCContainer) return; break; @@ -2371,7 +2368,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // the start of a declarator. The comma was probably a typo for a // semicolon. Diag(CommaLoc, diag::err_expected_semi_declaration) - << FixItHint::CreateReplacement(CommaLoc, ";"); + << FixItHint::CreateReplacement(CommaLoc, ";"); ExpectSemi = false; break; } @@ -2529,8 +2526,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( case ParsedTemplateKind::Template: case ParsedTemplateKind::ExplicitSpecialization: { ThisDecl = Actions.ActOnTemplateDeclarator(getCurScope(), - *TemplateInfo.TemplateParams, - D); + *TemplateInfo.TemplateParams, D); if (VarTemplateDecl *VT = dyn_cast_or_null<VarTemplateDecl>(ThisDecl)) { // Re-direct this decl to refer to the templated decl so that we can // initialize it. @@ -2577,7 +2573,7 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( } } break; - } + } } SemaCUDA::CUDATargetContextRAII X(Actions.CUDA(), @@ -2590,14 +2586,14 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( if (Tok.is(tok::kw_delete)) { if (D.isFunctionDeclarator()) Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration) - << 1 /* delete */; + << 1 /* delete */; else Diag(ConsumeToken(), diag::err_deleted_non_function); SkipDeletedFunctionBody(); } else if (Tok.is(tok::kw_default)) { if (D.isFunctionDeclarator()) Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration) - << 0 /* default */; + << 0 /* default */; else Diag(ConsumeToken(), diag::err_default_special_members) << getLangOpts().CPlusPlus20; @@ -2689,9 +2685,8 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( // Match the ')'. T.consumeClose(); - ExprResult Initializer = Actions.ActOnParenListExpr(T.getOpenLocation(), - T.getCloseLocation(), - Exprs); + ExprResult Initializer = Actions.ActOnParenListExpr( + T.getOpenLocation(), T.getCloseLocation(), Exprs); Actions.AddInitializerToDecl(ThisDecl, Initializer.get(), /*DirectInit=*/true); } @@ -2747,7 +2742,8 @@ void Parser::ParseSpecifierQualifierList( // Issue diagnostic and remove storage class if present. if (Specs & DeclSpec::PQ_StorageClassSpecifier) { if (DS.getStorageClassSpecLoc().isValid()) - Diag(DS.getStorageClassSpecLoc(),diag::err_typename_invalid_storageclass); + Diag(DS.getStorageClassSpecLoc(), + diag::err_typename_invalid_storageclass); else Diag(DS.getThreadStorageClassSpecLoc(), diag::err_typename_invalid_storageclass); @@ -2843,7 +2839,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, // Don't require a type specifier if we have the 'auto' storage class // specifier in C++98 -- we'll promote it to a type specifier. if (SS) - AnnotateScopeToken(*SS, /*IsNewAnnotation*/false); + AnnotateScopeToken(*SS, /*IsNewAnnotation*/ false); return false; } @@ -2876,18 +2872,33 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, tok::TokenKind TagKind = tok::unknown; switch (Actions.isTagName(*Tok.getIdentifierInfo(), getCurScope())) { - default: break; - case DeclSpec::TST_enum: - TagName="enum" ; FixitTagName = "enum " ; TagKind=tok::kw_enum ;break; - case DeclSpec::TST_union: - TagName="union" ; FixitTagName = "union " ;TagKind=tok::kw_union ;break; - case DeclSpec::TST_struct: - TagName="struct"; FixitTagName = "struct ";TagKind=tok::kw_struct;break; - case DeclSpec::TST_interface: - TagName="__interface"; FixitTagName = "__interface "; - TagKind=tok::kw___interface;break; - case DeclSpec::TST_class: - TagName="class" ; FixitTagName = "class " ;TagKind=tok::kw_class ;break; + default: + break; + case DeclSpec::TST_enum: + TagName = "enum"; + FixitTagName = "enum "; + TagKind = tok::kw_enum; + break; + case DeclSpec::TST_union: + TagName = "union"; + FixitTagName = "union "; + TagKind = tok::kw_union; + break; + case DeclSpec::TST_struct: + TagName = "struct"; + FixitTagName = "struct "; + TagKind = tok::kw_struct; + break; + case DeclSpec::TST_interface: + TagName = "__interface"; + FixitTagName = "__interface "; + TagKind = tok::kw___interface; + break; + case DeclSpec::TST_class: + TagName = "class"; + FixitTagName = "class "; + TagKind = tok::kw_class; + break; } if (TagName) { @@ -2896,14 +2907,14 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, Sema::LookupOrdinaryName); Diag(Loc, diag::err_use_of_tag_name_without_tag) - << TokenName << TagName << getLangOpts().CPlusPlus - << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName); + << TokenName << TagName << getLangOpts().CPlusPlus + << FixItHint::CreateInsertion(Tok.getLocation(), FixitTagName); if (Actions.LookupName(R, getCurScope())) { - for (LookupResult::iterator I = R.begin(), IEnd = R.end(); - I != IEnd; ++I) + for (LookupResult::iterator I = R.begin(), IEnd = R.end(); I != IEnd; + ++I) Diag((*I)->getLocation(), diag::note_decl_hiding_tag_type) - << TokenName << TagName; + << TokenName << TagName; } // Parse this as a tag as if the missing tag were present. @@ -2934,7 +2945,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, // parse to determine which case we're in. TentativeParsingAction PA(*this); ConsumeToken(); - TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/false); + TPResult TPR = TryParseDeclarator(/*mayBeAbstract*/ false); PA.Revert(); if (TPR != TPResult::False) { @@ -2950,8 +2961,8 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, IdentifierInfo *II = Tok.getIdentifierInfo(); if (Actions.isCurrentClassNameTypo(II, SS)) { Diag(Loc, diag::err_constructor_bad_name) - << Tok.getIdentifierInfo() << II - << FixItHint::CreateReplacement(Tok.getLocation(), II->getName()); + << Tok.getIdentifierInfo() << II + << FixItHint::CreateReplacement(Tok.getLocation(), II->getName()); Tok.setIdentifierInfo(II); } } @@ -2970,7 +2981,7 @@ bool Parser::ParseImplicitInt(DeclSpec &DS, CXXScopeSpec *SS, if (getCurScope()->isFunctionPrototypeScope()) break; if (SS) - AnnotateScopeToken(*SS, /*IsNewAnnotation*/false); + AnnotateScopeToken(*SS, /*IsNewAnnotation*/ false); return false; default: @@ -3242,15 +3253,14 @@ ExprResult Parser::ParseExtIntegerArgument() { return ExprError(); } - if(T.consumeClose()) + if (T.consumeClose()) return ExprError(); return ER; } -bool -Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS, - DeclSpecContext DSContext, - LateParsedAttrList *LateAttrs) { +bool Parser::DiagnoseMissingSemiAfterTagDefinition( + DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext, + LateParsedAttrList *LateAttrs) { assert(DS.hasTagDefinition() && "shouldn't call this"); bool EnteringContext = (DSContext == DeclSpecContext::DSC_class || @@ -3402,15 +3412,19 @@ void Parser::ParseDeclarationSpecifiers( SourceLocation Loc = Tok.getLocation(); // Helper for image types in OpenCL. - auto handleOpenCLImageKW = [&] (StringRef Ext, TypeSpecifierType ImageTypeSpec) { - // Check if the image type is supported and otherwise turn the keyword into an identifier - // because image types from extensions are not reserved identifiers. - if (!StringRef(Ext).empty() && !getActions().getOpenCLOptions().isSupported(Ext, getLangOpts())) { + auto handleOpenCLImageKW = [&](StringRef Ext, + TypeSpecifierType ImageTypeSpec) { + // Check if the image type is supported and otherwise turn the keyword + // into an identifier because image types from extensions are not reserved + // identifiers. + if (!StringRef(Ext).empty() && + !getActions().getOpenCLOptions().isSupported(Ext, getLangOpts())) { Tok.getIdentifierInfo()->revertTokenIDToIdentifier(); Tok.setKind(tok::identifier); return false; } - isInvalid = DS.SetTypeSpecType(ImageTypeSpec, Loc, PrevSpec, DiagID, Policy); + isInvalid = + DS.SetTypeSpecType(ImageTypeSpec, Loc, PrevSpec, DiagID, Policy); return true; }; @@ -3506,14 +3520,13 @@ void Parser::ParseDeclarationSpecifiers( SemaCodeCompletion::ParserCompletionContext CCC = SemaCodeCompletion::PCC_Namespace; if (DS.hasTypeSpecifier()) { - bool AllowNonIdentifiers - = (getCurScope()->getFlags() & (Scope::ControlScope | - Scope::BlockScope | - Scope::TemplateParamScope | - Scope::FunctionPrototypeScope | - Scope::AtCatchScope)) == 0; - bool AllowNestedNameSpecifiers - = DSContext == DeclSpecContext::DSC_top_level || + bool AllowNonIdentifiers = + (getCurScope()->getFlags() & + (Scope::ControlScope | Scope::BlockScope | + Scope::TemplateParamScope | Scope::FunctionPrototypeScope | + Scope::AtCatchScope)) == 0; + bool AllowNestedNameSpecifiers = + DSContext == DeclSpecContext::DSC_top_level || (DSContext == DeclSpecContext::DSC_class && DS.isFriendSpecified()); cutOffParsing(); @@ -3558,9 +3571,8 @@ void Parser::ParseDeclarationSpecifiers( CXXScopeSpec SS; if (TemplateInfo.TemplateParams) SS.setTemplateParamLists(*TemplateInfo.TemplateParams); - Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(), - Tok.getAnnotationRange(), - SS); + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); // We are looking for a qualified typename. Token Next = NextToken(); @@ -3623,8 +3635,8 @@ void Parser::ParseDeclarationSpecifiers( ConsumeAnnotationToken(); // The C++ scope. TypeResult T = getTypeAnnotation(Tok); isInvalid = DS.SetTypeSpecType(DeclSpec::TST_typename, - Tok.getAnnotationEndLoc(), - PrevSpec, DiagID, T, Policy); + Tok.getAnnotationEndLoc(), PrevSpec, + DiagID, T, Policy); if (isInvalid) break; DS.SetRangeEnd(Tok.getAnnotationEndLoc()); @@ -3653,8 +3665,7 @@ void Parser::ParseDeclarationSpecifiers( &SS) && isConstructorDeclarator(/*Unqualified=*/false, /*DeductionGuide=*/false, - DS.isFriendSpecified(), - &TemplateInfo)) + DS.isFriendSpecified(), &TemplateInfo)) goto DoneWithDeclSpec; // C++20 [temp.spec] 13.9/6. @@ -3887,8 +3898,7 @@ void Parser::ParseDeclarationSpecifiers( if (Tok.is(tok::less) && getLangOpts().ObjC) { SourceLocation NewEndLoc; TypeResult NewTypeRep = parseObjCTypeArgsAndProtocolQualifiers( - Loc, TypeRep, /*consumeLastToken=*/true, - NewEndLoc); + Loc, TypeRep, /*consumeLastToken=*/true, NewEndLoc); if (NewTypeRep.isUsable()) { DS.UpdateTypeRep(NewTypeRep.get()); DS.SetRangeEnd(NewEndLoc); @@ -3931,10 +3941,10 @@ void Parser::ParseDeclarationSpecifiers( break; } if (!NextToken().isOneOf(tok::kw_auto, tok::kw_decltype)) - goto DoneWithDeclSpec; + goto DoneWithDeclSpec; if (TemplateId && !isInvalid && Actions.CheckTypeConstraint(TemplateId)) - TemplateId = nullptr; + TemplateId = nullptr; ConsumeAnnotationToken(); SourceLocation AutoLoc = Tok.getLocation(); @@ -3948,9 +3958,8 @@ void Parser::ParseDeclarationSpecifiers( // Something like `void foo(Iterator decltype(int) i)` Tracker.skipToEnd(); Diag(Tok, diag::err_placeholder_expected_auto_or_decltype_auto) - << FixItHint::CreateReplacement(SourceRange(AutoLoc, - Tok.getLocation()), - "auto"); + << FixItHint::CreateReplacement( + SourceRange(AutoLoc, Tok.getLocation()), "auto"); } else { Tracker.consumeClose(); } @@ -4095,11 +4104,22 @@ void Parser::ParseDeclarationSpecifiers( case tok::kw_auto: if (getLangOpts().CPlusPlus11 || getLangOpts().C23) { if (isKnownToBeTypeSpecifier(GetLookAheadToken(1))) { - isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, - PrevSpec, DiagID, Policy); - if (!isInvalid && !getLangOpts().C23) - Diag(Tok, diag::ext_auto_storage_class) - << FixItHint::CreateRemoval(DS.getStorageClassSpecLoc()); + // In C++ (not C23), 'auto' cannot be combined with a type specifier. + // However, OpenCL has its own error handling for this case. + if (getLangOpts().CPlusPlus && !getLangOpts().C23 && + !getLangOpts().OpenCLCPlusPlus) { + // In C++11+, 'auto' cannot be combined with a type specifier. + // Don't set the storage class specifier to avoid Sema emitting a + // redundant error (GCC only emits one error for this case: "two or + // more data types in declaration"). + isInvalid = true; + PrevSpec = "auto"; + DiagID = diag::err_auto_type_specifier; + } else { + // C23 allows 'auto' as storage class with type specifier. + isInvalid = DS.SetStorageClassSpec(Actions, DeclSpec::SCS_auto, Loc, + PrevSpec, DiagID, Policy); + } } else isInvalid = DS.SetTypeSpecType(DeclSpec::TST_auto, Loc, PrevSpec, DiagID, Policy); @@ -4281,26 +4301,26 @@ void Parser::ParseDeclarationSpecifiers( case tok::kw__Complex: if (!getLangOpts().C99) Diag(Tok, diag::ext_c99_feature) << Tok.getName(); - isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_complex, Loc, PrevSpec, - DiagID); + isInvalid = + DS.SetTypeSpecComplex(DeclSpec::TSC_complex, Loc, PrevSpec, DiagID); break; case tok::kw__Imaginary: if (!getLangOpts().C99) Diag(Tok, diag::ext_c99_feature) << Tok.getName(); - isInvalid = DS.SetTypeSpecComplex(DeclSpec::TSC_imaginary, Loc, PrevSpec, - DiagID); + isInvalid = + DS.SetTypeSpecComplex(DeclSpec::TSC_imaginary, Loc, PrevSpec, DiagID); break; case tok::kw_void: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec, - DiagID, Policy); + isInvalid = + DS.SetTypeSpecType(DeclSpec::TST_void, Loc, PrevSpec, DiagID, Policy); break; case tok::kw_char: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec, - DiagID, Policy); + isInvalid = + DS.SetTypeSpecType(DeclSpec::TST_char, Loc, PrevSpec, DiagID, Policy); break; case tok::kw_int: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec, - DiagID, Policy); + isInvalid = + DS.SetTypeSpecType(DeclSpec::TST_int, Loc, PrevSpec, DiagID, Policy); break; case tok::kw__ExtInt: case tok::kw__BitInt: { @@ -4317,16 +4337,16 @@ void Parser::ParseDeclarationSpecifiers( DiagID, Policy); break; case tok::kw_half: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, - DiagID, Policy); + isInvalid = + DS.SetTypeSpecType(DeclSpec::TST_half, Loc, PrevSpec, DiagID, Policy); break; case tok::kw___bf16: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_BFloat16, Loc, PrevSpec, DiagID, Policy); break; case tok::kw_float: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec, - DiagID, Policy); + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_float, Loc, PrevSpec, DiagID, + Policy); break; case tok::kw_double: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_double, Loc, PrevSpec, @@ -4365,12 +4385,12 @@ void Parser::ParseDeclarationSpecifiers( DiagID, Policy); break; case tok::kw_wchar_t: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_wchar, Loc, PrevSpec, - DiagID, Policy); + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_wchar, Loc, PrevSpec, DiagID, + Policy); break; case tok::kw_char8_t: - isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char8, Loc, PrevSpec, - DiagID, Policy); + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char8, Loc, PrevSpec, DiagID, + Policy); break; case tok::kw_char16_t: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_char16, Loc, PrevSpec, @@ -4440,15 +4460,15 @@ void Parser::ParseDeclarationSpecifiers( // We only need to enumerate each image type once. #define IMAGE_READ_WRITE_TYPE(Type, Id, Ext) #define IMAGE_WRITE_TYPE(Type, Id, Ext) -#define IMAGE_READ_TYPE(ImgType, Id, Ext) \ - case tok::kw_##ImgType##_t: \ - if (!handleOpenCLImageKW(Ext, DeclSpec::TST_##ImgType##_t)) \ - goto DoneWithDeclSpec; \ - break; +#define IMAGE_READ_TYPE(ImgType, Id, Ext) \ + case tok::kw_##ImgType##_t: \ + if (!handleOpenCLImageKW(Ext, DeclSpec::TST_##ImgType##_t)) \ + goto DoneWithDeclSpec; \ + break; #include "clang/Basic/OpenCLImageTypes.def" case tok::kw___unknown_anytype: - isInvalid = DS.SetTypeSpecType(TST_unknown_anytype, Loc, - PrevSpec, DiagID, Policy); + isInvalid = DS.SetTypeSpecType(TST_unknown_anytype, Loc, PrevSpec, DiagID, + Policy); break; // class-specifier: @@ -4463,8 +4483,8 @@ void Parser::ParseDeclarationSpecifiers( // To produce better diagnostic, we parse them when // parsing class specifier. ParsedAttributes Attributes(AttrFactory); - ParseClassSpecifier(Kind, Loc, DS, TemplateInfo, AS, - EnteringContext, DSContext, Attributes); + ParseClassSpecifier(Kind, Loc, DS, TemplateInfo, AS, EnteringContext, + DSContext, Attributes); // If there are attributes following class specifier, // take them over and handle them here. @@ -4696,7 +4716,7 @@ void Parser::ParseStructDeclaration( if (Tok.is(tok::kw___extension__)) { // __extension__ silences extension warnings in the subexpression. - ExtensionRAIIObject O(Diags); // Use RAII to do this. + ExtensionRAIIObject O(Diags); // Use RAII to do this. ConsumeToken(); return ParseStructDeclaration(DS, FieldsCallback, LateFieldAttrs); } @@ -4851,7 +4871,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, if (T.consumeOpen()) return; - ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope); + ParseScope StructScope(this, Scope::ClassScope | Scope::DeclScope); Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); // `LateAttrParseExperimentalExtOnly=true` requests that only attributes @@ -5018,8 +5038,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, // we don't suppress if this turns out to be an elaborated type // specifier. bool shouldDelayDiagsInTag = - (TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation || - TemplateInfo.Kind == ParsedTemplateKind::ExplicitSpecialization); + (TemplateInfo.Kind == ParsedTemplateKind::ExplicitInstantiation || + TemplateInfo.Kind == ParsedTemplateKind::ExplicitSpecialization); SuppressAccessChecks diagsFromTag(*this, shouldDelayDiagsInTag); // Determine whether this declaration is permitted to have an enum-base. @@ -5057,7 +5077,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, SS = Spec; } - // Must have either 'enum name' or 'enum {...}' or (rarely) 'enum : T { ... }'. + // Must have either 'enum name' or 'enum {...}' or (rarely) 'enum : T { ... + // }'. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) && Tok.isNot(tok::colon)) { Diag(Tok, diag::err_expected_either) << tok::identifier << tok::l_brace; @@ -5142,7 +5163,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, DeclaratorContext::TypeName); BaseType = Actions.ActOnTypeName(DeclaratorInfo); - BaseRange = SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd()); + BaseRange = + SourceRange(ColonLoc, DeclaratorInfo.getSourceRange().getEnd()); if (!getLangOpts().ObjC) { if (getLangOpts().CPlusPlus) @@ -5176,7 +5198,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, else if (Tok.is(tok::l_brace)) { if (DS.isFriendSpecified()) { Diag(Tok.getLocation(), diag::err_friend_decl_defines_type) - << SourceRange(DS.getFriendSpecLoc()); + << SourceRange(DS.getFriendSpecLoc()); ConsumeBrace(); SkipUntil(tok::r_brace, StopAtSemi); // Discard any other definition-only pieces. @@ -5265,7 +5287,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, << (AllowEnumSpecifier == AllowDefiningTypeSpec::Yes) << BaseRange; else if (ScopedEnumKWLoc.isValid()) Diag(ScopedEnumKWLoc, diag::ext_elaborated_enum_class) - << FixItHint::CreateRemoval(ScopedEnumKWLoc) << IsScopedUsingClassTag; + << FixItHint::CreateRemoval(ScopedEnumKWLoc) << IsScopedUsingClassTag; } stripTypeAttributesOffDeclSpec(attrs, DS, TUK); @@ -5282,14 +5304,15 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, const char *PrevSpec = nullptr; unsigned DiagID; Decl *TagDecl = - Actions.ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, - Name, NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(), - TParams, Owned, IsDependent, ScopedEnumKWLoc, - IsScopedUsingClassTag, + Actions + .ActOnTag(getCurScope(), DeclSpec::TST_enum, TUK, StartLoc, SS, Name, + NameLoc, attrs, AS, DS.getModulePrivateSpecLoc(), TParams, + Owned, IsDependent, ScopedEnumKWLoc, IsScopedUsingClassTag, BaseType, DSC == DeclSpecContext::DSC_type_specifier, DSC == DeclSpecContext::DSC_template_param || DSC == DeclSpecContext::DSC_template_type_arg, - OffsetOfState, &SkipBody).get(); + OffsetOfState, &SkipBody) + .get(); if (SkipBody.ShouldSkip) { assert(TUK == TagUseKind::Definition && "can only skip a definition"); @@ -5299,8 +5322,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, T.skipToEnd(); if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, - NameLoc.isValid() ? NameLoc : StartLoc, - PrevSpec, DiagID, TagDecl, Owned, + NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, + DiagID, TagDecl, Owned, Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; return; @@ -5323,8 +5346,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, } if (DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, - NameLoc.isValid() ? NameLoc : StartLoc, - PrevSpec, DiagID, Type.get(), + NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, + DiagID, Type.get(), Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; @@ -5354,8 +5377,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, } if (DS.SetTypeSpecType(DeclSpec::TST_enum, StartLoc, - NameLoc.isValid() ? NameLoc : StartLoc, - PrevSpec, DiagID, TagDecl, Owned, + NameLoc.isValid() ? NameLoc : StartLoc, PrevSpec, + DiagID, TagDecl, Owned, Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; } @@ -5429,7 +5452,7 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl, // We're missing a comma between enumerators. SourceLocation Loc = getEndOfPreviousToken(); Diag(Loc, diag::err_enumerator_list_missing_comma) - << FixItHint::CreateInsertion(Loc, ", "); + << FixItHint::CreateInsertion(Loc, ", "); continue; } @@ -5438,8 +5461,8 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl, SourceLocation CommaLoc; if (Tok.isNot(tok::r_brace) && !TryConsumeToken(tok::comma, CommaLoc)) { if (EqualLoc.isValid()) - Diag(Tok.getLocation(), diag::err_expected_either) << tok::r_brace - << tok::comma; + Diag(Tok.getLocation(), diag::err_expected_either) + << tok::r_brace << tok::comma; else Diag(Tok.getLocation(), diag::err_expected_end_of_enumerator); if (SkipUntil(tok::comma, tok::r_brace, StopBeforeMatch)) { @@ -5453,13 +5476,13 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl, // If comma is followed by r_brace, emit appropriate warning. if (Tok.is(tok::r_brace) && CommaLoc.isValid()) { if (!getLangOpts().C99 && !getLangOpts().CPlusPlus11) - Diag(CommaLoc, getLangOpts().CPlusPlus ? - diag::ext_enumerator_list_comma_cxx : - diag::ext_enumerator_list_comma_c) - << FixItHint::CreateRemoval(CommaLoc); + Diag(CommaLoc, getLangOpts().CPlusPlus + ? diag::ext_enumerator_list_comma_cxx + : diag::ext_enumerator_list_comma_c) + << FixItHint::CreateRemoval(CommaLoc); else if (getLangOpts().CPlusPlus11) Diag(CommaLoc, diag::warn_cxx98_compat_enumerator_list_comma) - << FixItHint::CreateRemoval(CommaLoc); + << FixItHint::CreateRemoval(CommaLoc); break; } } @@ -5500,7 +5523,8 @@ void Parser::ParseEnumBody(SourceLocation StartLoc, Decl *EnumDecl, bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const { switch (Tok.getKind()) { - default: return false; + default: + return false; // type-specifiers case tok::kw_short: case tok::kw_long: @@ -5555,13 +5579,14 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const { bool Parser::isTypeSpecifierQualifier() { switch (Tok.getKind()) { - default: return false; + default: + return false; - case tok::identifier: // foo::bar + case tok::identifier: // foo::bar if (TryAltiVecVectorToken()) return true; [[fallthrough]]; - case tok::kw_typename: // typename T::type + 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()) @@ -5570,9 +5595,9 @@ bool Parser::isTypeSpecifierQualifier() { return false; return isTypeSpecifierQualifier(); - case tok::coloncolon: // ::foo::bar - if (NextToken().is(tok::kw_new) || // ::new - NextToken().is(tok::kw_delete)) // ::delete + case tok::coloncolon: // ::foo::bar + if (NextToken().is(tok::kw_new) || // ::new + NextToken().is(tok::kw_delete)) // ::delete return false; if (TryAnnotateTypeOrScopeToken()) @@ -5734,14 +5759,15 @@ bool Parser::isDeclarationSpecifier( ImplicitTypenameContext AllowImplicitTypename, bool DisambiguatingWithExpression) { switch (Tok.getKind()) { - default: return false; + default: + return false; // OpenCL 2.0 and later define this keyword. case tok::kw_pipe: return getLangOpts().OpenCL && getLangOpts().getOpenCLCompatibleVersion() >= 200; - case tok::identifier: // foo::bar + case tok::identifier: // foo::bar // Unfortunate hack to support "Class.factoryMethod" notation. if (getLangOpts().ObjC && NextToken().is(tok::period)) return false; @@ -5770,11 +5796,11 @@ bool Parser::isDeclarationSpecifier( return isDeclarationSpecifier(AllowImplicitTypename); - case tok::coloncolon: // ::foo::bar + case tok::coloncolon: // ::foo::bar if (!getLangOpts().CPlusPlus) return false; - if (NextToken().is(tok::kw_new) || // ::new - NextToken().is(tok::kw_delete)) // ::delete + if (NextToken().is(tok::kw_new) || // ::new + NextToken().is(tok::kw_delete)) // ::delete return false; // Annotate typenames and C++ scope specifiers. If we get one, just @@ -5923,7 +5949,7 @@ bool Parser::isDeclarationSpecifier( if (NextToken().is(tok::identifier) && TryAnnotateTypeConstraint()) return true; return isTypeConstraintAnnotation() && - GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype); + GetLookAheadToken(2).isOneOf(tok::kw_auto, tok::kw_decltype); } case tok::kw___declspec: @@ -6150,7 +6176,7 @@ void Parser::ParseTypeQualifierListOpt( return; case tok::kw_const: - isInvalid = DS.SetTypeQual(DeclSpec::TQ_const , Loc, PrevSpec, DiagID, + isInvalid = DS.SetTypeQual(DeclSpec::TQ_const, Loc, PrevSpec, DiagID, getLangOpts()); break; case tok::kw_volatile: @@ -6206,10 +6232,11 @@ void Parser::ParseTypeQualifierListOpt( getLangOpts()); break; case tok::kw___uptr: - // GNU libc headers in C mode use '__uptr' as an identifier which conflicts - // with the MS modifier keyword. - if ((AttrReqs & AR_DeclspecAttributesParsed) && !getLangOpts().CPlusPlus && - IdentifierRequired && DS.isEmpty() && NextToken().is(tok::semi)) { + // GNU libc headers in C mode use '__uptr' as an identifier which + // conflicts with the MS modifier keyword. + if ((AttrReqs & AR_DeclspecAttributesParsed) && + !getLangOpts().CPlusPlus && IdentifierRequired && DS.isEmpty() && + NextToken().is(tok::semi)) { if (TryKeywordIdentFallback(false)) continue; } @@ -6272,7 +6299,7 @@ void Parser::ParseTypeQualifierListOpt( // otherwise, FALL THROUGH! [[fallthrough]]; default: - DoneWithTypeQuals: + DoneWithTypeQuals: // If this is not a type-qualifier token, we're done reading type // qualifiers. First verify that DeclSpec's are consistent. DS.Finish(Actions, Actions.getASTContext().getPrintingPolicy()); @@ -6434,7 +6461,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, // Otherwise, '*' -> pointer, '^' -> block, '&' -> lvalue reference, // '&&' -> rvalue reference - SourceLocation Loc = ConsumeToken(); // Eat the *, ^, & or &&. + SourceLocation Loc = ConsumeToken(); // Eat the *, ^, & or &&. D.SetRangeEnd(Loc); if (Kind == tok::star || Kind == tok::caret) { @@ -6473,9 +6500,9 @@ void Parser::ParseDeclaratorInternal(Declarator &D, // Complain about rvalue references in C++03, but then go on and build // the declarator. if (Kind == tok::ampamp) - Diag(Loc, getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_rvalue_reference : - diag::ext_rvalue_reference); + Diag(Loc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_rvalue_reference + : diag::ext_rvalue_reference); // GNU-style and C++11 attributes are allowed here, as is restrict. ParseTypeQualifierListOpt(DS); @@ -6487,14 +6514,17 @@ void Parser::ParseDeclaratorInternal(Declarator &D, if (DS.getTypeQualifiers() != DeclSpec::TQ_unspecified) { if (DS.getTypeQualifiers() & DeclSpec::TQ_const) Diag(DS.getConstSpecLoc(), - diag::err_invalid_reference_qualifier_application) << "const"; + diag::err_invalid_reference_qualifier_application) + << "const"; if (DS.getTypeQualifiers() & DeclSpec::TQ_volatile) Diag(DS.getVolatileSpecLoc(), - diag::err_invalid_reference_qualifier_application) << "volatile"; + diag::err_invalid_reference_qualifier_application) + << "volatile"; // 'restrict' is permitted as an extension. if (DS.getTypeQualifiers() & DeclSpec::TQ_atomic) Diag(DS.getAtomicSpecLoc(), - diag::err_invalid_reference_qualifier_application) << "_Atomic"; + diag::err_invalid_reference_qualifier_application) + << "_Atomic"; } // Recursively parse the declarator. @@ -6503,14 +6533,14 @@ void Parser::ParseDeclaratorInternal(Declarator &D, if (D.getNumTypeObjects() > 0) { // C++ [dcl.ref]p4: There shall be no references to references. - DeclaratorChunk& InnerChunk = D.getTypeObject(D.getNumTypeObjects() - 1); + DeclaratorChunk &InnerChunk = D.getTypeObject(D.getNumTypeObjects() - 1); if (InnerChunk.Kind == DeclaratorChunk::Reference) { if (const IdentifierInfo *II = D.getIdentifier()) Diag(InnerChunk.Loc, diag::err_illegal_decl_reference_to_reference) - << II; + << II; else Diag(InnerChunk.Loc, diag::err_illegal_decl_reference_to_reference) - << "type name"; + << "type name"; // Once we've complained about the reference-to-reference, we // can go ahead and build the (technically ill-formed) @@ -6674,7 +6704,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { // We have a scope specifier but no following unqualified-id. Diag(PP.getLocForEndOfToken(D.getCXXScopeSpec().getEndLoc()), diag::err_expected_unqualified_id) - << /*C++*/1; + << /*C++*/ 1; D.SetIdentifier(nullptr, Tok.getLocation()); goto PastIdentifier; } @@ -6710,7 +6740,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { tok::comma, tok::semi, tok::equal, tok::l_brace, tok::kw_try); if (DiagnoseIdentifier) { Diag(Tok.getLocation(), diag::err_unexpected_unqualified_id) - << FixItHint::CreateRemoval(Tok.getLocation()); + << FixItHint::CreateRemoval(Tok.getLocation()); D.SetIdentifier(nullptr, Tok.getLocation()); ConsumeToken(); goto PastIdentifier; @@ -6742,9 +6772,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (D.getCXXScopeSpec().isSet()) { // If there was an error parsing parenthesized declarator, declarator // scope may have been entered before. Don't do it again. - if (!D.isInvalidType() && - Actions.ShouldEnterDeclaratorScope(getCurScope(), - D.getCXXScopeSpec())) + if (!D.isInvalidType() && Actions.ShouldEnterDeclaratorScope( + getCurScope(), D.getCXXScopeSpec())) // Change the declaration context for name lookup, until this function // is exited (and the declarator has been parsed). DeclScopeObj.EnterDeclaratorScope(); @@ -6810,7 +6839,7 @@ void Parser::ParseDirectDeclarator(Declarator &D) { D.setInvalidType(true); } - PastIdentifier: +PastIdentifier: assert(D.isPastIdentifier() && "Haven't past the location of the identifier yet?"); @@ -7042,7 +7071,7 @@ void Parser::ParseParenDeclarator(Declarator &D) { ParseMicrosoftTypeAttributes(attrs); // Eat any Borland extensions. - if (Tok.is(tok::kw___pascal)) + if (Tok.is(tok::kw___pascal)) ParseBorlandTypeAttributes(attrs); // If we haven't past the identifier yet (or where the identifier would be @@ -7159,8 +7188,7 @@ void Parser::InitCXXThisScopeForDeclaratorIfRelevant( void Parser::ParseFunctionDeclarator(Declarator &D, ParsedAttributes &FirstArgAttrs, BalancedDelimiterTracker &Tracker, - bool IsAmbiguous, - bool RequiresArg) { + bool IsAmbiguous, bool RequiresArg) { assert(getCurScope()->isFunctionPrototypeScope() && "Should call from a Function scope"); // lparen is already consumed! @@ -7283,12 +7311,9 @@ void Parser::ParseFunctionDeclarator(Declarator &D, // delayed parsing to give it a chance to find what it expects. Delayed = false; } - ESpecType = tryParseExceptionSpecification(Delayed, - ESpecRange, - DynamicExceptions, - DynamicExceptionRanges, - NoexceptExpr, - ExceptionSpecTokens); + ESpecType = tryParseExceptionSpecification( + Delayed, ESpecRange, DynamicExceptions, DynamicExceptionRanges, + NoexceptExpr, ExceptionSpecTokens); if (ESpecType != EST_None) EndLoc = ESpecRange.getEnd(); @@ -7338,26 +7363,24 @@ void Parser::ParseFunctionDeclarator(Declarator &D, } // Remember that we parsed a function type, and remember the attributes. - D.AddTypeInfo(DeclaratorChunk::getFunction( - HasProto, IsAmbiguous, LParenLoc, ParamInfo.data(), - ParamInfo.size(), EllipsisLoc, RParenLoc, - RefQualifierIsLValueRef, RefQualifierLoc, - /*MutableLoc=*/SourceLocation(), - ESpecType, ESpecRange, DynamicExceptions.data(), - DynamicExceptionRanges.data(), DynamicExceptions.size(), - NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr, - ExceptionSpecTokens, DeclsInPrototype, StartLoc, - LocalEndLoc, D, TrailingReturnType, TrailingReturnTypeLoc, - &DS), - std::move(FnAttrs), EndLoc); + D.AddTypeInfo( + DeclaratorChunk::getFunction( + HasProto, IsAmbiguous, LParenLoc, ParamInfo.data(), ParamInfo.size(), + EllipsisLoc, RParenLoc, RefQualifierIsLValueRef, RefQualifierLoc, + /*MutableLoc=*/SourceLocation(), ESpecType, ESpecRange, + DynamicExceptions.data(), DynamicExceptionRanges.data(), + DynamicExceptions.size(), + NoexceptExpr.isUsable() ? NoexceptExpr.get() : nullptr, + ExceptionSpecTokens, DeclsInPrototype, StartLoc, LocalEndLoc, D, + TrailingReturnType, TrailingReturnTypeLoc, &DS), + std::move(FnAttrs), EndLoc); } bool Parser::ParseRefQualifier(bool &RefQualifierIsLValueRef, SourceLocation &RefQualifierLoc) { if (Tok.isOneOf(tok::amp, tok::ampamp)) { - Diag(Tok, getLangOpts().CPlusPlus11 ? - diag::warn_cxx98_compat_ref_qualifier : - diag::ext_ref_qualifier); + Diag(Tok, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_ref_qualifier + : diag::ext_ref_qualifier); RefQualifierIsLValueRef = Tok.is(tok::amp); RefQualifierLoc = ConsumeToken(); @@ -7367,9 +7390,8 @@ bool Parser::ParseRefQualifier(bool &RefQualifierIsLValueRef, } bool Parser::isFunctionDeclaratorIdentifierList() { - return !getLangOpts().requiresStrictPrototypes() - && Tok.is(tok::identifier) - && !TryAltiVecVectorToken() + return !getLangOpts().requiresStrictPrototypes() && Tok.is(tok::identifier) && + !TryAltiVecVectorToken() // K&R identifier lists can't have typedefs as identifiers, per C99 // 6.7.5.3p11. && (TryAnnotateTypeOrScopeToken() || !Tok.is(tok::annot_typename)) @@ -7390,8 +7412,7 @@ bool Parser::isFunctionDeclaratorIdentifierList() { } void Parser::ParseFunctionDeclaratorIdentifierList( - Declarator &D, - SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo) { + Declarator &D, SmallVectorImpl<DeclaratorChunk::ParamInfo> &ParamInfo) { // We should never reach this point in C23 or C++. assert(!getLangOpts().requiresStrictPrototypes() && "Cannot parse an identifier list in C23 or C++"); @@ -7427,9 +7448,8 @@ void Parser::ParseFunctionDeclaratorIdentifierList( Diag(Tok, diag::err_param_redefinition) << ParmII; } else { // Remember this identifier in ParamInfo. - ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, - Tok.getLocation(), - nullptr)); + ParamInfo.push_back( + DeclaratorChunk::ParamInfo(ParmII, Tok.getLocation(), nullptr)); } // Eat the identifier. @@ -7666,9 +7686,9 @@ void Parser::ParseParameterDeclarationClause( } } - ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, - ParmDeclarator.getIdentifierLoc(), - Param, std::move(DefArgToks))); + ParamInfo.push_back( + DeclaratorChunk::ParamInfo(ParmII, ParmDeclarator.getIdentifierLoc(), + Param, std::move(DefArgToks))); } if (TryConsumeToken(tok::ellipsis, EllipsisLoc)) { @@ -7691,19 +7711,19 @@ void Parser::ParseParameterDeclarationClause( // point out where the ellipsis should have gone. SourceLocation ParmEllipsis = ParmDeclarator.getEllipsisLoc(); Diag(EllipsisLoc, diag::warn_misplaced_ellipsis_vararg) - << ParmEllipsis.isValid() << ParmEllipsis; + << ParmEllipsis.isValid() << ParmEllipsis; if (ParmEllipsis.isValid()) { Diag(ParmEllipsis, diag::note_misplaced_ellipsis_vararg_existing_ellipsis); } else { Diag(ParmDeclarator.getIdentifierLoc(), diag::note_misplaced_ellipsis_vararg_add_ellipsis) - << FixItHint::CreateInsertion(ParmDeclarator.getIdentifierLoc(), - "...") - << !ParmDeclarator.hasName(); + << FixItHint::CreateInsertion(ParmDeclarator.getIdentifierLoc(), + "...") + << !ParmDeclarator.hasName(); } Diag(EllipsisLoc, diag::note_misplaced_ellipsis_vararg_add_comma) - << FixItHint::CreateInsertion(EllipsisLoc, ", "); + << FixItHint::CreateInsertion(EllipsisLoc, ", "); } // We can't have any more parameters after an ellipsis. @@ -7779,11 +7799,11 @@ void Parser::ParseBracketDeclarator(Declarator &D) { // the token after the star is a ']'. Since stars in arrays are // infrequent, use of lookahead is not costly here. if (Tok.is(tok::star) && GetLookAheadToken(1).is(tok::r_square)) { - ConsumeToken(); // Eat the '*'. + ConsumeToken(); // Eat the '*'. if (StaticLoc.isValid()) { Diag(StaticLoc, diag::err_unspecified_vla_size_with_static); - StaticLoc = SourceLocation(); // Drop the static. + StaticLoc = SourceLocation(); // Drop the static. } isStar = true; } else if (Tok.isNot(tok::r_square)) { @@ -7804,7 +7824,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) { } else { if (StaticLoc.isValid()) { Diag(StaticLoc, diag::err_unspecified_size_with_static); - StaticLoc = SourceLocation(); // Drop the static. + StaticLoc = SourceLocation(); // Drop the static. } } @@ -7958,8 +7978,7 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) { // Check for duplicate type specifiers (e.g. "int typeof(int)"). if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualType : DeclSpec::TST_typeofType, - StartLoc, PrevSpec, - DiagID, CastTy, + StartLoc, PrevSpec, DiagID, CastTy, Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; return; @@ -7983,8 +8002,7 @@ void Parser::ParseTypeofSpecifier(DeclSpec &DS) { // Check for duplicate type specifiers (e.g. "int typeof(int)"). if (DS.SetTypeSpecType(IsUnqual ? DeclSpec::TST_typeof_unqualExpr : DeclSpec::TST_typeofExpr, - StartLoc, PrevSpec, - DiagID, Operand.get(), + StartLoc, PrevSpec, DiagID, Operand.get(), Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; } @@ -8015,8 +8033,8 @@ void Parser::ParseAtomicSpecifier(DeclSpec &DS) { const char *PrevSpec = nullptr; unsigned DiagID; - if (DS.SetTypeSpecType(DeclSpec::TST_atomic, StartLoc, PrevSpec, - DiagID, Result.get(), + if (DS.SetTypeSpecType(DeclSpec::TST_atomic, StartLoc, PrevSpec, DiagID, + Result.get(), Actions.getASTContext().getPrintingPolicy())) Diag(StartLoc, DiagID) << PrevSpec; } @@ -8024,7 +8042,8 @@ void Parser::ParseAtomicSpecifier(DeclSpec &DS) { bool Parser::TryAltiVecVectorTokenOutOfLine() { Token Next = NextToken(); switch (Next.getKind()) { - default: return false; + default: + return false; case tok::kw_short: case tok::kw_long: case tok::kw_signed: @@ -8078,7 +8097,8 @@ bool Parser::TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc, return true; case tok::identifier: if (Next.getIdentifierInfo() == Ident_pixel) { - isInvalid = DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID,Policy); + isInvalid = + DS.SetTypeAltiVecVector(true, Loc, PrevSpec, DiagID, Policy); return true; } if (Next.getIdentifierInfo() == Ident_bool || diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp index 723a79628116c..da8732dc6e62c 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p2.cpp @@ -5,14 +5,18 @@ // The auto or register specifiers can be applied only to names of objects // declared in a block (6.3) or to function parameters (8.4). -auto int ao; // expected-error {{illegal storage class on file-scoped variable}} +auto int ao; #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else +// expected-error@-4 {{illegal storage class on file-scoped variable}} #endif -auto void af(); // expected-error {{illegal storage class on function}} +auto void af(); #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else +// expected-error@-4 {{illegal storage class on function}} #endif register int ro; // expected-error {{illegal storage class on file-scoped variable}} @@ -25,13 +29,17 @@ register int ro; // expected-error {{illegal storage class on file-scoped variab register void rf(); // expected-error {{illegal storage class on function}} struct S { - auto int ao; // expected-error {{storage class specified for a member declaration}} + auto int ao; #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else +// expected-error@-4 {{storage class specified for a member declaration}} #endif - auto void af(); // expected-error {{storage class specified for a member declaration}} + auto void af(); #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else +// expected-error@-4 {{storage class specified for a member declaration}} #endif register int ro; // expected-error {{storage class specified for a member declaration}} @@ -40,19 +48,21 @@ struct S { void foo(auto int ap, register int rp) { #if __cplusplus >= 201703L -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} // expected-error@-3 {{ISO C++17 does not allow 'register' storage class specifier}} #elif __cplusplus >= 201103L -// expected-warning@-5 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-5 {{'auto' cannot be combined with a type specifier in C++}} // expected-warning@-6 {{'register' storage class specifier is deprecated}} #endif auto int abo; #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} #endif - auto void abf(); // expected-error {{illegal storage class on function}} + auto void abf(); #if __cplusplus >= 201103L // C++11 or later -// expected-warning@-2 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else +// expected-error@-4 {{illegal storage class on function}} #endif register int rbo; diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp index e8f12156a4242..983119d6af8d0 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3-1y.cpp @@ -56,7 +56,7 @@ namespace p3_example { auto x = 5; const auto *v = &x, u = 6; static auto y = 0.0; - auto int r; // expected-warning {{storage class}} expected-error {{file-scope}} + auto int r; // expected-error {{'auto' cannot be combined with a type specifier in C++}} static_assert(is_same<decltype(x), int>(), ""); static_assert(is_same<decltype(v), const int*>(), ""); diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp index 440c78201293b..0a2688ee288ae 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p3.cpp @@ -42,7 +42,12 @@ void p3example() { static auto y = 0.0; // In C++98: 'auto' storage class specifier is redundant and incompatible with C++0x // In C++0x: 'auto' storage class specifier is not permitted in C++0x, and will not be supported in future releases - auto int r; // expected-warning {{'auto' storage class specifier}} + auto int r; +#if __cplusplus >= 201103L + // expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else + // expected-warning@-4 {{'auto' storage class specifier}} +#endif same<__typeof(x), int> xHasTypeInt; same<__typeof(v), const int*> vHasTypeConstIntPtr; diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp index bbd87c060801a..905591d9518a8 100644 --- a/clang/test/CXX/drs/cwg3xx.cpp +++ b/clang/test/CXX/drs/cwg3xx.cpp @@ -1732,11 +1732,16 @@ namespace cwg395 { // cwg395: 3.0 namespace cwg396 { // cwg396: 3.0 void f() { auto int a(); - // since-cxx11-error@-1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} - // expected-error@-2 {{illegal storage class on function}} + // since-cxx11-error@-1 {{'auto' cannot be combined with a type specifier in C++}} +#if __cplusplus < 201103L + // expected-error@-3 {{illegal storage class on function}} +#else + // expected-warning@-3 {{empty parentheses interpreted as a function declaration}} + // expected-note@-3 {{replace parentheses with an initializer to declare a variable}} +#endif int (i); // #cwg396-i auto int (i); - // since-cxx11-error@-1 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} + // since-cxx11-error@-1 {{'auto' cannot be combined with a type specifier in C++}} // expected-error@-2 {{redefinition of 'i'}} // expected-note@#cwg396-i {{previous definition is here}} } diff --git a/clang/test/Parser/cxx-auto-type-specifier.cpp b/clang/test/Parser/cxx-auto-type-specifier.cpp new file mode 100644 index 0000000000000..f34195934cc85 --- /dev/null +++ b/clang/test/Parser/cxx-auto-type-specifier.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++23 %s + +// Test that 'auto' cannot be combined with a type specifier in C++. +void f() { + auto int x = 1; // expected-error {{'auto' cannot be combined with a type specifier in C++}} + auto char c = 'a'; // expected-error {{'auto' cannot be combined with a type specifier in C++}} + auto float f = 1.0f; // expected-error {{'auto' cannot be combined with a type specifier in C++}} + auto double d = 1.0; // expected-error {{'auto' cannot be combined with a type specifier in C++}} + auto long l = 1L; // expected-error {{'auto' cannot be combined with a type specifier in C++}} +} + +// Test that regular 'auto' (type deduction) still works in C++. +void h() { + auto x = 1; + auto y = 2.0; + auto z = 'c'; +} + diff --git a/clang/test/SemaCXX/auto-cxx0x.cpp b/clang/test/SemaCXX/auto-cxx0x.cpp index 07687b6066790..2662319b900cd 100644 --- a/clang/test/SemaCXX/auto-cxx0x.cpp +++ b/clang/test/SemaCXX/auto-cxx0x.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 // RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++1y void f() { - auto int a; // expected-warning {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} + auto int a; // expected-error {{'auto' cannot be combined with a type specifier in C++}} int auto b; // expected-error{{cannot combine with previous 'int' declaration specifier}} } diff --git a/clang/test/SemaCXX/class.cpp b/clang/test/SemaCXX/class.cpp index f1e02d5158aac..e0aa09dca82e4 100644 --- a/clang/test/SemaCXX/class.cpp +++ b/clang/test/SemaCXX/class.cpp @@ -2,11 +2,12 @@ // RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx98 -Wc++11-compat %s -std=c++98 class C { public: - auto int errx; // expected-error {{storage class specified for a member declaration}} + auto int errx; #if __cplusplus <= 199711L - // expected-warning@-2 {{'auto' storage class specifier is redundant}} + // expected-error@-2 {{storage class specified for a member declaration}} + // expected-warning@-3 {{'auto' storage class specifier is redundant and incompatible with C++11}} #else - // expected-warning@-4 {{'auto' storage class specifier is not permitted in C++11, and will not be supported in future releases}} + // expected-error@-5 {{'auto' cannot be combined with a type specifier in C++}} #endif register int erry; // expected-error {{storage class specified for a member declaration}} extern int errz; // expected-error {{storage class specified for a member declaration}} diff --git a/clang/test/SemaCXX/static-data-member.cpp b/clang/test/SemaCXX/static-data-member.cpp index fb63da9b40099..49c016dee892b 100644 --- a/clang/test/SemaCXX/static-data-member.cpp +++ b/clang/test/SemaCXX/static-data-member.cpp @@ -13,7 +13,12 @@ double ABC::a = 1.0; extern double ABC::b = 1.0; // expected-error {{static data member definition cannot specify a storage class}} static double ABC::c = 1.0; // expected-error {{'static' can only be specified inside the class definition}} __private_extern__ double ABC::d = 1.0; // expected-error {{static data member definition cannot specify a storage class}} -auto double ABC::e = 1.0; // expected-error {{static data member definition cannot specify a storage class}} +auto double ABC::e = 1.0; +#if __cplusplus >= 201103L +// expected-error@-2 {{'auto' cannot be combined with a type specifier in C++}} +#else +// expected-error@-4 {{static data member definition cannot specify a storage class}} +#endif #if __cplusplus < 201703L register double ABC::f = 1.0; // expected-error {{static data member definition cannot specify a storage class}} #endif _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
