[clang] p1099 using enum part 2

This implements the 'using enum maybe-qualified-enum-tag ;' part of
1099. It introduces a new 'UsingEnumDecl', subclassed from
'BaseUsingDecl'. Much of the diff is the boilerplate needed to get the
new class set up.

There is one case where we accept ill-formed, but I believe this is
merely an extended case of an existing bug, so consider it
orthogonal. AFAICT in class-scope the c++20 rule is that no 2 using
decls can bring in the same target decl ([namespace.udecl]/8). But we
already accept:

struct A { enum { a }; };
struct B : A { using A::a; };
struct C : B { using A::a;
using B::a; }; // same enumerator

this patch permits mixtures of 'using enum Bob;' and 'using Bob::member;' in the same way.

Differential Revision: https://reviews.llvm.org/D102241
This commit is contained in:
Nathan Sidwell 2021-05-05 08:55:02 -07:00
parent de98da2ece
commit b2d0c16e91
42 changed files with 838 additions and 99 deletions

View File

@ -181,6 +181,9 @@ public:
for (const UsingShadowDecl *S : UD->shadows()) for (const UsingShadowDecl *S : UD->shadows())
add(S->getUnderlyingDecl(), Flags); add(S->getUnderlyingDecl(), Flags);
Flags |= Rel::Alias; // continue with the alias. Flags |= Rel::Alias; // continue with the alias.
} else if (const UsingEnumDecl *UED = dyn_cast<UsingEnumDecl>(D)) {
add(UED->getEnumDecl(), Flags);
Flags |= Rel::Alias; // continue with the alias.
} else if (const auto *NAD = dyn_cast<NamespaceAliasDecl>(D)) { } else if (const auto *NAD = dyn_cast<NamespaceAliasDecl>(D)) {
add(NAD->getUnderlyingDecl(), Flags | Rel::Underlying); add(NAD->getUnderlyingDecl(), Flags | Rel::Underlying);
Flags |= Rel::Alias; // continue with the alias Flags |= Rel::Alias; // continue with the alias

View File

@ -518,6 +518,17 @@ private:
/// B<int> to the UnresolvedUsingDecl in B<T>. /// B<int> to the UnresolvedUsingDecl in B<T>.
llvm::DenseMap<NamedDecl *, NamedDecl *> InstantiatedFromUsingDecl; llvm::DenseMap<NamedDecl *, NamedDecl *> InstantiatedFromUsingDecl;
/// Like InstantiatedFromUsingDecl, but for using-enum-declarations. Maps
/// from the instantiated using-enum to the templated decl from whence it
/// came.
/// Note that using-enum-declarations cannot be dependent and
/// thus will never be instantiated from an "unresolved"
/// version thereof (as with using-declarations), so each mapping is from
/// a (resolved) UsingEnumDecl to a (resolved) UsingEnumDecl.
llvm::DenseMap<UsingEnumDecl *, UsingEnumDecl *>
InstantiatedFromUsingEnumDecl;
/// Simlarly maps instantiated UsingShadowDecls to their origin.
llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*> llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*>
InstantiatedFromUsingShadowDecl; InstantiatedFromUsingShadowDecl;
@ -886,30 +897,38 @@ public:
MemberSpecializationInfo *getInstantiatedFromStaticDataMember( MemberSpecializationInfo *getInstantiatedFromStaticDataMember(
const VarDecl *Var); const VarDecl *Var);
TemplateOrSpecializationInfo
getTemplateOrSpecializationInfo(const VarDecl *Var);
/// Note that the static data member \p Inst is an instantiation of /// Note that the static data member \p Inst is an instantiation of
/// the static data member template \p Tmpl of a class template. /// the static data member template \p Tmpl of a class template.
void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl, void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl,
TemplateSpecializationKind TSK, TemplateSpecializationKind TSK,
SourceLocation PointOfInstantiation = SourceLocation()); SourceLocation PointOfInstantiation = SourceLocation());
TemplateOrSpecializationInfo
getTemplateOrSpecializationInfo(const VarDecl *Var);
void setTemplateOrSpecializationInfo(VarDecl *Inst, void setTemplateOrSpecializationInfo(VarDecl *Inst,
TemplateOrSpecializationInfo TSI); TemplateOrSpecializationInfo TSI);
/// If the given using decl \p Inst is an instantiation of a /// If the given using decl \p Inst is an instantiation of
/// (possibly unresolved) using decl from a template instantiation, /// another (possibly unresolved) using decl, return it.
/// return it.
NamedDecl *getInstantiatedFromUsingDecl(NamedDecl *Inst); NamedDecl *getInstantiatedFromUsingDecl(NamedDecl *Inst);
/// Remember that the using decl \p Inst is an instantiation /// Remember that the using decl \p Inst is an instantiation
/// of the using decl \p Pattern of a class template. /// of the using decl \p Pattern of a class template.
void setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern); void setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern);
/// If the given using-enum decl \p Inst is an instantiation of
/// another using-enum decl, return it.
UsingEnumDecl *getInstantiatedFromUsingEnumDecl(UsingEnumDecl *Inst);
/// Remember that the using enum decl \p Inst is an instantiation
/// of the using enum decl \p Pattern of a class template.
void setInstantiatedFromUsingEnumDecl(UsingEnumDecl *Inst,
UsingEnumDecl *Pattern);
UsingShadowDecl *getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst);
void setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst, void setInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst,
UsingShadowDecl *Pattern); UsingShadowDecl *Pattern);
UsingShadowDecl *getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst);
FieldDecl *getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field); FieldDecl *getInstantiatedFromUnnamedFieldDecl(FieldDecl *Field);

View File

@ -3366,7 +3366,7 @@ public:
void removeShadowDecl(UsingShadowDecl *S); void removeShadowDecl(UsingShadowDecl *S);
static bool classof(const Decl *D) { return classofKind(D->getKind()); } static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == Using; } static bool classofKind(Kind K) { return K == Using || K == UsingEnum; }
}; };
/// Represents a C++ using-declaration. /// Represents a C++ using-declaration.
@ -3568,6 +3568,65 @@ public:
static bool classofKind(Kind K) { return K == ConstructorUsingShadow; } static bool classofKind(Kind K) { return K == ConstructorUsingShadow; }
}; };
/// Represents a C++ using-enum-declaration.
///
/// For example:
/// \code
/// using enum SomeEnumTag ;
/// \endcode
class UsingEnumDecl : public BaseUsingDecl, public Mergeable<UsingEnumDecl> {
/// The source location of the 'using' keyword itself.
SourceLocation UsingLocation;
/// Location of the 'enum' keyword.
SourceLocation EnumLocation;
/// The enum
EnumDecl *Enum;
UsingEnumDecl(DeclContext *DC, DeclarationName DN, SourceLocation UL,
SourceLocation EL, SourceLocation NL, EnumDecl *ED)
: BaseUsingDecl(UsingEnum, DC, NL, DN), UsingLocation(UL),
EnumLocation(EL), Enum(ED) {}
void anchor() override;
public:
friend class ASTDeclReader;
friend class ASTDeclWriter;
/// The source location of the 'using' keyword.
SourceLocation getUsingLoc() const { return UsingLocation; }
void setUsingLoc(SourceLocation L) { UsingLocation = L; }
/// The source location of the 'enum' keyword.
SourceLocation getEnumLoc() const { return EnumLocation; }
void setEnumLoc(SourceLocation L) { EnumLocation = L; }
public:
EnumDecl *getEnumDecl() const { return Enum; }
static UsingEnumDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation UsingL, SourceLocation EnumL,
SourceLocation NameL, EnumDecl *ED);
static UsingEnumDecl *CreateDeserialized(ASTContext &C, unsigned ID);
SourceRange getSourceRange() const override LLVM_READONLY;
/// Retrieves the canonical declaration of this declaration.
UsingEnumDecl *getCanonicalDecl() override {
return cast<UsingEnumDecl>(getFirstDecl());
}
const UsingEnumDecl *getCanonicalDecl() const {
return cast<UsingEnumDecl>(getFirstDecl());
}
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == UsingEnum; }
};
/// Represents a pack of using declarations that a single /// Represents a pack of using declarations that a single
/// using-declarator pack-expanded into. /// using-declarator pack-expanded into.
/// ///

View File

@ -234,6 +234,7 @@ public:
void VisitUsingDirectiveDecl(const UsingDirectiveDecl *UDD); void VisitUsingDirectiveDecl(const UsingDirectiveDecl *UDD);
void VisitNamespaceAliasDecl(const NamespaceAliasDecl *NAD); void VisitNamespaceAliasDecl(const NamespaceAliasDecl *NAD);
void VisitUsingDecl(const UsingDecl *UD); void VisitUsingDecl(const UsingDecl *UD);
void VisitUsingEnumDecl(const UsingEnumDecl *UED);
void VisitUsingShadowDecl(const UsingShadowDecl *USD); void VisitUsingShadowDecl(const UsingShadowDecl *USD);
void VisitVarDecl(const VarDecl *VD); void VisitVarDecl(const VarDecl *VD);
void VisitFieldDecl(const FieldDecl *FD); void VisitFieldDecl(const FieldDecl *FD);

