Skip to content

Commit

Permalink
Reland "[AST] Add UsingType: a sugar type for types found via UsingDecl"
Browse files Browse the repository at this point in the history
This reverts commit cc56c66.
Fixed a bad assertion, the target of a UsingShadowDecl must not have
*local* qualifiers, but it can be a typedef whose underlying type is qualified.
  • Loading branch information
sam-mccall committed Dec 20, 2021
1 parent cc56c66 commit af27466
Show file tree
Hide file tree
Showing 42 changed files with 382 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ void UpgradeGoogletestCaseCheck::registerMatchers(MatchFinder *Finder) {
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) {
Expand Down
16 changes: 13 additions & 3 deletions clang-tools-extra/clang-tidy/misc/UnusedUsingDeclsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ AST_MATCHER_P(DeducedTemplateSpecializationType, refsToTemplatedDecl,
return DeclMatcher.matches(*TD, Finder, Builder);
return false;
}

} // namespace

// A function that helps to tell whether a TargetDecl in a UsingDecl will be
Expand All @@ -60,13 +61,10 @@ static bool shouldCheckDecl(const Decl *TargetDecl) {
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(
Expand All @@ -76,6 +74,12 @@ void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) {
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) {
Expand Down Expand Up @@ -137,6 +141,12 @@ void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) {
return;
}

if (const auto *UsedShadow =
Result.Nodes.getNodeAs<UsingShadowDecl>("usedShadow")) {
removeFromFoundDecls(UsedShadow->getTargetDecl());
return;
}

if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) {
if (Used->getKind() == TemplateArgument::Template) {
if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ struct UnqualNameVisitor : public RecursiveASTVisitor<UnqualNameVisitor> {
TL.getAs<TypedefTypeLoc>().getTypePtr()->getDecl()->getName()))
return false;
break;
case TypeLoc::Using:
if (visitUnqualName(TL.getAs<UsingTypeLoc>()
.getTypePtr()
->getFoundDecl()
->getName()))
return false;
break;
default:
break;
}
Expand Down
11 changes: 11 additions & 0 deletions clang-tools-extra/clangd/FindTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ struct TargetFinder {
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);
}
Expand Down Expand Up @@ -855,6 +859,13 @@ refInTypeLoc(TypeLoc L, const HeuristicResolver *Resolver) {
}
}

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(),
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/IncludeCleaner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ class ReferencedLocationCrawler
return true;
}

bool VisitUsingType(UsingType *UT) {
add(UT->getFoundDecl());
return true;
}

bool VisitTypedefType(TypedefType *TT) {
add(TT->getDecl());
return true;
Expand Down
12 changes: 4 additions & 8 deletions clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ TEST(IncludeCleaner, ReferencedLocations) {
"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();",
Expand Down Expand Up @@ -198,14 +202,6 @@ TEST(IncludeCleaner, ReferencedLocations) {
{
"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;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/unittests/XRefsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,7 @@ TEST(LocateSymbol, Alias) {

R"cpp(
namespace ns { class [[Foo]] {}; }
using ns::Foo;
using ns::[[Foo]];
F^oo f;
)cpp",

Expand Down
10 changes: 9 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,9 @@ Floating Point Support in Clang
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
--------------------
Expand All @@ -269,6 +271,12 @@ AST Matchers
- The ``hasAnyCapture`` matcher now only accepts an inner matcher of type
``Matcher<LambdaCapture>``. The matcher originally accepted an inner matcher
of type ``Matcher<CXXThisExpr>`` or ``Matcher<VarDecl>``.
- 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
------------
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::ContextualFoldingSet<TemplateSpecializationType, ASTContext&>
TemplateSpecializationTypes;
mutable llvm::FoldingSet<ParenType> ParenTypes;
mutable llvm::FoldingSet<UsingType> UsingTypes;
mutable llvm::FoldingSet<ElaboratedType> ElaboratedTypes;
mutable llvm::FoldingSet<DependentNameType> DependentNameTypes;
mutable llvm::ContextualFoldingSet<DependentTemplateSpecializationType,
Expand Down Expand Up @@ -1555,6 +1556,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
return getTypeDeclTypeSlow(Decl);
}

QualType getUsingType(const UsingShadowDecl *Found,
QualType Underlying) const;

/// Return the unique reference to the type for the specified
/// typedef-name decl.
QualType getTypedefType(const TypedefNameDecl *Decl,
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; }
SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>;
def TemplateTemplateParmDeclRef :
SubclassPropertyType<"TemplateTemplateParmDecl", DeclRef>;
def UsingShadowDeclRef :
SubclassPropertyType<"UsingShadowDecl", DeclRef>;
def ValueDeclRef :
SubclassPropertyType<"ValueDecl", DeclRef>;
def ElaboratedTypeKeyword : EnumPropertyType;
Expand Down
10 changes: 9 additions & 1 deletion clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ DEF_TRAVERSE_TYPE(FunctionProtoType, {
TRY_TO(TraverseStmt(NE));
})

DEF_TRAVERSE_TYPE(UsingType, {})
DEF_TRAVERSE_TYPE(UnresolvedUsingType, {})
DEF_TRAVERSE_TYPE(TypedefType, {})

Expand Down Expand Up @@ -1252,6 +1253,7 @@ DEF_TRAVERSE_TYPELOC(FunctionProtoType, {
TRY_TO(TraverseStmt(NE));
})

DEF_TRAVERSE_TYPELOC(UsingType, {})
DEF_TRAVERSE_TYPELOC(UnresolvedUsingType, {})
DEF_TRAVERSE_TYPELOC(TypedefType, {})

Expand Down Expand Up @@ -2095,7 +2097,13 @@ bool RecursiveASTVisitor<Derived>::TraverseFunctionHelper(FunctionDecl *D) {
}

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<UsingShadowDecl>(Child))
TRY_TO(TraverseDecl(Child));
}
}
return true;
}
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/TextNodeDumper.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ class TextNodeDumper
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);
Expand Down
22 changes: 22 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class TemplateArgumentLoc;
class TemplateTypeParmDecl;
class TypedefNameDecl;
class UnresolvedUsingTypenameDecl;
class UsingShadowDecl;

