blob: 2101b72a1d83fcd906507039771e73673807bdcc [file] [log] [blame]
//===--- iwyu_preprocessor.h - handle #includes/#defines for iwyu ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// The class that gets preprocessor callbacks: #includes, #defines,
// #pragmas, and the like.
//
// It has three main tasks in iwyu:
//
// 1) Record the #include hierarchy. For each file it sees, it creates
// an IwyuOutput class for that file, which is set up to hold the direct
// includes that the file has.
//
// 2) Record iwyu violations for macro uses. In particular, if a macro
// is #defined in one file and the token is accessed in another, do an
// iwyu check on that use.
//
// 3) Process iwyu pragma-like constructs. Comments beginning "//"
// are allowed to follow any pragma, otherwise extraneous text on the
// line will result in an error message being logged. Here are the
// constructs we look for:
// Full-line constructs:
// a) // IWYU pragma: private, include "foo/bar/baz.h"
// b) // IWYU pragma: private
// c) // IWYU pragma: begin_exports
// d) // IWYU pragma: end_exports
// e) // IWYU pragma: no_include "foo/bar/baz.h"
// f) // IWYU pragma: no_forward_declare foo::Bar
// g) // IWYU pragma: friend <regexp>
// // IWYU pragma: friend "<regexp>" -- needed if spaces in regexp.
// 'Annotation' constructs:
// h) #include "foo/bar/baz.h" // IWYU pragma: export
// i) #include "foo/bar/baz.h" // IWYU pragma: keep
//
// 4) Process doxygen @headername directives. In later versions of GCC,
// these directives are like IWYU pragma private directives:
// @headername{foo} means to include <foo> instead.
// The arguments are allowed to be a comma-separated list.
// See
// http://gcc.gnu.org/onlinedocs/libstdc++/manual/documentation_hacking.html
//
// This class finishes its processing before the 'main' iwyu
// processing is done, so other iwyu consumers can access the main
// outputs of this class:
// * The map from include-name to FileEntry*.
// * The map from FileEntry* to its IwyuFileInfo object.
// * TODO(csilvers): Information about direct includes of a FileEntry*
// * The 'intends to provide' map, which encapsulates some
// of the information about public vs private headers.
// * Testing and reporting membership in the main compilation unit.
#ifndef DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_IWYU_PREPROCESSOR_H_
#define DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_IWYU_PREPROCESSOR_H_
#include <map> // for map
#include <set> // for set
#include <stack> // for stack
#include <string> // for string
#include <utility> // for pair
#include <vector> // for vector
#include "iwyu_output.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/Token.h"
namespace clang {
class FileEntry;
class MacroInfo;
}
namespace include_what_you_use {
using std::map;
using std::pair;
using std::set;
using std::stack;
using std::string;
using std::vector;
class IwyuPreprocessorInfo : public clang::PPCallbacks,
public clang::CommentHandler {
public:
IwyuPreprocessorInfo() : main_file_(NULL),
empty_file_info_(NULL, this, "") {}
// The client *must* call this from the beginning of HandleTranslationUnit()
void HandlePreprocessingDone();
// More direct ways of getting at this information
const clang::FileEntry* main_file() const {
return main_file_;
}
const set<const clang::FileEntry*>* files_to_report_iwyu_violations_for()
const {
return &files_to_report_iwyu_violations_for_;
}
// Given a quoted include like '<vector>', or '"ads/base.h"',
// returns the FileEntry for that file, or NULL if none is
// found. If multiple files are included under the same
// quoted-include name (which can happen via #include-next),
// one is returned arbitrarily. (But always the same one.)
const clang::FileEntry* IncludeToFileEntry(const string quoted_include) const;
// Returns an IwyuFileInfo struct (from iwyu_output.h) corresponding
// to the given file, or NULL if no such struct can be found.
// Note this is a const method that returns a non-const pointer.
// Be careful if using this method in threaded contexts.
IwyuFileInfo* FileInfoFor(const clang::FileEntry* file) const;
// Instead of returning NULL if no file info can be found, returns
// an empty IwyuFileInfo struct.
const IwyuFileInfo& FileInfoOrEmptyFor(const clang::FileEntry* file) const;
// For every file we've seen (that is, that we've #included),
// returns what files it 'intends' to provide full type information
// for. The motivation is that a file like <vector> #includes
// <memory> and doesn't expect you to have to, even though
// technically it's required whenever you create a vector<Foo>,
// which is really vector<Foo, alloc<Foo> >. We say you don't have
// to #include <memory> because vector intends to provide the full
// types from <memory> for you.
// The rule we use is every file intends to provide full type
// information for the files it directly includes. For public
// header files -- ones that the include-picker can map another file
// to -- we relax the rule to say the public header intends to
// provide *every* header file behind it, direct or no. This scheme
// isn't perfect -- it says <map> intends to provide the full type
// of pair<>, when really it just uses it internally -- but it's a
// reasonable heuristic.
// Returns true iff our analysis shows that public_header intends
// to provide all the symbols in other_file.
bool PublicHeaderIntendsToProvide(const clang::FileEntry* public_header,
const clang::FileEntry* other_file) const;
// Returns true if the first file directly or indirectly includes
// the second.
bool FileTransitivelyIncludes(const clang::FileEntry* includer,
const clang::FileEntry* includee) const;
bool FileTransitivelyIncludes(const clang::FileEntry* includer,
const string& quoted_includee) const;
// This seems like a weird way to call this function, but we
// happen to need this in iwyu_output.cc:
bool FileTransitivelyIncludes(const string& quoted_includer,
const clang::FileEntry* includee) const;
// Return true if the given file has
// "// IWYU pragma: no_include <other_filename>".
bool IncludeIsInhibited(const clang::FileEntry* file,
const string& other_filename) const;
// Return true if the given file has
// "// IWYU pragma: no_forward_declare <qualified_symbol_name>".
bool ForwardDeclareIsInhibited(
const clang::FileEntry* file, const string& qualified_symbol_name) const;
protected:
// Preprocessor event handlers called by Clang.
virtual void MacroExpands(const clang::Token& id,
const clang::MacroInfo* macro,
clang::SourceRange range);
virtual void MacroDefined(const clang::Token& id,
const clang::MacroInfo* macro);
// Not needed for iwyu:
// virtual void MacroUndefined(const clang::Token&, const clang::MacroInfo*);
virtual void If(clang::SourceLocation loc,
clang::SourceRange condition_range);
virtual void Elif(clang::SourceLocation loc,
clang::SourceRange condition_range,
clang::SourceLocation if_loc);
virtual void Ifdef(clang::SourceLocation loc, const clang::Token& id);
virtual void Ifndef(clang::SourceLocation loc, const clang::Token& id);
// Not needed for iwyu:
// virtual void Else();
// virtual void Endif();
virtual void InclusionDirective(clang::SourceLocation hash_loc,
const clang::Token& include_token,
llvm::StringRef filename,
bool is_angled,
clang::CharSourceRange filename_range,
const clang::FileEntry* file,
llvm::StringRef search_path,
llvm::StringRef relative_path,
const clang::Module* imported);
virtual void FileChanged(clang::SourceLocation loc, FileChangeReason reason,
clang::SrcMgr::CharacteristicKind file_type,
clang::FileID PrevFID);
virtual void FileSkipped(const clang::FileEntry& file,
const clang::Token &filename,
clang::SrcMgr::CharacteristicKind file_type);
// FileChanged is actually a multi-plexer for 4 different callbacks.
void FileChanged_EnterFile(clang::SourceLocation file_beginning);
void FileChanged_ExitToFile(clang::SourceLocation include_loc,
clang::FileID exiting_from);
void FileChanged_RenameFile(clang::SourceLocation new_file);
void FileChanged_SystemHeaderPragma(clang::SourceLocation loc);
// CommentHandler callback.
// Clang doc: The handler shall return true if it has pushed any
// tokens to be read using e.g. EnterToken or EnterTokenStream.
virtual bool HandleComment(clang::Preprocessor& pp,
clang::SourceRange comment_range);
private:
// Returns true if includee is considered part of the main
// compilation unit. We always generate warnings for violations in
// files are part of the main compilation unit.
bool BelongsToMainCompilationUnit(const clang::FileEntry* includer,
const clang::FileEntry* includee) const;
// Creates a new iwyu_file_info_map_[file_entry] if it doesn't exist,
// or a noop otherwise. quoted_include_name is used to create the
// new entry if necessary.
void InsertIntoFileInfoMap(const clang::FileEntry* file,
const string& quoted_include_name);
// Helper function that returns iwyu_file_info_map_[file_entry] if
// it already exists, or creates a new one and returns it otherwise.
// If it creates a new one, it generates the quoted_include_name
// from the file-path for 'file'.
// TODO(csilvers): see if, in practice, all uses in here are just 'get's.
IwyuFileInfo* GetFromFileInfoMap(const clang::FileEntry* file);
// Helper for AddDirectInclude. Checks if we should protect the
// #include from iwyu removal.
void MaybeProtectInclude(clang::SourceLocation includer_loc,
const clang::FileEntry* includee,
const string& include_name_as_typed);
// Called whenever an #include is seen in the preprocessor output.
void AddDirectInclude(clang::SourceLocation includer_loc,
const clang::FileEntry* includee,
const string& include_name_as_typed);
// Report a "begin_exports"/"end_exports" pragma pair.
// begin_line is first line, end_line is just after the last line.
void AddExportedRange(const clang::FileEntry* file,
int begin_line, int end_line);
// Determine if the comment is a pragma, and if so, process it.
void HandlePragmaComment(clang::SourceRange comment_range);
// Process @headername directives in a file.
void ProcessHeadernameDirectivesInFile(clang::SourceLocation file_beginning);
// Checks whether it's OK to use the given macro defined in file defined_in.
void ReportMacroUse(const string& name,
clang::SourceLocation usage_location,
clang::SourceLocation dfn_location);
// As above, but get the definition location from macros_definition_loc_.
void FindAndReportMacroUse(const string& name, clang::SourceLocation loc);
void CheckIfOrElif(clang::SourceRange range);
// Final-processing routines done after all header files have been read.
void DoFinalMacroChecks();
// Helper for PopulateIntendsToProvideMap().
void AddAllIncludesAsFileEntries(const clang::FileEntry* includer,
set<const clang::FileEntry*>* retval) const;
void PopulateIntendsToProvideMap();
void PopulateTransitiveIncludeMap();
// Return true if at the current point in the parse of the given file,
// there is a pending "begin_exports" pragma.
bool HasOpenBeginExports(const clang::FileEntry* file_entry) const;
// The C++ source file passed in as an argument to the compiler (as
// opposed to other files seen via #includes).
const clang::FileEntry* main_file_;
// All files that we should report iwyu violations in. It defaults
// to the "main compilation unit" (e.g. foo.cc, foo.h, foo-inl.h):
// main_file_ and its associated .h and -inl.h files, if they exist.
// But users can add to it via the --check_also flag.
set<const clang::FileEntry*> files_to_report_iwyu_violations_for_;
// These store macros seen, as we see them, and also macros that are
// called from other macros. We use this to do limited iwyu-testing
// on macro tokens (we'd love to test macro bodies more completely
// -- like we do template bodies -- but macros don't give us enough
// context to know how to interpret the tokens we see, in general).
map<string, clang::SourceLocation> macros_definition_loc_; // key: macro name
// This should logically be a set, but set<> needs Token::operator<
// which we don't have. Luckily, a vector works just as well.
vector<clang::Token> macros_called_from_macros_;
// This maps from the include-name as typed in the program
// (including <>'s or ""'s) to the FileEntry we loaded for that
// #include.
map<string, const clang::FileEntry*> include_to_fileentry_map_;
map<const clang::FileEntry*, IwyuFileInfo> iwyu_file_info_map_;
// How many #include lines we've encountered from the given file.
map<const clang::FileEntry*, int> num_includes_seen_;
// Maps from a FileEntry* to all files that this file "intends" to
// provide the symbols from. For now, we say a file intentionally
// provides a symbol if it defines it, or if any file it directly
// #includes defines it. However, if the header is a private header
// -- as determined by the fact it's in the private->public header
// map -- we relax the second requirement to allow any file directly
// or indirectly included by the public file. This isn't perfect,
// but is as close as we can be to matching the intent of the author
// of the public/private system.
map<const clang::FileEntry*,
set<const clang::FileEntry*> > intends_to_provide_map_;
// Maps from a FileEntry* to all the files that this file includes,
// either directly or indirectly.
map<const clang::FileEntry*,
set<const clang::FileEntry*> > transitive_include_map_;
// Maps from a FileEntry* to the quoted names of files that its file
// is directed *not* to include via the "no_include" pragma.
map<const clang::FileEntry*, set<string> > no_include_map_;
// Maps from a FileEntry* to the qualified names of symbols that its
// file is directed *not* to forward-declare via the
// "no_forward_declare" pragma.
map<const clang::FileEntry*, set<string> > no_forward_declare_map_;
const IwyuFileInfo empty_file_info_;
// For processing pragmas. It is the current stack of open
// "begin_exports". There should be at most one item in this stack
// per file in the current inclusion chain..
stack<clang::SourceLocation> begin_exports_location_stack_;
// Filename spelling location in the last encountered inclusion directive.
// Should be used only in FileChanged_EnterFile, FileSkipped when
// corresponding callback is caused by inclusion directive. Don't use in
// other places because it is unclear which inclusion directive filename
// location corresponds to.
clang::SourceLocation include_filename_loc_;
};
} // namespace include_what_you_use
#endif // DEVTOOLS_MAINTENANCE_INCLUDE_WHAT_YOU_USE_IWYU_PREPROCESSOR_H_