View File

@ -1593,6 +1593,8 @@ DEF_TRAVERSE_DECL(UsingDecl, {
TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo())); TRY_TO(TraverseDeclarationNameInfo(D->getNameInfo()));
}) })
DEF_TRAVERSE_DECL(UsingEnumDecl, {})
DEF_TRAVERSE_DECL(UsingPackDecl, {}) DEF_TRAVERSE_DECL(UsingPackDecl, {})
DEF_TRAVERSE_DECL(UsingDirectiveDecl, { DEF_TRAVERSE_DECL(UsingDirectiveDecl, {

View File

@ -351,6 +351,7 @@ public:
void VisitUsingDecl(const UsingDecl *D); void VisitUsingDecl(const UsingDecl *D);
void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D); void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D);
void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D); void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D);
void VisitUsingEnumDecl(const UsingEnumDecl *D);
void VisitUsingShadowDecl(const UsingShadowDecl *D); void VisitUsingShadowDecl(const UsingShadowDecl *D);
void VisitConstructorUsingShadowDecl(const ConstructorUsingShadowDecl *D); void VisitConstructorUsingShadowDecl(const ConstructorUsingShadowDecl *D);
void VisitLinkageSpecDecl(const LinkageSpecDecl *D); void VisitLinkageSpecDecl(const LinkageSpecDecl *D);

View File

