Extend the preprocessing record and libclang with support for

inclusion directives, keeping track of every #include, #import,
etc. in the translation unit. We keep track of the source location and
kind of the inclusion, how the file name was spelled, and the
underlying file to which the inclusion resolved.

llvm-svn: 116952
This commit is contained in:
Douglas Gregor 2010-10-20 22:00:55 +00:00
parent 2edaa2fb24
commit 796d76a663
19 changed files with 342 additions and 46 deletions

View File

@ -1190,8 +1190,9 @@ enum CXCursorKind {
CXCursor_PreprocessingDirective = 500, CXCursor_PreprocessingDirective = 500,
CXCursor_MacroDefinition = 501, CXCursor_MacroDefinition = 501,
CXCursor_MacroInstantiation = 502, CXCursor_MacroInstantiation = 502,
CXCursor_InclusionDirective = 503,
CXCursor_FirstPreprocessing = CXCursor_PreprocessingDirective, CXCursor_FirstPreprocessing = CXCursor_PreprocessingDirective,
CXCursor_LastPreprocessing = CXCursor_MacroInstantiation CXCursor_LastPreprocessing = CXCursor_InclusionDirective
}; };
/** /**
@ -1465,6 +1466,12 @@ CINDEX_LINKAGE void clang_getOverriddenCursors(CXCursor cursor,
*/ */
CINDEX_LINKAGE void clang_disposeOverriddenCursors(CXCursor *overridden); CINDEX_LINKAGE void clang_disposeOverriddenCursors(CXCursor *overridden);
/**
* \brief Retrieve the file that is included by the given inclusion directive
* cursor.
*/
CINDEX_LINKAGE CXFile clang_getIncludedFile(CXCursor cursor);
/** /**
* @} * @}
*/ */

View File

@ -54,6 +54,35 @@ public:
SrcMgr::CharacteristicKind FileType) { SrcMgr::CharacteristicKind FileType) {
} }
/// \brief This callback is invoked whenever an inclusion directive of
/// any kind (\c #include, \c #import, etc.) has been processed, regardless
/// of whether the inclusion will actually result in an inclusion.
///
/// \param HashLoc The location of the '#' that starts the inclusion
/// directive.
///
/// \param IncludeTok The token that indicates the kind of inclusion
/// directive, e.g., 'include' or 'import'.
///
/// \param FileName The name of the file being included, as written in the
/// source code.
///
/// \param IsAngled Whether the file name was enclosed in angle brackets;
/// otherwise, it was enclosed in quotes.
///
/// \param File The actual file that may be included by this inclusion
/// directive.
///
/// \param EndLoc The location of the last token within the inclusion
/// directive.
virtual void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
llvm::StringRef FileName,
bool IsAngled,
const FileEntry *File,
SourceLocation EndLoc) {
}
/// EndOfMainFile - This callback is invoked when the end of the main file is /// EndOfMainFile - This callback is invoked when the end of the main file is
/// reach, no subsequent callbacks will be made. /// reach, no subsequent callbacks will be made.
virtual void EndOfMainFile() { virtual void EndOfMainFile() {

View File

@ -35,6 +35,7 @@ void operator delete(void* ptr, clang::PreprocessingRecord& PR,
namespace clang { namespace clang {
class MacroDefinition; class MacroDefinition;
class FileEntry;
/// \brief Base class that describes a preprocessed entity, which may be a /// \brief Base class that describes a preprocessed entity, which may be a
/// preprocessor directive or macro instantiation. /// preprocessor directive or macro instantiation.
@ -54,8 +55,12 @@ namespace clang {
/// \brief A macro definition. /// \brief A macro definition.
MacroDefinitionKind, MacroDefinitionKind,
/// \brief An inclusion directive, such as \c #include, \c
/// #import, or \c #include_next.
InclusionDirectiveKind,
FirstPreprocessingDirective = PreprocessingDirectiveKind, FirstPreprocessingDirective = PreprocessingDirectiveKind,
LastPreprocessingDirective = MacroDefinitionKind LastPreprocessingDirective = InclusionDirectiveKind
}; };
private: private:
@ -173,6 +178,68 @@ namespace clang {
} }
static bool classof(const MacroDefinition *) { return true; } static bool classof(const MacroDefinition *) { return true; }
}; };
/// \brief Record the location of an inclusion directive, such as an
/// \c #include or \c #import statement.
class InclusionDirective : public PreprocessingDirective {
public:
/// \brief The kind of inclusion directives known to the
/// preprocessor.
enum InclusionKind {
/// \brief An \c #include directive.
Include,
/// \brief An Objective-C \c #import directive.
Import,
/// \brief A GNU \c #include_next directive.
IncludeNext,
/// \brief A Clang \c #__include_macros directive.
IncludeMacros
};
private:
/// \brief The name of the file that was included, as written in
/// the source.
std::string FileName;
/// \brief Whether the file name was in quotation marks; otherwise, it was
/// in angle brackets.
unsigned InQuotes : 1;
/// \brief The kind of inclusion directive we have.
///
/// This is a value of type InclusionKind.
unsigned Kind : 2;
/// \brief The file that was included.
const FileEntry *File;
public:
explicit InclusionDirective(InclusionKind Kind,
const std::string &FileName, bool InQuotes,
const FileEntry *File, SourceRange Range)
: PreprocessingDirective(InclusionDirectiveKind, Range),
FileName(FileName), InQuotes(InQuotes), Kind(Kind), File(File) { }
/// \brief Determine what kind of inclusion directive this is.
InclusionKind getKind() const { return static_cast<InclusionKind>(Kind); }
/// \brief Retrieve the included file name as it was written in the source.
llvm::StringRef getFileName() const { return FileName; }
/// \brief Determine whether the included file name was written in quotes;
/// otherwise, it was written in angle brackets.
bool wasInQuotes() const { return InQuotes; }
/// \brief Retrieve the file entry for the actual file that was included
/// by this directive.
const FileEntry *getFile() const { return File; }
// Implement isa/cast/dyncast/etc.
static bool classof(const PreprocessedEntity *PE) {
return PE->getKind() == InclusionDirectiveKind;
}
static bool classof(const InclusionDirective *) { return true; }
};
/// \brief An abstract class that should be subclassed by any external source /// \brief An abstract class that should be subclassed by any external source
/// of preprocessing record entries. /// of preprocessing record entries.
@ -263,6 +330,12 @@ namespace clang {
virtual void MacroDefined(const IdentifierInfo *II, const MacroInfo *MI); virtual void MacroDefined(const IdentifierInfo *II, const MacroInfo *MI);
virtual void MacroUndefined(SourceLocation Loc, const IdentifierInfo *II, virtual void MacroUndefined(SourceLocation Loc, const IdentifierInfo *II,
const MacroInfo *MI); const MacroInfo *MI);
virtual void InclusionDirective(SourceLocation HashLoc,
const Token &IncludeTok,
llvm::StringRef FileName,
bool IsAngled,
const FileEntry *File,
SourceLocation EndLoc);
}; };
} // end namespace clang } // end namespace clang

