diff --git a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp --- a/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/UpgradeGoogletestCaseCheck.cpp @@ -196,6 +196,12 @@ usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(TestCaseTypeAlias))) .bind("using"), this); + Finder->addMatcher( + typeLoc(loc(usingType(hasUnderlyingType( + typedefType(hasDeclaration(TestCaseTypeAlias))))), + unless(hasAncestor(decl(isImplicit()))), LocationFilter) + .bind("typeloc"), + this); } static llvm::StringRef getNewMethodName(llvm::StringRef CurrentName) { diff --git a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp @@ -45,6 +45,7 @@ return DeclMatcher.matches(*TD, Finder, Builder); return false; } + } // namespace // A function that helps to tell whether a TargetDecl in a UsingDecl will be @@ -60,13 +61,10 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this); auto DeclMatcher = hasDeclaration(namedDecl().bind("used")); - Finder->addMatcher(loc(enumType(DeclMatcher)), this); - Finder->addMatcher(loc(recordType(DeclMatcher)), this); Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this); Finder->addMatcher(loc(deducedTemplateSpecializationType( refsToTemplatedDecl(namedDecl().bind("used")))), this); - Finder->addMatcher(declRefExpr().bind("used"), this); Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))), this); Finder->addMatcher( @@ -76,6 +74,12 @@ Finder->addMatcher(loc(templateSpecializationType(forEachTemplateArgument( templateArgument().bind("used")))), this); + // Cases where we can identify the UsingShadowDecl directly, rather than + // just its target. + // FIXME: cover more cases in this way, as the AST supports it. + auto ThroughShadowMatcher = throughUsingDecl(namedDecl().bind("usedShadow")); + Finder->addMatcher(declRefExpr(ThroughShadowMatcher), this); + Finder->addMatcher(loc(usingType(ThroughShadowMatcher)), this); } void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { @@ -137,6 +141,12 @@ return; } + if (const auto *UsedShadow = + Result.Nodes.getNodeAs("usedShadow")) { + removeFromFoundDecls(UsedShadow->getTargetDecl()); + return; + } + if (const auto *Used = Result.Nodes.getNodeAs("used")) { if (Used->getKind() == TemplateArgument::Template) { if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl()) diff --git a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp @@ -70,6 +70,13 @@ TL.getAs().getTypePtr()->getDecl()->getName())) return false; break; + case TypeLoc::Using: + if (visitUnqualName(TL.getAs() + .getTypePtr() + ->getFoundDecl() + ->getName())) + return false; + break; default: break; } diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -364,6 +364,10 @@ Outer.add(ET->desugar(), Flags); } + void VisitUsingType(const UsingType *ET) { + Outer.add(ET->getFoundDecl(), Flags); + } + void VisitInjectedClassNameType(const InjectedClassNameType *ICNT) { Outer.add(ICNT->getDecl(), Flags); } @@ -855,6 +859,13 @@ } } + void VisitUsingTypeLoc(UsingTypeLoc L) { + Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), + L.getLocalSourceRange().getBegin(), + /*IsDecl=*/false, + {L.getFoundDecl()}}); + } + void VisitTagTypeLoc(TagTypeLoc L) { Refs.push_back(ReferenceLoc{NestedNameSpecifierLoc(), L.getNameLoc(), diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -74,6 +74,11 @@ return true; } + bool VisitUsingType(UsingType *UT) { + add(UT->getFoundDecl()); + return true; + } + bool VisitTypedefType(TypedefType *TT) { add(TT->getDecl()); return true; diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -85,6 +85,10 @@ "struct Foo; struct ^Foo{}; typedef Foo ^Bar;", "Bar b;", }, + { + "namespace ns { class X; }; using ns::^X;", + "X *y;", + }, // MemberExpr { "struct ^X{int ^a;}; X ^foo();", @@ -198,14 +202,6 @@ { "enum class ^Color : char {};", "Color *c;", - }, - { - // When a type is resolved via a using declaration, the - // UsingShadowDecl is not referenced in the AST. - // Compare to TypedefType, or DeclRefExpr::getFoundDecl(). - // ^ - "namespace ns { class ^X; }; using ns::X;", - "X *y;", }}; for (const TestCase &T : Cases) { TestTU TU; diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -1372,7 +1372,7 @@ R"cpp( namespace ns { class [[Foo]] {}; } - using ns::Foo; + using ns::[[Foo]]; F^oo f; )cpp", diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -244,7 +244,9 @@ Internal API Changes -------------------- -- ... +- A new sugar ``Type`` AST node represents types accessed via a C++ using + declaration. Given code ``using std::error_code; error_code x;``, ``x`` has + a ``UsingType`` which desugars to the previous ``RecordType``. Build System Changes -------------------- @@ -269,6 +271,12 @@ - The ``hasAnyCapture`` matcher now only accepts an inner matcher of type ``Matcher``. The matcher originally accepted an inner matcher of type ``Matcher`` or ``Matcher``. +- The ``usingType`` matcher is now available and needed to refer to types that + are referred to via using C++ using declarations. + The associated ``UsingShadowDecl`` can be matched using ``throughUsingDecl`` + and the underlying ``Type`` with ``hasUnderlyingType``. + ``hasDeclaration`` continues to see through the alias and apply to the + underlying type. clang-format ------------ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -248,6 +248,7 @@ mutable llvm::ContextualFoldingSet TemplateSpecializationTypes; mutable llvm::FoldingSet ParenTypes; + mutable llvm::FoldingSet UsingTypes; mutable llvm::FoldingSet ElaboratedTypes; mutable llvm::FoldingSet DependentNameTypes; mutable llvm::ContextualFoldingSet; def TemplateTemplateParmDeclRef : SubclassPropertyType<"TemplateTemplateParmDecl", DeclRef>; + def UsingShadowDeclRef : + SubclassPropertyType<"UsingShadowDecl", DeclRef>; def ValueDeclRef : SubclassPropertyType<"ValueDecl", DeclRef>; def ElaboratedTypeKeyword : EnumPropertyType; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -981,6 +981,7 @@ TRY_TO(TraverseStmt(NE)); }) +DEF_TRAVERSE_TYPE(UsingType, {}) DEF_TRAVERSE_TYPE(UnresolvedUsingType, {}) DEF_TRAVERSE_TYPE(TypedefType, {}) @@ -1252,6 +1253,7 @@ TRY_TO(TraverseStmt(NE)); }) +DEF_TRAVERSE_TYPELOC(UsingType, {}) DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {}) DEF_TRAVERSE_TYPELOC(TypedefType, {}) @@ -2095,7 +2097,13 @@ } if (VisitBody) { - TRY_TO(TraverseStmt(D->getBody())); // Function body. + TRY_TO(TraverseStmt(D->getBody())); + // Body may contain using declarations whose shadows are parented to the + // FunctionDecl itself. + for (auto *Child : D->decls()) { + if (isa(Child)) + TRY_TO(TraverseDecl(Child)); + } } return true; } diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -311,6 +311,7 @@ void VisitFunctionType(const FunctionType *T); void VisitFunctionProtoType(const FunctionProtoType *T); void VisitUnresolvedUsingType(const UnresolvedUsingType *T); + void VisitUsingType(const UsingType *T); void VisitTypedefType(const TypedefType *T); void VisitUnaryTransformType(const UnaryTransformType *T); void VisitTagType(const TagType *T); diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -129,6 +129,7 @@ class TemplateTypeParmDecl; class TypedefNameDecl; class UnresolvedUsingTypenameDecl; +class UsingShadowDecl; using CanQualType = CanQual; @@ -4368,6 +4369,27 @@ } }; +class UsingType : public Type, public llvm::FoldingSetNode { + UsingShadowDecl *Found; + friend class ASTContext; // ASTContext creates these. + + UsingType(const UsingShadowDecl *Found, QualType Underlying, QualType Canon); + +public: + UsingShadowDecl *getFoundDecl() const { return Found; } + QualType getUnderlyingType() const; + + bool isSugared() const { return true; } + QualType desugar() const { return getUnderlyingType(); } + + void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Found); } + static void Profile(llvm::FoldingSetNodeID &ID, + const UsingShadowDecl *Found) { + ID.AddPointer(Found); + } + static bool classof(const Type *T) { return T->getTypeClass() == Using; } +}; + class TypedefType : public Type { TypedefNameDecl *Decl; diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h --- a/clang/include/clang/AST/TypeLoc.h +++ b/clang/include/clang/AST/TypeLoc.h @@ -665,6 +665,16 @@ } }; +/// Wrapper for source info for types used via transparent aliases. +class UsingTypeLoc : public InheritingConcreteTypeLoc { +public: + QualType getUnderlyingType() const { + return getTypePtr()->getUnderlyingType(); + } + UsingShadowDecl *getFoundDecl() const { return getTypePtr()->getFoundDecl(); } +}; + /// Wrapper for source info for typedefs. class TypedefTypeLoc : public InheritingConcreteTypeLoc; } +let Class = UsingType in { + def : Property<"foundDeclaration", UsingShadowDeclRef> { + let Read = [{ node->getFoundDecl() }]; + } + def : Property<"underlyingType", QualType> { + let Read = [{ node->getUnderlyingType() }]; + } + + def : Creator<[{ + return ctx.getUsingType(foundDeclaration, underlyingType); + }]>; +} + let Class = TypedefType in { def : Property<"declaration", DeclRef> { let Read = [{ node->getDecl() }]; diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -4128,25 +4128,34 @@ InnerMatcher.matches(*DeclNode, Finder, Builder)); } -/// Matches a \c DeclRefExpr that refers to a declaration through a -/// specific using shadow declaration. +/// Matches if a node refers to a declaration through a specific +/// using shadow declaration. /// -/// Given +/// Examples: /// \code -/// namespace a { void f() {} } +/// namespace a { int f(); } /// using a::f; -/// void g() { -/// f(); // Matches this .. -/// a::f(); // .. but not this. -/// } +/// int x = f(); /// \endcode /// declRefExpr(throughUsingDecl(anything())) -/// matches \c f() -AST_MATCHER_P(DeclRefExpr, throughUsingDecl, - internal::Matcher, InnerMatcher) { +/// matches \c f +/// +/// \code +/// namespace a { class X{}; } +/// using a::X; +/// X x; +/// \code +/// typeLoc(loc(usingType(throughUsingDecl(anything())))) +/// matches \c X +/// +/// Usable as: Matcher, Matcher +AST_POLYMORPHIC_MATCHER_P(throughUsingDecl, + AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr, + UsingType), + internal::Matcher, Inner) { const NamedDecl *FoundDecl = Node.getFoundDecl(); if (const UsingShadowDecl *UsingDecl = dyn_cast(FoundDecl)) - return InnerMatcher.matches(*UsingDecl, Finder, Builder); + return Inner.matches(*UsingDecl, Finder, Builder); return false; } @@ -6843,7 +6852,7 @@ AST_TYPE_TRAVERSE_MATCHER(hasDeducedType, getDeducedType, AST_POLYMORPHIC_SUPPORTED_TYPES(AutoType)); -/// Matches \c DecltypeType nodes to find out the underlying type. +/// Matches \c DecltypeType or \c UsingType nodes to find the underlying type. /// /// Given /// \code @@ -6853,9 +6862,10 @@ /// decltypeType(hasUnderlyingType(isInteger())) /// matches the type of "a" /// -/// Usable as: Matcher +/// Usable as: Matcher, Matcher AST_TYPE_TRAVERSE_MATCHER(hasUnderlyingType, getUnderlyingType, - AST_POLYMORPHIC_SUPPORTED_TYPES(DecltypeType)); + AST_POLYMORPHIC_SUPPORTED_TYPES(DecltypeType, + UsingType)); /// Matches \c FunctionType nodes. /// @@ -7183,6 +7193,18 @@ return InnerMatcher.matches(Node.getNamedType(), Finder, Builder); } +/// Matches types specified through a using declaration. +/// +/// Given +/// \code +/// namespace a { struct S {}; } +/// using a::S; +/// S s; +/// \endcode +/// +/// \c usingType() matches the type of the variable declaration of \c s. +extern const AstTypeMatcher usingType; + /// Matches types that represent the result of substituting a type for a /// template type parameter. /// diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1090,6 +1090,12 @@ if (const auto *S = dyn_cast(&Node)) { return matchesSpecialized(S->desugar(), Finder, Builder); } + // Similarly types found via using declarations. + // These are *usually* meaningless sugar, and this matches the historical + // behavior prior to the introduction of UsingType. + if (const auto *S = dyn_cast(&Node)) { + return matchesSpecialized(S->desugar(), Finder, Builder); + } return false; } diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td --- a/clang/include/clang/Basic/TypeNodes.td +++ b/clang/include/clang/Basic/TypeNodes.td @@ -75,6 +75,7 @@ def FunctionType : TypeNode; def FunctionProtoType : TypeNode; def FunctionNoProtoType : TypeNode; +def UsingType : TypeNode, NeverCanonical; def UnresolvedUsingType : TypeNode, AlwaysDependent; def ParenType : TypeNode, NeverCanonical; def TypedefType : TypeNode, NeverCanonical; diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -62,5 +62,6 @@ TYPE_BIT_CODE(DependentBitInt, DEPENDENT_BIT_INT, 51) TYPE_BIT_CODE(ConstantMatrix, CONSTANT_MATRIX, 52) TYPE_BIT_CODE(DependentSizedMatrix, DEPENDENT_SIZE_MATRIX, 53) +TYPE_BIT_CODE(Using, USING, 54) #undef TYPE_BIT_CODE diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2349,6 +2349,9 @@ case Type::ObjCTypeParam: return getTypeInfo(cast(T)->desugar().getTypePtr()); + case Type::Using: + return getTypeInfo(cast(T)->desugar().getTypePtr()); + case Type::Typedef: { const TypedefNameDecl *Typedef = cast(T)->getDecl(); TypeInfo Info = getTypeInfo(Typedef->getUnderlyingType().getTypePtr()); @@ -4591,6 +4594,27 @@ return QualType(newType, 0); } +QualType ASTContext::getUsingType(const UsingShadowDecl *Found, + QualType Underlying) const { + llvm::FoldingSetNodeID ID; + UsingType::Profile(ID, Found); + + void *InsertPos = nullptr; + UsingType *T = UsingTypes.FindNodeOrInsertPos(ID, InsertPos); + if (T) + return QualType(T, 0); + + assert(!Underlying.hasQualifiers()); + assert(Underlying == getTypeDeclType(cast(Found->getTargetDecl()))); + QualType Canon = Underlying.getCanonicalType(); + + UsingType *NewType = + new (*this, TypeAlignment) UsingType(Found, Underlying, Canon); + Types.push_back(NewType); + UsingTypes.InsertNode(NewType, InsertPos); + return QualType(NewType, 0); +} + QualType ASTContext::getRecordType(const RecordDecl *Decl) const { if (Decl->TypeForDecl) return QualType(Decl->TypeForDecl, 0); diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp --- a/clang/lib/AST/ASTDiagnostic.cpp +++ b/clang/lib/AST/ASTDiagnostic.cpp @@ -38,6 +38,11 @@ QT = ET->desugar(); continue; } + // ... or a using type ... + if (const UsingType *UT = dyn_cast(Ty)) { + QT = UT->desugar(); + continue; + } // ... or a paren type ... if (const ParenType *PT = dyn_cast(Ty)) { QT = PT->desugar(); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -354,6 +354,7 @@ ExpectedType VisitTypeOfExprType(const TypeOfExprType *T); // FIXME: DependentTypeOfExprType ExpectedType VisitTypeOfType(const TypeOfType *T); + ExpectedType VisitUsingType(const UsingType *T); ExpectedType VisitDecltypeType(const DecltypeType *T); ExpectedType VisitUnaryTransformType(const UnaryTransformType *T); ExpectedType VisitAutoType(const AutoType *T); @@ -1340,6 +1341,17 @@ return Importer.getToContext().getTypeOfType(*ToUnderlyingTypeOrErr); } +ExpectedType ASTNodeImporter::VisitUsingType(const UsingType *T) { + Expected FoundOrErr = import(T->getFoundDecl()); + if (!FoundOrErr) + return FoundOrErr.takeError(); + Expected UnderlyingOrErr = import(T->getUnderlyingType()); + if (!UnderlyingOrErr) + return UnderlyingOrErr.takeError(); + + return Importer.getToContext().getUsingType(*FoundOrErr, *UnderlyingOrErr); +} + ExpectedType ASTNodeImporter::VisitDecltypeType(const DecltypeType *T) { // FIXME: Make sure that the "to" context supports C++0x! ExpectedExpr ToExprOrErr = import(T->getUnderlyingExpr()); diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -945,6 +945,12 @@ return false; break; + case Type::Using: + if (!IsStructurallyEquivalent(Context, cast(T1)->getFoundDecl(), + cast(T2)->getFoundDecl())) + return false; + break; + case Type::Typedef: if (!IsStructurallyEquivalent(Context, cast(T1)->getDecl(), cast(T2)->getDecl())) diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2380,6 +2380,9 @@ break; } + case Type::Using: + return mangleUnresolvedTypeOrSimpleId(cast(Ty)->desugar(), + Prefix); case Type::Elaborated: return mangleUnresolvedTypeOrSimpleId( cast(Ty)->getNamedType(), Prefix); diff --git a/clang/lib/AST/QualTypeNames.cpp b/clang/lib/AST/QualTypeNames.cpp --- a/clang/lib/AST/QualTypeNames.cpp +++ b/clang/lib/AST/QualTypeNames.cpp @@ -418,6 +418,13 @@ return QT; } + // We don't consider the alias introduced by `using a::X` as a new type. + // The qualified name is still a::X. + if (isa(QT.getTypePtr())) { + return getFullyQualifiedType(QT.getSingleStepDesugaredType(Ctx), Ctx, + WithGlobalNsPrefix); + } + // Remove the part of the type related to the type being a template // parameter (we won't report it as part of the 'type name' and it // is actually make the code below to be more complex (to handle diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1534,6 +1534,10 @@ dumpDeclRef(T->getDecl()); } +void TextNodeDumper::VisitUsingType(const UsingType *T) { + dumpDeclRef(T->getFoundDecl()); +} + void TextNodeDumper::VisitTypedefType(const TypedefType *T) { dumpDeclRef(T->getDecl()); } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3407,6 +3407,17 @@ return getDecl()->getUnderlyingType(); } +UsingType::UsingType(const UsingShadowDecl *Found, QualType Underlying, + QualType Canon) + : Type(Using, Canon, Underlying->getDependence()), + Found(const_cast(Found)) { + assert(Underlying == getUnderlyingType()); +} + +QualType UsingType::getUnderlyingType() const { + return QualType(cast(Found->getTargetDecl())->getTypeForDecl(), 0); +} + QualType MacroQualifiedType::desugar() const { return getUnderlyingType(); } QualType MacroQualifiedType::getModifiedType() const { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -212,6 +212,7 @@ case Type::Builtin: case Type::Complex: case Type::UnresolvedUsing: + case Type::Using: case Type::Typedef: case Type::TypeOfExpr: case Type::TypeOf: @@ -1046,6 +1047,21 @@ void TypePrinter::printUnresolvedUsingAfter(const UnresolvedUsingType *T, raw_ostream &OS) {} +void TypePrinter::printUsingBefore(const UsingType *T, raw_ostream &OS) { + // After `namespace b { using a::X }`, is the type X within B a::X or b::X? + // + // - b::X is more formally correct given the UsingType model + // - b::X makes sense if "re-exporting" a symbol in a new namespace + // - a::X makes sense if "importing" a symbol for convenience + // + // The "importing" use seems much more common, so we print a::X. + // This could be a policy option, but the right choice seems to rest more + // with the intent of the code than the caller. + printTypeSpec(T->getFoundDecl()->getUnderlyingDecl(), OS); +} + +void TypePrinter::printUsingAfter(const UsingType *T, raw_ostream &OS) {} + void TypePrinter::printTypedefBefore(const TypedefType *T, raw_ostream &OS) { printTypeSpec(T->getDecl(), OS); } diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -1059,6 +1059,7 @@ const AstTypeMatcher recordType; const AstTypeMatcher tagType; const AstTypeMatcher elaboratedType; +const AstTypeMatcher usingType; const AstTypeMatcher substTemplateTypeParmType; const AstTypeMatcher templateTypeParmType; const AstTypeMatcher injectedClassNameType; diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -228,6 +228,7 @@ REGISTER_MATCHER(eachOf); REGISTER_MATCHER(elaboratedType); REGISTER_MATCHER(elaboratedTypeLoc); + REGISTER_MATCHER(usingType); REGISTER_MATCHER(enumConstantDecl); REGISTER_MATCHER(enumDecl); REGISTER_MATCHER(enumType); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3355,6 +3355,9 @@ case Type::Elaborated: T = cast(T)->getNamedType(); break; + case Type::Using: + T = cast(T)->getUnderlyingType(); + break; case Type::Paren: T = cast(T)->getInnerType(); break; @@ -3547,6 +3550,7 @@ case Type::Decayed: case Type::DeducedTemplateSpecialization: case Type::Elaborated: + case Type::Using: case Type::Paren: case Type::MacroQualified: case Type::SubstTemplateTypeParm: diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2199,6 +2199,7 @@ case Type::Record: case Type::Enum: case Type::Elaborated: + case Type::Using: case Type::TemplateSpecialization: case Type::ObjCTypeParam: case Type::ObjCObject: diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -736,8 +736,15 @@ QualType T = Context.getTypeDeclType(cast(SD->getUnderlyingDecl())); + + if (T->isEnumeralType()) + Diag(IdInfo.IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec); + TypeLocBuilder TLB; - if (isa(T)) { + if (const auto *USD = dyn_cast(SD)) { + T = Context.getUsingType(USD, T); + TLB.pushTypeSpec(T).setNameLoc(IdInfo.IdentifierLoc); + } else if (isa(T)) { InjectedClassNameTypeLoc InjectedTL = TLB.push(T); InjectedTL.setNameLoc(IdInfo.IdentifierLoc); @@ -770,9 +777,6 @@ llvm_unreachable("Unhandled TypeDecl node in nested-name-specifier"); } - if (T->isEnumeralType()) - Diag(IdInfo.IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec); - SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T), IdInfo.CCLoc); return false; 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 @@ -372,6 +372,7 @@ } NamedDecl *IIDecl = nullptr; + UsingShadowDecl *FoundUsingShadow = nullptr; switch (Result.getResultKind()) { case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: @@ -441,8 +442,10 @@ (AllowDeducedTemplate && getAsTypeTemplateDecl(RealRes))) { if (!IIDecl || // Make the selection of the recovery decl deterministic. - RealRes->getLocation() < IIDecl->getLocation()) + RealRes->getLocation() < IIDecl->getLocation()) { IIDecl = RealRes; + FoundUsingShadow = dyn_cast(*Res); + } } } @@ -465,6 +468,7 @@ case LookupResult::Found: IIDecl = Result.getFoundDecl(); + FoundUsingShadow = dyn_cast(*Result.begin()); break; } @@ -491,14 +495,20 @@ (void)DiagnoseUseOfDecl(IDecl, NameLoc); if (!HasTrailingDot) T = Context.getObjCInterfaceType(IDecl); + FoundUsingShadow = nullptr; // FIXME: Target must be a TypeDecl. } else if (auto *UD = dyn_cast(IIDecl)) { (void)DiagnoseUseOfDecl(UD, NameLoc); // Recover with 'int' T = Context.IntTy; + FoundUsingShadow = nullptr; } else if (AllowDeducedTemplate) { - if (auto *TD = getAsTypeTemplateDecl(IIDecl)) + if (auto *TD = getAsTypeTemplateDecl(IIDecl)) { + // FIXME: TemplateName should include FoundUsingShadow sugar. T = Context.getDeducedTemplateSpecializationType(TemplateName(TD), QualType(), false); + // Don't wrap in a further UsingType. + FoundUsingShadow = nullptr; + } } if (T.isNull()) { @@ -507,6 +517,9 @@ return nullptr; } + if (FoundUsingShadow) + T = Context.getUsingType(FoundUsingShadow, T); + // NOTE: avoid constructing an ElaboratedType(Loc) if this is a // constructor or destructor name (in such a case, the scope specifier // will be attached to the enclosing Expr or Decl node). @@ -843,21 +856,6 @@ return false; } -/// Build a ParsedType for a simple-type-specifier with a nested-name-specifier. -static ParsedType buildNestedType(Sema &S, CXXScopeSpec &SS, - QualType T, SourceLocation NameLoc) { - ASTContext &Context = S.Context; - - TypeLocBuilder Builder; - Builder.pushTypeSpec(T).setNameLoc(NameLoc); - - T = S.getElaboratedType(ETK_None, SS, T); - ElaboratedTypeLoc ElabTL = Builder.push(T); - ElabTL.setElaboratedKeywordLoc(SourceLocation()); - ElabTL.setQualifierLoc(SS.getWithLocInContext(Context)); - return S.CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); -} - Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS, IdentifierInfo *&Name, SourceLocation NameLoc, @@ -1134,14 +1132,28 @@ : NameClassification::TypeTemplate(Template); } + auto BuildTypeFor = [&](TypeDecl *Type, NamedDecl *Found) { + QualType T = Context.getTypeDeclType(Type); + if (const auto *USD = dyn_cast(Found)) + T = Context.getUsingType(USD, T); + + if (SS.isEmpty()) // No elaborated type, trivial location info + return ParsedType::make(T); + + TypeLocBuilder Builder; + Builder.pushTypeSpec(T).setNameLoc(NameLoc); + T = getElaboratedType(ETK_None, SS, T); + ElaboratedTypeLoc ElabTL = Builder.push(T); + ElabTL.setElaboratedKeywordLoc(SourceLocation()); + ElabTL.setQualifierLoc(SS.getWithLocInContext(Context)); + return CreateParsedType(T, Builder.getTypeSourceInfo(Context, T)); + }; + NamedDecl *FirstDecl = (*Result.begin())->getUnderlyingDecl(); if (TypeDecl *Type = dyn_cast(FirstDecl)) { DiagnoseUseOfDecl(Type, NameLoc); MarkAnyDeclReferenced(Type->getLocation(), Type, /*OdrUse=*/false); - QualType T = Context.getTypeDeclType(Type); - if (SS.isNotEmpty()) - return buildNestedType(*this, SS, T, NameLoc); - return ParsedType::make(T); + return BuildTypeFor(Type, *Result.begin()); } ObjCInterfaceDecl *Class = dyn_cast(FirstDecl); @@ -1190,10 +1202,7 @@ isTagTypeWithMissingTag(*this, Result, S, SS, Name, NameLoc)) { TypeDecl *Type = Result.getAsSingle(); DiagnoseUseOfDecl(Type, NameLoc); - QualType T = Context.getTypeDeclType(Type); - if (SS.isNotEmpty()) - return buildNestedType(*this, SS, T, NameLoc); - return ParsedType::make(T); + return BuildTypeFor(Type, *Result.begin()); } // If we already know which single declaration is referenced, just annotate diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4443,6 +4443,9 @@ case Type::Decltype: T = cast(Ty)->desugar(); break; + case Type::Using: + T = cast(Ty)->desugar(); + break; case Type::Auto: case Type::DeducedTemplateSpecialization: T = cast(Ty)->getDeducedType(); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -933,6 +933,11 @@ /// the UnresolvedUsingTypenameDecl was transformed to. QualType RebuildUnresolvedUsingType(SourceLocation NameLoc, Decl *D); + /// Build a new type found via an alias. + QualType RebuildUsingType(UsingShadowDecl *Found, QualType Underlying) { + return SemaRef.Context.getUsingType(Found, Underlying); + } + /// Build a new typedef type. QualType RebuildTypedefType(TypedefNameDecl *Typedef) { return SemaRef.Context.getTypeDeclType(Typedef); @@ -6072,9 +6077,9 @@ return Result; } -template QualType -TreeTransform::TransformUnresolvedUsingType(TypeLocBuilder &TLB, - UnresolvedUsingTypeLoc TL) { +template +QualType TreeTransform::TransformUnresolvedUsingType( + TypeLocBuilder &TLB, UnresolvedUsingTypeLoc TL) { const UnresolvedUsingType *T = TL.getTypePtr(); Decl *D = getDerived().TransformDecl(TL.getNameLoc(), T->getDecl()); if (!D) @@ -6095,6 +6100,32 @@ return Result; } +template +QualType TreeTransform::TransformUsingType(TypeLocBuilder &TLB, + UsingTypeLoc TL) { + const UsingType *T = TL.getTypePtr(); + + auto *Found = cast_or_null(getDerived().TransformDecl( + TL.getLocalSourceRange().getBegin(), T->getFoundDecl())); + if (!Found) + return QualType(); + + QualType Underlying = getDerived().TransformType(T->desugar()); + if (Underlying.isNull()) + return QualType(); + + QualType Result = TL.getType(); + if (getDerived().AlwaysRebuild() || Found != T->getFoundDecl() || + Underlying != T->getUnderlyingType()) { + Result = getDerived().RebuildUsingType(Found, Underlying); + if (Result.isNull()) + return QualType(); + } + + TLB.pushTypeSpec(Result).setNameLoc(TL.getNameLoc()); + return Result; +} + template QualType TreeTransform::TransformTypedefType(TypeLocBuilder &TLB, TypedefTypeLoc TL) { @@ -14462,7 +14493,6 @@ if (D->isInvalidDecl()) return QualType(); // FIXME: Doesn't account for ObjCInterfaceDecl! - TypeDecl *Ty; if (auto *UPD = dyn_cast(D)) { // A valid resolved using typename pack expansion decl can have multiple // UsingDecls, but they must each have exactly one type, and it must be @@ -14498,17 +14528,18 @@ // A valid resolved using typename decl points to exactly one type decl. assert(++Using->shadow_begin() == Using->shadow_end()); - NamedDecl *Target = Using->shadow_begin()->getTargetDecl(); - if (SemaRef.DiagnoseUseOfDecl(Target, Loc)) + UsingShadowDecl *Shadow = *Using->shadow_begin(); + if (SemaRef.DiagnoseUseOfDecl(Shadow->getTargetDecl(), Loc)) return QualType(); - Ty = cast(Target); + return SemaRef.Context.getUsingType( + Shadow, SemaRef.Context.getTypeDeclType( + cast(Shadow->getTargetDecl()))); } else { assert(isa(D) && "UnresolvedUsingTypenameDecl transformed to non-using decl"); - Ty = cast(D); + return SemaRef.Context.getTypeDeclType( + cast(D)); } - - return SemaRef.Context.getTypeDeclType(Ty); } template diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6607,6 +6607,10 @@ TL.setNameLoc(readSourceLocation()); } +void TypeLocReader::VisitUsingTypeLoc(UsingTypeLoc TL) { + TL.setNameLoc(readSourceLocation()); +} + void TypeLocReader::VisitTypedefTypeLoc(TypedefTypeLoc TL) { TL.setNameLoc(readSourceLocation()); } diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -396,6 +396,10 @@ Record.AddSourceLocation(TL.getNameLoc()); } +void TypeLocWriter::VisitUsingTypeLoc(UsingTypeLoc TL) { + Record.AddSourceLocation(TL.getNameLoc()); +} + void TypeLocWriter::VisitTypedefTypeLoc(TypedefTypeLoc TL) { Record.AddSourceLocation(TL.getNameLoc()); } diff --git a/clang/test/AST/ast-dump-using.cpp b/clang/test/AST/ast-dump-using.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/ast-dump-using.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -ast-dump %s | FileCheck -strict-whitespace %s + +namespace a { +struct S; +} +namespace b { +using a::S; +// CHECK: UsingDecl {{.*}} a::S +// CHECK-NEXT: UsingShadowDecl {{.*}} implicit CXXRecord {{.*}} 'S' +// CHECK-NEXT: `-RecordType {{.*}} 'a::S' +typedef S f; // to dump the introduced type +// CHECK: TypedefDecl +// CHECK-NEXT: `-UsingType {{.*}} 'a::S' sugar +// CHECK-NEXT: |-UsingShadow {{.*}} 'S' +// CHECK-NEXT: `-RecordType {{.*}} 'a::S' +} diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -1666,6 +1666,8 @@ return Visit(TL.getPointeeLoc()); } +bool CursorVisitor::VisitUsingTypeLoc(UsingTypeLoc TL) { return false; } + bool CursorVisitor::VisitAttributedTypeLoc(AttributedTypeLoc TL) { return Visit(TL.getModifiedLoc()); } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -562,6 +562,15 @@ functionDecl(hasDescendant(typedefDecl(has(atomicType()))))); } +TEST_P(ImportType, ImportUsingType) { + MatchVerifier Verifier; + testImport("struct C {};" + "void declToImport() { using ::C; new C{}; }", + Lang_CXX11, "", Lang_CXX11, Verifier, + functionDecl(hasDescendant( + cxxNewExpr(hasType(pointerType(pointee(usingType()))))))); +} + TEST_P(ImportDecl, ImportFunctionTemplateDecl) { MatchVerifier Verifier; testImport("template void declToImport() { };", Lang_CXX03, "", diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -2581,6 +2581,7 @@ case clang::Type::Typedef: case clang::Type::TypeOf: case clang::Type::TypeOfExpr: + case clang::Type::Using: type = type->getLocallyUnqualifiedSingleStepDesugaredType(); break; default: @@ -4063,6 +4064,7 @@ case clang::Type::Paren: case clang::Type::TypeOf: case clang::Type::TypeOfExpr: + case clang::Type::Using: llvm_unreachable("Handled in RemoveWrappingTypes!"); case clang::Type::UnaryTransform: break; @@ -4722,6 +4724,7 @@ case clang::Type::Typedef: case clang::Type::TypeOf: case clang::Type::TypeOfExpr: + case clang::Type::Using: llvm_unreachable("Handled in RemoveWrappingTypes!"); case clang::Type::UnaryTransform: @@ -5104,6 +5107,7 @@ case clang::Type::Typedef: case clang::Type::TypeOf: case clang::Type::TypeOfExpr: + case clang::Type::Using: llvm_unreachable("Handled in RemoveWrappingTypes!"); case clang::Type::UnaryTransform: break;