@ -1752,6 +1752,18 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt,
/// matches \code using X::x \endcode /// matches \code using X::x \endcode
extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl; extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl;
/// Matches using-enum declarations.
///
/// Given
/// \code
/// namespace X { enum x {...}; }
/// using enum X::x;
/// \endcode
/// usingEnumDecl()
/// matches \code using enum X::x \endcode
extern const internal::VariadicDynCastAllOfMatcher<Decl, UsingEnumDecl>
usingEnumDecl;
/// Matches using namespace declarations. /// Matches using namespace declarations.
/// ///
/// Given /// Given
@ -6197,7 +6209,7 @@ AST_POLYMORPHIC_MATCHER_P(
/// \endcode /// \endcode
/// usingDecl(hasAnyUsingShadowDecl(hasName("b")))) /// usingDecl(hasAnyUsingShadowDecl(hasName("b"))))
/// matches \code using X::b \endcode /// matches \code using X::b \endcode
AST_MATCHER_P(UsingDecl, hasAnyUsingShadowDecl, AST_MATCHER_P(BaseUsingDecl, hasAnyUsingShadowDecl,
internal::Matcher<UsingShadowDecl>, InnerMatcher) { internal::Matcher<UsingShadowDecl>, InnerMatcher) {
return matchesFirstInPointerRange(InnerMatcher, Node.shadow_begin(), return matchesFirstInPointerRange(InnerMatcher, Node.shadow_begin(),
Node.shadow_end(), Finder, Node.shadow_end(), Finder,

View File

@ -73,6 +73,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
def Concept : DeclNode<Template>; def Concept : DeclNode<Template>;
def BaseUsing : DeclNode<Named, "", 1>; def BaseUsing : DeclNode<Named, "", 1>;
def Using : DeclNode<BaseUsing>; def Using : DeclNode<BaseUsing>;
def UsingEnum : DeclNode<BaseUsing>;
def UsingPack : DeclNode<Named>; def UsingPack : DeclNode<Named>;
def UsingShadow : DeclNode<Named>; def UsingShadow : DeclNode<Named>;
def ConstructorUsingShadow : DeclNode<UsingShadow>; def ConstructorUsingShadow : DeclNode<UsingShadow>;

View File

@ -580,6 +580,12 @@ def warn_cxx98_compat_noexcept_decl : Warning<
def err_expected_catch : Error<"expected catch">; def err_expected_catch : Error<"expected catch">;
def err_using_namespace_in_class : Error< def err_using_namespace_in_class : Error<
"'using namespace' is not allowed in classes">; "'using namespace' is not allowed in classes">;
def warn_cxx17_compat_using_enum_declaration : Warning<
"using enum declaration is incompatible with C++ standards before C++20">,
InGroup<CXXPre20Compat>, DefaultIgnore;
def ext_using_enum_declaration : ExtWarn<
"using enum declaration is a C++20 extension">,
InGroup<CXX20>;
def err_constructor_bad_name : Error< def err_constructor_bad_name : Error<
"missing return type for function %0; did you mean the constructor name %1?">; "missing return type for function %0; did you mean the constructor name %1?">;
def err_destructor_tilde_identifier : Error< def err_destructor_tilde_identifier : Error<

View File

@ -554,8 +554,6 @@ def warn_cxx17_compat_using_decl_class_member_enumerator : Warning<
def ext_using_decl_class_member_enumerator : ExtWarn< def ext_using_decl_class_member_enumerator : ExtWarn<
"member using declaration naming a non-member enumerator is " "member using declaration naming a non-member enumerator is "
"a C++20 extension">, InGroup<CXX20>; "a C++20 extension">, InGroup<CXX20>;
def err_using_enum_lacks_definition : Error<
"enum named by using-enum declaration lacks a definition">;
def err_using_enum_is_dependent : Error< def err_using_enum_is_dependent : Error<
"using-enum cannot name a dependent type">; "using-enum cannot name a dependent type">;
def err_ambiguous_inherited_constructor : Error< def err_ambiguous_inherited_constructor : Error<
@ -598,6 +596,9 @@ def note_empty_using_if_exists_here : Note<
"using declaration annotated with 'using_if_exists' here">; "using declaration annotated with 'using_if_exists' here">;
def err_using_if_exists_on_ctor : Error< def err_using_if_exists_on_ctor : Error<
"'using_if_exists' attribute cannot be applied to an inheriting constructor">; "'using_if_exists' attribute cannot be applied to an inheriting constructor">;
def err_using_enum_decl_redeclaration : Error<
"redeclaration of using-enum declaration">;
def note_using_enum_decl : Note<"%select{|previous }0using-enum declaration">;
def warn_access_decl_deprecated : Warning< def warn_access_decl_deprecated : Warning<
"access declarations are deprecated; use using declarations instead">, "access declarations are deprecated; use using declarations instead">,
@ -1760,6 +1761,8 @@ def warn_cxx98_compat_sfinae_access_control : Warning<
// C++ name lookup // C++ name lookup
def err_incomplete_nested_name_spec : Error< def err_incomplete_nested_name_spec : Error<
"incomplete type %0 named in nested name specifier">; "incomplete type %0 named in nested name specifier">;
def err_incomplete_enum : Error<
"enumeration %0 is incomplete">;
def err_dependent_nested_name_spec : Error< def err_dependent_nested_name_spec : Error<
"nested name specifier for a declaration cannot depend on a template " "nested name specifier for a declaration cannot depend on a template "
"parameter">; "parameter">;

View File

@ -75,6 +75,7 @@ enum class SymbolSubKind : uint8_t {
AccessorSetter, AccessorSetter,
UsingTypename, UsingTypename,
UsingValue, UsingValue,
UsingEnum,
}; };
typedef uint16_t SymbolPropertySet; typedef uint16_t SymbolPropertySet;

View File

@ -5701,6 +5701,7 @@ public:
SourceLocation IdentLoc, SourceLocation IdentLoc,
IdentifierInfo *Ident); IdentifierInfo *Ident);
void FilterUsingLookup(Scope *S, LookupResult &lookup);
void HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow); void HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow);
bool CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Target, bool CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Target,
const LookupResult &PreviousDecls, const LookupResult &PreviousDecls,
@ -5721,14 +5722,16 @@ public:
const LookupResult *R = nullptr, const LookupResult *R = nullptr,
const UsingDecl *UD = nullptr); const UsingDecl *UD = nullptr);
NamedDecl *BuildUsingDeclaration(Scope *S, AccessSpecifier AS, NamedDecl *BuildUsingDeclaration(
SourceLocation UsingLoc, Scope *S, AccessSpecifier AS, SourceLocation UsingLoc,
bool HasTypenameKeyword, bool HasTypenameKeyword, SourceLocation TypenameLoc, CXXScopeSpec &SS,
SourceLocation TypenameLoc, CXXScopeSpec &SS, DeclarationNameInfo NameInfo, SourceLocation EllipsisLoc,
DeclarationNameInfo NameInfo, const ParsedAttributesView &AttrList, bool IsInstantiation,
SourceLocation EllipsisLoc, bool IsUsingIfExists);
const ParsedAttributesView &AttrList, NamedDecl *BuildUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
bool IsInstantiation, bool IsUsingIfExists); SourceLocation UsingLoc,
SourceLocation EnumLoc,
SourceLocation NameLoc, EnumDecl *ED);
NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom, NamedDecl *BuildUsingPackDecl(NamedDecl *InstantiatedFrom,
ArrayRef<NamedDecl *> Expansions); ArrayRef<NamedDecl *> Expansions);
@ -5746,6 +5749,9 @@ public:
SourceLocation TypenameLoc, CXXScopeSpec &SS, SourceLocation TypenameLoc, CXXScopeSpec &SS,
UnqualifiedId &Name, SourceLocation EllipsisLoc, UnqualifiedId &Name, SourceLocation EllipsisLoc,
const ParsedAttributesView &AttrList); const ParsedAttributesView &AttrList);
Decl *ActOnUsingEnumDeclaration(Scope *CurScope, AccessSpecifier AS,
SourceLocation UsingLoc,
SourceLocation EnumLoc, const DeclSpec &);
Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS, Decl *ActOnAliasDeclaration(Scope *CurScope, AccessSpecifier AS,
MultiTemplateParamsArg TemplateParams, MultiTemplateParamsArg TemplateParams,
SourceLocation UsingLoc, UnqualifiedId &Name, SourceLocation UsingLoc, UnqualifiedId &Name,

View File

@ -537,6 +537,8 @@ enum class TemplateSubstitutionKind : char {
Decl *VisitDecl(Decl *D); Decl *VisitDecl(Decl *D);
Decl *VisitVarDecl(VarDecl *D, bool InstantiatingVarTemplate, Decl *VisitVarDecl(VarDecl *D, bool InstantiatingVarTemplate,
ArrayRef<BindingDecl *> *Bindings = nullptr); ArrayRef<BindingDecl *> *Bindings = nullptr);
Decl *VisitBaseUsingDecls(BaseUsingDecl *D, BaseUsingDecl *Inst,
LookupResult *Lookup);
// Enable late instantiation of attributes. Late instantiated attributes // Enable late instantiation of attributes. Late instantiated attributes
// will be stored in LA. // will be stored in LA.

View File

@ -1339,6 +1339,9 @@ enum DeclCode {
/// A UsingDecl record. /// A UsingDecl record.
DECL_USING, DECL_USING,
/// A UsingEnumDecl record.
DECL_USING_ENUM,
/// A UsingPackDecl record. /// A UsingPackDecl record.
DECL_USING_PACK, DECL_USING_PACK,

View File

@ -1571,6 +1571,21 @@ ASTContext::setInstantiatedFromUsingDecl(NamedDecl *Inst, NamedDecl *Pattern) {
InstantiatedFromUsingDecl[Inst] = Pattern; InstantiatedFromUsingDecl[Inst] = Pattern;
} }
UsingEnumDecl *
ASTContext::getInstantiatedFromUsingEnumDecl(UsingEnumDecl *UUD) {
auto Pos = InstantiatedFromUsingEnumDecl.find(UUD);
if (Pos == InstantiatedFromUsingEnumDecl.end())
return nullptr;
return Pos->second;
}
void ASTContext::setInstantiatedFromUsingEnumDecl(UsingEnumDecl *Inst,
UsingEnumDecl *Pattern) {
assert(!InstantiatedFromUsingEnumDecl[Inst] && "pattern already exists");
InstantiatedFromUsingEnumDecl[Inst] = Pattern;
}
UsingShadowDecl * UsingShadowDecl *
ASTContext::getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst) { ASTContext::getInstantiatedFromUsingShadowDecl(UsingShadowDecl *Inst) {
llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*>::const_iterator Pos llvm::DenseMap<UsingShadowDecl*, UsingShadowDecl*>::const_iterator Pos

View File

@ -512,6 +512,8 @@ namespace clang {
ExpectedDecl VisitUsingDecl(UsingDecl *D); ExpectedDecl VisitUsingDecl(UsingDecl *D);
ExpectedDecl VisitUsingShadowDecl(UsingShadowDecl *D); ExpectedDecl VisitUsingShadowDecl(UsingShadowDecl *D);
ExpectedDecl VisitUsingDirectiveDecl(UsingDirectiveDecl *D); ExpectedDecl VisitUsingDirectiveDecl(UsingDirectiveDecl *D);
ExpectedDecl ImportUsingShadowDecls(BaseUsingDecl *D, BaseUsingDecl *ToSI);
ExpectedDecl VisitUsingEnumDecl(UsingEnumDecl *D);
ExpectedDecl VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D); ExpectedDecl VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
ExpectedDecl VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D); ExpectedDecl VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
ExpectedDecl VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D); ExpectedDecl VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D);
@ -4564,6 +4566,19 @@ ExpectedDecl ASTNodeImporter::VisitLinkageSpecDecl(LinkageSpecDecl *D) {
return ToLinkageSpec; return ToLinkageSpec;
} }
ExpectedDecl ASTNodeImporter::ImportUsingShadowDecls(BaseUsingDecl *D,
BaseUsingDecl *ToSI) {
for (UsingShadowDecl *FromShadow : D->shadows()) {
if (Expected<UsingShadowDecl *> ToShadowOrErr = import(FromShadow))
ToSI->addShadowDecl(*ToShadowOrErr);
else
// FIXME: We return error here but the definition is already created
// and available with lookups. How to fix this?..
return ToShadowOrErr.takeError();
}
return ToSI;
}
ExpectedDecl ASTNodeImporter::VisitUsingDecl(UsingDecl *D) { ExpectedDecl ASTNodeImporter::VisitUsingDecl(UsingDecl *D) {
DeclContext *DC, *LexicalDC; DeclContext *DC, *LexicalDC;
DeclarationName Name; DeclarationName Name;
@ -4603,15 +4618,44 @@ ExpectedDecl ASTNodeImporter::VisitUsingDecl(UsingDecl *D) {
return ToPatternOrErr.takeError(); return ToPatternOrErr.takeError();
} }
for (UsingShadowDecl *FromShadow : D->shadows()) { return ImportUsingShadowDecls(D, ToUsing);
if (Expected<UsingShadowDecl *> ToShadowOrErr = import(FromShadow)) }
ToUsing->addShadowDecl(*ToShadowOrErr);
ExpectedDecl ASTNodeImporter::VisitUsingEnumDecl(UsingEnumDecl *D) {
DeclContext *DC, *LexicalDC;
DeclarationName Name;
SourceLocation Loc;
NamedDecl *ToD = nullptr;
if (Error Err = ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc))
return std::move(Err);
if (ToD)
return ToD;
Error Err = Error::success();
auto ToUsingLoc = importChecked(Err, D->getUsingLoc());
auto ToEnumLoc = importChecked(Err, D->getEnumLoc());
auto ToEnumDecl = importChecked(Err, D->getEnumDecl());
if (Err)
return std::move(Err);
UsingEnumDecl *ToUsingEnum;
if (GetImportedOrCreateDecl(ToUsingEnum, D, Importer.getToContext(), DC,
ToUsingLoc, ToEnumLoc, Loc, ToEnumDecl))
return ToUsingEnum;
ToUsingEnum->setLexicalDeclContext(LexicalDC);
LexicalDC->addDeclInternal(ToUsingEnum);
if (UsingEnumDecl *FromPattern =
Importer.getFromContext().getInstantiatedFromUsingEnumDecl(D)) {
if (Expected<UsingEnumDecl *> ToPatternOrErr = import(FromPattern))
Importer.getToContext().setInstantiatedFromUsingEnumDecl(ToUsingEnum,
*ToPatternOrErr);
else else
// FIXME: We return error here but the definition is already created return ToPatternOrErr.takeError();
// and available with lookups. How to fix this?..
return ToShadowOrErr.takeError();
} }
return ToUsing;
return ImportUsingShadowDecls(D, ToUsingEnum);
} }
ExpectedDecl ASTNodeImporter::VisitUsingShadowDecl(UsingShadowDecl *D) { ExpectedDecl ASTNodeImporter::VisitUsingShadowDecl(UsingShadowDecl *D) {

View File

@ -1370,6 +1370,7 @@ LinkageInfo LinkageComputer::computeLVForDecl(const NamedDecl *D,
case Decl::NamespaceAlias: case Decl::NamespaceAlias:
case Decl::ParmVar: case Decl::ParmVar:
case Decl::Using: case Decl::Using:
case Decl::UsingEnum:
case Decl::UsingShadow: case Decl::UsingShadow:
case Decl::UsingDirective: case Decl::UsingDirective:
return LinkageInfo::none(); return LinkageInfo::none();

View File

@ -784,6 +784,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case Using: case Using:
case UsingPack: case UsingPack:
case UsingEnum:
return IDNS_Using; return IDNS_Using;
case ObjCProtocol: case ObjCProtocol:

View File

@ -3082,6 +3082,23 @@ SourceRange UsingDecl::getSourceRange() const {
return SourceRange(Begin, getNameInfo().getEndLoc()); return SourceRange(Begin, getNameInfo().getEndLoc());
} }
void UsingEnumDecl::anchor() {}
UsingEnumDecl *UsingEnumDecl::Create(ASTContext &C, DeclContext *DC,
SourceLocation UL, SourceLocation EL,
SourceLocation NL, EnumDecl *Enum) {
return new (C, DC) UsingEnumDecl(DC, Enum->getDeclName(), UL, EL, NL, Enum);
}
UsingEnumDecl *UsingEnumDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID) UsingEnumDecl(nullptr, DeclarationName(), SourceLocation(),
SourceLocation(), SourceLocation(), nullptr);
}
SourceRange UsingEnumDecl::getSourceRange() const {
return SourceRange(EnumLocation, getLocation());
}
void UsingPackDecl::anchor() {} void UsingPackDecl::anchor() {}
UsingPackDecl *UsingPackDecl::Create(ASTContext &C, DeclContext *DC, UsingPackDecl *UsingPackDecl::Create(ASTContext &C, DeclContext *DC,

View File

@ -98,6 +98,7 @@ namespace {
void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D); void VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D);
void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D); void VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D);
void VisitUsingDecl(UsingDecl *D); void VisitUsingDecl(UsingDecl *D);
void VisitUsingEnumDecl(UsingEnumDecl *D);
void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitUsingShadowDecl(UsingShadowDecl *D);
void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D); void VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D);
void VisitOMPAllocateDecl(OMPAllocateDecl *D); void VisitOMPAllocateDecl(OMPAllocateDecl *D);
@ -1609,6 +1610,10 @@ void DeclPrinter::VisitUsingDecl(UsingDecl *D) {
Out << *D; Out << *D;
} }
void DeclPrinter::VisitUsingEnumDecl(UsingEnumDecl *D) {
Out << "using enum " << D->getEnumDecl();
}
void void
DeclPrinter::VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D) { DeclPrinter::VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D) {
Out << "using typename "; Out << "using typename ";

View File

@ -756,6 +756,10 @@ void JSONNodeDumper::VisitUsingDecl(const UsingDecl *UD) {
JOS.attribute("name", Name); JOS.attribute("name", Name);
} }
void JSONNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *UED) {
JOS.attribute("target", createBareDeclRef(UED->getEnumDecl()));
}
void JSONNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *USD) { void JSONNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *USD) {
JOS.attribute("target", createBareDeclRef(USD->getTargetDecl())); JOS.attribute("target", createBareDeclRef(USD->getTargetDecl()));
} }