View File

@ -823,7 +823,8 @@ public:
/// This code concatenates and consumes tokens up to the '>' token. It /// This code concatenates and consumes tokens up to the '>' token. It
/// returns false if the > was found, otherwise it returns true if it finds /// returns false if the > was found, otherwise it returns true if it finds
/// and consumes the EOM marker. /// and consumes the EOM marker.
bool ConcatenateIncludeName(llvm::SmallString<128> &FilenameBuffer); bool ConcatenateIncludeName(llvm::SmallString<128> &FilenameBuffer,
SourceLocation &End);
private: private:
@ -972,12 +973,13 @@ private:
void HandleIdentSCCSDirective(Token &Tok); void HandleIdentSCCSDirective(Token &Tok);
// File inclusion. // File inclusion.
void HandleIncludeDirective(Token &Tok, void HandleIncludeDirective(SourceLocation HashLoc,
Token &Tok,
const DirectoryLookup *LookupFrom = 0, const DirectoryLookup *LookupFrom = 0,
bool isImport = false); bool isImport = false);
void HandleIncludeNextDirective(Token &Tok); void HandleIncludeNextDirective(SourceLocation HashLoc, Token &Tok);
void HandleIncludeMacrosDirective(Token &Tok); void HandleIncludeMacrosDirective(SourceLocation HashLoc, Token &Tok);
void HandleImportDirective(Token &Tok); void HandleImportDirective(SourceLocation HashLoc, Token &Tok);
// Macro handling. // Macro handling.
void HandleDefineDirective(Token &Tok); void HandleDefineDirective(Token &Tok);

View File

@ -372,7 +372,11 @@ namespace clang {
PP_MACRO_INSTANTIATION = 4, PP_MACRO_INSTANTIATION = 4,
/// \brief Describes a macro definition within the preprocessing record. /// \brief Describes a macro definition within the preprocessing record.
PP_MACRO_DEFINITION = 5 PP_MACRO_DEFINITION = 5,
/// \brief Describes am inclusion directive within the preprocessing
/// record.
PP_INCLUSION_DIRECTIVE = 6
}; };
/// \defgroup ASTAST AST file AST constants /// \defgroup ASTAST AST file AST constants

View File

@ -277,6 +277,9 @@ private:
/// all of the macro definitions. /// all of the macro definitions.
llvm::BitstreamCursor MacroCursor; llvm::BitstreamCursor MacroCursor;
/// \brief The offset of the start of the set of defined macros.
uint64_t MacroStartOffset;
/// \brief The number of macro definitions in this file. /// \brief The number of macro definitions in this file.
unsigned LocalNumMacroDefinitions; unsigned LocalNumMacroDefinitions;

View File

