Add --prefix_header_includes option (fixes issue #112).

--prefix_header_includes controls presence of includes and forward declarations
involving files included via command-line option -include. Issue is reported by
Max Dyckhoff.
This commit is contained in:
Volodymyr Sapsai 2013-12-15 12:33:18 +00:00
parent c234879b6a
commit d30ef5a05f
14 changed files with 362 additions and 12 deletions

View File

@ -68,6 +68,16 @@ static void PrintHelp(const char* extra_msg) {
" --transitive_includes_only: do not suggest that a file add\n"
" foo.h unless foo.h is already visible in the file's\n"
" transitive includes.\n"
" --prefix_header_includes=<value>: tells iwyu what to do with\n"
" in-source includes and forward declarations involving\n"
" prefix headers. Prefix header is a file included via\n"
" command-line option -include. If prefix header makes\n"
" include or forward declaration obsolete, presence of such\n"
" include can be controlled with the following values\n"
" add: new lines are added\n"
" keep: new lines aren't added, existing are kept intact\n"
" remove: new lines aren't added, existing are removed\n"
" Default value is 'add'.\n"
" --verbose=<level>: the higher the level, the more output.\n");
if (extra_msg)
printf("\n%s\n\n", extra_msg);
@ -79,7 +89,8 @@ CommandlineFlags::CommandlineFlags()
cwd(""),
transitive_includes_only(false),
verbose(getenv("IWYU_VERBOSE") ? atoi(getenv("IWYU_VERBOSE")) : 1),
no_default_mappings(false) {
no_default_mappings(false),
prefix_header_include_policy(CommandlineFlags::kAdd) {
}
int CommandlineFlags::ParseArgv(int argc, char** argv) {
@ -92,6 +103,7 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) {
{"verbose", required_argument, NULL, 'v'},
{"mapping_file", required_argument, NULL, 'm'},
{"no_default_mappings", no_argument, NULL, 'n'},
{"prefix_header_includes", required_argument, NULL, 'x'},
{0, 0, 0, 0}
};
static const char shortopts[] = "d::p:v:c:m:n";
@ -105,6 +117,18 @@ int CommandlineFlags::ParseArgv(int argc, char** argv) {
case 'v': verbose = atoi(optarg); break;
case 'm': mapping_files.push_back(optarg); break;
case 'n': no_default_mappings = true; break;
case 'x':
if (strcmp(optarg, "add") == 0) {
prefix_header_include_policy = CommandlineFlags::kAdd;
} else if (strcmp(optarg, "keep") == 0) {
prefix_header_include_policy = CommandlineFlags::kKeep;
} else if (strcmp(optarg, "remove") == 0) {
prefix_header_include_policy = CommandlineFlags::kRemove;
} else {
PrintHelp("FATAL ERROR: unknown --prefix_header_includes value.");
exit(1);
}
break;
case -1: return optind; // means 'no more input'
default: PrintHelp("FATAL ERROR: unknown flag."); exit(1); break;
}

View File

@ -65,6 +65,7 @@ void InitGlobalsAndFlagsForTesting();
// 10: like 7, and add tons more debug info (for all non-system header files).
// 11: like 10, and add tons *more* debug info (for all header files).
struct CommandlineFlags {
enum PrefixHeaderIncludePolicy { kAdd, kKeep, kRemove };
CommandlineFlags(); // sets flags to default values
int ParseArgv(int argc, char** argv); // parses flags from argv
static const char kUnspecified[]; // for -d, which takes an optional arg
@ -76,6 +77,8 @@ struct CommandlineFlags {
int verbose; // -v: how much information to emit as we parse
vector<string> mapping_files; // -m: mapping files
bool no_default_mappings; // -n: no default mappings
// Policy regarding files included via -include option. No short option.
PrefixHeaderIncludePolicy prefix_header_include_policy;
};
const CommandlineFlags& GlobalFlags();

View File

@ -365,7 +365,7 @@ OneIncludeOrForwardDeclareLine::OneIncludeOrForwardDeclareLine(
: line_(internal::MungedForwardDeclareLine(fwd_decl)),
start_linenum_(-1), end_linenum_(-1), // set 'for real' below
is_desired_(false), is_present_(false), symbol_counts_(),
quoted_include_(), fwd_decl_(fwd_decl) {
quoted_include_(), included_file_(NULL), fwd_decl_(fwd_decl) {
const SourceRange decl_lines = GetSourceRangeOfClassDecl(fwd_decl);
// We always want to use the instantiation line numbers: for code like
// FORWARD_DECLARE_CLASS(MyClass);
@ -375,11 +375,12 @@ OneIncludeOrForwardDeclareLine::OneIncludeOrForwardDeclareLine(
}
OneIncludeOrForwardDeclareLine::OneIncludeOrForwardDeclareLine(
const string& quoted_include, int linenum)
const FileEntry* included_file, const string& quoted_include, int linenum)
: line_("#include " + quoted_include),
start_linenum_(linenum), end_linenum_(linenum),
is_desired_(false), is_present_(false), symbol_counts_(),
quoted_include_(quoted_include), fwd_decl_(NULL) {
quoted_include_(quoted_include), included_file_(included_file),
fwd_decl_(NULL) {
}
bool OneIncludeOrForwardDeclareLine::HasSymbolUse(const string& symbol_name)
@ -410,6 +411,7 @@ IwyuFileInfo::IwyuFileInfo(const clang::FileEntry* this_file,
: file_(this_file),
preprocessor_info_(preprocessor_info),
quoted_file_(quoted_include_name),
is_prefix_header_(false),
internal_headers_(),
symbol_uses_(),
lines_(),
@ -428,7 +430,8 @@ void IwyuFileInfo::AddInternalHeader(const IwyuFileInfo* other) {
void IwyuFileInfo::AddInclude(const clang::FileEntry* includee,
const string& quoted_includee, int linenumber) {
OneIncludeOrForwardDeclareLine new_include(quoted_includee, linenumber);
OneIncludeOrForwardDeclareLine new_include(includee, quoted_includee,
linenumber);
new_include.set_present();
// It's possible for the same #include to be seen multiple times
@ -1399,8 +1402,8 @@ void CalculateDesiredIncludesAndForwardDeclares(
if (use->is_full_use()) {
CHECK_(use->has_suggested_header() && "Full uses should have #includes");
if (!ContainsKey(include_map, use->suggested_header())) { // must be added
lines->push_back(OneIncludeOrForwardDeclareLine(use->suggested_header(),
-1));
lines->push_back(OneIncludeOrForwardDeclareLine(
GetFileEntry(use->decl()), use->suggested_header(), -1));
include_map[use->suggested_header()] = lines->size() - 1;
}
const int index = include_map[use->suggested_header()];
@ -1451,6 +1454,49 @@ void CalculateDesiredIncludesAndForwardDeclares(
}
}
bool IsPrefixHeader(const FileEntry* file_entry,
const IwyuPreprocessorInfo* preprocessor_info) {
if (file_entry) {
IwyuFileInfo* file_info = preprocessor_info->FileInfoFor(file_entry);
if (file_info)
return file_info->is_prefix_header();
}
return false;
}
void CleanupPrefixHeaderIncludes(
const IwyuPreprocessorInfo* preprocessor_info,
vector<OneIncludeOrForwardDeclareLine>* lines) {
CommandlineFlags::PrefixHeaderIncludePolicy policy =
GlobalFlags().prefix_header_include_policy;
if (policy == CommandlineFlags::kAdd)
return;
for (vector<OneIncludeOrForwardDeclareLine>::iterator it = lines->begin();
it != lines->end(); ++it) {
if (!it->is_desired())
continue;
if (it->is_present() && (policy == CommandlineFlags::kKeep))
continue; // Keep present line according to policy.
const FileEntry* file_entry = NULL;
if (it->IsIncludeLine()) {
file_entry = it->included_file();
CHECK_(file_entry && "Valid file_entry is expected");
} else {
const RecordDecl* dfn = GetDefinitionForClass(it->fwd_decl());
file_entry = GetFileEntry(dfn);
}
if (IsPrefixHeader(file_entry, preprocessor_info)) {
CHECK_(file_entry && "FileEntry should exist to be prefix header");
it->clear_desired();
VERRS(6) << "Ignoring '" << it->line()
<< "': is superseded by command line include "
<< file_entry->getName() << "\n";
}
}
}
// Used by GetSymbolsSortedByFrequency().
class CountGt {
public:
@ -1650,6 +1696,8 @@ int IwyuFileInfo::CalculateAndReportIwyuViolations() {
}
}
internal::CleanupPrefixHeaderIncludes(preprocessor_info_, &lines_);
EmitDiffs(lines_);
return retval;
}

View File

@ -106,7 +106,8 @@ class OneUse {
class OneIncludeOrForwardDeclareLine {
public:
explicit OneIncludeOrForwardDeclareLine(const clang::NamedDecl* fwd_decl);
OneIncludeOrForwardDeclareLine(const string& quoted_include, int linenum);
OneIncludeOrForwardDeclareLine(const clang::FileEntry* included_file,
const string& quoted_include, int linenum);
const string& line() const { return line_; }
bool IsIncludeLine() const; // vs forward-declare line
@ -119,9 +120,15 @@ class OneIncludeOrForwardDeclareLine {
CHECK_(!fwd_decl_ && "quoted_include and fwd_decl are mutually exclusive");
return quoted_include_;
}
const clang::FileEntry* included_file() const {
CHECK_(IsIncludeLine() && "Must call included_file() on include lines");
CHECK_(!fwd_decl_ && "included_file and fwd_decl are mutually exclusive");
return included_file_;
}
const clang::NamedDecl* fwd_decl() const {
CHECK_(!IsIncludeLine() && "Must call fwd_decl() on forward-declare lines");
CHECK_(quoted_include_.empty() && "quoted_include and fwd_decl don't mix");
CHECK_(quoted_include_.empty() && !included_file_ &&
"quoted_include and fwd_decl don't mix");
return fwd_decl_;
}
@ -145,9 +152,11 @@ class OneIncludeOrForwardDeclareLine {
bool is_desired_; // IWYU will recommend this line
bool is_present_; // line was present before the IWYU run
map<string, int> symbol_counts_; // how many times we referenced each symbol
// Only one of the following two is ever set a given line.
string quoted_include_; // the file we're including, for includes
const clang::NamedDecl* fwd_decl_; // or the fwd-decl we're emitting
// Only either two following members are set for includes
string quoted_include_; // quoted file name we're including
const clang::FileEntry* included_file_; // the file we're including
// ...or this member is set for the fwd-decl we're emitting.
const clang::NamedDecl* fwd_decl_;
};
@ -165,6 +174,9 @@ class IwyuFileInfo {
const IwyuPreprocessorInfo* preprocessor_info,
const string& quoted_include_name);
bool is_prefix_header() const { return is_prefix_header_; }
void set_prefix_header() { is_prefix_header_ = true; }
// An 'internal' header is a header that this file #includes
// (possibly indirectly) that we should treat as being logically
// part of this file. In particular, when computing the direct
@ -262,6 +274,9 @@ class IwyuFileInfo {
string quoted_file_;
// Prefix header means included from command line via -include option.
bool is_prefix_header_;
// internal_headers_ are the files 'associated' with this file: if
// this file is foo.cc, internal_headers_ are the IwyuFileInfo's for
// foo.h and foo-inl.h, if present.

View File

@ -694,6 +694,23 @@ void IwyuPreprocessorInfo::FileChanged_EnterFile(
if (ShouldReportIWYUViolationsFor(new_file)) {
files_to_report_iwyu_violations_for_.insert(new_file);
}
// Mark is_prefix_header.
CHECK_(new_file && "is_prefix_header is applicable to usual files only");
IwyuFileInfo *includee_file_info = GetFromFileInfoMap(new_file);
const FileEntry* includer_file = GetFileEntry(include_loc);
bool is_prefix_header = false;
if (includer_file) {
// File included from another prefix header file is prefix header too.
IwyuFileInfo *includer_file_info = GetFromFileInfoMap(includer_file);
is_prefix_header = includer_file_info->is_prefix_header();
} else {
// Files included from command line are prefix headers, unless it's the
// main file.
is_prefix_header = (new_file != main_file_);
}
if (is_prefix_header)
includee_file_info->set_prefix_header();
}
// Called when done with an #included file and returning to the parent file.

View File

@ -63,11 +63,21 @@ class OneIwyuTest(unittest.TestCase):
'--transitive_includes_only'],
'no_h_includes_cc.cc': [CheckAlsoExtension('.c')],
'overloaded_class.cc': [CheckAlsoExtension('-i1.h')],
'prefix_header_includes_add.cc': ['--prefix_header_includes=add'],
'prefix_header_includes_keep.cc': ['--prefix_header_includes=keep'],
'prefix_header_includes_remove.cc': ['--prefix_header_includes=remove'],
}
prefix_headers = ['-include', 'tests/prefix_header_includes-d1.h',
'-include', 'tests/prefix_header_includes-d2.h',
'-include', 'tests/prefix_header_includes-d3.h',
'-include', 'tests/prefix_header_includes-d4.h']
clang_flags_map = {
'auto_type_within_template.cc': ['-std=c++11'],
'conversion_ctor.cc': ['-std=c++11'],
'ms_inline_asm.cc': ['-fms-extensions'],
'prefix_header_includes_add.cc': prefix_headers,
'prefix_header_includes_keep.cc': prefix_headers,
'prefix_header_includes_remove.cc': prefix_headers,
}
# Internally, we like it when the paths start with TEST_ROOTDIR.
self._iwyu_flags_map = dict((os.path.join(TEST_ROOTDIR, k), v)

View File

@ -0,0 +1,15 @@
//===--- prefix_header_includes-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.
//
//===----------------------------------------------------------------------===//
#ifndef DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D1_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D1_H_
class CommandLineIncludeD1 {};
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D1_H_

View File

@ -0,0 +1,17 @@
//===--- prefix_header_includes-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.
//
//===----------------------------------------------------------------------===//
#ifndef DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D2_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D2_H_
#include "tests/prefix_header_includes-i1.h"
class CommandLineIncludeD2 {};
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D2_H_

View File

@ -0,0 +1,15 @@
//===--- prefix_header_includes-d3.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.
//
//===----------------------------------------------------------------------===//
#ifndef DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D3_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D3_H_
class CommandLineIncludeD3 {};
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D3_H_

View File

@ -0,0 +1,15 @@
//===--- prefix_header_includes-d4.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.
//
//===----------------------------------------------------------------------===//
#ifndef DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D4_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D4_H_
class CommandLineIncludeD4 {};
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_D4_H_

View File

@ -0,0 +1,15 @@
//===--- prefix_header_includes-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.
//
//===----------------------------------------------------------------------===//
#ifndef DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_I1_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_I1_H_
class CommandLineIncludeI1 {};
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_TESTS_PREFIX_HEADER_INCLUDES_I1_H_

View File

@ -0,0 +1,56 @@
//===--- prefix_header_includes_add.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.
//
//===----------------------------------------------------------------------===//
// Tests --prefix_header_includes option. All prefix_header_includes_*.cc files
// are the same to show the difference between --prefix_header_includes values.
#include "tests/direct.h"
#include "tests/prefix_header_includes-d1.h"
// Included in source code and via command line option.
CommandLineIncludeD1 cli_d1;
// Included via command line option only.
// IWYU: CommandLineIncludeD2 is...*prefix_header_includes-d2.h
CommandLineIncludeD2 cli_d2;
// Forward declared in source code and included via command line option.
class CommandLineIncludeD3;
CommandLineIncludeD3* cli_d3_ptr;
// Included via command line option only.
// IWYU: CommandLineIncludeD4 needs a declaration
CommandLineIncludeD4* cli_d4_ptr;
// Test that is_prefix_header property is preserved for indirect includes.
// IWYU: CommandLineIncludeI1 is...*prefix_header_includes-i1.h
CommandLineIncludeI1 cli_i1;
// Test not prefix header include.
// IWYU: IndirectClass is...*indirect.h
IndirectClass ic;
/**** IWYU_SUMMARY
tests/prefix_header_includes_add.cc should add these lines:
#include "tests/indirect.h"
#include "tests/prefix_header_includes-d2.h"
#include "tests/prefix_header_includes-i1.h"
class CommandLineIncludeD4;
tests/prefix_header_includes_add.cc should remove these lines:
- #include "tests/direct.h" // lines XX-XX
The full include-list for tests/prefix_header_includes_add.cc:
#include "tests/indirect.h" // for IndirectClass
#include "tests/prefix_header_includes-d1.h" // for CommandLineIncludeD1
#include "tests/prefix_header_includes-d2.h" // for CommandLineIncludeD2
#include "tests/prefix_header_includes-i1.h" // for CommandLineIncludeI1
class CommandLineIncludeD3; // lines XX-XX
class CommandLineIncludeD4;
***** IWYU_SUMMARY */

View File

@ -0,0 +1,50 @@
//===--- prefix_header_includes_keep.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.
//
//===----------------------------------------------------------------------===//
// Tests --prefix_header_includes option. All prefix_header_includes_*.cc files
// are the same to show the difference between --prefix_header_includes values.
#include "tests/direct.h"
#include "tests/prefix_header_includes-d1.h"
// Included in source code and via command line option.
CommandLineIncludeD1 cli_d1;
// Included via command line option only.
// IWYU: CommandLineIncludeD2 is...*prefix_header_includes-d2.h
CommandLineIncludeD2 cli_d2;
// Forward declared in source code and included via command line option.
class CommandLineIncludeD3;
CommandLineIncludeD3* cli_d3_ptr;
// Included via command line option only.
// IWYU: CommandLineIncludeD4 needs a declaration
CommandLineIncludeD4* cli_d4_ptr;
// Test that is_prefix_header property is preserved for indirect includes.
// IWYU: CommandLineIncludeI1 is...*prefix_header_includes-i1.h
CommandLineIncludeI1 cli_i1;
// Test not prefix header include.
// IWYU: IndirectClass is...*indirect.h
IndirectClass ic;
/**** IWYU_SUMMARY
tests/prefix_header_includes_keep.cc should add these lines:
#include "tests/indirect.h"
tests/prefix_header_includes_keep.cc should remove these lines:
- #include "tests/direct.h" // lines XX-XX
The full include-list for tests/prefix_header_includes_keep.cc:
#include "tests/indirect.h" // for IndirectClass
#include "tests/prefix_header_includes-d1.h" // for CommandLineIncludeD1
class CommandLineIncludeD3; // lines XX-XX
***** IWYU_SUMMARY */

View File

@ -0,0 +1,50 @@
//===--- prefix_header_includes_remove.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.
//
//===----------------------------------------------------------------------===//
// Tests --prefix_header_includes option. All prefix_header_includes_*.cc files
// are the same to show the difference between --prefix_header_includes values.
#include "tests/direct.h"
#include "tests/prefix_header_includes-d1.h"
// Included in source code and via command line option.
CommandLineIncludeD1 cli_d1;
// Included via command line option only.
// IWYU: CommandLineIncludeD2 is...*prefix_header_includes-d2.h
CommandLineIncludeD2 cli_d2;
// Forward declared in source code and included via command line option.
class CommandLineIncludeD3;
CommandLineIncludeD3* cli_d3_ptr;
// Included via command line option only.
// IWYU: CommandLineIncludeD4 needs a declaration
CommandLineIncludeD4* cli_d4_ptr;
// Test that is_prefix_header property is preserved for indirect includes.
// IWYU: CommandLineIncludeI1 is...*prefix_header_includes-i1.h
CommandLineIncludeI1 cli_i1;
// Test not prefix header include.
// IWYU: IndirectClass is...*indirect.h
IndirectClass ic;
/**** IWYU_SUMMARY
tests/prefix_header_includes_remove.cc should add these lines:
#include "tests/indirect.h"
tests/prefix_header_includes_remove.cc should remove these lines:
- #include "tests/direct.h" // lines XX-XX
- #include "tests/prefix_header_includes-d1.h" // lines XX-XX
- class CommandLineIncludeD3; // lines XX-XX
The full include-list for tests/prefix_header_includes_remove.cc:
#include "tests/indirect.h" // for IndirectClass
***** IWYU_SUMMARY */