View File

@ -2058,6 +2058,11 @@ void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) {
OS << D->getDeclName(); OS << D->getDeclName();
} }
void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) {
OS << ' ';
dumpBareDeclRef(D->getEnumDecl());
}
void TextNodeDumper::VisitUnresolvedUsingTypenameDecl( void TextNodeDumper::VisitUnresolvedUsingTypenameDecl(
const UnresolvedUsingTypenameDecl *D) { const UnresolvedUsingTypenameDecl *D) {
OS << ' '; OS << ' ';

View File

@ -841,6 +841,7 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, ParenListExpr> parenListExpr;
const internal::VariadicDynCastAllOfMatcher<Stmt, SubstNonTypeTemplateParmExpr> const internal::VariadicDynCastAllOfMatcher<Stmt, SubstNonTypeTemplateParmExpr>
substNonTypeTemplateParmExpr; substNonTypeTemplateParmExpr;
const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl; const internal::VariadicDynCastAllOfMatcher<Decl, UsingDecl> usingDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, UsingEnumDecl> usingEnumDecl;
const internal::VariadicDynCastAllOfMatcher<Decl, UsingDirectiveDecl> const internal::VariadicDynCastAllOfMatcher<Decl, UsingDirectiveDecl>
usingDirectiveDecl; usingDirectiveDecl;
const internal::VariadicDynCastAllOfMatcher<Stmt, UnresolvedLookupExpr> const internal::VariadicDynCastAllOfMatcher<Stmt, UnresolvedLookupExpr>

View File

@ -561,6 +561,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(userDefinedLiteral); REGISTER_MATCHER(userDefinedLiteral);
REGISTER_MATCHER(usesADL); REGISTER_MATCHER(usesADL);
REGISTER_MATCHER(usingDecl); REGISTER_MATCHER(usingDecl);
REGISTER_MATCHER(usingEnumDecl);
REGISTER_MATCHER(usingDirectiveDecl); REGISTER_MATCHER(usingDirectiveDecl);
REGISTER_MATCHER(valueDecl); REGISTER_MATCHER(valueDecl);
REGISTER_MATCHER(varDecl); REGISTER_MATCHER(varDecl);

View File

@ -5002,6 +5002,17 @@ void CGDebugInfo::EmitUsingDecl(const UsingDecl &UD) {
} }
} }
void CGDebugInfo::EmitUsingEnumDecl(const UsingEnumDecl &UD) {
if (!CGM.getCodeGenOpts().hasReducedDebugInfo())
return;
assert(UD.shadow_size() &&
"We shouldn't be codegening an invalid UsingEnumDecl"
" containing no decls");
for (const auto *USD : UD.shadows())
EmitUsingShadowDecl(*USD);
}
void CGDebugInfo::EmitImportDecl(const ImportDecl &ID) { void CGDebugInfo::EmitImportDecl(const ImportDecl &ID) {
if (CGM.getCodeGenOpts().getDebuggerTuning() != llvm::DebuggerKind::LLDB) if (CGM.getCodeGenOpts().getDebuggerTuning() != llvm::DebuggerKind::LLDB)
return; return;

View File

@ -508,6 +508,9 @@ public:
/// Emit C++ using declaration. /// Emit C++ using declaration.
void EmitUsingDecl(const UsingDecl &UD); void EmitUsingDecl(const UsingDecl &UD);
/// Emit C++ using-enum declaration.
void EmitUsingEnumDecl(const UsingEnumDecl &UD);
/// Emit an @import declaration. /// Emit an @import declaration.
void EmitImportDecl(const ImportDecl &ID); void EmitImportDecl(const ImportDecl &ID);

View File

@ -138,6 +138,10 @@ void CodeGenFunction::EmitDecl(const Decl &D) {
if (CGDebugInfo *DI = getDebugInfo()) if (CGDebugInfo *DI = getDebugInfo())
DI->EmitUsingDecl(cast<UsingDecl>(D)); DI->EmitUsingDecl(cast<UsingDecl>(D));
return; return;
case Decl::UsingEnum: // using enum X; [C++]
if (CGDebugInfo *DI = getDebugInfo())
DI->EmitUsingEnumDecl(cast<UsingEnumDecl>(D));
return;
case Decl::UsingPack: case Decl::UsingPack:
for (auto *Using : cast<UsingPackDecl>(D).expansions()) for (auto *Using : cast<UsingPackDecl>(D).expansions())
EmitDecl(*Using); EmitDecl(*Using);

View File

@ -5740,6 +5740,10 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
if (CGDebugInfo *DI = getModuleDebugInfo()) if (CGDebugInfo *DI = getModuleDebugInfo())
DI->EmitUsingDecl(cast<UsingDecl>(*D)); DI->EmitUsingDecl(cast<UsingDecl>(*D));
break; break;
case Decl::UsingEnum: // using enum X; [C++]
if (CGDebugInfo *DI = getModuleDebugInfo())
DI->EmitUsingEnumDecl(cast<UsingEnumDecl>(*D));
break;
case Decl::NamespaceAlias: case Decl::NamespaceAlias:
if (CGDebugInfo *DI = getModuleDebugInfo()) if (CGDebugInfo *DI = getModuleDebugInfo())
DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(*D)); DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(*D));

View File

@ -329,6 +329,11 @@ SymbolInfo index::getSymbolInfo(const Decl *D) {
Info.Kind = SymbolKind::Using; Info.Kind = SymbolKind::Using;
Info.Lang = SymbolLanguage::CXX; Info.Lang = SymbolLanguage::CXX;
break; break;
case Decl::UsingEnum:
Info.Kind = SymbolKind::Using;
Info.Lang = SymbolLanguage::CXX;
Info.SubKind = SymbolSubKind::UsingEnum;
break;
case Decl::Binding: case Decl::Binding:
Info.Kind = SymbolKind::Variable; Info.Kind = SymbolKind::Variable;
Info.Lang = SymbolLanguage::CXX; Info.Lang = SymbolLanguage::CXX;
@ -542,6 +547,8 @@ StringRef index::getSymbolSubKindString(SymbolSubKind K) {
case SymbolSubKind::AccessorSetter: return "acc-set"; case SymbolSubKind::AccessorSetter: return "acc-set";
case SymbolSubKind::UsingTypename: return "using-typename"; case SymbolSubKind::UsingTypename: return "using-typename";
case SymbolSubKind::UsingValue: return "using-value"; case SymbolSubKind::UsingValue: return "using-value";
case SymbolSubKind::UsingEnum:
return "using-enum";
} }
llvm_unreachable("invalid symbol subkind"); llvm_unreachable("invalid symbol subkind");
} }

View File

