Compare commits

...

10 Commits

Author SHA1 Message Date
Sameer Rahmani 785ae7e7b1
Fix the incorrect include path of Triple.h 2023-02-08 15:30:48 +00:00
Petr Bred 1a2cab9b71 [clang compat] Remove unused llvm::Optional
Clang 125f4457a54a550846732763ee36b1447ec8d66e changed.
The LLVM project is migrating from llvm::Optional to std::optional.

We would previously see llvm::Optional via an indirect include:
    iwyu_preprocessor.h
      iwyu_output.h
        clang/AST/Decl.h
          clang/AST/APValue.h
            clang/Basic/LLVM.h

Since we don't use it, remove the using-declaration.

Signed-off-by: Petr Bred <bredpetr@gmail.com>
2023-02-02 19:27:36 +01:00
Bolshakov f9f4fd2eef Test reporting types hidden in tpl arguments
The full type use reporting in the three covered cases is ensured
with excessive `CanIgnoreType` calls which check not only the type
to be reported but also the type before dereferencing (which occurs
to be in the resugaring map). This approach should be refactored
in subsequent commits.
2023-02-01 21:26:28 +01:00
Bolshakov eb1697e002 Fix comment 2023-02-01 21:26:28 +01:00
Kim Gräsman 81ee985377 Ignore uses of declarations from inside of functions
Using type deduction, a function can define a local type and return it:

    auto func() {
      struct X {};
      return X();
    }

Before this change, IWYU would register X as a use for callers of func.

In certain situations (union inside lambda inside macro) this would lead
to a forward-declare use trying to format a forward-declaration of the
type local to the function in PrintForwardDeclare. This led to a crash
since the lambda was unnamed, but there's really no point trying to
forward-declare a type that is private to a function.

Generalize this so we ignore all uses of symbols declared inside a
function. In order to get in contact with that symbol, we must already
have access to the function, so the containing header must already be
included.

Fixes issue: 795
2023-01-22 20:35:42 +01:00
Kim Gräsman 80410de523 Renumber the full-use trimming steps
... to make room for a new one at the very top, now denoted 'TBD'.

We only do this separately to keep the diff less noisy.
2023-01-22 20:35:42 +01:00
Kim Gräsman bd305afe7d Support 'IWYU pragma: export' for forward declarations
Covers both begin_export/end_export blocks and single-line export
pragmas.

Like with 'IWYU pragma: keep' marks the forward decl as automatically
desired to avoid removing manually exported but unused decls.

Add a simple testcase and update documentation.
2023-01-22 20:32:32 +01:00
Kim Gräsman 2dc0645882 Simplify pragma keep handling for forward declarations
Rename ForwardDeclareInKeepRange to ForwardDeclareIsMarkedKeep, take a
NamedDecl and move all location and text wrangling into the function.

This is a layering violation in a sense, because the preprocessor
doesn't know anything about declarations. But the API is much cleaner
that way from the callers' perspective -- they typically just have a
decl and shouldn't be trusted to use the right location info.

No functional change.
2023-01-22 20:32:32 +01:00
Bolshakov f7c5a795ab Desugar UsingType among others
`clang::UsingType` denotes a sugar type which is introduced by a using-
declaration (e.g., `using ns::SomeType;`). It has not such a special
meaning in IWYU as `TypedefType` or `TemplateSpecializationType`.

No functional change is expected.
2023-01-22 20:22:59 +01:00
Bolshakov 971a300bb9 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.
2023-01-22 20:08:02 +01:00
23 changed files with 487 additions and 79 deletions

View File

