[clang] [Clang][Sema] Diagnose friend declarations with enum elaborated-type-specifier in all language modes (PR #80171)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 31 09:51:18 PST 2024
https://github.com/sdkrystian created https://github.com/llvm/llvm-project/pull/80171
According to [[dcl.type.elab]p4](http://eel.is/c++draft/dcl.type.elab#4):
> If an _elaborated-type-specifier_ appears with the `friend` specifier as an entire _member-declaration_, the _member-declaration_ shall have one of the following forms:
> `friend` _class-key_ _nested-name-specifier_(opt) _identifier_ `;`
> `friend` _class-key_ _simple-template-id_ `;`
> `friend` _class-key_ _nested-name-specifier_ `template`(opt) _simple-template-id_ `;`
Notably absent from this list is the `enum` form of an _elaborated-type-specifier_ "`enum` _nested-name-specifier_(opt) _identifier_", which appears to be intentional per the resolution of [CWG2363](http://wg21.link/CWG2363).
Most major implementations accept these declarations, so the diagnostic is a pedantic warning across all C++ versions.
In addition to the trivial cases previously diagnosed in C++98, we now diagnose cases where the _elaborated-type-specifier_ has a dependent _nested-name-specifier_:
```cpp
template<typename T>
struct A
{
enum class E;
};
struct B
{
template<typename T>
friend enum A<T>::E; // pedantic warning: elaborated enumeration type cannot be a friend
};
template<typename T>
struct C
{
friend enum T::E; // pedantic warning: elaborated enumeration type cannot be a friend
};
```
>From 4328b548228cc9ffa761887afefdb11261b904f7 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
Date: Wed, 31 Jan 2024 11:09:11 -0500
Subject: [PATCH] [Clang][Sema] Diagnose friend declarations with enum
elaborated-type-specifier in all language modes
---
clang/docs/ReleaseNotes.rst | 2 +
.../clang/Basic/DiagnosticSemaKinds.td | 6 +-
clang/include/clang/Parse/Parser.h | 8 +-
clang/include/clang/Sema/DeclSpec.h | 20 +--
clang/include/clang/Sema/Sema.h | 3 -
clang/lib/Parse/ParseDecl.cpp | 2 +-
clang/lib/Parse/ParseTentative.cpp | 2 +-
clang/lib/Sema/DeclSpec.cpp | 10 +-
clang/lib/Sema/SemaDecl.cpp | 16 ++
clang/lib/Sema/SemaDeclCXX.cpp | 137 +++++-------------
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 +-
.../dcl.spec/dcl.type/dcl.type.elab/p3.cpp | 6 +-
.../dcl.spec/dcl.type/dcl.type.elab/p4.cpp | 33 +++++
clang/test/CXX/drs/dr16xx.cpp | 1 +
.../temp.class/temp.mem.enum/p1.cpp | 5 +-
clang/test/FixIt/fixit-c++11.cpp | 1 +
clang/test/Parser/cxx-decl.cpp | 3 -
clang/test/Parser/cxx0x-decl.cpp | 2 +-
clang/test/SemaCXX/cxx98-compat.cpp | 2 +-
clang/test/SemaCXX/enum-scoped.cpp | 5 +
20 files changed, 124 insertions(+), 147 deletions(-)
create mode 100644 clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 323157c4db1f1..68987cb179221 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -143,6 +143,8 @@ Improvements to Clang's diagnostics
- Clang now applies syntax highlighting to the code snippets it
prints.
+- Clang now diagnoses friend declarations with an `enum` elaborated-type-specifier outside of C++98.
+
Improvements to Clang's time-trace
----------------------------------
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1c0ebbe4e6834..7bb026679f6cd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1637,10 +1637,8 @@ def err_inline_namespace_std : Error<
def err_unexpected_friend : Error<
"friends can only be classes or functions">;
def ext_enum_friend : ExtWarn<
- "befriending enumeration type %0 is a C++11 extension">, InGroup<CXX11>;
-def warn_cxx98_compat_enum_friend : Warning<
- "befriending enumeration type %0 is incompatible with C++98">,
- InGroup<CXX98Compat>, DefaultIgnore;
+ "elaborated enumeration type cannot be a friend">,
+ InGroup<DiagGroup<"friend-enum">>;
def ext_nonclass_type_friend : ExtWarn<
"non-class friend type %0 is a C++11 extension">, InGroup<CXX11>;
def warn_cxx98_compat_nonclass_type_friend : Warning<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 4a066acf511a1..16411e1947ffc 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -2551,10 +2551,10 @@ class Parser : public CodeCompletionHandler {
/// Starting with a scope specifier, identifier, or
/// template-id that refers to the current class, determine whether
/// this is a constructor declarator.
- bool isConstructorDeclarator(
- bool Unqualified, bool DeductionGuide = false,
- DeclSpec::FriendSpecified IsFriend = DeclSpec::FriendSpecified::No,
- const ParsedTemplateInfo *TemplateInfo = nullptr);
+ bool
+ isConstructorDeclarator(bool Unqualified, bool DeductionGuide = false,
+ bool IsFriend = false,
+ const ParsedTemplateInfo *TemplateInfo = nullptr);
/// Specifies the context in which type-id/expression
/// disambiguation will occur.
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 77638def60063..c7266b66c014d 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -346,11 +346,6 @@ class DeclSpec {
// FIXME: Attributes should be included here.
};
- enum FriendSpecified : bool {
- No,
- Yes,
- };
-
private:
// storage-class-specifier
/*SCS*/unsigned StorageClassSpec : 3;
@@ -380,7 +375,8 @@ class DeclSpec {
unsigned FS_noreturn_specified : 1;
// friend-specifier
- unsigned Friend_specified : 1;
+ unsigned FriendSpecified : 1;
+ unsigned FriendSpecifiedFirst : 1;
// constexpr-specifier
unsigned ConstexprSpecifier : 2;
@@ -470,9 +466,9 @@ class DeclSpec {
TypeSpecPipe(false), TypeSpecSat(false), ConstrainedAuto(false),
TypeQualifiers(TQ_unspecified), FS_inline_specified(false),
FS_forceinline_specified(false), FS_virtual_specified(false),
- FS_noreturn_specified(false), Friend_specified(false),
- ConstexprSpecifier(
- static_cast<unsigned>(ConstexprSpecKind::Unspecified)),
+ FS_noreturn_specified(false), FriendSpecified(false),
+ FriendSpecifiedFirst(false), ConstexprSpecifier(static_cast<unsigned>(
+ ConstexprSpecKind::Unspecified)),
Attrs(attrFactory), writtenBS(), ObjCQualifiers(nullptr) {}
// storage-class-specifier
@@ -796,9 +792,9 @@ class DeclSpec {
bool SetConstexprSpec(ConstexprSpecKind ConstexprKind, SourceLocation Loc,
const char *&PrevSpec, unsigned &DiagID);
- FriendSpecified isFriendSpecified() const {
- return static_cast<FriendSpecified>(Friend_specified);
- }
+ bool isFriendSpecified() const { return FriendSpecified; }
+
+ bool isFriendSpecifiedFirst() const { return FriendSpecifiedFirst; }
SourceLocation getFriendSpecLoc() const { return FriendLoc; }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 59eab0185ae63..491a7b9893472 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7975,9 +7975,6 @@ class Sema final {
SourceLocation RParenLoc, bool Failed);
void DiagnoseStaticAssertDetails(const Expr *E);
- FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart,
- SourceLocation FriendLoc,
- TypeSourceInfo *TSInfo);
Decl *ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
MultiTemplateParamsArg TemplateParams);
NamedDecl *ActOnFriendFunctionDecl(Scope *S, Declarator &D,
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index bcbe2d9c635a6..80622543ce88b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -5876,7 +5876,7 @@ bool Parser::isDeclarationSpecifier(
}
bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
- DeclSpec::FriendSpecified IsFriend,
+ bool IsFriend,
const ParsedTemplateInfo *TemplateInfo) {
RevertingTentativeParsingAction TPA(*this);
// Parse the C++ scope specifier.
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index f1737cb844767..64aaa4ea0b206 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -81,7 +81,7 @@ bool Parser::isCXXDeclarationStatement(
isDeductionGuide) {
if (isConstructorDeclarator(/*Unqualified=*/SS.isEmpty(),
isDeductionGuide,
- DeclSpec::FriendSpecified::No))
+ /*IsFriend*/ false))
return true;
} else if (SS.isNotEmpty()) {
// If the scope is not empty, it could alternatively be something like
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 313f073445e8f..1c99508802ee7 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -1102,18 +1102,14 @@ bool DeclSpec::setFunctionSpecNoreturn(SourceLocation Loc,
bool DeclSpec::SetFriendSpec(SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID) {
- if (Friend_specified) {
+ if (FriendSpecified) {
PrevSpec = "friend";
- // Keep the later location, so that we can later diagnose ill-formed
- // declarations like 'friend class X friend;'. Per [class.friend]p3,
- // 'friend' must be the first token in a friend declaration that is
- // not a function declaration.
- FriendLoc = Loc;
DiagID = diag::warn_duplicate_declspec;
return true;
}
- Friend_specified = true;
+ FriendSpecified = true;
+ FriendSpecifiedFirst = isEmpty();
FriendLoc = Loc;
return false;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c89c3c487272d..edd9408ebb2c4 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17258,6 +17258,22 @@ Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, SourceLocation KWLoc,
return true;
}
+ if (TUK == TUK_Friend && Kind == TagTypeKind::Enum) {
+ // C++23 [dcl.type.elab]p4:
+ // If an elaborated-type-specifier appears with the friend specifier as
+ // an entire member-declaration, the member-declaration shall have one
+ // of the following forms:
+ // friend class-key nested-name-specifier(opt) identifier ;
+ // friend class-key simple-template-id ;
+ // friend class-key nested-name-specifier template(opt)
+ // simple-template-id ;
+ //
+ // Since enum is not a class-key, so declarations like "friend enum E;"
+ // are ill-formed. Although CWG2363 reaffirms that such declarations are
+ // invalid, most implementations accept so we issue a pedantic warning.
+ Diag(KWLoc, diag::ext_enum_friend);
+ }
+
// Figure out the underlying type if this a enum declaration. We need to do
// this early, because it's needed to detect if this is an incompatible
// redeclaration.
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5adc262cd6bc9..c0a6c8ca81a49 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17534,79 +17534,6 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
return Decl;
}
-/// Perform semantic analysis of the given friend type declaration.
-///
-/// \returns A friend declaration that.
-FriendDecl *Sema::CheckFriendTypeDecl(SourceLocation LocStart,
- SourceLocation FriendLoc,
- TypeSourceInfo *TSInfo) {
- assert(TSInfo && "NULL TypeSourceInfo for friend type declaration");
-
- QualType T = TSInfo->getType();
- SourceRange TypeRange = TSInfo->getTypeLoc().getSourceRange();
-
- // C++03 [class.friend]p2:
- // An elaborated-type-specifier shall be used in a friend declaration
- // for a class.*
- //
- // * The class-key of the elaborated-type-specifier is required.
- if (!CodeSynthesisContexts.empty()) {
- // Do not complain about the form of friend template types during any kind
- // of code synthesis. For template instantiation, we will have complained
- // when the template was defined.
- } else {
- if (!T->isElaboratedTypeSpecifier()) {
- // If we evaluated the type to a record type, suggest putting
- // a tag in front.
- if (const RecordType *RT = T->getAs<RecordType>()) {
- RecordDecl *RD = RT->getDecl();
-
- SmallString<16> InsertionText(" ");
- InsertionText += RD->getKindName();
-
- Diag(TypeRange.getBegin(),
- getLangOpts().CPlusPlus11 ?
- diag::warn_cxx98_compat_unelaborated_friend_type :
- diag::ext_unelaborated_friend_type)
- << (unsigned) RD->getTagKind()
- << T
- << FixItHint::CreateInsertion(getLocForEndOfToken(FriendLoc),
- InsertionText);
- } else {
- Diag(FriendLoc,
- getLangOpts().CPlusPlus11 ?
- diag::warn_cxx98_compat_nonclass_type_friend :
- diag::ext_nonclass_type_friend)
- << T
- << TypeRange;
- }
- } else if (T->getAs<EnumType>()) {
- Diag(FriendLoc,
- getLangOpts().CPlusPlus11 ?
- diag::warn_cxx98_compat_enum_friend :
- diag::ext_enum_friend)
- << T
- << TypeRange;
- }
-
- // C++11 [class.friend]p3:
- // A friend declaration that does not declare a function shall have one
- // of the following forms:
- // friend elaborated-type-specifier ;
- // friend simple-type-specifier ;
- // friend typename-specifier ;
- if (getLangOpts().CPlusPlus11 && LocStart != FriendLoc)
- Diag(FriendLoc, diag::err_friend_not_first_in_declaration) << T;
- }
-
- // If the type specifier in a friend declaration designates a (possibly
- // cv-qualified) class type, that class is declared as a friend; otherwise,
- // the friend declaration is ignored.
- return FriendDecl::Create(Context, CurContext,
- TSInfo->getTypeLoc().getBeginLoc(), TSInfo,
- FriendLoc);
-}
-
/// Handle a friend tag declaration where the scope specifier was
/// templated.
DeclResult Sema::ActOnTemplatedFriendTag(
@@ -17744,6 +17671,7 @@ DeclResult Sema::ActOnTemplatedFriendTag(
Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
MultiTemplateParamsArg TempParams) {
SourceLocation Loc = DS.getBeginLoc();
+ SourceLocation FriendLoc = DS.getFriendSpecLoc();
assert(DS.isFriendSpecified());
assert(DS.getStorageClassSpec() == DeclSpec::SCS_unspecified);
@@ -17755,9 +17683,10 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
// friend simple-type-specifier ;
// friend typename-specifier ;
//
- // Any declaration with a type qualifier does not have that form. (It's
- // legal to specify a qualified type as a friend, you just can't write the
- // keywords.)
+ // If the friend keyword isn't first, or if the declarations has any type
+ // qualifiers, then the declaration doesn't have that form.
+ if (getLangOpts().CPlusPlus11 && !DS.isFriendSpecifiedFirst())
+ Diag(FriendLoc, diag::err_friend_not_first_in_declaration);
if (DS.getTypeQualifiers()) {
if (DS.getTypeQualifiers() & DeclSpec::TQ_const)
Diag(DS.getConstSpecLoc(), diag::err_friend_decl_spec) << "const";
@@ -17784,24 +17713,35 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
if (DiagnoseUnexpandedParameterPack(Loc, TSI, UPPC_FriendDeclaration))
return nullptr;
- // This is definitely an error in C++98. It's probably meant to
- // be forbidden in C++0x, too, but the specification is just
- // poorly written.
- //
- // The problem is with declarations like the following:
- // template <T> friend A<T>::foo;
- // where deciding whether a class C is a friend or not now hinges
- // on whether there exists an instantiation of A that causes
- // 'foo' to equal C. There are restrictions on class-heads
- // (which we declare (by fiat) elaborated friend declarations to
- // be) that makes this tractable.
- //
- // FIXME: handle "template <> friend class A<T>;", which
- // is possibly well-formed? Who even knows?
- if (TempParams.size() && !T->isElaboratedTypeSpecifier()) {
- Diag(Loc, diag::err_tagless_friend_type_template)
- << DS.getSourceRange();
- return nullptr;
+ if (!T->isElaboratedTypeSpecifier()) {
+ if (TempParams.size()) {
+ // C++23 [dcl.pre]p5:
+ // In a simple-declaration, the optional init-declarator-list can be
+ // omitted only when declaring a class or enumeration, that is, when
+ // the decl-specifier-seq contains either a class-specifier, an
+ // elaborated-type-specifier with a class-key, or an enum-specifier.
+ //
+ // The declaration of a template-declaration or explicit-specialization
+ // is never a member-declaration, so this must be a simple-declaration
+ // with no init-declarator-list. Therefore, this is ill-formed.
+ Diag(Loc, diag::err_tagless_friend_type_template) << DS.getSourceRange();
+ return nullptr;
+ } else if (const RecordDecl *RD = T->getAsRecordDecl()) {
+ SmallString<16> InsertionText(" ");
+ InsertionText += RD->getKindName();
+
+ Diag(Loc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_unelaborated_friend_type
+ : diag::ext_unelaborated_friend_type)
+ << (unsigned)RD->getTagKind() << T
+ << FixItHint::CreateInsertion(getLocForEndOfToken(FriendLoc),
+ InsertionText);
+ } else {
+ Diag(FriendLoc, getLangOpts().CPlusPlus11
+ ? diag::warn_cxx98_compat_nonclass_type_friend
+ : diag::ext_nonclass_type_friend)
+ << T << DS.getSourceRange();
+ }
}
// C++98 [class.friend]p1: A friend of a class is a function
@@ -17817,12 +17757,11 @@ Decl *Sema::ActOnFriendTypeDecl(Scope *S, const DeclSpec &DS,
Decl *D;
if (!TempParams.empty())
- D = FriendTemplateDecl::Create(Context, CurContext, Loc,
- TempParams,
- TSI,
- DS.getFriendSpecLoc());
+ D = FriendTemplateDecl::Create(Context, CurContext, Loc, TempParams, TSI,
+ FriendLoc);
else
- D = CheckFriendTypeDecl(Loc, DS.getFriendSpecLoc(), TSI);
+ D = FriendDecl::Create(Context, CurContext, TSI->getTypeLoc().getBeginLoc(),
+ TSI, FriendLoc);
if (!D)
return nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index fcb27a880290b..36d2c441391d8 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1407,11 +1407,8 @@ Decl *TemplateDeclInstantiator::VisitFriendDecl(FriendDecl *D) {
if (!InstTy)
return nullptr;
- FriendDecl *FD = SemaRef.CheckFriendTypeDecl(D->getBeginLoc(),
- D->getFriendLoc(), InstTy);
- if (!FD)
- return nullptr;
-
+ FriendDecl *FD = FriendDecl::Create(
+ SemaRef.Context, Owner, D->getLocation(), InstTy, D->getFriendLoc());
FD->setAccess(AS_public);
FD->setUnsupportedFriend(D->isUnsupportedFriend());
Owner->addDecl(FD);
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
index 19406518402ff..75a33c0390897 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p3.cpp
@@ -16,10 +16,8 @@ class A1 {
friend union A; // expected-error {{use of 'A' with tag type that does not match previous declaration}}
friend enum A; // expected-error {{use of 'A' with tag type that does not match previous declaration}}
- friend enum E;
-#if __cplusplus <= 199711L // C++03 or earlier modes
- // expected-warning at -2 {{befriending enumeration type 'enum E' is a C++11 extension}}
-#endif
+ // expected-warning at -1 {{cannot be a friend}}
+ friend enum E; // expected-warning {{cannot be a friend}}
};
template <class T> struct B { // expected-note {{previous use is here}}
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp
new file mode 100644
index 0000000000000..c7792f3dd29ac
--- /dev/null
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.elab/p4.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -verify %s -std=c++11 -pedantic-errors
+
+enum class E;
+
+template<typename T>
+struct A {
+ enum class F;
+};
+
+struct B {
+ template<typename T>
+ friend enum A<T>::F; // expected-error {{elaborated enumeration type cannot be a friend}}
+
+ // FIXME: Per [temp.expl.spec]p19, a friend declaration cannot be an explicit specialization
+ template<>
+ friend enum A<int>::F; // expected-error {{elaborated enumeration type cannot be a friend}}
+
+ enum class G;
+
+ friend enum E; // expected-error {{elaborated enumeration type cannot be a friend}}
+};
+
+template<typename T>
+struct C {
+ friend enum T::G; // expected-error {{elaborated enumeration type cannot be a friend}}
+ friend enum A<T>::G; // expected-error {{elaborated enumeration type cannot be a friend}}
+};
+
+struct D {
+ friend enum B::G; // expected-error {{elaborated enumeration type cannot be a friend}}
+ friend enum class B::G; // expected-error {{elaborated enumeration type cannot be a friend}}
+ // expected-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+};
diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp
index 6ce77fbba7cee..a6de1887baa87 100644
--- a/clang/test/CXX/drs/dr16xx.cpp
+++ b/clang/test/CXX/drs/dr16xx.cpp
@@ -107,6 +107,7 @@ namespace dr1638 { // dr1638: 3.1
struct B {
friend enum class A<unsigned>::E;
// since-cxx11-error at -1 {{reference to enumeration must use 'enum' not 'enum class'}}
+ // since-cxx11-error at -2 {{cannot be a friend}}
};
#endif
}
diff --git a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp
index 2884be146c7c3..012248de06057 100644
--- a/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.class/temp.mem.enum/p1.cpp
@@ -101,10 +101,11 @@ template<> enum class D<short>::E;
struct F {
// Per C++11 [class.friend]p3, these friend declarations have no effect.
// Only classes and functions can be friends.
- template<typename T> friend enum D<T>::E;
- template<> friend enum D<short>::E;
+ template<typename T> friend enum D<T>::E; // expected-warning {{cannot be a friend}}
+ template<> friend enum D<short>::E; // expected-warning {{cannot be a friend}}
template<> friend enum D<double>::E { e3 }; // expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{cannot be a friend}}
private:
static const int n = 1; // expected-note {{private here}}
diff --git a/clang/test/FixIt/fixit-c++11.cpp b/clang/test/FixIt/fixit-c++11.cpp
index a5a47b7c937ba..627ef73d44d1e 100644
--- a/clang/test/FixIt/fixit-c++11.cpp
+++ b/clang/test/FixIt/fixit-c++11.cpp
@@ -44,6 +44,7 @@ namespace ScopedEnum {
enum class E b = E::a; // expected-error {{must use 'enum' not 'enum class'}}
struct S {
friend enum class E; // expected-error {{must use 'enum' not 'enum class'}}
+ // expected-warning at -1 {{cannot be a friend}}
};
}
diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp
index 8a6e6546cd3ed..4c4bb87b1b953 100644
--- a/clang/test/Parser/cxx-decl.cpp
+++ b/clang/test/Parser/cxx-decl.cpp
@@ -252,9 +252,6 @@ namespace DuplicateFriend {
struct A {
friend void friend f(); // expected-warning {{duplicate 'friend' declaration specifier}}
friend struct B friend; // expected-warning {{duplicate 'friend' declaration specifier}}
-#if __cplusplus >= 201103L
- // expected-error at -2 {{'friend' must appear first in a non-function declaration}}
-#endif
};
}
diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp
index 18095a4d989dd..a0b3266c738ff 100644
--- a/clang/test/Parser/cxx0x-decl.cpp
+++ b/clang/test/Parser/cxx0x-decl.cpp
@@ -157,7 +157,7 @@ namespace DuplicateSpecifier {
struct A {
friend constexpr int constexpr friend f(); // expected-warning {{duplicate 'friend' declaration specifier}} \
// expected-error {{duplicate 'constexpr' declaration specifier}}
- friend struct A friend; // expected-warning {{duplicate 'friend'}} expected-error {{'friend' must appear first}}
+ friend struct A friend; // expected-warning {{duplicate 'friend'}}
};
constinit constexpr int n1 = 0; // expected-error {{cannot combine with previous 'constinit'}}
diff --git a/clang/test/SemaCXX/cxx98-compat.cpp b/clang/test/SemaCXX/cxx98-compat.cpp
index d26e3a1e684d5..0a4eea14ce3d9 100644
--- a/clang/test/SemaCXX/cxx98-compat.cpp
+++ b/clang/test/SemaCXX/cxx98-compat.cpp
@@ -220,7 +220,7 @@ struct HasExplicitConversion {
struct Struct {};
enum Enum { enum_val = 0 };
struct BadFriends {
- friend enum ::Enum; // expected-warning {{befriending enumeration type 'enum ::Enum' is incompatible with C++98}}
+ friend enum ::Enum; // expected-warning {{elaborated enumeration type cannot be a friend}}
friend int; // expected-warning {{non-class friend type 'int' is incompatible with C++98}}
friend Struct; // expected-warning {{befriending 'Struct' without 'struct' keyword is incompatible with C++98}}
};
diff --git a/clang/test/SemaCXX/enum-scoped.cpp b/clang/test/SemaCXX/enum-scoped.cpp
index a4da0607d74ae..fb7b41a30f3b7 100644
--- a/clang/test/SemaCXX/enum-scoped.cpp
+++ b/clang/test/SemaCXX/enum-scoped.cpp
@@ -174,11 +174,16 @@ namespace N2764 {
struct S {
friend enum class E; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-warning at -1 {{cannot be a friend}}
friend enum class F; // expected-error {{reference to enumeration must use 'enum' not 'enum class'}}
+ // expected-warning at -1 {{cannot be a friend}}
friend enum G {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{cannot be a friend}}
friend enum class H {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{cannot be a friend}}
friend enum I : int {}; // expected-error {{forward reference}} expected-error {{cannot define a type in a friend declaration}}
+ // expected-warning at -1 {{cannot be a friend}}
enum A : int;
A a;
More information about the cfe-commits
mailing list