@ -667,10 +667,48 @@ bool Parser::ParseUsingDeclarator(DeclaratorContext Context,
/// alias-declaration: C++11 [dcl.dcl]p1 /// alias-declaration: C++11 [dcl.dcl]p1
/// 'using' identifier attribute-specifier-seq[opt] = type-id ; /// 'using' identifier attribute-specifier-seq[opt] = type-id ;
/// ///
Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration( /// using-enum-declaration: [C++20, dcl.enum]
/// 'using' elaborated-enum-specifier ;
///
/// elaborated-enum-specifier:
/// 'enum' nested-name-specifier[opt] identifier
Parser::DeclGroupPtrTy
Parser::ParseUsingDeclaration(
DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
SourceLocation UsingLoc, SourceLocation &DeclEnd, SourceLocation UsingLoc, SourceLocation &DeclEnd,
ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) { ParsedAttributesWithRange &PrefixAttrs, AccessSpecifier AS) {
SourceLocation UELoc;
if (TryConsumeToken(tok::kw_enum, UELoc)) {
// C++20 using-enum
Diag(UELoc, getLangOpts().CPlusPlus20
? diag::warn_cxx17_compat_using_enum_declaration
: diag::ext_using_enum_declaration);
DiagnoseCXX11AttributeExtension(PrefixAttrs);
DeclSpec DS(AttrFactory);
ParseEnumSpecifier(UELoc, DS, TemplateInfo, AS,
// DSC_trailing has the semantics we desire
DeclSpecContext::DSC_trailing);
if (TemplateInfo.Kind) {
SourceRange R = TemplateInfo.getSourceRange();
Diag(UsingLoc, diag::err_templated_using_directive_declaration)
<< 1 /* declaration */ << R << FixItHint::CreateRemoval(R);
return nullptr;
}
Decl *UED = Actions.ActOnUsingEnumDeclaration(getCurScope(), AS, UsingLoc,
UELoc, DS);
DeclEnd = Tok.getLocation();
if (ExpectAndConsume(tok::semi, diag::err_expected_after,
"using-enum declaration"))
SkipUntil(tok::semi);
return Actions.ConvertDeclToDeclGroup(UED);
}
// Check for misplaced attributes before the identifier in an // Check for misplaced attributes before the identifier in an
// alias-declaration. // alias-declaration.
ParsedAttributesWithRange MisplacedAttrs(AttrFactory); ParsedAttributesWithRange MisplacedAttrs(AttrFactory);
@ -768,8 +806,9 @@ Parser::DeclGroupPtrTy Parser::ParseUsingDeclaration(
// Eat ';'. // Eat ';'.
DeclEnd = Tok.getLocation(); DeclEnd = Tok.getLocation();
if (ExpectAndConsume(tok::semi, diag::err_expected_after, if (ExpectAndConsume(tok::semi, diag::err_expected_after,
!Attrs.empty() ? "attributes list" !Attrs.empty() ? "attributes list"
: "using declaration")) : UELoc.isValid() ? "using-enum declaration"
: "using declaration"))
SkipUntil(tok::semi); SkipUntil(tok::semi);
return Actions.BuildDeclaratorGroup(DeclsInGroup); return Actions.BuildDeclaratorGroup(DeclsInGroup);

View File

@ -240,7 +240,6 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS,
/// ///
bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L, bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L,
CXXScopeSpec *SS) { CXXScopeSpec *SS) {
assert (SS && "missing scope");
if (EnumD->isCompleteDefinition()) { if (EnumD->isCompleteDefinition()) {
// If we know about the definition but it is not visible, complain. // If we know about the definition but it is not visible, complain.
NamedDecl *SuggestedDef = nullptr; NamedDecl *SuggestedDef = nullptr;
@ -264,16 +263,22 @@ bool Sema::RequireCompleteEnumDecl(EnumDecl *EnumD, SourceLocation L,
if (InstantiateEnum(L, EnumD, Pattern, if (InstantiateEnum(L, EnumD, Pattern,
getTemplateInstantiationArgs(EnumD), getTemplateInstantiationArgs(EnumD),
TSK_ImplicitInstantiation)) { TSK_ImplicitInstantiation)) {
SS->SetInvalid(SS->getRange()); if (SS)
SS->SetInvalid(SS->getRange());
return true; return true;
} }
return false; return false;
} }
} }
Diag(L, diag::err_incomplete_nested_name_spec) if (SS) {
<< QualType(EnumD->getTypeForDecl(), 0) << SS->getRange(); Diag(L, diag::err_incomplete_nested_name_spec)
SS->SetInvalid(SS->getRange()); << QualType(EnumD->getTypeForDecl(), 0) << SS->getRange();
SS->SetInvalid(SS->getRange());
} else {
Diag(L, diag::err_incomplete_enum) << QualType(EnumD->getTypeForDecl(), 0);
Diag(EnumD->getLocation(), diag::note_declared_at);
}
return true; return true;
} }

View File

@ -3915,6 +3915,9 @@ CXCursorKind clang::getCursorKindForDecl(const Decl *D) {
case Decl::UnresolvedUsingTypename: case Decl::UnresolvedUsingTypename:
return CXCursor_UsingDeclaration; return CXCursor_UsingDeclaration;
case Decl::UsingEnum:
return CXCursor_EnumDecl;
case Decl::ObjCPropertyImpl: case Decl::ObjCPropertyImpl:
switch (cast<ObjCPropertyImplDecl>(D)->getPropertyImplementation()) { switch (cast<ObjCPropertyImplDecl>(D)->getPropertyImplementation()) {
case ObjCPropertyImplDecl::Dynamic: case ObjCPropertyImplDecl::Dynamic:

View File

@ -1835,9 +1835,11 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
case Decl::UsingDirective: case Decl::UsingDirective:
case Decl::UnresolvedUsingTypename: case Decl::UnresolvedUsingTypename:
case Decl::UnresolvedUsingValue: case Decl::UnresolvedUsingValue:
case Decl::UsingEnum:
// - static_assert-declarations // - static_assert-declarations
// - using-declarations, // - using-declarations,
// - using-directives, // - using-directives,
// - using-enum-declaration
continue; continue;
case Decl::Typedef: case Decl::Typedef:
@ -11604,11 +11606,44 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, AccessSpecifier AS,
} }
} }
NamedDecl *UD = BuildUsingDeclaration( NamedDecl *UD =
S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc, SS, TargetNameInfo, BuildUsingDeclaration(S, AS, UsingLoc, TypenameLoc.isValid(), TypenameLoc,
EllipsisLoc, AttrList, SS, TargetNameInfo, EllipsisLoc, AttrList,
/*IsInstantiation=*/false, /*IsInstantiation*/ false,
AttrList.hasAttribute(ParsedAttr::AT_UsingIfExists)); AttrList.hasAttribute(ParsedAttr::AT_UsingIfExists));
if (UD)
PushOnScopeChains(UD, S, /*AddToContext*/ false);
return UD;
}
Decl *Sema::ActOnUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
SourceLocation UsingLoc,
SourceLocation EnumLoc,
const DeclSpec &DS) {
switch (DS.getTypeSpecType()) {
case DeclSpec::TST_error:
// This will already have been diagnosed
return nullptr;
case DeclSpec::TST_enum:
break;
case DeclSpec::TST_typename:
Diag(DS.getTypeSpecTypeLoc(), diag::err_using_enum_is_dependent);
return nullptr;
default:
llvm_unreachable("unexpected DeclSpec type");
}
// As with enum-decls, we ignore attributes for now.
auto *Enum = cast<EnumDecl>(DS.getRepAsDecl());
if (auto *Def = Enum->getDefinition())
Enum = Def;
auto *UD = BuildUsingEnumDeclaration(S, AS, UsingLoc, EnumLoc,
DS.getTypeSpecTypeNameLoc(), Enum);
if (UD) if (UD)
PushOnScopeChains(UD, S, /*AddToContext*/ false); PushOnScopeChains(UD, S, /*AddToContext*/ false);
@ -11712,7 +11747,7 @@ bool Sema::CheckUsingShadowDecl(BaseUsingDecl *BUD, NamedDecl *Orig,
// We can have UsingDecls in our Previous results because we use the same // We can have UsingDecls in our Previous results because we use the same
// LookupResult for checking whether the UsingDecl itself is a valid // LookupResult for checking whether the UsingDecl itself is a valid
// redeclaration. // redeclaration.
if (isa<UsingDecl>(D) || isa<UsingPackDecl>(D)) if (isa<UsingDecl>(D) || isa<UsingPackDecl>(D) || isa<UsingEnumDecl>(D))
continue; continue;
if (auto *RD = dyn_cast<CXXRecordDecl>(D)) { if (auto *RD = dyn_cast<CXXRecordDecl>(D)) {
@ -12014,6 +12049,29 @@ private:
}; };
} // end anonymous namespace } // end anonymous namespace
/// Remove decls we can't actually see from a lookup being used to declare
/// shadow using decls.
///
/// \param S - The scope of the potential shadow decl
/// \param Previous - The lookup of a potential shadow decl's name.
void Sema::FilterUsingLookup(Scope *S, LookupResult &Previous) {
// It is really dumb that we have to do this.
LookupResult::Filter F = Previous.makeFilter();
while (F.hasNext()) {
NamedDecl *D = F.next();
if (!isDeclInScope(D, CurContext, S))
F.erase();
// If we found a local extern declaration that's not ordinarily visible,
// and this declaration is being added to a non-block scope, ignore it.
// We're only checking for scope conflicts here, not also for violations
// of the linkage rules.
else if (!CurContext->isFunctionOrMethod() && D->isLocalExternDecl() &&
!(D->getIdentifierNamespace() & Decl::IDNS_Ordinary))
F.erase();
}
F.done();
}
/// Builds a using declaration. /// Builds a using declaration.
/// ///
/// \param IsInstantiation - Whether this call arises from an /// \param IsInstantiation - Whether this call arises from an
@ -12047,21 +12105,7 @@ NamedDecl *Sema::BuildUsingDeclaration(
if (S) { if (S) {
LookupName(Previous, S); LookupName(Previous, S);
// It is really dumb that we have to do this. FilterUsingLookup(S, Previous);
LookupResult::Filter F = Previous.makeFilter();
while (F.hasNext()) {
NamedDecl *D = F.next();
if (!isDeclInScope(D, CurContext, S))
F.erase();
// If we found a local extern declaration that's not ordinarily visible,
// and this declaration is being added to a non-block scope, ignore it.
// We're only checking for scope conflicts here, not also for violations
// of the linkage rules.
else if (!CurContext->isFunctionOrMethod() && D->isLocalExternDecl() &&
!(D->getIdentifierNamespace() & Decl::IDNS_Ordinary))
F.erase();
}
F.done();
} else { } else {
assert(IsInstantiation && "no scope in non-instantiation"); assert(IsInstantiation && "no scope in non-instantiation");
if (CurContext->isRecord()) if (CurContext->isRecord())
@ -12287,6 +12331,61 @@ NamedDecl *Sema::BuildUsingDeclaration(
return UD; return UD;
} }
NamedDecl *Sema::BuildUsingEnumDeclaration(Scope *S, AccessSpecifier AS,
SourceLocation UsingLoc,
SourceLocation EnumLoc,
SourceLocation NameLoc,
EnumDecl *ED) {
bool Invalid = false;
if (CurContext->getRedeclContext()->isRecord()) {
/// In class scope, check if this is a duplicate, for better a diagnostic.
DeclarationNameInfo UsingEnumName(ED->getDeclName(), NameLoc);
LookupResult Previous(*this, UsingEnumName, LookupUsingDeclName,
ForVisibleRedeclaration);
LookupName(Previous, S);
for (NamedDecl *D : Previous)
if (UsingEnumDecl *UED = dyn_cast<UsingEnumDecl>(D))
if (UED->getEnumDecl() == ED) {
Diag(UsingLoc, diag::err_using_enum_decl_redeclaration)
<< SourceRange(EnumLoc, NameLoc);
Diag(D->getLocation(), diag::note_using_enum_decl) << 1;
Invalid = true;
break;
}
}
if (RequireCompleteEnumDecl(ED, NameLoc))
Invalid = true;
UsingEnumDecl *UD = UsingEnumDecl::Create(Context, CurContext, UsingLoc,
EnumLoc, NameLoc, ED);
UD->setAccess(AS);
CurContext->addDecl(UD);
if (Invalid) {
UD->setInvalidDecl();
return UD;
}
// Create the shadow decls for each enumerator
for (EnumConstantDecl *EC : ED->enumerators()) {
UsingShadowDecl *PrevDecl = nullptr;
DeclarationNameInfo DNI(EC->getDeclName(), EC->getLocation());
LookupResult Previous(*this, DNI, LookupOrdinaryName,
ForVisibleRedeclaration);
LookupName(Previous, S);
FilterUsingLookup(S, Previous);
if (!CheckUsingShadowDecl(UD, EC, Previous, PrevDecl))
BuildUsingShadowDecl(S, UD, EC, PrevDecl);
}
return UD;
}
NamedDecl *Sema::BuildUsingPackDecl(NamedDecl *InstantiatedFrom, NamedDecl *Sema::BuildUsingPackDecl(NamedDecl *InstantiatedFrom,
ArrayRef<NamedDecl *> Expansions) { ArrayRef<NamedDecl *> Expansions) {
assert(isa<UnresolvedUsingValueDecl>(InstantiatedFrom) || assert(isa<UnresolvedUsingValueDecl>(InstantiatedFrom) ||

View File

@ -3013,6 +3013,53 @@ Decl *TemplateDeclInstantiator::VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
return Inst; return Inst;
} }
Decl *TemplateDeclInstantiator::VisitBaseUsingDecls(BaseUsingDecl *D,
BaseUsingDecl *Inst,
LookupResult *Lookup) {
bool isFunctionScope = Owner->isFunctionOrMethod();
for (auto *Shadow : D->shadows()) {
// FIXME: UsingShadowDecl doesn't preserve its immediate target, so
// reconstruct it in the case where it matters. Hm, can we extract it from
// the DeclSpec when parsing and save it in the UsingDecl itself?
NamedDecl *OldTarget = Shadow->getTargetDecl();
if (auto *CUSD = dyn_cast<ConstructorUsingShadowDecl>(Shadow))
if (auto *BaseShadow = CUSD->getNominatedBaseClassShadowDecl())
OldTarget = BaseShadow;
NamedDecl *InstTarget = nullptr;
if (auto *EmptyD =
dyn_cast<UnresolvedUsingIfExistsDecl>(Shadow->getTargetDecl())) {
InstTarget = UnresolvedUsingIfExistsDecl::Create(
SemaRef.Context, Owner, EmptyD->getLocation(), EmptyD->getDeclName());
} else {
InstTarget = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
Shadow->getLocation(), OldTarget, TemplateArgs));
}
if (!InstTarget)
return nullptr;
UsingShadowDecl *PrevDecl = nullptr;
if (Lookup &&
SemaRef.CheckUsingShadowDecl(Inst, InstTarget, *Lookup, PrevDecl))
continue;
if (UsingShadowDecl *OldPrev = getPreviousDeclForInstantiation(Shadow))
PrevDecl = cast_or_null<UsingShadowDecl>(SemaRef.FindInstantiatedDecl(
Shadow->getLocation(), OldPrev, TemplateArgs));
UsingShadowDecl *InstShadow = SemaRef.BuildUsingShadowDecl(
/*Scope*/ nullptr, Inst, InstTarget, PrevDecl);
SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstShadow, Shadow);
if (isFunctionScope)
SemaRef.CurrentInstantiationScope->InstantiatedLocal(Shadow, InstShadow);
}
return Inst;
}
Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) { Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
// The nested name specifier may be dependent, for example // The nested name specifier may be dependent, for example
@ -3038,11 +3085,9 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
NameInfo.setName(SemaRef.Context.DeclarationNames.getCXXConstructorName( NameInfo.setName(SemaRef.Context.DeclarationNames.getCXXConstructorName(
SemaRef.Context.getCanonicalType(SemaRef.Context.getRecordType(RD)))); SemaRef.Context.getCanonicalType(SemaRef.Context.getRecordType(RD))));
// We only need to do redeclaration lookups if we're in a class // We only need to do redeclaration lookups if we're in a class scope (in
// scope (in fact, it's not really even possible in non-class // fact, it's not really even possible in non-class scopes).
// scopes).
bool CheckRedeclaration = Owner->isRecord(); bool CheckRedeclaration = Owner->isRecord();
LookupResult Prev(SemaRef, NameInfo, Sema::LookupUsingDeclName, LookupResult Prev(SemaRef, NameInfo, Sema::LookupUsingDeclName,
Sema::ForVisibleRedeclaration); Sema::ForVisibleRedeclaration);
@ -3063,13 +3108,13 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
D->hasTypename(), SS, D->hasTypename(), SS,
D->getLocation(), Prev)) D->getLocation(), Prev))
NewUD->setInvalidDecl(); NewUD->setInvalidDecl();
} }
if (!NewUD->isInvalidDecl() && if (!NewUD->isInvalidDecl() &&
SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), SS, SemaRef.CheckUsingDeclQualifier(D->getUsingLoc(), D->hasTypename(), SS,
NameInfo, D->getLocation(), nullptr, D)) NameInfo, D->getLocation(), nullptr, D))
NewUD->setInvalidDecl(); NewUD->setInvalidDecl();
SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D); SemaRef.Context.setInstantiatedFromUsingDecl(NewUD, D);
NewUD->setAccess(D->getAccess()); NewUD->setAccess(D->getAccess());
Owner->addDecl(NewUD); Owner->addDecl(NewUD);
@ -3083,49 +3128,34 @@ Decl *TemplateDeclInstantiator::VisitUsingDecl(UsingDecl *D) {
if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName) if (NameInfo.getName().getNameKind() == DeclarationName::CXXConstructorName)
SemaRef.CheckInheritingConstructorUsingDecl(NewUD); SemaRef.CheckInheritingConstructorUsingDecl(NewUD);
bool isFunctionScope = Owner->isFunctionOrMethod(); return VisitBaseUsingDecls(D, NewUD, CheckRedeclaration ? &Prev : nullptr);
}
// Process the shadow decls. Decl *TemplateDeclInstantiator::VisitUsingEnumDecl(UsingEnumDecl *D) {
for (auto *Shadow : D->shadows()) { // Cannot be a dependent type, but still could be an instantiation
// FIXME: UsingShadowDecl doesn't preserve its immediate target, so EnumDecl *EnumD = cast_or_null<EnumDecl>(SemaRef.FindInstantiatedDecl(
// reconstruct it in the case where it matters. D->getLocation(), D->getEnumDecl(), TemplateArgs));
NamedDecl *OldTarget = Shadow->getTargetDecl();
if (auto *CUSD = dyn_cast<ConstructorUsingShadowDecl>(Shadow))
if (auto *BaseShadow = CUSD->getNominatedBaseClassShadowDecl())
OldTarget = BaseShadow;
NamedDecl *InstTarget = nullptr; if (SemaRef.RequireCompleteEnumDecl(EnumD, EnumD->getLocation()))
if (auto *EmptyD = return nullptr;
dyn_cast<UnresolvedUsingIfExistsDecl>(Shadow->getTargetDecl())) {
InstTarget = UnresolvedUsingIfExistsDecl::Create(
SemaRef.Context, Owner, EmptyD->getLocation(), EmptyD->getDeclName());
} else {
InstTarget = cast_or_null<NamedDecl>(SemaRef.FindInstantiatedDecl(
Shadow->getLocation(), OldTarget, TemplateArgs));
}
if (!InstTarget)
return nullptr;
UsingShadowDecl *PrevDecl = nullptr; UsingEnumDecl *NewUD =
if (CheckRedeclaration) { UsingEnumDecl::Create(SemaRef.Context, Owner, D->getUsingLoc(),
if (SemaRef.CheckUsingShadowDecl(NewUD, InstTarget, Prev, PrevDecl)) D->getEnumLoc(), D->getLocation(), EnumD);
continue;
} else if (UsingShadowDecl *OldPrev =
getPreviousDeclForInstantiation(Shadow)) {
PrevDecl = cast_or_null<UsingShadowDecl>(SemaRef.FindInstantiatedDecl(
Shadow->getLocation(), OldPrev, TemplateArgs));
}
UsingShadowDecl *InstShadow = SemaRef.Context.setInstantiatedFromUsingEnumDecl(NewUD, D);
SemaRef.BuildUsingShadowDecl(/*Scope*/nullptr, NewUD, InstTarget, NewUD->setAccess(D->getAccess());
PrevDecl); Owner->addDecl(NewUD);
SemaRef.Context.setInstantiatedFromUsingShadowDecl(InstShadow, Shadow);
if (isFunctionScope) // Don't process the shadow decls for an invalid decl.
SemaRef.CurrentInstantiationScope->InstantiatedLocal(Shadow, InstShadow); if (NewUD->isInvalidDecl())
} return NewUD;
return NewUD; // We don't have to recheck for duplication of the UsingEnumDecl itself, as it
// cannot be dependent, and will therefore have been checked during template
// definition.
return VisitBaseUsingDecls(D, NewUD, nullptr);
} }
Decl *TemplateDeclInstantiator::VisitUsingShadowDecl(UsingShadowDecl *D) { Decl *TemplateDeclInstantiator::VisitUsingShadowDecl(UsingShadowDecl *D) {

View File

@ -394,6 +394,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::NonTypeTemplateParm: case Decl::NonTypeTemplateParm:
case Decl::TemplateTemplateParm: case Decl::TemplateTemplateParm:
case Decl::Using: case Decl::Using:
case Decl::UsingEnum:
case Decl::UsingPack: case Decl::UsingPack:
case Decl::ObjCMethod: case Decl::ObjCMethod:
case Decl::ObjCCategory: case Decl::ObjCCategory:

View File

@ -390,6 +390,7 @@ namespace clang {
void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D); void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D);
void VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D); void VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
void VisitUsingDecl(UsingDecl *D); void VisitUsingDecl(UsingDecl *D);
void VisitUsingEnumDecl(UsingEnumDecl *D);
void VisitUsingPackDecl(UsingPackDecl *D); void VisitUsingPackDecl(UsingPackDecl *D);
void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitUsingShadowDecl(UsingShadowDecl *D);
void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D); void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D);
@ -1652,6 +1653,17 @@ void ASTDeclReader::VisitUsingDecl(UsingDecl *D) {
mergeMergeable(D); mergeMergeable(D);
} }
void ASTDeclReader::VisitUsingEnumDecl(UsingEnumDecl *D) {
VisitNamedDecl(D);
D->setUsingLoc(readSourceLocation());
D->setEnumLoc(readSourceLocation());
D->Enum = readDeclAs<EnumDecl>();
D->FirstUsingShadow.setPointer(readDeclAs<UsingShadowDecl>());
if (auto *Pattern = readDeclAs<UsingEnumDecl>())
Reader.getContext().setInstantiatedFromUsingEnumDecl(D, Pattern);
mergeMergeable(D);
}
void ASTDeclReader::VisitUsingPackDecl(UsingPackDecl *D) { void ASTDeclReader::VisitUsingPackDecl(UsingPackDecl *D) {
VisitNamedDecl(D); VisitNamedDecl(D);
D->InstantiatedFrom = readDeclAs<NamedDecl>(); D->InstantiatedFrom = readDeclAs<NamedDecl>();
@ -3844,6 +3856,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
case DECL_USING_SHADOW: case DECL_USING_SHADOW:
D = UsingShadowDecl::CreateDeserialized(Context, ID); D = UsingShadowDecl::CreateDeserialized(Context, ID);
break; break;
case DECL_USING_ENUM:
D = UsingEnumDecl::CreateDeserialized(Context, ID);
break;
case DECL_CONSTRUCTOR_USING_SHADOW: case DECL_CONSTRUCTOR_USING_SHADOW:
D = ConstructorUsingShadowDecl::CreateDeserialized(Context, ID); D = ConstructorUsingShadowDecl::CreateDeserialized(Context, ID);
break; break;

View File

@ -114,6 +114,7 @@ namespace clang {
void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D); void VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D);
void VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D); void VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D);
void VisitUsingDecl(UsingDecl *D); void VisitUsingDecl(UsingDecl *D);
void VisitUsingEnumDecl(UsingEnumDecl *D);
void VisitUsingPackDecl(UsingPackDecl *D); void VisitUsingPackDecl(UsingPackDecl *D);
void VisitUsingShadowDecl(UsingShadowDecl *D); void VisitUsingShadowDecl(UsingShadowDecl *D);
void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D); void VisitConstructorUsingShadowDecl(ConstructorUsingShadowDecl *D);
@ -1278,6 +1279,16 @@ void ASTDeclWriter::VisitUsingDecl(UsingDecl *D) {
Code = serialization::DECL_USING; Code = serialization::DECL_USING;
} }
void ASTDeclWriter::VisitUsingEnumDecl(UsingEnumDecl *D) {
VisitNamedDecl(D);
Record.AddSourceLocation(D->getUsingLoc());
Record.AddSourceLocation(D->getEnumLoc());
Record.AddDeclRef(D->getEnumDecl());
Record.AddDeclRef(D->FirstUsingShadow.getPointer());
Record.AddDeclRef(Context.getInstantiatedFromUsingEnumDecl(D));
Code = serialization::DECL_USING_ENUM;
}
void ASTDeclWriter::VisitUsingPackDecl(UsingPackDecl *D) { void ASTDeclWriter::VisitUsingPackDecl(UsingPackDecl *D) {
Record.push_back(D->NumExpansions); Record.push_back(D->NumExpansions);
VisitNamedDecl(D); VisitNamedDecl(D);

View File

@ -0,0 +1,30 @@
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++20 -ast-dump -ast-dump-filter Foo %s | FileCheck -strict-whitespace %s
// Test with serialization:
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown -emit-pch -o %t %s
// RUN: %clang_cc1 -x c++ -std=c++20 -triple x86_64-unknown-unknown -include-pch %t \
// RUN: -ast-dump-all -ast-dump-filter Foo /dev/null \
// RUN: | FileCheck --strict-whitespace %s
namespace Bob {
enum class Foo {
Foo_a,
Foo_b
};
}; // namespace Bob
using enum Bob::Foo;
// CHECK-LABEL: Dumping Bob::Foo
// CHECK-NEXT: EnumDecl {{.*}} class Foo 'int'
// CHECK-NEXT: |-EnumConstantDecl {{.*}} Foo_a 'Bob::Foo'
// CHECK-NEXT: `-EnumConstantDecl {{.*}} Foo_b 'Bob::Foo'
// CHECK-LABEL: Dumping Foo:
// CHECK-NEXT: UsingEnumDecl {{.*}} Enum {{.*}} 'Foo'
// CHECK-LABEL: Dumping Foo_a:
// CHECK-NEXT: UsingShadowDecl {{.*}} implicit EnumConstant {{.*}} 'Foo_a' 'Bob::Foo'
// CHECK-LABEL: Dumping Foo_b:
// CHECK-NEXT: UsingShadowDecl {{.*}} implicit EnumConstant {{.*}} 'Foo_b' 'Bob::Foo'

View File

@ -0,0 +1,233 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify %s
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify %s
// p1099 'using enum ELABORATED-ENUM-SPECIFIER ;'
namespace One {
namespace Bob {
enum A { a, // expected-note{{declared here}}
b,
c };
class C; // expected-note{{previous use}}
enum class D : int;
enum class D { d,
e,
f };
enum class D : int;
} // namespace Bob
using enum Bob::A;
#if __cplusplus < 202002
// expected-warning@-2{{is a C++20 extension}}
#endif
using enum Bob::B; // expected-error{{no enum named 'B'}}
#if __cplusplus < 202002
// expected-warning@-2{{is a C++20 extension}}
#endif
using enum Bob::C; // expected-error{{tag type that does not match}}
#if __cplusplus < 202002
// expected-warning@-2{{is a C++20 extension}}
#endif
auto v = a;
A g; // expected-error{{unknown type name 'A'}}
int A;
using enum Bob::D;
#if __cplusplus < 202002
// expected-warning@-2{{is a C++20 extension}}
#endif
} // namespace One
namespace Two {
namespace Kevin {
enum class B { d,
e,
f };
}
using enum Kevin::B;
#if __cplusplus < 202002
// expected-warning@-2{{is a C++20 extension}}
#endif
auto w = e;
} // namespace Two
#if __cplusplus >= 202002
// Now only check c++20 onwards
namespace Three {
namespace Stuart {
enum class C : int; // expected-note{{declared here}}
}
using enum Stuart::C; // expected-error{{is incomplete}}
} // namespace Three
namespace Four {
class Dave {
public:
enum D { a,
b,
c };
private:
enum class E { d, // expected-note{{declared private here}}
e,
f };
};
using enum Dave::D;
using enum Dave::E; // expected-error{{is a private member}}
} // namespace Four
namespace Five {
enum class A { b,
c };
class Dave {
public:
using enum A;
A f = b;
};
} // namespace Five
namespace Six {
template <typename T> class TPL;
template <> class TPL<int> {
public:
enum A { a };
};
template <typename T> class USR {
using enum TPL<T>::B; // expected-error{{cannot name a dependent type}}
using enum TPL<int>::A;
};
} // namespace Six
// Now instantiate things
namespace Seven {
namespace Stuart {
enum class A { a,
b,
c };
}
static_assert(!int(Stuart::A::a));
constexpr int Bar() {
using enum Stuart::A;
return int(b);
}
static_assert(Bar() == 1);
template <int I> constexpr int Foo() {
using enum Stuart::A;
return int(b) + I;
}
static_assert(Foo<10>() == 11);
template <int I> struct C {
using enum Stuart::A;
static constexpr int V = int(c) + I;
enum class D { d,
e,
f };
using enum D;
static constexpr int W = int(f) + I;
};
static_assert(C<2>::V == 4);
static_assert(C<20>::W == 22);
} // namespace Seven
namespace Eight {
enum class Bob : int {};
using enum Bob;
} // namespace Eight
namespace Nine {
template <int I> struct C {
enum class D { i = I };
enum class E : int; // expected-note{{declared here}}
};
using enum C<2>::D;
constexpr auto d = i;
static_assert(unsigned(d) == 2);
using enum C<2>::E; // expected-error{{instantiation of undefined member}}
} // namespace Nine
namespace Ten {
enum class Bob { a };
void Foo() {
extern void a();
}
// We don't see the hidden extern a fn!
using enum Bob;
auto v = a;
} // namespace Ten
namespace Eleven {
enum class Bob { a }; // expected-note{{conflicting declaration}}
struct Base {
enum { a }; // expected-note{{target of using}}
};
template <typename B>
class TPLa : B {
using enum Bob;
using B::a; // expected-error{{target of using declaration}}
};
TPLa<Base> a; // expected-note{{in instantiation}}
} // namespace Eleven
namespace Twelve {
enum class Bob { a }; // expected-note{{target of using}}
struct Base {
enum { a };
};
template <typename B>
class TPLb : B {
using B::a; // expected-note{{conflicting declaration}}
using enum Bob; // expected-error{{target of using declaration}}
};
TPLb<Base> b;
} // namespace Twelve
namespace Thirteen {
enum class Bob { a };
class Foo {
using enum Bob; // expected-note{{previous using-enum}}
using enum Bob; // expected-error{{redeclaration of using-enum}}
};
template <typename B>
class TPLa {
using enum Bob; // expected-note{{previous using-enum}}
using enum Bob; // expected-error{{redeclaration of using-enum}}
};
TPLa<int> a;
} // namespace Thirteen
#endif

View File

@ -6541,6 +6541,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
} }
case Decl::Using: case Decl::Using:
case Decl::UsingEnum:
return MakeCursorOverloadedDeclRef(cast<BaseUsingDecl>(D), D->getLocation(), return MakeCursorOverloadedDeclRef(cast<BaseUsingDecl>(D), D->getLocation(),
TU); TU);