@ -591,9 +591,11 @@ TryAgain:
// C99 6.10.2 - Source File Inclusion. // C99 6.10.2 - Source File Inclusion.
case tok::pp_include: case tok::pp_include:
return HandleIncludeDirective(Result); // Handle #include. // Handle #include.
return HandleIncludeDirective(SavedHash.getLocation(), Result);
case tok::pp___include_macros: case tok::pp___include_macros:
return HandleIncludeMacrosDirective(Result); // Handle -imacros. // Handle -imacros.
return HandleIncludeMacrosDirective(SavedHash.getLocation(), Result);
// C99 6.10.3 - Macro Replacement. // C99 6.10.3 - Macro Replacement.
case tok::pp_define: case tok::pp_define:
@ -615,9 +617,9 @@ TryAgain:
// GNU Extensions. // GNU Extensions.
case tok::pp_import: case tok::pp_import:
return HandleImportDirective(Result); return HandleImportDirective(SavedHash.getLocation(), Result);
case tok::pp_include_next: case tok::pp_include_next:
return HandleIncludeNextDirective(Result); return HandleIncludeNextDirective(SavedHash.getLocation(), Result);
case tok::pp_warning: case tok::pp_warning:
Diag(Result, diag::ext_pp_warning_directive); Diag(Result, diag::ext_pp_warning_directive);
@ -1034,11 +1036,14 @@ bool Preprocessor::GetIncludeFilenameSpelling(SourceLocation Loc,
/// false if the > was found, otherwise it returns true if it finds and consumes /// false if the > was found, otherwise it returns true if it finds and consumes
/// the EOM marker. /// the EOM marker.
bool Preprocessor::ConcatenateIncludeName( bool Preprocessor::ConcatenateIncludeName(
llvm::SmallString<128> &FilenameBuffer) { llvm::SmallString<128> &FilenameBuffer,
SourceLocation &End) {
Token CurTok; Token CurTok;
Lex(CurTok); Lex(CurTok);
while (CurTok.isNot(tok::eom)) { while (CurTok.isNot(tok::eom)) {
End = CurTok.getLocation();
// Append the spelling of this token to the buffer. If there was a space // Append the spelling of this token to the buffer. If there was a space
// before it, add it now. // before it, add it now.
if (CurTok.hasLeadingSpace()) if (CurTok.hasLeadingSpace())
@ -1077,7 +1082,8 @@ bool Preprocessor::ConcatenateIncludeName(
/// routine with functionality shared between #include, #include_next and /// routine with functionality shared between #include, #include_next and
/// #import. LookupFrom is set when this is a #include_next directive, it /// #import. LookupFrom is set when this is a #include_next directive, it
/// specifies the file to start searching from. /// specifies the file to start searching from.
void Preprocessor::HandleIncludeDirective(Token &IncludeTok, void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
Token &IncludeTok,
const DirectoryLookup *LookupFrom, const DirectoryLookup *LookupFrom,
bool isImport) { bool isImport) {
@ -1087,7 +1093,8 @@ void Preprocessor::HandleIncludeDirective(Token &IncludeTok,
// Reserve a buffer to get the spelling. // Reserve a buffer to get the spelling.
llvm::SmallString<128> FilenameBuffer; llvm::SmallString<128> FilenameBuffer;
llvm::StringRef Filename; llvm::StringRef Filename;
SourceLocation End;
switch (FilenameTok.getKind()) { switch (FilenameTok.getKind()) {
case tok::eom: case tok::eom:
// If the token kind is EOM, the error has already been diagnosed. // If the token kind is EOM, the error has already been diagnosed.
@ -1096,13 +1103,14 @@ void Preprocessor::HandleIncludeDirective(Token &IncludeTok,
case tok::angle_string_literal: case tok::angle_string_literal:
case tok::string_literal: case tok::string_literal:
Filename = getSpelling(FilenameTok, FilenameBuffer); Filename = getSpelling(FilenameTok, FilenameBuffer);
End = FilenameTok.getLocation();
break; break;
case tok::less: case tok::less:
// This could be a <foo/bar.h> file coming from a macro expansion. In this // This could be a <foo/bar.h> file coming from a macro expansion. In this
// case, glue the tokens together into FilenameBuffer and interpret those. // case, glue the tokens together into FilenameBuffer and interpret those.
FilenameBuffer.push_back('<'); FilenameBuffer.push_back('<');
if (ConcatenateIncludeName(FilenameBuffer)) if (ConcatenateIncludeName(FilenameBuffer, End))
return; // Found <eom> but no ">"? Diagnostic already emitted. return; // Found <eom> but no ">"? Diagnostic already emitted.
Filename = FilenameBuffer.str(); Filename = FilenameBuffer.str();
break; break;
@ -1141,6 +1149,11 @@ void Preprocessor::HandleIncludeDirective(Token &IncludeTok,
return; return;
} }
// Notify the callback object that we've seen an inclusion directive.
if (Callbacks)
Callbacks->InclusionDirective(HashLoc, IncludeTok, Filename, isAngled, File,
End);
// The #included file will be considered to be a system header if either it is // The #included file will be considered to be a system header if either it is
// in a system include directory, or if the #includer is a system include // in a system include directory, or if the #includer is a system include
// header. // header.
@ -1170,7 +1183,8 @@ void Preprocessor::HandleIncludeDirective(Token &IncludeTok,
/// HandleIncludeNextDirective - Implements #include_next. /// HandleIncludeNextDirective - Implements #include_next.
/// ///
void Preprocessor::HandleIncludeNextDirective(Token &IncludeNextTok) { void Preprocessor::HandleIncludeNextDirective(SourceLocation HashLoc,
Token &IncludeNextTok) {
Diag(IncludeNextTok, diag::ext_pp_include_next_directive); Diag(IncludeNextTok, diag::ext_pp_include_next_directive);
// #include_next is like #include, except that we start searching after // #include_next is like #include, except that we start searching after
@ -1187,23 +1201,25 @@ void Preprocessor::HandleIncludeNextDirective(Token &IncludeNextTok) {
++Lookup; ++Lookup;
} }
return HandleIncludeDirective(IncludeNextTok, Lookup); return HandleIncludeDirective(HashLoc, IncludeNextTok, Lookup);
} }
/// HandleImportDirective - Implements #import. /// HandleImportDirective - Implements #import.
/// ///
void Preprocessor::HandleImportDirective(Token &ImportTok) { void Preprocessor::HandleImportDirective(SourceLocation HashLoc,
Token &ImportTok) {
if (!Features.ObjC1) // #import is standard for ObjC. if (!Features.ObjC1) // #import is standard for ObjC.
Diag(ImportTok, diag::ext_pp_import_directive); Diag(ImportTok, diag::ext_pp_import_directive);
return HandleIncludeDirective(ImportTok, 0, true); return HandleIncludeDirective(HashLoc, ImportTok, 0, true);
} }
/// HandleIncludeMacrosDirective - The -imacros command line option turns into a /// HandleIncludeMacrosDirective - The -imacros command line option turns into a
/// pseudo directive in the predefines buffer. This handles it by sucking all /// pseudo directive in the predefines buffer. This handles it by sucking all
/// tokens through the preprocessor and discarding them (only keeping the side /// tokens through the preprocessor and discarding them (only keeping the side
/// effects on the preprocessor). /// effects on the preprocessor).
void Preprocessor::HandleIncludeMacrosDirective(Token &IncludeMacrosTok) { void Preprocessor::HandleIncludeMacrosDirective(SourceLocation HashLoc,
Token &IncludeMacrosTok) {
// This directive should only occur in the predefines buffer. If not, emit an // This directive should only occur in the predefines buffer. If not, emit an
// error and reject it. // error and reject it.
SourceLocation Loc = IncludeMacrosTok.getLocation(); SourceLocation Loc = IncludeMacrosTok.getLocation();
@ -1216,7 +1232,7 @@ void Preprocessor::HandleIncludeMacrosDirective(Token &IncludeMacrosTok) {
// Treat this as a normal #include for checking purposes. If this is // Treat this as a normal #include for checking purposes. If this is
// successful, it will push a new lexer onto the include stack. // successful, it will push a new lexer onto the include stack.
HandleIncludeDirective(IncludeMacrosTok, 0, false); HandleIncludeDirective(HashLoc, IncludeMacrosTok, 0, false);
Token TmpTok; Token TmpTok;
do { do {

View File

@ -570,7 +570,8 @@ static bool EvaluateHasIncludeCommon(bool &Result, Token &Tok,
// Reserve a buffer to get the spelling. // Reserve a buffer to get the spelling.
llvm::SmallString<128> FilenameBuffer; llvm::SmallString<128> FilenameBuffer;
llvm::StringRef Filename; llvm::StringRef Filename;
SourceLocation EndLoc;
switch (Tok.getKind()) { switch (Tok.getKind()) {
case tok::eom: case tok::eom:
// If the token kind is EOM, the error has already been diagnosed. // If the token kind is EOM, the error has already been diagnosed.
@ -589,7 +590,7 @@ static bool EvaluateHasIncludeCommon(bool &Result, Token &Tok,
// This could be a <foo/bar.h> file coming from a macro expansion. In this // This could be a <foo/bar.h> file coming from a macro expansion. In this
// case, glue the tokens together into FilenameBuffer and interpret those. // case, glue the tokens together into FilenameBuffer and interpret those.
FilenameBuffer.push_back('<'); FilenameBuffer.push_back('<');
if (PP.ConcatenateIncludeName(FilenameBuffer)) if (PP.ConcatenateIncludeName(FilenameBuffer, EndLoc))
return false; // Found <eom> but no ">"? Diagnostic already emitted. return false; // Found <eom> but no ">"? Diagnostic already emitted.
Filename = FilenameBuffer.str(); Filename = FilenameBuffer.str();
break; break;

View File

@ -14,6 +14,8 @@
#include "clang/Lex/PreprocessingRecord.h" #include "clang/Lex/PreprocessingRecord.h"
#include "clang/Lex/MacroInfo.h" #include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Token.h" #include "clang/Lex/Token.h"
#include "clang/Basic/IdentifierTable.h"
#include "llvm/Support/ErrorHandling.h"
using namespace clang; using namespace clang;
@ -127,3 +129,38 @@ void PreprocessingRecord::MacroUndefined(SourceLocation Loc,
MacroDefinitions.erase(Pos); MacroDefinitions.erase(Pos);
} }
void PreprocessingRecord::InclusionDirective(SourceLocation HashLoc,
const clang::Token &IncludeTok,
llvm::StringRef FileName,
bool IsAngled,
const FileEntry *File,
clang::SourceLocation EndLoc) {
InclusionDirective::InclusionKind Kind = InclusionDirective::Include;
switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) {
case tok::pp_include:
Kind = InclusionDirective::Include;
break;
case tok::pp_import:
Kind = InclusionDirective::Import;
break;
case tok::pp_include_next:
Kind = InclusionDirective::IncludeNext;
break;
case tok::pp___include_macros:
Kind = InclusionDirective::IncludeMacros;
break;
default:
llvm_unreachable("Unknown include directive kind");
return;
}
clang::InclusionDirective *ID
= new (*this) clang::InclusionDirective(Kind, FileName, !IsAngled, File,
SourceRange(HashLoc, EndLoc));
PreprocessedEntities.push_back(ID);
}

View File

@ -1337,18 +1337,21 @@ bool ASTReader::ReadBlockAbbrevs(llvm::BitstreamCursor &Cursor,
} }
while (true) { while (true) {
uint64_t Offset = Cursor.GetCurrentBitNo();
unsigned Code = Cursor.ReadCode(); unsigned Code = Cursor.ReadCode();
// We expect all abbrevs to be at the start of the block. // We expect all abbrevs to be at the start of the block.
if (Code != llvm::bitc::DEFINE_ABBREV) if (Code != llvm::bitc::DEFINE_ABBREV) {
Cursor.JumpToBit(Offset);
return false; return false;
}
Cursor.ReadAbbrevRecord(); Cursor.ReadAbbrevRecord();
} }
} }
void ASTReader::ReadMacroRecord(PerFileData &F, uint64_t Offset) { void ASTReader::ReadMacroRecord(PerFileData &F, uint64_t Offset) {
assert(PP && "Forgot to set Preprocessor ?"); assert(PP && "Forgot to set Preprocessor ?");
llvm::BitstreamCursor &Stream = F.Stream; llvm::BitstreamCursor &Stream = F.MacroCursor;
// Keep track of where we are in the stream, then jump back there // Keep track of where we are in the stream, then jump back there
// after reading this macro. // after reading this macro.
@ -1381,9 +1384,12 @@ void ASTReader::ReadMacroRecord(PerFileData &F, uint64_t Offset) {
} }
// Read a record. // Read a record.
const char *BlobStart = 0;
unsigned BlobLen = 0;
Record.clear(); Record.clear();
PreprocessorRecordTypes RecType = PreprocessorRecordTypes RecType =
(PreprocessorRecordTypes)Stream.ReadRecord(Code, Record); (PreprocessorRecordTypes)Stream.ReadRecord(Code, Record, BlobStart,
BlobLen);
switch (RecType) { switch (RecType) {
case PP_MACRO_OBJECT_LIKE: case PP_MACRO_OBJECT_LIKE:
case PP_MACRO_FUNCTION_LIKE: { case PP_MACRO_FUNCTION_LIKE: {
@ -1524,6 +1530,41 @@ void ASTReader::ReadMacroRecord(PerFileData &F, uint64_t Offset) {
return; return;
} }
case PP_INCLUSION_DIRECTIVE: {
// If we already have a macro, that means that we've hit the end
// of the definition of the macro we were looking for. We're
// done.
if (Macro)
return;
if (!PP->getPreprocessingRecord()) {
Error("missing preprocessing record in AST file");
return;
}
PreprocessingRecord &PPRec = *PP->getPreprocessingRecord();
if (PPRec.getPreprocessedEntity(Record[0]))
return;
const char *FullFileNameStart = BlobStart + Record[3];
const FileEntry *File
= PP->getFileManager().getFile(FullFileNameStart,
FullFileNameStart + (BlobLen - Record[3]));
// FIXME: Stable encoding
InclusionDirective::InclusionKind Kind
= static_cast<InclusionDirective::InclusionKind>(Record[5]);
InclusionDirective *ID
= new (PPRec) InclusionDirective(Kind,
llvm::StringRef(BlobStart, Record[3]),
Record[4],
File,
SourceRange(ReadSourceLocation(F, Record[1]),
ReadSourceLocation(F, Record[2])));
PPRec.SetPreallocatedEntity(Record[0], ID);
return;
}
} }
} }
} }
@ -1538,22 +1579,14 @@ void ASTReader::ReadDefinedMacros() {
continue; continue;
llvm::BitstreamCursor Cursor = MacroCursor; llvm::BitstreamCursor Cursor = MacroCursor;
if (Cursor.EnterSubBlock(PREPROCESSOR_BLOCK_ID)) { Cursor.JumpToBit(F.MacroStartOffset);
Error("malformed preprocessor block record in AST file");
return;
}
RecordData Record; RecordData Record;
while (true) { while (true) {
uint64_t Offset = Cursor.GetCurrentBitNo(); uint64_t Offset = Cursor.GetCurrentBitNo();
unsigned Code = Cursor.ReadCode(); unsigned Code = Cursor.ReadCode();
if (Code == llvm::bitc::END_BLOCK) { if (Code == llvm::bitc::END_BLOCK)
if (Cursor.ReadBlockEnd()) {
Error("error at end of preprocessor block in AST file");
return;
}
break; break;
}
if (Code == llvm::bitc::ENTER_SUBBLOCK) { if (Code == llvm::bitc::ENTER_SUBBLOCK) {
// No known subblocks, always skip them. // No known subblocks, always skip them.
@ -1589,6 +1622,7 @@ void ASTReader::ReadDefinedMacros() {
case PP_MACRO_INSTANTIATION: case PP_MACRO_INSTANTIATION:
case PP_MACRO_DEFINITION: case PP_MACRO_DEFINITION:
case PP_INCLUSION_DIRECTIVE:
// Read the macro record. // Read the macro record.
// FIXME: That's a stupid way to do this. We should reuse this cursor. // FIXME: That's a stupid way to do this. We should reuse this cursor.
ReadMacroRecord(F, Offset); ReadMacroRecord(F, Offset);
@ -1686,10 +1720,12 @@ ASTReader::ReadASTBlock(PerFileData &F) {
if (PP) if (PP)
PP->setExternalSource(this); PP->setExternalSource(this);
if (Stream.SkipBlock()) { if (Stream.SkipBlock() ||
ReadBlockAbbrevs(F.MacroCursor, PREPROCESSOR_BLOCK_ID)) {
Error("malformed block record in AST file"); Error("malformed block record in AST file");
return Failure; return Failure;
} }
F.MacroStartOffset = F.MacroCursor.GetCurrentBitNo();
break; break;
case SOURCE_MANAGER_BLOCK_ID: case SOURCE_MANAGER_BLOCK_ID:

View File

@ -1256,16 +1256,32 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP) {
} }
// Enter the preprocessor block. // Enter the preprocessor block.
Stream.EnterSubblock(PREPROCESSOR_BLOCK_ID, 2); Stream.EnterSubblock(PREPROCESSOR_BLOCK_ID, 3);
// If the AST file contains __DATE__ or __TIME__ emit a warning about this. // If the AST file contains __DATE__ or __TIME__ emit a warning about this.
// FIXME: use diagnostics subsystem for localization etc. // FIXME: use diagnostics subsystem for localization etc.
if (PP.SawDateOrTime()) if (PP.SawDateOrTime())
fprintf(stderr, "warning: precompiled header used __DATE__ or __TIME__.\n"); fprintf(stderr, "warning: precompiled header used __DATE__ or __TIME__.\n");
// Loop over all the macro definitions that are live at the end of the file, // Loop over all the macro definitions that are live at the end of the file,
// emitting each to the PP section. // emitting each to the PP section.
PreprocessingRecord *PPRec = PP.getPreprocessingRecord(); PreprocessingRecord *PPRec = PP.getPreprocessingRecord();
unsigned InclusionAbbrev = 0;
if (PPRec) {
using namespace llvm;
BitCodeAbbrev *Abbrev = new BitCodeAbbrev();
Abbrev->Add(BitCodeAbbrevOp(PP_INCLUSION_DIRECTIVE));
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // index
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // start location
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // end location
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // filename length
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // in quotes
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // kind
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
InclusionAbbrev = Stream.EmitAbbrev(Abbrev);
}
for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end(); for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end();
I != E; ++I) { I != E; ++I) {
// FIXME: This emits macros in hash table order, we should do it in a stable // FIXME: This emits macros in hash table order, we should do it in a stable
@ -1381,6 +1397,21 @@ void ASTWriter::WritePreprocessor(const Preprocessor &PP) {
Stream.EmitRecord(PP_MACRO_DEFINITION, Record); Stream.EmitRecord(PP_MACRO_DEFINITION, Record);
continue; continue;
} }
if (InclusionDirective *ID = dyn_cast<InclusionDirective>(*E)) {
Record.push_back(PP_INCLUSION_DIRECTIVE);
Record.push_back(IndexBase + NumPreprocessingRecords++);
AddSourceLocation(ID->getSourceRange().getBegin(), Record);
AddSourceLocation(ID->getSourceRange().getEnd(), Record);
Record.push_back(ID->getFileName().size());
Record.push_back(ID->wasInQuotes());
Record.push_back(static_cast<unsigned>(ID->getKind()));
llvm::SmallString<64> Buffer;
Buffer += ID->getFileName();
Buffer += ID->getFile()->getName();
Stream.EmitRecordWithBlob(InclusionAbbrev, Record, Buffer);
continue;
}
} }
} }

View File

@ -1,6 +1,7 @@
#include "annotate-tokens-include.h" #include "annotate-tokens-include.h"
// RUN: c-index-test -test-annotate-tokens=%s:1:1:2:1 %s | FileCheck %s // RUN: c-index-test -test-annotate-tokens=%s:1:1:2:1 %s | FileCheck %s
// CHECK: Punctuation: "#" [1:1 - 1:2] inclusion directive=annotate-tokens-include.h
// CHECK: Identifier: "include" [1:2 - 1:9] preprocessing directive= // CHECK: Identifier: "include" [1:2 - 1:9] preprocessing directive=
// CHECK: Literal: ""annotate-tokens-include.h"" [1:10 - 1:37] preprocessing directive= // CHECK: Literal: ""annotate-tokens-include.h"" [1:10 - 1:37] preprocessing directive=

View File

@ -68,7 +68,7 @@ void test() {
// CHECK: Identifier: "BAR" [6:5 - 6:8] macro instantiation=BAR:3:9 // CHECK: Identifier: "BAR" [6:5 - 6:8] macro instantiation=BAR:3:9
// CHECK: Identifier: "STILL_NOTHING" [6:9 - 6:22] macro instantiation=STILL_NOTHING:2:9 // CHECK: Identifier: "STILL_NOTHING" [6:9 - 6:22] macro instantiation=STILL_NOTHING:2:9
// CHECK: Punctuation: ";" [6:22 - 6:23] // CHECK: Punctuation: ";" [6:22 - 6:23]
// CHECK: Punctuation: "#" [7:1 - 7:2] preprocessing directive= // CHECK: Punctuation: "#" [7:1 - 7:2] inclusion directive=foo.h
// CHECK: Identifier: "include" [7:2 - 7:9] preprocessing directive= // CHECK: Identifier: "include" [7:2 - 7:9] preprocessing directive=
// CHECK: Literal: ""foo.h"" [7:10 - 7:17] preprocessing directive= // CHECK: Literal: ""foo.h"" [7:10 - 7:17] preprocessing directive=
// CHECK: Punctuation: "#" [8:1 - 8:2] preprocessing directive= // CHECK: Punctuation: "#" [8:1 - 8:2] preprocessing directive=

View File

@ -271,6 +271,13 @@ static void PrintCursor(CXCursor Cursor) {
printf("]"); printf("]");
clang_disposeOverriddenCursors(overridden); clang_disposeOverriddenCursors(overridden);
} }
if (Cursor.kind == CXCursor_InclusionDirective) {
CXFile File = clang_getIncludedFile(Cursor);
CXString Included = clang_getFileName(File);
printf(" (%s)", clang_getCString(Included));
clang_disposeString(Included);
}
} }
} }

View File

@ -555,6 +555,13 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) {
continue; continue;
} }
if (InclusionDirective *ID = dyn_cast<InclusionDirective>(*E)) {
if (Visit(MakeInclusionDirectiveCursor(ID, CXXUnit)))
return true;
continue;
}
} }
} }
return false; return false;
@ -2565,6 +2572,9 @@ CXString clang_getCursorSpelling(CXCursor C) {
return createCXString(getCursorMacroDefinition(C)->getName() return createCXString(getCursorMacroDefinition(C)->getName()
->getNameStart()); ->getNameStart());
if (C.kind == CXCursor_InclusionDirective)
return createCXString(getCursorInclusionDirective(C)->getFileName());
if (clang_isDeclaration(C.kind)) if (clang_isDeclaration(C.kind))
return getDeclSpelling(getCursorDecl(C)); return getDeclSpelling(getCursorDecl(C));
@ -2757,6 +2767,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
return createCXString("macro definition"); return createCXString("macro definition");
case CXCursor_MacroInstantiation: case CXCursor_MacroInstantiation:
return createCXString("macro instantiation"); return createCXString("macro instantiation");
case CXCursor_InclusionDirective:
return createCXString("inclusion directive");
case CXCursor_Namespace: case CXCursor_Namespace:
return createCXString("Namespace"); return createCXString("Namespace");
case CXCursor_LinkageSpec: case CXCursor_LinkageSpec:
@ -2977,7 +2989,13 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) {
SourceLocation L = cxcursor::getCursorMacroDefinition(C)->getLocation(); SourceLocation L = cxcursor::getCursorMacroDefinition(C)->getLocation();
return cxloc::translateSourceLocation(getCursorContext(C), L); return cxloc::translateSourceLocation(getCursorContext(C), L);
} }
if (C.kind == CXCursor_InclusionDirective) {
SourceLocation L
= cxcursor::getCursorInclusionDirective(C)->getSourceRange().getBegin();
return cxloc::translateSourceLocation(getCursorContext(C), L);
}
if (C.kind < CXCursor_FirstDecl || C.kind > CXCursor_LastDecl) if (C.kind < CXCursor_FirstDecl || C.kind > CXCursor_LastDecl)
return clang_getNullLocation(); return clang_getNullLocation();
@ -3043,12 +3061,14 @@ static SourceRange getRawCursorExtent(CXCursor C) {
if (C.kind == CXCursor_MacroDefinition) if (C.kind == CXCursor_MacroDefinition)
return cxcursor::getCursorMacroDefinition(C)->getSourceRange(); return cxcursor::getCursorMacroDefinition(C)->getSourceRange();
if (C.kind == CXCursor_InclusionDirective)
return cxcursor::getCursorInclusionDirective(C)->getSourceRange();
if (C.kind >= CXCursor_FirstDecl && C.kind <= CXCursor_LastDecl) if (C.kind >= CXCursor_FirstDecl && C.kind <= CXCursor_LastDecl)
return getCursorDecl(C)->getSourceRange(); return getCursorDecl(C)->getSourceRange();
return SourceRange(); return SourceRange();}
}
extern "C" { extern "C" {
@ -4149,6 +4169,14 @@ void clang_disposeOverriddenCursors(CXCursor *overridden) {
delete [] overridden; delete [] overridden;
} }
CXFile clang_getIncludedFile(CXCursor cursor) {
if (cursor.kind != CXCursor_InclusionDirective)
return 0;
InclusionDirective *ID = getCursorInclusionDirective(cursor);
return (void *)ID->getFile();
}
} // end: extern "C" } // end: extern "C"

