Skip to content

Commit

Permalink
[clang compat] Fix alias template handling
Browse files Browse the repository at this point in the history
llvm/llvm-project@7f78f99 removed
SubstTemplateTypeParmType nodes from an instantiated alias template
type. GetProvidedTypes function relied on them to filter out substituted
argument types. Now, it is done "manually" inside the added
GetAliasTemplateProvidedTypes function, as discussed
in llvm/llvm-project#101858.

When traversing instantiated type alias internals, bare RecordType nodes
appear now instead of ones wrapped in SubstTemplateTypeParmType, hence
the new visiting method added into InstantiatedTemplateVisitor. (Enum
types are not expected to need handling there, hence VisitRecordType
instead of VisitTagType.)

A test case added to cover the change inside GetProvidedTypes function
(i.e. correct handling of intermediate alias templates in a chain).
  • Loading branch information
bolshakov-a committed Aug 10, 2024
1 parent b7cbe76 commit 3135750
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 8 deletions.
40 changes: 33 additions & 7 deletions iwyu.cc
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ using clang::Preprocessor;
using clang::QualType;
using clang::QualifiedTypeLoc;
using clang::RecordDecl;
using clang::RecordType;
using clang::RecursiveASTVisitor;
using clang::ReferenceType;
using clang::Sema;
Expand Down Expand Up @@ -2602,10 +2603,9 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
dyn_cast<TemplateSpecializationType>(component)) {
const NamedDecl* decl = TypeToDeclAsWritten(tpl_spec_type);
if (const auto* al_tpl_decl = dyn_cast<TypeAliasTemplateDecl>(decl)) {
const TypeAliasDecl* al_decl = al_tpl_decl->getTemplatedDecl();
InsertAllInto(GetProvidedTypes(tpl_spec_type->desugar().getTypePtr(),
GetLocation(al_decl)),
&retval);
InsertAllInto(
GetAliasTemplateProvidedTypes(tpl_spec_type, al_tpl_decl),
&retval);
continue;
}
}
Expand Down Expand Up @@ -2653,6 +2653,16 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
bool SourceOrTargetTypeIsProvided(const ASTNode* construct_expr_node) const =
delete;

set<const Type*> GetAliasTemplateProvidedTypes(
const TemplateSpecializationType* type,
const TypeAliasTemplateDecl* al_tpl_decl) const {
const TypeAliasDecl* al_decl = al_tpl_decl->getTemplatedDecl();
set<const Type*> res = GetProvidedTypes(type->getAliasedType().getTypePtr(),
GetLocation(al_decl));
RemoveAllFrom(GetCanonicalArgComponents(type), &res);
return res;
}