View File

@ -844,7 +844,15 @@ TEST_P(ImportDecl, ImportUsingDecl) {
testImport("namespace foo { int bar; }" testImport("namespace foo { int bar; }"
"void declToImport() { using foo::bar; }", "void declToImport() { using foo::bar; }",
Lang_CXX03, "", Lang_CXX03, Verifier, Lang_CXX03, "", Lang_CXX03, Verifier,
functionDecl(hasDescendant(usingDecl()))); functionDecl(hasDescendant(usingDecl(hasName("bar")))));
}
TEST_P(ImportDecl, ImportUsingEnumDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { enum bar { baz, toto, quux }; }"
"void declToImport() { using enum foo::bar; }",
Lang_CXX20, "", Lang_CXX20, Verifier,
functionDecl(hasDescendant(usingEnumDecl(hasName("bar")))));
} }
/// \brief Matches shadow declarations introduced into a scope by a /// \brief Matches shadow declarations introduced into a scope by a
@ -862,10 +870,16 @@ const internal::VariadicDynCastAllOfMatcher<Decl,
TEST_P(ImportDecl, ImportUsingShadowDecl) { TEST_P(ImportDecl, ImportUsingShadowDecl) {
MatchVerifier<Decl> Verifier; MatchVerifier<Decl> Verifier;
// from using-decl
testImport("namespace foo { int bar; }" testImport("namespace foo { int bar; }"
"namespace declToImport { using foo::bar; }", "namespace declToImport { using foo::bar; }",
Lang_CXX03, "", Lang_CXX03, Verifier, Lang_CXX03, "", Lang_CXX03, Verifier,
namespaceDecl(has(usingShadowDecl()))); namespaceDecl(has(usingShadowDecl(hasName("bar")))));
// from using-enum-decl
testImport("namespace foo { enum bar {baz, toto, quux }; }"
"namespace declToImport { using enum foo::bar; }",
Lang_CXX20, "", Lang_CXX20, Verifier,
namespaceDecl(has(usingShadowDecl(hasName("baz")))));
} }
TEST_P(ImportExpr, ImportUnresolvedLookupExpr) { TEST_P(ImportExpr, ImportUnresolvedLookupExpr) {

View File

@ -1428,6 +1428,22 @@ TEST_P(ASTMatchersTest, UsingDecl_MatchesShadowUsingDelcarations) {
usingDecl(hasAnyUsingShadowDecl(hasName("a"))))); usingDecl(hasAnyUsingShadowDecl(hasName("a")))));
} }
TEST_P(ASTMatchersTest, UsingEnumDecl_MatchesUsingEnumDeclarations) {
if (!GetParam().isCXX20OrLater()) {
return;
}
EXPECT_TRUE(
matches("namespace X { enum x {}; } using enum X::x;", usingEnumDecl()));
}
TEST_P(ASTMatchersTest, UsingEnumDecl_MatchesShadowUsingDeclarations) {
if (!GetParam().isCXX20OrLater()) {
return;
}
EXPECT_TRUE(matches("namespace f { enum a {b}; } using enum f::a;",
usingEnumDecl(hasAnyUsingShadowDecl(hasName("b")))));
}
TEST_P(ASTMatchersTest, UsingDirectiveDecl_MatchesUsingNamespace) { TEST_P(ASTMatchersTest, UsingDirectiveDecl_MatchesUsingNamespace) {
if (!GetParam().isCXX()) { if (!GetParam().isCXX()) {
return; return;