using CanQualType = CanQual<Type>;

Expand Down Expand Up @@ -4368,6 +4369,27 @@ class UnresolvedUsingType : public Type {
}
};

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;

Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,16 @@ class BuiltinTypeLoc : public ConcreteTypeLoc<UnqualTypeLoc,
}
};

/// Wrapper for source info for types used via transparent aliases.
class UsingTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
UsingTypeLoc, UsingType> {
public:
QualType getUnderlyingType() const {
return getTypePtr()->getUnderlyingType();
}
UsingShadowDecl *getFoundDecl() const { return getTypePtr()->getFoundDecl(); }
};

/// Wrapper for source info for typedefs.
class TypedefTypeLoc : public InheritingConcreteTypeLoc<TypeSpecTypeLoc,
TypedefTypeLoc,
Expand Down
13 changes: 13 additions & 0 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,19 @@ let Class = UnresolvedUsingType in {
}]>;
}

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() }];
Expand Down
52 changes: 37 additions & 15 deletions clang/include/clang/ASTMatchers/ASTMatchers.h
Original file line number Diff line number Diff line change
Expand Up @@ -4128,25 +4128,34 @@ AST_MATCHER_P(DeclRefExpr, to, internal::Matcher<Decl>,
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<UsingShadowDecl>, 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<DeclRefExpr>, Matcher<UsingType>
AST_POLYMORPHIC_MATCHER_P(throughUsingDecl,
AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
UsingType),
internal::Matcher<UsingShadowDecl>, Inner) {
const NamedDecl *FoundDecl = Node.getFoundDecl();
if (const UsingShadowDecl *UsingDecl = dyn_cast<UsingShadowDecl>(FoundDecl))
return InnerMatcher.matches(*UsingDecl, Finder, Builder);
return Inner.matches(*UsingDecl, Finder, Builder);
return false;
}

Expand Down Expand Up @@ -6843,7 +6852,7 @@ extern const AstTypeMatcher<DecltypeType> decltypeType;
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
Expand All @@ -6853,9 +6862,10 @@ AST_TYPE_TRAVERSE_MATCHER(hasDeducedType, getDeducedType,
/// decltypeType(hasUnderlyingType(isInteger()))
/// matches the type of "a"
///
/// Usable as: Matcher<DecltypeType>
/// Usable as: Matcher<DecltypeType>, Matcher<UsingType>
AST_TYPE_TRAVERSE_MATCHER(hasUnderlyingType, getUnderlyingType,
AST_POLYMORPHIC_SUPPORTED_TYPES(DecltypeType));
AST_POLYMORPHIC_SUPPORTED_TYPES(DecltypeType,
UsingType));

/// Matches \c FunctionType nodes.
///
Expand Down Expand Up @@ -7183,6 +7193,18 @@ AST_MATCHER_P(ElaboratedType, namesType, internal::Matcher<QualType>,
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> usingType;

/// Matches types that represent the result of substituting a type for a
/// template type parameter.
///
Expand Down
Loading

0 comments on commit af27466

Please sign in to comment.