@ -31,31 +31,37 @@ In the provided case nothing within the bounds of `begin_keep` and `end_keep` wi
## IWYU pragma: export ##
This pragma applies to a single `#include` directive. It says that the current file is to be considered the provider of any symbol from the included file.
This pragma applies to a single `#include` directive or forward declaration. It says that the current file is to be considered the provider of any symbol from the included file or declaration.
facade.h:
#include "detail/constants.h" // IWYU pragma: export
#include "detail/types.h" // IWYU pragma: export
#include <vector> // don't export stuff from <vector>
class Other; // IWYU pragma: export
main.cc:
#include "facade.h"
// Assuming Thing comes from detail/types.h and MAX_THINGS from detail/constants.h
std::vector<Thing> things(MAX_THINGS);
Here, since `detail/constants.h` and `detail/types.h` have both been exported, IWYU is happy with the `facade.h` include for `Thing` and `MAX_THINGS`.
// Satisfied with forward-declaration from facade.h
void foo(Other* thing);
Here, since `detail/constants.h`, `detail/types.h` and `Other` have all been exported, IWYU is happy with the `facade.h` include for `Thing` and `MAX_THINGS` and does not suggest a local forward declaration for `Other`.
In contrast, since `<vector>` has not been exported from `facade.h`, it will be suggested as an additional include.
## IWYU pragma: begin_exports/end_exports ##
This pragma applies to a set of `#include` directives. It declares that the including file is to be considered the provider of any symbol from these included files. This is the same as decorating every `#include` directive with `IWYU pragma: export`.
This pragma applies to a set of `#include` directives or forward declarations. It declares that the including file is to be considered the provider any symbol from contained included files or declarations. This is the same as decorating every line with `IWYU pragma: export`.
facade.h:
// IWYU pragma: begin_exports
#include "detail/constants.h"
#include "detail/types.h"
class Other;
// IWYU pragma: end_exports
#include <vector> // don't export stuff from <vector>

View File

@ -251,13 +251,13 @@ and forward declarations instead of a single line.
.B // IWYU pragma: export
Used after an
.B #include
directive it indicates that the current file is considered to be a provider of
the included file.
directive or forward declaration it indicates that the current file is
considered to be a provider of any symbol from the included file or declaration.
.TP
.BR "// IWYU pragma: begin_exports" , " // IWYU pragma: end_exports"
Has the same effect as the previous pragma comment, but applies to a range of
.BR #include s
instead of a single line.
or forward declarations instead of a single line.
.TP
.BR "// IWYU pragma: private" [ ", include \fIheader" ]
Indicates that the current file is considered private,