void ReportTypeUseInternal(SourceLocation used_loc, const Type* type,
set<const Type*> blocked_types,
DerefKind deref_kind) {
Expand Down Expand Up @@ -2729,10 +2739,10 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
if (template_spec_type->isTypeAlias()) {
const NamedDecl* decl = TypeToDeclAsWritten(template_spec_type);
if (const auto* al_tpl_decl = dyn_cast<TypeAliasTemplateDecl>(decl)) {
const TypeAliasDecl* al_decl = al_tpl_decl->getTemplatedDecl();
InsertAllInto(
GetAliasTemplateProvidedTypes(template_spec_type, al_tpl_decl),
&blocked_types);
const Type* type = template_spec_type->getAliasedType().getTypePtr();
InsertAllInto(GetProvidedTypes(type, GetLocation(al_decl)),
&blocked_types);
ReportTypeUseInternal(used_loc, type, blocked_types, deref_kind);
}
return;
Expand Down Expand Up @@ -3262,6 +3272,22 @@ class InstantiatedTemplateVisitor
return Base::VisitSubstTemplateTypeParmType(type);
}

bool VisitRecordType(RecordType* type) {
if (CanIgnoreCurrentASTNode() || CanIgnoreType(type))
return true;

// CanForwardDeclareType function relies on the specific placement of
// the type node in the AST. An intermediate SubstTemplateTypeParmType could
// break that logic. However, such cases don't even need to be considered
// here, because they are handled in VisitSubstTemplateTypeParmType. But
// maybe using AncestorIsA, or replacing VisitSubst...Type with
// TraverseSubst...Type and Traverse...TypeLoc would be more reliable.
if (!current_ast_node()->ParentIsA<SubstTemplateTypeParmType>())
AnalyzeTemplateTypeParmUse(type);

return Base::VisitRecordType(type);
}

bool VisitTemplateSpecializationType(TemplateSpecializationType* type) {
if (CanIgnoreCurrentASTNode())
return true;
Expand Down
12 changes: 12 additions & 0 deletions iwyu_ast_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ using llvm::errs;
using llvm::isa;
using llvm::raw_string_ostream;
using std::function;
using std::vector;

namespace include_what_you_use {

Expand Down Expand Up @@ -1617,6 +1618,17 @@ bool CanBeOpaqueDeclared(const EnumType* type) {
return type->getDecl()->isFixed();
}

vector<const Type*> GetCanonicalArgComponents(
const TemplateSpecializationType* type) {
vector<const Type*> res;
SugaredTypeEnumerator enumerator;
for (const TemplateArgument& arg : type->template_arguments()) {
for (const Type* component : enumerator.Enumerate(arg))
res.push_back(GetCanonicalType(component));
}
return res;
}

// --- Utilities for Stmt.

bool IsAddressOf(const Expr* expr) {
Expand Down
6 changes: 6 additions & 0 deletions iwyu_ast_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <map> // for map
#include <set> // for set
#include <string> // for string
#include <vector> // for vector

#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/TemplateBase.h"
Expand Down Expand Up @@ -858,6 +859,11 @@ TemplateInstantiationData GetTplInstDataForClassNoComponentTypes(
// according to the standard.
bool CanBeOpaqueDeclared(const clang::EnumType* type);

// Collects template argument type components and returns them desugared.
// The result may contain duplicates.
std::vector<const clang::Type*> GetCanonicalArgComponents(
const clang::TemplateSpecializationType*);

// --- Utilities for Stmt.

// Returns true if the given expr is '&<something>'.
Expand Down
5 changes: 5 additions & 0 deletions tests/cxx/typedef_chain_in_template-d5.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ struct Tpl {

using TplWithNonProvidedAliased1 = Tpl<NonProvidingAlias>;
using TplWithNonProvidedAliased2 = Tpl<NonProvidingAliasTpl<1>>;

class TypedefChainClass;

template <int>
using NonProvidingAliasByOther = IdentityAlias2<TypedefChainClass>;
3 changes: 3 additions & 0 deletions tests/cxx/typedef_chain_in_template-i1.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ struct TypedefWrapper {
typedef value_type& reference;
};

template <typename T>
using IdentityAlias2 = T;

#endif // INCLUDE_WHAT_YOU_USE_TESTS_CXX_TYPEDEF_CHAIN_IN_TEMPLATE_I1_H_
7 changes: 6 additions & 1 deletion tests/cxx/typedef_chain_in_template.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ void Fn() {
(void)sizeof(TplWithNonProvidedAliased1);
// IWYU: TypedefChainClass is...*typedef_chain_class.h
(void)sizeof(TplWithNonProvidedAliased2);

// IWYU: TypedefChainClass is...*typedef_chain_class.h
NonProvidingAliasByOther<1> npabo;
// IWYU: TypedefChainClass is...*typedef_chain_class.h
npabo.Method();
}

/**** IWYU_SUMMARY
Expand All @@ -89,6 +94,6 @@ The full include-list for tests/cxx/typedef_chain_in_template.cc:
#include "tests/cxx/typedef_chain_in_template-d2.h" // for ContainerAsLibcpp
#include "tests/cxx/typedef_chain_in_template-d3.h" // for ContainerShortTypedefChain
#include "tests/cxx/typedef_chain_in_template-d4.h" // for ContainerLongTypedefChain
#include "tests/cxx/typedef_chain_in_template-d5.h" // for IdentityAliasComplex, IdentityStructComplex, TplWithNonProvidedAliased1, TplWithNonProvidedAliased2
#include "tests/cxx/typedef_chain_in_template-d5.h" // for IdentityAliasComplex, IdentityStructComplex, NonProvidingAliasByOther, TplWithNonProvidedAliased1, TplWithNonProvidedAliased2
***** IWYU_SUMMARY */

0 comments on commit 3135750

Please sign in to comment.