Report only explicit typedefs

Previously, ReportDeclUse was called for TypedefDecl when TypedefType
is passed to ReportTypeUse. This change short-circuits that path. But
an underlying type should still be reported if the typedef doesn't
"provide" it. Hence, corresponding logic is moved into ReportTypeUse.
So ReportDeclUse reports typedef declarations and ReportTypeUse reports
typedef underlying types now.
This commit is contained in:
Bolshakov 2023-01-06 22:35:59 +03:00 committed by Kim Gräsman
parent 8a6009f587
commit 971a300bb9
6 changed files with 137 additions and 35 deletions

73
iwyu.cc
View File

@ -1604,34 +1604,6 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
preprocessor_info().FileInfoFor(used_in)->ReportUsingDeclUse(
used_loc, using_decl, use_flags, "(for using decl)");
}
// For typedefs, the user of the type is sometimes the one
// responsible for the underlying type. We check if that is the
// case here, since we might be using a typedef type from
// anywhere. ('autocast' is similar, but is handled in
// VisitCastExpr; 'fn-return-type' is also similar and is
// handled in HandleFunctionCall.)
if (const TypedefNameDecl* typedef_decl = DynCastFrom(target_decl)) {
// One exception: if this TypedefType is being used in another
// typedef (that is, 'typedef MyTypedef OtherTypdef'), then the
// user -- the other typedef -- is never responsible for the
// underlying type. Instead, users of that typedef are.
const ASTNode* ast_node = MostElaboratedAncestor(current_ast_node());
if (!ast_node->ParentIsA<TypedefNameDecl>()) {
const set<const Type*>& underlying_types =
GetCallerResponsibleTypesForTypedef(typedef_decl);
if (!underlying_types.empty()) {
VERRS(6) << "User, not author, of typedef "
<< typedef_decl->getQualifiedNameAsString()
<< " owns the underlying type:\n";
// If any of the used types are themselves typedefs, this will
// result in a recursive expansion. Note we are careful to
// recurse inside this class, and not go back to subclasses.
for (const Type* type : underlying_types)
IwyuBaseAstVisitor<Derived>::ReportTypeUse(used_loc, type);
}
}
}
}
// The comment, if not nullptr, is extra text that is included along
@ -1699,6 +1671,36 @@ class IwyuBaseAstVisitor : public BaseAstVisitor<Derived> {
if (IsPointerOrReferenceAsWritten(type))
return;
// For typedefs, the user of the type is sometimes the one
// responsible for the underlying type. We check if that is the
// case here, since we might be using a typedef type from
// anywhere. ('autocast' is similar, but is handled in
// VisitCastExpr; 'fn-return-type' is also similar and is
// handled in HandleFunctionCall.)
if (const auto* typedef_type = type->getAs<TypedefType>()) {
// One exception: if this TypedefType is being used in another
// typedef (that is, 'typedef MyTypedef OtherTypdef'), then the
// user -- the other typedef -- is never responsible for the
// underlying type. Instead, users of that typedef are.
const ASTNode* ast_node = MostElaboratedAncestor(current_ast_node());
if (!ast_node->ParentIsA<TypedefNameDecl>()) {
const TypedefNameDecl* typedef_decl = typedef_type->getDecl();
const set<const Type*>& underlying_types =
GetCallerResponsibleTypesForTypedef(typedef_decl);
if (!underlying_types.empty()) {
VERRS(6) << "User, not author, of typedef "
<< typedef_decl->getQualifiedNameAsString()
<< " owns the underlying type:\n";
// If any of the used types are themselves typedefs, this will
// result in a recursive expansion. Note we are careful to
// recurse inside this class, and not go back to subclasses.
for (const Type* type : underlying_types)
IwyuBaseAstVisitor<Derived>::ReportTypeUse(used_loc, type);
}
}
return;
}
// Map private types like __normal_iterator to their public counterpart.
type = MapPrivateTypeToPublicType(type);
// For the below, we want to be careful to call *our*
@ -4123,11 +4125,9 @@ class IwyuAstConsumer
if (CanIgnoreCurrentASTNode())
return true;
// TypedefType::getDecl() returns the place where the typedef is defined.
if (CanForwardDeclareType(current_ast_node())) {
ReportDeclForwardDeclareUse(CurrentLoc(), type->getDecl());
} else {
ReportDeclUse(CurrentLoc(), type->getDecl());
}
ReportDeclUse(CurrentLoc(), type->getDecl());
if (!CanForwardDeclareType(current_ast_node()))
ReportTypeUse(CurrentLoc(), type);
return Base::VisitTypedefType(type);
}
@ -4135,11 +4135,16 @@ class IwyuAstConsumer
if (CanIgnoreCurrentASTNode())
return true;
// UsingType is similar to TypedefType, so treat it the same.
if (CanForwardDeclareType(current_ast_node())) {
ReportDeclForwardDeclareUse(CurrentLoc(), type->getFoundDecl());
} else {
ReportDeclUse(CurrentLoc(), type->getFoundDecl());
// If UsingType refers to a typedef, report the underlying type of that
// typedef if needed (which is determined in ReportTypeUse).
const Type* underlying_type = type->getUnderlyingType().getTypePtr();
if (isa<TypedefType>(underlying_type))
ReportTypeUse(CurrentLoc(), underlying_type);
}
return Base::VisitUsingType(type);