View File

@ -363,6 +363,17 @@ MacroInstantiation *cxcursor::getCursorMacroInstantiation(CXCursor C) {
return static_cast<MacroInstantiation *>(C.data[0]); return static_cast<MacroInstantiation *>(C.data[0]);
} }
CXCursor cxcursor::MakeInclusionDirectiveCursor(InclusionDirective *ID,
ASTUnit *TU) {
CXCursor C = { CXCursor_InclusionDirective, { ID, 0, TU } };
return C;
}
InclusionDirective *cxcursor::getCursorInclusionDirective(CXCursor C) {
assert(C.kind == CXCursor_InclusionDirective);
return static_cast<InclusionDirective *>(C.data[0]);
}
CXCursor cxcursor::MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, CXCursor cxcursor::MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc,
ASTUnit *TU) { ASTUnit *TU) {

View File

@ -28,6 +28,7 @@ class CXXBaseSpecifier;
class Decl; class Decl;
class Expr; class Expr;
class FieldDecl; class FieldDecl;
class InclusionDirective;
class LabelStmt; class LabelStmt;
class MacroDefinition; class MacroDefinition;
class MacroInstantiation; class MacroInstantiation;
@ -133,6 +134,13 @@ CXCursor MakeMacroInstantiationCursor(MacroInstantiation *, ASTUnit *TU);
/// source range. /// source range.
MacroInstantiation *getCursorMacroInstantiation(CXCursor C); MacroInstantiation *getCursorMacroInstantiation(CXCursor C);
/// \brief Create an inclusion directive cursor.
CXCursor MakeInclusionDirectiveCursor(InclusionDirective *, ASTUnit *TU);
/// \brief Unpack a given inclusion directive cursor to retrieve its
/// source range.
InclusionDirective *getCursorInclusionDirective(CXCursor C);
/// \brief Create a label reference at the given location. /// \brief Create a label reference at the given location.
CXCursor MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, ASTUnit *TU); CXCursor MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, ASTUnit *TU);

View File

@ -68,6 +68,7 @@ _clang_getFile
_clang_getFileName _clang_getFileName
_clang_getFileTime _clang_getFileTime
_clang_getIBOutletCollectionType _clang_getIBOutletCollectionType
_clang_getIncludedFile
_clang_getInclusions _clang_getInclusions
_clang_getInstantiationLocation _clang_getInstantiationLocation
_clang_getLocation _clang_getLocation

View File

@ -68,6 +68,7 @@ clang_getFile
clang_getFileName clang_getFileName
clang_getFileTime clang_getFileTime
clang_getIBOutletCollectionType clang_getIBOutletCollectionType
clang_getIncludedFile
clang_getInclusions clang_getInclusions
clang_getInstantiationLocation clang_getInstantiationLocation
clang_getLocation clang_getLocation