diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -169,6 +169,11 @@ def err_duplicate_declspec : Error<"%sub{duplicate_declspec}0">; +def warn_redundant_weaker_declspec_constexpr : Warning< + "'%0' declaration specifier is redundant due to the presence " + "of stricter '%sub{select_constexpr_spec_kind}1' declaration specifier">, + InGroup; + def err_friend_decl_spec : Error<"'%0' is invalid in friend declarations">; def err_invalid_member_in_interface : Error< diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -641,6 +641,7 @@ TautologicalObjCBoolCompare]>; def HeaderHygiene : DiagGroup<"header-hygiene">; def DuplicateDeclSpecifier : DiagGroup<"duplicate-decl-specifier">; +def RedundantDeclSpecifier : DiagGroup<"redundant-decl-specifier">; def CompareDistinctPointerType : DiagGroup<"compare-distinct-pointer-types">; def GNUUnionCast : DiagGroup<"gnu-union-cast">; def GNUVariableSizedTypeNotAtEnd : DiagGroup<"gnu-variable-sized-type-not-at-end">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2707,6 +2707,7 @@ static bool adjustContextForLocalExternDecl(DeclContext *&DC); void DiagnoseFunctionSpecifiers(const DeclSpec &DS); + void DiagnoseRedundantSpecifiers(QualType T, const DeclSpec &DS); NamedDecl *getShadowedDeclaration(const TypedefNameDecl *D, const LookupResult &R); NamedDecl *getShadowedDeclaration(const VarDecl *D, const LookupResult &R); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6209,6 +6209,20 @@ diag::err_noreturn_non_function); } +/// Diagnose specifiers on a declaration of an identifier that +/// are redundant due to the presence of stricter specifiers +void Sema::DiagnoseRedundantSpecifiers(QualType T, const DeclSpec &DS) { + if (T->isFunctionType()) { + // [dcl.constexpr]p1: A function declared with the constexpr or consteval + // specifier is implicitly an inline function + if (DS.hasConstexprSpecifier() && DS.isInlineSpecified()) { + Diag(DS.getInlineSpecLoc(), + diag::warn_redundant_weaker_declspec_constexpr) + << "inline" << static_cast(DS.getConstexprSpecifier()); + } + } +} + NamedDecl* Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, TypeSourceInfo *TInfo, LookupResult &Previous) { @@ -9046,6 +9060,8 @@ if (R.getCanonicalType()->castAs()->getCmseNSCallAttr()) Diag(D.getIdentifierLoc(), diag::err_function_decl_cmse_ns_call); + DiagnoseRedundantSpecifiers(R, D.getDeclSpec()); + SmallVector TemplateParamLists; for (TemplateParameterList *TPL : TemplateParamListsRef) TemplateParamLists.push_back(TPL); diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp --- a/clang/test/Parser/cxx0x-decl.cpp +++ b/clang/test/Parser/cxx0x-decl.cpp @@ -172,6 +172,15 @@ consteval constinit int huh(); // expected-error {{cannot combine with previous 'consteval'}} } +namespace RedundantSpecifier { + consteval inline int f1(); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'consteval' declaration specifier}} + inline constexpr int f2(); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'constexpr' declaration specifier}} + inline constexpr inline int f3(); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'constexpr' declaration specifier}} \ + // expected-warning {{duplicate 'inline' declaration specifier}} + template inline consteval int f4(T t); // expected-warning {{'inline' declaration specifier is redundant due to the presence of stricter 'consteval' declaration specifier}} + inline constexpr int i1 = 0; +} + namespace ColonColonDecltype { struct S { struct T {}; }; ::decltype(S())::T invalid; // expected-error {{expected unqualified-id}}