View File

@ -1111,7 +1111,8 @@ int main() {
// a() returns a FOO, which in this case is I2_Enum.
local_d1_template_class.a();
(void)(local_i1_enum);
// IWYU: I1_UnnamedStruct is...*badinc-i1.h
// Typedef of unnamed type always provides that type, and it should be
// included due to variable declaration.
(void)(local_i1_unnamed_struct.a);
local_d1_subclass.a();
(void)(local_i2_class_ptr);

View File

@ -0,0 +1,10 @@
//===--- no_implicit_typedef_reporting-d1.h - test input file for iwyu ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "tests/cxx/no_implicit_typedef_reporting-i1.h"

View File

@ -0,0 +1,28 @@
//===--- no_implicit_typedef_reporting-d2.h - test input file for iwyu ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "tests/cxx/no_implicit_typedef_reporting-d1.h"
struct Struct {
// IWYU: Int is...*no_implicit_typedef_reporting-i1.h
Int typedefed;
};
/**** IWYU_SUMMARY
tests/cxx/no_implicit_typedef_reporting-d2.h should add these lines:
#include "tests/cxx/no_implicit_typedef_reporting-i1.h"
tests/cxx/no_implicit_typedef_reporting-d2.h should remove these lines:
- #include "tests/cxx/no_implicit_typedef_reporting-d1.h" // lines XX-XX
The full include-list for tests/cxx/no_implicit_typedef_reporting-d2.h:
#include "tests/cxx/no_implicit_typedef_reporting-i1.h" // for Int
***** IWYU_SUMMARY */

View File

@ -0,0 +1,10 @@
//===--- no_implicit_typedef_reporting-i1.h - test input file for iwyu ----===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
typedef int Int;

View File

@ -0,0 +1,48 @@
//===--- no_implicit_typedef_reporting.cc - test input file for iwyu ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// IWYU_ARGS: -I . \
// -Xiwyu --check_also="tests/cxx/no_implicit_typedef_reporting-d2.h"
// Tests that IWYU doesn't suggest to include typedef-containing header due to
// implicit-only use of the typedef, i.e. when it is not explicitly written
// in a source.
#include "tests/cxx/no_implicit_typedef_reporting-d1.h"
#include "tests/cxx/no_implicit_typedef_reporting-d2.h"
template <class T1, class T2>
struct Template {
T1 t1;
decltype(T2::typedefed) t2;
};
void Fn() {
// Test use in unary expression.
(void)sizeof(Struct::typedefed);
Struct s;
// Test use for pointer arithmetic.
void* p = &s.typedefed + 10;
// Test use in instantiated template.
Template<decltype(Struct::typedefed), Struct> tpl;
}
/**** IWYU_SUMMARY
tests/cxx/no_implicit_typedef_reporting.cc should add these lines:
tests/cxx/no_implicit_typedef_reporting.cc should remove these lines:
- #include "tests/cxx/no_implicit_typedef_reporting-d1.h" // lines XX-XX
The full include-list for tests/cxx/no_implicit_typedef_reporting.cc:
#include "tests/cxx/no_implicit_typedef_reporting-d2.h" // for Struct
***** IWYU_SUMMARY */