Skip to content

Commit

Permalink
[include-cleaner] Attribute references to explicit specializations
Browse files Browse the repository at this point in the history
Fixes #61652

Differential Revision: https://reviews.llvm.org/D146732
  • Loading branch information
kadircet committed Mar 24, 2023
1 parent ff426a6 commit 03101e1
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 8 deletions.
27 changes: 23 additions & 4 deletions clang-tools-extra/include-cleaner/lib/WalkAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
//===----------------------------------------------------------------------===//

#include "AnalysisInternal.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/Support/Casting.h"

namespace clang::include_cleaner {
Expand Down Expand Up @@ -62,6 +65,24 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
return resolveTemplateName(TST->getTemplateName());
return Base->getAsRecordDecl();
}
// Templated as TemplateSpecializationType and
// DeducedTemplateSpecializationType doesn't share a common base.
template <typename T>
// Picks the most specific specialization for a
// (Deduced)TemplateSpecializationType, while prioritizing using-decls.
NamedDecl *getMostRelevantTemplatePattern(const T *TST) {
// This is the underlying decl used by TemplateSpecializationType, can be
// null when type is dependent.
auto *RD = TST->getAsTagDecl();
auto *ND = resolveTemplateName(TST->getTemplateName());
// In case of exported template names always prefer the using-decl. This
// implies we'll point at the using-decl even when there's an explicit
// specializaiton using the exported name, but that's rare.
if (llvm::isa_and_present<UsingShadowDecl, TypeAliasTemplateDecl>(ND))
return ND;
// Fallback to primary template for dependent instantiations.
return RD ? RD : ND;
}

public:
ASTWalker(DeclCallback Callback) : Callback(Callback) {}
Expand Down Expand Up @@ -161,17 +182,15 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
}

bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
// FIXME: Handle explicit specializations.
report(TL.getTemplateNameLoc(),
resolveTemplateName(TL.getTypePtr()->getTemplateName()));
getMostRelevantTemplatePattern(TL.getTypePtr()));
return true;
}

bool VisitDeducedTemplateSpecializationTypeLoc(
DeducedTemplateSpecializationTypeLoc TL) {
// FIXME: Handle specializations.
report(TL.getTemplateNameLoc(),
resolveTemplateName(TL.getTypePtr()->getTemplateName()));
getMostRelevantTemplatePattern(TL.getTypePtr()));
return true;
}

Expand Down
42 changes: 38 additions & 4 deletions clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ TEST(WalkAST, TagType) {
// One explicit call from the TypeLoc in constructor spelling, another
// implicit reference through the constructor call.
testWalk("struct $explicit^$implicit^S { static int x; };", "auto y = ^S();");
testWalk("template<typename> struct $explicit^Foo {};", "^Foo<int> x;");
testWalk(R"cpp(
template<typename> struct Foo {};
template<> struct $explicit^Foo<int> {};)cpp",
"^Foo<int> x;");
testWalk(R"cpp(
template<typename> struct Foo {};
template<typename T> struct $explicit^Foo<T*> { void x(); };)cpp",
"^Foo<int *> x;");
testWalk(R"cpp(
template<typename> struct Foo {};
template struct $explicit^Foo<int>;)cpp",
"^Foo<int> x;");
// FIXME: This is broken due to
// https://github.com/llvm/llvm-project/issues/42259.
testWalk(R"cpp(
template<typename T> struct $explicit^Foo { Foo(T); };
template<> struct Foo<int> { void get(); Foo(int); };)cpp",
"^Foo x(3);");
}

TEST(WalkAST, Alias) {
Expand All @@ -124,6 +143,25 @@ TEST(WalkAST, Alias) {
"int y = ^x;");
testWalk("using $explicit^foo = int;", "^foo x;");
testWalk("struct S {}; using $explicit^foo = S;", "^foo x;");
testWalk(R"cpp(
template<typename> struct Foo {};
template<> struct Foo<int> {};
namespace ns { using ::$explicit^Foo; })cpp",
"ns::^Foo<int> x;");
testWalk(R"cpp(
template<typename> struct Foo {};
namespace ns { using ::Foo; }
template<> struct ns::$explicit^Foo<int> {};)cpp",
"^Foo<int> x;");
// AST doesn't have enough information to figure out whether specialization
// happened through an exported type or not. So err towards attributing use to
// the using-decl, specializations on the exported type should be rare and
// they're not permitted on type-aliases.
testWalk(R"cpp(
template<typename> struct Foo {};
namespace ns { using ::$explicit^Foo; }
template<> struct ns::Foo<int> {};)cpp",
"ns::^Foo<int> x;");
}

TEST(WalkAST, Using) {
Expand Down Expand Up @@ -183,10 +221,6 @@ TEST(WalkAST, TemplateNames) {
template <template <typename> typename> struct X {};
X<^S> x;)cpp");
testWalk("template<typename T> struct $explicit^S { S(T); };", "^S s(42);");
// Should we mark the specialization instead?
testWalk(
"template<typename> struct $explicit^S {}; template <> struct S<int> {};",
"^S<int> s;");
}

TEST(WalkAST, MemberExprs) {
Expand Down

0 comments on commit 03101e1

Please sign in to comment.