84
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*
@ -3985,13 +3987,10 @@ class IwyuAstConsumer
definitely_keep_fwd_decl = true;
}
}
} else {
SourceLocation decl_end_location = decl->getSourceRange().getEnd();
if (LineHasText(decl_end_location, "// IWYU pragma: keep") ||
LineHasText(decl_end_location, "/* IWYU pragma: keep") ||
preprocessor_info().ForwardDeclareInKeepRange(decl_end_location)) {
definitely_keep_fwd_decl = true;
}
} else if (preprocessor_info().ForwardDeclareIsMarkedKeep(decl)) {
definitely_keep_fwd_decl = true;
} else if (preprocessor_info().ForwardDeclareIsExported(decl)) {
definitely_keep_fwd_decl = true;
}
preprocessor_info().FileInfoFor(CurrentFileEntry())->AddForwardDeclare(
@ -4123,11 +4122,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 +4132,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

@ -1144,7 +1144,6 @@ const Type* Desugar(const Type* type) {
while (true) {
// Don't desugar types that (potentially) add a name.
if (cur->getTypeClass() == Type::Typedef ||
cur->getTypeClass() == Type::Using ||
cur->getTypeClass() == Type::TemplateSpecialization) {
return cur;
}
@ -1508,4 +1507,9 @@ string GetKindName(const TypeLoc typeloc) {
return string(typeloc.getTypePtr()->getTypeClassName()) + "TypeLoc";
}
bool IsDeclaredInsideFunction(const Decl* decl) {
const DeclContext* decl_ctx = decl->getDeclContext();
return isa<FunctionDecl>(decl_ctx);
}
} // namespace include_what_you_use

View File

@ -839,6 +839,10 @@ std::string GetKindName(const clang::Stmt* stmt);
std::string GetKindName(const clang::Type* type);
std::string GetKindName(const clang::TypeLoc typeloc);
// Returns true if decl is entirely inside a function, which implies it's only
// visible from said function.
bool IsDeclaredInsideFunction(const clang::Decl* decl);
} // namespace include_what_you_use
#endif // INCLUDE_WHAT_YOU_USE_IWYU_AST_UTIL_H_

View File

@ -21,7 +21,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Host.h"

View File

@ -976,40 +976,46 @@ set<string> CalculateMinimalIncludes(
// A4) If the file containing the use has a pragma inhibiting the forward
// declaration of the symbol, change the use to a full info use in order
// to make sure that the compiler can see some declaration of the symbol.
// A5) If a nested class, discard this use (the parent class declaration
// is sufficient).
// A5) If declaration is nested inside a class or a function, discard this use.
// The containing class/function is required to use the nested decl, and
// so will force use of the containing header.
// A6) If any of the redeclarations of this declaration is in the same
// file as the use (and before it), and is actually a definition,
// discard the forward-declare use.
// A7) If --no_fwd_decls has been passed, recategorize as a full use.
// A7) If any redeclaration is marked with IWYU pragma: export, mark as a
// full use of this decl to keep its containing file included.
// A8) If --no_fwd_decls has been passed, recategorize as a full use.
// Trimming symbol uses (1st pass):
// B1) If the definition of a full use comes after the use, change the
// B1) If declaration is nested inside a function, discard this use.
// The function is required to use the nested decl, and so will force use of
// the containing header.
// B2) If the definition of a full use comes after the use, change the
// full use to a forward-declare use that points to a fwd-decl
// that comes before the use. (This is for cases like typedefs
// where iwyu demands a full use but the language allows a
// forward-declare.)
// B2) Discard symbol uses of a symbol defined in the same file it's used.
// B3) Discard symbol uses of a symbol defined in the same file it's used.
// If the symbol is a typedef, function, or var -- every decl
// that is re-declarable except for TagDecl -- discard if *any*
// declaration is in the same file as the use.
// B3) Discard symbol uses for builtin symbols ('__builtin_memcmp') and
// B4) Discard symbol uses for builtin symbols ('__builtin_memcmp') and
// for operator new and operator delete (excluding placement new),
// which are effectively built-in even though they're in <new>.
// B4) Discard symbol uses for member functions that live in the same
// B5) Discard symbol uses for member functions that live in the same
// file as the class they're part of (the parent check suffices).
// B5) Sanity check: Discard 'backwards' #includes. These are
// B6) Sanity check: Discard 'backwards' #includes. These are
// #includes where we say a.h should #include b.h, but b.h is
// already #including a.h. This happens when iwyu attributes a
// use to the wrong file.
// B6) In --transitive_includes_only mode, discard 'new' #includes.
// B7) In --transitive_includes_only mode, discard 'new' #includes.
// These are #includes where we say a.h should #include b.h, but
// a.h does not see b.h in its transitive #includes. (Note: This
// happens before include-picker mapping, so it's still possible to
// see 'new' includes via a manual mapping.)
// B1') Discard macro uses in the same file as the definition (B2 redux).
// B2') Discard macro uses that form a 'backwards' #include (B5 redux).
// B3') Discard macro uses from a 'new' #include (B6 redux).
// B1') Discard macro uses in the same file as the definition (B3 redux).
// B2') Discard macro uses that form a 'backwards' #include (B6 redux).
// B3') Discard macro uses from a 'new' #include (B7 redux).
// Determining 'desired' #includes:
// C1) Get a list of 'effective' direct includes. For most files, it's
@ -1105,7 +1111,8 @@ void ProcessForwardDeclare(OneUse* use,
}
}
// (A5) If using a nested class, discard this use.
// (A5) If using a nested class or a type declared inside a function, discard
// this use.
if (IsNestedClass(tag_decl)) {
// iwyu will require the full type of the parent class when it
// recurses on the qualifier (any use of Foo::Bar requires the
@ -1124,6 +1131,12 @@ void ProcessForwardDeclare(OneUse* use,
use->set_ignore_use();
return;
}
} else if (IsDeclaredInsideFunction(tag_decl)) {
VERRS(6) << "Ignoring fwd-decl use of " << use->symbol_name() << " ("
<< use->PrintableUseLoc() << "): declared inside a "
<< "function\n";
use->set_ignore_use();
return;
}
// (A6) If a definition exists earlier in this file, discard this use.
@ -1143,7 +1156,20 @@ void ProcessForwardDeclare(OneUse* use,
}
}
// (A7) If --no_fwd_decls has been passed, and a decl can be found in one of
// (A7) If any arbitrary redeclaration is marked with IWYU pragma: export,
// reset use as a full use of this decl to keep its containing file included.
if (!use->is_full_use()) {
for (const Decl* redecl : use->decl()->redecls()) {
const auto* decl = cast<NamedDecl>(redecl);
if (preprocessor_info->ForwardDeclareIsExported(decl)) {
use->reset_decl(decl);
use->set_full_use();
break;
}
}
}
// (A8) If --no_fwd_decls has been passed, and a decl can be found in one of
// the headers, suggest that header, and recategorize as a full use. If we can
// only find a decl in this file, it must be a self-sufficent decl being used,
// so we can just let IWYU do its work, and there is no need to recategorize.
@ -1181,6 +1207,16 @@ void ProcessFullUse(OneUse* use,
if (use->ignore_use()) // we're already ignoring it
return;
// (B1) If declaration is inside a function, it can only be seen via said
// function. Discard this use and assume the use of the function provides.
if (IsDeclaredInsideFunction(use->decl())) {
VERRS(6) << "Ignoring full use of " << use->symbol_name() << " ("
<< use->PrintableUseLoc() << "): declared inside a "
<< "function\n";
use->set_ignore_use();
return;
}
// We normally ignore uses for builtins, but when there is a mapping defined
// for the symbol, we should respect that. So, we need to determine whether
// the symbol has any mappings.
@ -1189,7 +1225,7 @@ void ProcessFullUse(OneUse* use,
bool is_builtin_function_with_mappings =
is_builtin_function && HasMapping(use->symbol_name());
// (B1) If the definition is after the use, re-point to a prior decl.
// (B2) If the definition is after the use, re-point to a prior decl.
// If iwyu followed the language precisely, this wouldn't be
// necessary: code wouldn't compile if a full-use didn't have the
// definition handy yet. But in fact, iwyu sometimes requires a full
@ -1222,7 +1258,7 @@ void ProcessFullUse(OneUse* use,
return;
}
// (B2) Discard symbol uses of a symbol defined in the same file it's used.
// (B3) Discard symbol uses of a symbol defined in the same file it's used.
// If the symbol can be declared in multiple places, we count it if
// *any* declaration is in the same file, unless the symbol is a
// class or enum. (Every other kind of redeclarable symbol, such as
@ -1250,7 +1286,7 @@ void ProcessFullUse(OneUse* use,
}
}
// (B3) Discard symbol uses for builtin symbols, including new/delete and
// (B4) Discard symbol uses for builtin symbols, including new/delete and
// template builtins.
if (isa<clang::BuiltinTemplateDecl>(use->decl())) {
VERRS(6) << "Ignoring use of " << use->symbol_name()
@ -1281,7 +1317,7 @@ void ProcessFullUse(OneUse* use,
}
}
// (B4) Discard symbol uses for class members in the same file as parent.
// (B5) Discard symbol uses for class members in the same file as parent.
if (const CXXRecordDecl* parent_decl =
DynCastFrom(use->decl()->getDeclContext())) {
// See if we also recorded a use of the parent.
@ -1319,7 +1355,7 @@ void ProcessFullUse(OneUse* use,
}
}
// (B5) Discard uses of symbols that form a 'backwards' #include.
// (B6) Discard uses of symbols that form a 'backwards' #include.
// This means that we say a.h is using a symbol in b.h, but b.h
// already #includes a.h (either directly or indirectly). Since the
// include graph should be acyclic, this means that iwyu messed up,
@ -1338,7 +1374,7 @@ void ProcessFullUse(OneUse* use,
return;
}
// (B6) In --transitive_includes_only mode, discard 'new' #includes.
// (B7) In --transitive_includes_only mode, discard 'new' #includes.
// In practice, if we tell a.h to add an #include that is not in its
// transitive includes, it's usually (but not always) an iwyu error
// of some sort. So we allow a flag to discard such recommendations.

View File

@ -28,6 +28,7 @@
#include "iwyu_string_util.h"
#include "iwyu_verrs.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Lex/MacroInfo.h"
@ -37,12 +38,12 @@ using clang::FileID;
using clang::MacroDefinition;
using clang::MacroDirective;
using clang::MacroInfo;
using clang::NamedDecl;
using clang::Preprocessor;
using clang::SourceLocation;
using clang::SourceRange;
using clang::Token;
using llvm::errs;
using llvm::Optional;
using llvm::StringRef;
using std::make_pair;
using std::string;
@ -207,7 +208,11 @@ void IwyuPreprocessorInfo::HandlePragmaComment(SourceRange comment_range) {
if (HasOpenBeginExports(this_file_entry)) {
if (MatchOneToken(tokens, "end_exports", 1, begin_loc)) {
ERRSYM(this_file_entry) << "end_exports pragma seen\n";
SourceLocation export_loc_begin = begin_exports_location_stack_.top();
begin_exports_location_stack_.pop();
SourceRange export_range(export_loc_begin, begin_loc);
export_location_ranges_.insert(
std::make_pair(this_file_entry, export_range));
} else {
// No pragma allowed within "begin_exports" - "end_exports"
Warn(begin_loc, "Expected end_exports pragma");
@ -1123,7 +1128,12 @@ bool IwyuPreprocessorInfo::ForwardDeclareIsInhibited(
ContainsKey(*inhibited_forward_declares, normalized_symbol_name);
}
bool IwyuPreprocessorInfo::ForwardDeclareInKeepRange(SourceLocation loc) const {
bool IwyuPreprocessorInfo::ForwardDeclareIsMarkedKeep(
const NamedDecl* decl) const {
// Use end-location so that any trailing comments match only on the last line.
SourceLocation loc = decl->getEndLoc();
// Is the decl part of a begin_keep/end_keep block?
const FileEntry* file = GetFileEntry(loc);
auto keep_ranges = keep_location_ranges_.equal_range(file);
for (auto it = keep_ranges.first; it != keep_ranges.second; ++it) {
@ -1131,7 +1141,26 @@ bool IwyuPreprocessorInfo::ForwardDeclareInKeepRange(SourceLocation loc) const {
return true;
}
}
return false;
// Is the declaration itself marked with trailing comment?
return (LineHasText(loc, "// IWYU pragma: keep") ||
LineHasText(loc, "/* IWYU pragma: keep"));
}
bool IwyuPreprocessorInfo::ForwardDeclareIsExported(
const NamedDecl* decl) const {
// Use end-location so that any trailing comments match only on the last line.
SourceLocation loc = decl->getEndLoc();
// Is the decl part of a begin_exports/end_exports block?
const FileEntry* file = GetFileEntry(loc);
auto export_ranges = export_location_ranges_.equal_range(file);
for (auto it = export_ranges.first; it != export_ranges.second; ++it) {
if (it->second.fullyContains(loc)) {
return true;
}
}
// Is the declaration itself marked with trailing comment?
return (LineHasText(loc, "// IWYU pragma: export") ||
LineHasText(loc, "/* IWYU pragma: export"));
}
} // namespace include_what_you_use

View File

@ -79,6 +79,7 @@
namespace clang {
class FileEntry;
class MacroInfo;
class NamedDecl;
} // namespace clang
namespace include_what_you_use {
@ -168,9 +169,11 @@ class IwyuPreprocessorInfo : public clang::PPCallbacks,
bool ForwardDeclareIsInhibited(
const clang::FileEntry* file, const string& qualified_symbol_name) const;
// Return true if the fwd decl is in the range of a begin_keep -> end_keep
// block.
bool ForwardDeclareInKeepRange(clang::SourceLocation loc) const;
// Return true if the fwd decl is marked with "IWYU pragma: keep".
bool ForwardDeclareIsMarkedKeep(const clang::NamedDecl* decl) const;
// Return true if the fwd decl is marked with "IWYU pragma: export".
bool ForwardDeclareIsExported(const clang::NamedDecl* decl) const;
protected:
// Preprocessor event handlers called by Clang.
@ -365,6 +368,10 @@ class IwyuPreprocessorInfo : public clang::PPCallbacks,
// every keep range.
multimap<const clang::FileEntry*, clang::SourceRange> keep_location_ranges_;
// For processing forward decls. It is a multimap containing the bounds of
// every export range.
multimap<const clang::FileEntry*, clang::SourceRange> export_location_ranges_;
// For processing associated pragma. It is the current open
// "associated" pragma.
clang::SourceLocation associated_pragma_location_;

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 @@
//===--- decl_inside_func-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/decl_inside_func-i1.h"

View File

@ -0,0 +1,34 @@
//===--- decl_inside_func-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.
//
//===----------------------------------------------------------------------===//
// Reduced/derived/copied from Quill 1.6.3:
// https://github.com/odygrd/quill/blob/v1.6.3/quill/include/quill/PatternFormatter.h#L35
// This macro defines a lambda containing a function-local declaration of a
// union X, which is cleverly returned from the lambda.
//
// Declarations inside a function are only directly visible from the function
// itself, so any uses of X should be ignored -- the only way to get in any
// contact with X is to use MACRO, so this header will be desired already.
#define MACRO(str) \
[] { \
union X { \
static constexpr auto value() { \
return str; \
} \
}; \
return X{}; \
}()
/**** IWYU_SUMMARY
(tests/cxx/decl_inside_func-i1.h has correct #includes/fwd-decls)
***** IWYU_SUMMARY */

View File

@ -0,0 +1,41 @@
//===--- decl_inside_func.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.
//
//===----------------------------------------------------------------------===//
// A declaration inside a function can sometimes escape (see implementation of
// MACRO), and while such a declaration may look like it's independently used
// here, the only way to make contact with it is through its containing
// function.
// Since we must have a declaration of _the function_ in order to call it, the
// contained declaration will have followed along and we can ignore any direct
// uses of the contained decl.
// This used to lead to the contained declaration being fwd-decl used, and IWYU
// crashing when trying to format a printable forward-decl. Ignoring it
// completely avoids that malfunction.
// IWYU_ARGS: -I . -Xiwyu --check_also="tests/cxx/decl_inside_func-i1.h"
#include "tests/cxx/decl_inside_func-d1.h"
const char* f() {
// IWYU: MACRO is ...*decl_inside_func-i1.h
return MACRO("bleh").value();
}
/**** IWYU_SUMMARY
tests/cxx/decl_inside_func.cc should add these lines:
#include "tests/cxx/decl_inside_func-i1.h"
tests/cxx/decl_inside_func.cc should remove these lines:
- #include "tests/cxx/decl_inside_func-d1.h" // lines XX-XX
The full include-list for tests/cxx/decl_inside_func.cc:
#include "tests/cxx/decl_inside_func-i1.h" // for MACRO
***** IWYU_SUMMARY */

View File

@ -120,6 +120,27 @@ void ViaMacro(const IndirectTemplate<IndirectClass>& ic) {
IC_CALL_METHOD;
}
// Test type use with member expression inside a template.
template <typename T>
void MemberExprInside() {
T* t = nullptr;
(*t)->Method();
}
class IndirectClass; // IndirectClassPtr doesn't provide IndirectClass.
typedef IndirectClass* IndirectClassPtr;
namespace ns {
using ::IndirectClassPtr;
}
void Fn() {
// IndirectClass is hidden by a pointer and (at least) two levels of sugar.
// IWYU: IndirectClass is...*indirect.h
MemberExprInside<ns::IndirectClassPtr>();
}
/**** IWYU_SUMMARY
tests/cxx/member_expr.cc should add these lines:
@ -127,6 +148,7 @@ tests/cxx/member_expr.cc should add these lines:
tests/cxx/member_expr.cc should remove these lines:
- #include "tests/cxx/direct.h" // lines XX-XX
- class IndirectClass; // lines XX-XX
The full include-list for tests/cxx/member_expr.cc:
#include "tests/cxx/indirect.h" // for IndirectClass, IndirectTemplate

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 */

View File

@ -43,6 +43,12 @@ void ExpressionsBuiltinTypes() {
}
// New- and delete-expressions with user-defined types.
template <typename T>
void TplFnWithDelete(T p) {
delete p;
}
void ExpressionsUserTypes() {
// IWYU: IndirectClass needs a declaration
// IWYU: IndirectClass is...*indirect.h
@ -55,6 +61,11 @@ void ExpressionsUserTypes() {
IndirectClass* arr = new IndirectClass[4];
// IWYU: IndirectClass is...*indirect.h
delete[] arr;
// Hide the pointer type behind "decltype" sugar.
decltype(elem) elem2 = nullptr;
// IWYU: IndirectClass is...*indirect.h
TplFnWithDelete(elem2);
}
// Aligned allocation uses operator new(size_t, std::align_val_t) under the

View File

@ -0,0 +1,14 @@
//===--- pragma_export_fwd-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.
//
//===----------------------------------------------------------------------===//
class FwdDecl3; // IWYU pragma: export
// IWYU pragma: begin_exports
class FwdDecl4;
// IWYU pragma: end_exports

View File

@ -0,0 +1,31 @@
//===--- pragma_export_fwd.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 .
#include "tests/cxx/pragma_export_fwd.h"
// Uses do not trigger warnings as they are already provided for.
void a(const FwdDecl1&);
void b(const FwdDecl2*);
void c(const FwdDecl3&);
void d(const FwdDecl4*);
/**** IWYU_SUMMARY
tests/cxx/pragma_export_fwd.cc should add these lines:
#include "tests/cxx/pragma_export_fwd-d1.h"
tests/cxx/pragma_export_fwd.cc should remove these lines:
The full include-list for tests/cxx/pragma_export_fwd.cc:
#include "tests/cxx/pragma_export_fwd.h"
#include "tests/cxx/pragma_export_fwd-d1.h" // for FwdDecl3, FwdDecl4
***** IWYU_SUMMARY */

View File

@ -0,0 +1,33 @@
//===--- pragma_export_fwd.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.
//
//===----------------------------------------------------------------------===//
// Forward declarations exported from this header are not used here, so it will
// be suggested for removal.
#include "tests/cxx/pragma_export_fwd-d1.h"
// Unused forward declarations in associated header would normally be moved to
// main source file. Make sure they are left alone when exported.
class FwdDecl1; // IWYU pragma: export
// IWYU pragma: begin_exports
class FwdDecl2;
// IWYU pragma: end_exports
/**** IWYU_SUMMARY
tests/cxx/pragma_export_fwd.h should add these lines:
tests/cxx/pragma_export_fwd.h should remove these lines:
- #include "tests/cxx/pragma_export_fwd-d1.h" // lines XX-XX
The full include-list for tests/cxx/pragma_export_fwd.h:
class FwdDecl1; // lines XX-XX
class FwdDecl2; // lines XX-XX
***** IWYU_SUMMARY */

View File

@ -11,6 +11,10 @@
// Tests that sizeof(reference) is treated the same as
// sizeof(underlying_object), like it's supposed to be.
//
// C++ [expr.sizeof]p2:
// When applied to a reference or a reference type,
// the result is the size of the referenced type.
#include <stddef.h>
#include "tests/cxx/direct.h"
@ -76,11 +80,17 @@ SizeofTakingStruct<IndirectClass&> sizeof_taking_struct1;
// IWYU: IndirectClass needs a declaration
SizeofTakingStructRef<IndirectClass> sizeof_taking_struct2;
// Not sure why, but C++ doesn't require full type of IndirectClass here.
// sizeof(IndirectTemplateStruct<IndirectClass&>) doesn't require IndirectClass
// full type because IndirectTemplateStruct<IndirectClass&> stores just
// a pointer, in fact. Hence, its size doesn't depend on IndirectClass size.
// C++ [expr.sizeof]p2:
// When applied to a class, the result is the number of bytes in an object of
// that class...
// IWYU: IndirectClass needs a declaration
SizeofTakingStructTpl<IndirectClass&> sizeof_taking_struct3;
// Not sure why, but C++ doesn't require full type of IndirectClass here.
// sizeof(IndirectTemplateStruct<IndirectClass&>) doesn't require IndirectClass
// full type.
// IWYU: IndirectClass needs a declaration
SizeofTakingStructTplRef<IndirectClass> sizeof_taking_struct4;
@ -88,10 +98,27 @@ SizeofTakingStructTplRef<IndirectClass> sizeof_taking_struct4;
// IWYU: IndirectClass needs a declaration
SizeofTakingStructTplRef2<IndirectClass> sizeof_taking_struct5;
// Not sure why, but C++ doesn't require full type of IndirectClass here.
// sizeof(IndirectTemplateStruct<IndirectClass&>) doesn't require IndirectClass
// full type.
// IWYU: IndirectClass needs a declaration
SizeofTakingStructTplRef2<IndirectClass&> sizeof_taking_struct6;
// The same with some sugar.
// IWYU: IndirectClass is...*indirect.h
SizeofTakingStruct<decltype(ref)> sizeof_taking_struct7;
// IWYU: IndirectClass is...*indirect.h
SizeofTakingStructRef<decltype(dummy)> sizeof_taking_struct8;
SizeofTakingStructTpl<decltype(ref)> sizeof_taking_struct9;
SizeofTakingStructTplRef<decltype(dummy)> sizeof_taking_struct10;
// IWYU: IndirectClass is...*indirect.h
SizeofTakingStructTplRef2<decltype(dummy)> sizeof_taking_struct11;
SizeofTakingStructTplRef2<decltype(ref)> sizeof_taking_struct12;
/**** IWYU_SUMMARY