#include "PreprocessorTracker.h"
#include "ModularizeUtilities.h"
#include "clang/Lex/LexDiagnostic.h"
#include "clang/Lex/MacroArgs.h"
#include "clang/Lex/PPCallbacks.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/raw_ostream.h"
namespace Modularize {
typedef llvm::StringRef StringHandle;
typedef int HeaderHandle;
const HeaderHandle HeaderHandleInvalid = -1;
typedef int InclusionPathHandle;
const InclusionPathHandle InclusionPathHandleInvalid = -1;
static std::string getSourceLocationString(clang::Preprocessor &PP,
clang::SourceLocation Loc) {
if (Loc.isInvalid())
return std::string("(none)");
else
return Loc.printToString(PP.getSourceManager());
}
static std::string getSourceLocationFile(clang::Preprocessor &PP,
clang::SourceLocation Loc) {
std::string Source(getSourceLocationString(PP, Loc));
size_t Offset = Source.find(':', 2);
if (Offset == std::string::npos)
return Source;
return Source.substr(0, Offset);
}
static void getSourceLocationLineAndColumn(clang::Preprocessor &PP,
clang::SourceLocation Loc, int &Line,
int &Column) {
clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);
if (PLoc.isInvalid()) {
Line = 0;
Column = 0;
return;
}
Line = PLoc.getLine();
Column = PLoc.getColumn();
}
static std::string getSourceString(clang::Preprocessor &PP,
clang::SourceRange Range) {
clang::SourceLocation BeginLoc = Range.getBegin();
clang::SourceLocation EndLoc = Range.getEnd();
const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc);
size_t Length = EndPtr - BeginPtr;
return llvm::StringRef(BeginPtr, Length).trim().str();
}
static std::string getSourceLine(clang::Preprocessor &PP,
clang::SourceLocation Loc) {
llvm::MemoryBufferRef MemBuffer = PP.getSourceManager().getBufferOrFake(
PP.getSourceManager().getFileID(Loc));
const char *Buffer = MemBuffer.getBufferStart();
const char *BufferEnd = MemBuffer.getBufferEnd();
const char *BeginPtr = PP.getSourceManager().getCharacterData(Loc);
const char *EndPtr = BeginPtr;
while (BeginPtr > Buffer) {
if (*BeginPtr == '\n') {
BeginPtr++;
break;
}
BeginPtr--;
}
while (EndPtr < BufferEnd) {
if (*EndPtr == '\n') {
break;
}
EndPtr++;
}
size_t Length = EndPtr - BeginPtr;
return llvm::StringRef(BeginPtr, Length).str();
}
static std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID,
int Line) {
llvm::MemoryBufferRef MemBuffer =
PP.getSourceManager().getBufferOrFake(FileID);
const char *Buffer = MemBuffer.getBufferStart();
const char *BufferEnd = MemBuffer.getBufferEnd();
const char *BeginPtr = Buffer;
const char *EndPtr = BufferEnd;
int LineCounter = 1;
if (Line == 1)
BeginPtr = Buffer;
else {
while (Buffer < BufferEnd) {
if (*Buffer == '\n') {
if (++LineCounter == Line) {
BeginPtr = Buffer++ + 1;
break;
}
}
Buffer++;
}
}
while (Buffer < BufferEnd) {
if (*Buffer == '\n') {
EndPtr = Buffer;
break;
}
Buffer++;
}
size_t Length = EndPtr - BeginPtr;
return llvm::StringRef(BeginPtr, Length).str();
}
static std::string getMacroUnexpandedString(clang::SourceRange Range,
clang::Preprocessor &PP,
llvm::StringRef MacroName,
const clang::MacroInfo *MI) {
clang::SourceLocation BeginLoc(Range.getBegin());
const char *BeginPtr = PP.getSourceManager().getCharacterData(BeginLoc);
size_t Length;
std::string Unexpanded;
if (MI->isFunctionLike()) {
clang::SourceLocation EndLoc(Range.getEnd());
const char *EndPtr = PP.getSourceManager().getCharacterData(EndLoc) + 1;
Length = (EndPtr - BeginPtr) + 1;
} else
Length = MacroName.size();
return llvm::StringRef(BeginPtr, Length).trim().str();
}
static std::string getMacroExpandedString(clang::Preprocessor &PP,
llvm::StringRef MacroName,
const clang::MacroInfo *MI,
const clang::MacroArgs *Args) {
std::string Expanded;
for (const auto &T : MI->tokens()) {
clang::IdentifierInfo *II = T.getIdentifierInfo();
int ArgNo = (II && Args ? MI->getParameterNum(II) : -1);
if (ArgNo == -1) {
if (II == nullptr)
Expanded += PP.getSpelling(T);
else {
std::string Name = II->getName().str();
clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
if (MacroInfo && (Name != MacroName))
Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
else
Expanded += Name;
}
continue;
}
const clang::Token *ResultArgToks;
const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo);
if (Args->ArgNeedsPreexpansion(ArgTok, PP))
ResultArgToks = &(const_cast<clang::MacroArgs *>(Args))
->getPreExpArgument(ArgNo, PP)[0];
else
ResultArgToks = ArgTok;
if (ResultArgToks->is(clang::tok::eof))
continue;
unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks);
for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) {
const clang::Token &AT = ResultArgToks[ArgumentIndex];
clang::IdentifierInfo *II = AT.getIdentifierInfo();
if (II == nullptr)
Expanded += PP.getSpelling(AT);
else {
std::string Name = II->getName().str();
clang::MacroInfo *MacroInfo = PP.getMacroInfo(II);
if (MacroInfo)
Expanded += getMacroExpandedString(PP, Name, MacroInfo, nullptr);
else
Expanded += Name;
}
}
}
return Expanded;
}
namespace {
const char *
ConditionValueKindStrings[] = {
"(not evaluated)", "false", "true"
};
class PPItemKey {
public:
PPItemKey(clang::Preprocessor &PP, StringHandle Name, HeaderHandle File,
clang::SourceLocation Loc)
: Name(Name), File(File) {
getSourceLocationLineAndColumn(PP, Loc, Line, Column);
}
PPItemKey(StringHandle Name, HeaderHandle File, int Line, int Column)
: Name(Name), File(File), Line(Line), Column(Column) {}
PPItemKey(const PPItemKey &Other)
: Name(Other.Name), File(Other.File), Line(Other.Line),
Column(Other.Column) {}
PPItemKey() : File(HeaderHandleInvalid), Line(0), Column(0) {}
bool operator==(const PPItemKey &Other) const {
if (Name != Other.Name)
return false;
if (File != Other.File)
return false;
if (Line != Other.Line)
return false;
return Column == Other.Column;
}
bool operator<(const PPItemKey &Other) const {
if (Name < Other.Name)
return true;
else if (Name > Other.Name)
return false;
if (File < Other.File)
return true;
else if (File > Other.File)
return false;
if (Line < Other.Line)
return true;
else if (Line > Other.Line)
return false;
return Column < Other.Column;
}
StringHandle Name;
HeaderHandle File;
int Line;
int Column;
};
class HeaderInclusionPath {
public:
HeaderInclusionPath(std::vector<HeaderHandle> HeaderInclusionPath)
: Path(HeaderInclusionPath) {}
HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {}
HeaderInclusionPath() {}
std::vector<HeaderHandle> Path;
};
class MacroExpansionInstance {
public:
MacroExpansionInstance(StringHandle MacroExpanded,
PPItemKey &DefinitionLocation,
StringHandle DefinitionSourceLine,
InclusionPathHandle H)
: MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation),
DefinitionSourceLine(DefinitionSourceLine) {
InclusionPathHandles.push_back(H);
}
MacroExpansionInstance() {}
bool haveInclusionPathHandle(InclusionPathHandle H) {
for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
I != E; ++I) {
if (*I == H)
return true;
}
return InclusionPathHandleInvalid;
}
void addInclusionPathHandle(InclusionPathHandle H) {
if (!haveInclusionPathHandle(H))
InclusionPathHandles.push_back(H);
}
StringHandle MacroExpanded;
PPItemKey DefinitionLocation;
StringHandle DefinitionSourceLine;
std::vector<InclusionPathHandle> InclusionPathHandles;
};
class MacroExpansionTracker {
public:
MacroExpansionTracker(StringHandle MacroUnexpanded,
StringHandle MacroExpanded,
StringHandle InstanceSourceLine,
PPItemKey &DefinitionLocation,
StringHandle DefinitionSourceLine,
InclusionPathHandle InclusionPathHandle)
: MacroUnexpanded(MacroUnexpanded),
InstanceSourceLine(InstanceSourceLine) {
addMacroExpansionInstance(MacroExpanded, DefinitionLocation,
DefinitionSourceLine, InclusionPathHandle);
}
MacroExpansionTracker() {}
MacroExpansionInstance *
findMacroExpansionInstance(StringHandle MacroExpanded,
PPItemKey &DefinitionLocation) {
for (auto I = MacroExpansionInstances.begin(),
E = MacroExpansionInstances.end();
I != E; ++I) {
if ((I->MacroExpanded == MacroExpanded) &&
(I->DefinitionLocation == DefinitionLocation)) {
return &*I;
}
}
return nullptr;
}
void addMacroExpansionInstance(StringHandle MacroExpanded,
PPItemKey &DefinitionLocation,
StringHandle DefinitionSourceLine,
InclusionPathHandle InclusionPathHandle) {
MacroExpansionInstances.push_back(
MacroExpansionInstance(MacroExpanded, DefinitionLocation,
DefinitionSourceLine, InclusionPathHandle));
}
bool hasMismatch() { return MacroExpansionInstances.size() > 1; }
StringHandle MacroUnexpanded;
StringHandle InstanceSourceLine;
std::vector<MacroExpansionInstance> MacroExpansionInstances;
};
class ConditionalExpansionInstance {
public:
ConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue, InclusionPathHandle H)
: ConditionValue(ConditionValue) {
InclusionPathHandles.push_back(H);
}
ConditionalExpansionInstance() {}
bool haveInclusionPathHandle(InclusionPathHandle H) {
for (auto I = InclusionPathHandles.begin(), E = InclusionPathHandles.end();
I != E; ++I) {
if (*I == H)
return true;
}
return InclusionPathHandleInvalid;
}
void addInclusionPathHandle(InclusionPathHandle H) {
if (!haveInclusionPathHandle(H))
InclusionPathHandles.push_back(H);
}
clang::PPCallbacks::ConditionValueKind ConditionValue;
std::vector<InclusionPathHandle> InclusionPathHandles;
};
class ConditionalTracker {
public:
ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind,
clang::PPCallbacks::ConditionValueKind ConditionValue,
StringHandle ConditionUnexpanded,
InclusionPathHandle InclusionPathHandle)
: DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) {
addConditionalExpansionInstance(ConditionValue, InclusionPathHandle);
}
ConditionalTracker() {}
ConditionalExpansionInstance *
findConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue) {
for (auto I = ConditionalExpansionInstances.begin(),
E = ConditionalExpansionInstances.end();
I != E; ++I) {
if (I->ConditionValue == ConditionValue) {
return &*I;
}
}
return nullptr;
}
void
addConditionalExpansionInstance(clang::PPCallbacks::ConditionValueKind ConditionValue,
InclusionPathHandle InclusionPathHandle) {
ConditionalExpansionInstances.push_back(
ConditionalExpansionInstance(ConditionValue, InclusionPathHandle));
}
bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; }
clang::tok::PPKeywordKind DirectiveKind;
StringHandle ConditionUnexpanded;
std::vector<ConditionalExpansionInstance> ConditionalExpansionInstances;
};
class PreprocessorTrackerImpl;
class PreprocessorCallbacks : public clang::PPCallbacks {
public:
PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker,
clang::Preprocessor &PP, llvm::StringRef rootHeaderFile)
: PPTracker(ppTracker), PP(PP), RootHeaderFile(rootHeaderFile) {}
~PreprocessorCallbacks() override {}
void
InclusionDirective(clang::SourceLocation HashLoc,
const clang::Token &IncludeTok, llvm::StringRef FileName,
bool IsAngled, clang::CharSourceRange FilenameRange,
clang::OptionalFileEntryRef File,
llvm::StringRef SearchPath, llvm::StringRef RelativePath,
const clang::Module *SuggestedModule, bool ModuleImported,
clang::SrcMgr::CharacteristicKind FileType) override;
void FileChanged(clang::SourceLocation Loc,
clang::PPCallbacks::FileChangeReason Reason,
clang::SrcMgr::CharacteristicKind FileType,
clang::FileID PrevFID = clang::FileID()) override;
void MacroExpands(const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD, clang::SourceRange Range,
const clang::MacroArgs *Args) override;
void Defined(const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD,
clang::SourceRange Range) override;
void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
clang::PPCallbacks::ConditionValueKind ConditionResult) override;
void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange,
clang::PPCallbacks::ConditionValueKind ConditionResult,
clang::SourceLocation IfLoc) override;
void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD) override;
void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD) override;
private:
PreprocessorTrackerImpl &PPTracker;
clang::Preprocessor &PP;
std::string RootHeaderFile;
};
typedef std::map<PPItemKey, MacroExpansionTracker> MacroExpansionMap;
typedef std::map<PPItemKey, MacroExpansionTracker>::iterator
MacroExpansionMapIter;
typedef std::map<PPItemKey, ConditionalTracker> ConditionalExpansionMap;
typedef std::map<PPItemKey, ConditionalTracker>::iterator
ConditionalExpansionMapIter;
class PreprocessorTrackerImpl : public PreprocessorTracker {
public:
PreprocessorTrackerImpl(llvm::SmallVector<std::string, 32> &Headers,
bool DoBlockCheckHeaderListOnly)
: BlockCheckHeaderListOnly(DoBlockCheckHeaderListOnly),
CurrentInclusionPathHandle(InclusionPathHandleInvalid),
InNestedHeader(false) {
for (llvm::ArrayRef<std::string>::iterator I = Headers.begin(),
E = Headers.end();
I != E; ++I) {
HeaderList.push_back(getCanonicalPath(*I));
}
}
~PreprocessorTrackerImpl() override {}
void handlePreprocessorEntry(clang::Preprocessor &PP,
llvm::StringRef rootHeaderFile) override {
HeadersInThisCompile.clear();
assert((HeaderStack.size() == 0) && "Header stack should be empty.");
pushHeaderHandle(addHeader(rootHeaderFile));
PP.addPPCallbacks(std::make_unique<PreprocessorCallbacks>(*this, PP,
rootHeaderFile));
}
void handlePreprocessorExit() override { HeaderStack.clear(); }
void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine,
int DirectiveColumn,
llvm::StringRef TargetPath) override {
if (BlockCheckHeaderListOnly && !isHeaderListHeader(TargetPath))
return;
HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath);
StringHandle IncludeHeaderHandle = addString(TargetPath);
for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
E = IncludeDirectives.end();
I != E; ++I) {
if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine))
return;
}
PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle,
DirectiveLine, DirectiveColumn);
IncludeDirectives.push_back(IncludeDirectiveItem);
}
bool checkForIncludesInBlock(clang::Preprocessor &PP,
clang::SourceRange BlockSourceRange,
const char *BlockIdentifierMessage,
llvm::raw_ostream &OS) override {
clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin();
clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd();
clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc);
std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc);
SourcePath = ModularizeUtilities::getCanonicalPath(SourcePath);
HeaderHandle SourceHandle = findHeaderHandle(SourcePath);
if (SourceHandle == -1)
return true;
int BlockStartLine, BlockStartColumn, BlockEndLine, BlockEndColumn;
bool returnValue = true;
getSourceLocationLineAndColumn(PP, BlockStartLoc, BlockStartLine,
BlockStartColumn);
getSourceLocationLineAndColumn(PP, BlockEndLoc, BlockEndLine,
BlockEndColumn);
for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(),
E = IncludeDirectives.end();
I != E; ++I) {
if ((I->File == SourceHandle) && (I->Line >= BlockStartLine) &&
(I->Line < BlockEndLine)) {
returnValue = false;
OS << SourcePath << ":" << I->Line << ":" << I->Column << ":\n";
OS << getSourceLine(PP, FileID, I->Line) << "\n";
if (I->Column > 0)
OS << std::string(I->Column - 1, ' ') << "^\n";
OS << "error: Include directive within " << BlockIdentifierMessage
<< ".\n";
OS << SourcePath << ":" << BlockStartLine << ":" << BlockStartColumn
<< ":\n";
OS << getSourceLine(PP, BlockStartLoc) << "\n";
if (BlockStartColumn > 0)
OS << std::string(BlockStartColumn - 1, ' ') << "^\n";
OS << "The \"" << BlockIdentifierMessage << "\" block is here.\n";
}
}
return returnValue;
}
void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) {
if (HeaderPath.starts_with("<"))
return;
HeaderHandle H = addHeader(HeaderPath);
if (H != getCurrentHeaderHandle())
pushHeaderHandle(H);
if (!InNestedHeader)
InNestedHeader = !HeadersInThisCompile.insert(H).second;
}
void handleHeaderExit(llvm::StringRef HeaderPath) {
if (HeaderPath.starts_with("<"))
return;
HeaderHandle H = findHeaderHandle(HeaderPath);
HeaderHandle TH;
if (isHeaderHandleInStack(H)) {
do {
TH = getCurrentHeaderHandle();
popHeaderHandle();
} while ((TH != H) && (HeaderStack.size() != 0));
}
InNestedHeader = false;
}
StringHandle addString(llvm::StringRef Str) {
return Strings.insert(Str).first->first();
}
std::string getCanonicalPath(llvm::StringRef path) const {
std::string CanonicalPath(path);
std::replace(CanonicalPath.begin(), CanonicalPath.end(), '\\', '/');
return CanonicalPath;
}
bool isHeaderListHeader(llvm::StringRef HeaderPath) const {
std::string CanonicalPath = getCanonicalPath(HeaderPath);
for (llvm::ArrayRef<std::string>::iterator I = HeaderList.begin(),
E = HeaderList.end();
I != E; ++I) {
if (*I == CanonicalPath)
return true;
}
return false;
}
HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const {
std::string CanonicalPath = getCanonicalPath(HeaderPath);
HeaderHandle H = 0;
for (auto I = HeaderPaths.begin(), E = HeaderPaths.end(); I != E;
++I, ++H) {
if (*I == CanonicalPath)
return H;
}
return HeaderHandleInvalid;
}
HeaderHandle addHeader(llvm::StringRef HeaderPath) {
std::string CanonicalPath = getCanonicalPath(HeaderPath);
HeaderHandle H = findHeaderHandle(CanonicalPath);
if (H == HeaderHandleInvalid) {
H = HeaderPaths.size();
HeaderPaths.push_back(addString(CanonicalPath));
}
return H;
}
StringHandle getHeaderFilePath(HeaderHandle H) const {
if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size()))
return HeaderPaths[H];
return StringHandle();
}
InclusionPathHandle pushHeaderHandle(HeaderHandle H) {
HeaderStack.push_back(H);
return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
}
void popHeaderHandle() {
if (HeaderStack.size() != 0) {
HeaderStack.pop_back();
CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack);
}
}
HeaderHandle getCurrentHeaderHandle() const {
if (HeaderStack.size() != 0)
return HeaderStack.back();
return HeaderHandleInvalid;
}
bool isHeaderHandleInStack(HeaderHandle H) const {
return llvm::is_contained(HeaderStack, H);
}
InclusionPathHandle
findInclusionPathHandle(const std::vector<HeaderHandle> &Path) const {
InclusionPathHandle H = 0;
for (auto I = InclusionPaths.begin(), E = InclusionPaths.end(); I != E;
++I, ++H) {
if (I->Path == Path)
return H;
}
return HeaderHandleInvalid;
}
InclusionPathHandle
addInclusionPathHandle(const std::vector<HeaderHandle> &Path) {
InclusionPathHandle H = findInclusionPathHandle(Path);
if (H == HeaderHandleInvalid) {
H = InclusionPaths.size();
InclusionPaths.push_back(HeaderInclusionPath(Path));
}
return H;
}
InclusionPathHandle getCurrentInclusionPathHandle() const {
return CurrentInclusionPathHandle;
}
const std::vector<HeaderHandle> &
getInclusionPath(InclusionPathHandle H) const {
if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size()))
return InclusionPaths[H].Path;
static std::vector<HeaderHandle> Empty;
return Empty;
}
void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
clang::SourceLocation InstanceLoc,
clang::SourceLocation DefinitionLoc,
clang::IdentifierInfo *II,
llvm::StringRef MacroUnexpanded,
llvm::StringRef MacroExpanded,
InclusionPathHandle InclusionPathHandle) {
if (InNestedHeader)
return;
StringHandle MacroName = addString(II->getName());
PPItemKey InstanceKey(PP, MacroName, H, InstanceLoc);
PPItemKey DefinitionKey(PP, MacroName, H, DefinitionLoc);
auto I = MacroExpansions.find(InstanceKey);
if (I == MacroExpansions.end()) {
std::string InstanceSourceLine =
getSourceLocationString(PP, InstanceLoc) + ":\n" +
getSourceLine(PP, InstanceLoc) + "\n";
std::string DefinitionSourceLine =
getSourceLocationString(PP, DefinitionLoc) + ":\n" +
getSourceLine(PP, DefinitionLoc) + "\n";
MacroExpansions[InstanceKey] = MacroExpansionTracker(
addString(MacroUnexpanded), addString(MacroExpanded),
addString(InstanceSourceLine), DefinitionKey,
addString(DefinitionSourceLine), InclusionPathHandle);
} else {
MacroExpansionTracker &CondTracker = I->second;
MacroExpansionInstance *MacroInfo =
CondTracker.findMacroExpansionInstance(addString(MacroExpanded),
DefinitionKey);
if (MacroInfo)
MacroInfo->addInclusionPathHandle(InclusionPathHandle);
else {
std::string DefinitionSourceLine =
getSourceLocationString(PP, DefinitionLoc) + ":\n" +
getSourceLine(PP, DefinitionLoc) + "\n";
CondTracker.addMacroExpansionInstance(
addString(MacroExpanded), DefinitionKey,
addString(DefinitionSourceLine), InclusionPathHandle);
}
}
}
void
addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H,
clang::SourceLocation InstanceLoc,
clang::tok::PPKeywordKind DirectiveKind,
clang::PPCallbacks::ConditionValueKind ConditionValue,
llvm::StringRef ConditionUnexpanded,
InclusionPathHandle InclusionPathHandle) {
if (InNestedHeader)
return;
StringHandle ConditionUnexpandedHandle(addString(ConditionUnexpanded));
PPItemKey InstanceKey(PP, ConditionUnexpandedHandle, H, InstanceLoc);
auto I = ConditionalExpansions.find(InstanceKey);
if (I == ConditionalExpansions.end()) {
std::string InstanceSourceLine =
getSourceLocationString(PP, InstanceLoc) + ":\n" +
getSourceLine(PP, InstanceLoc) + "\n";
ConditionalExpansions[InstanceKey] =
ConditionalTracker(DirectiveKind, ConditionValue,
ConditionUnexpandedHandle, InclusionPathHandle);
} else {
ConditionalTracker &CondTracker = I->second;
ConditionalExpansionInstance *MacroInfo =
CondTracker.findConditionalExpansionInstance(ConditionValue);
if (MacroInfo)
MacroInfo->addInclusionPathHandle(InclusionPathHandle);
else {
CondTracker.addConditionalExpansionInstance(ConditionValue,
InclusionPathHandle);
}
}
}
bool reportInconsistentMacros(llvm::raw_ostream &OS) override {
bool ReturnValue = false;
for (auto I = MacroExpansions.begin(), E = MacroExpansions.end(); I != E;
++I) {
const PPItemKey &ItemKey = I->first;
MacroExpansionTracker &MacroExpTracker = I->second;
if (!MacroExpTracker.hasMismatch())
continue;
ReturnValue = true;
OS << MacroExpTracker.InstanceSourceLine;
if (ItemKey.Column > 0)
OS << std::string(ItemKey.Column - 1, ' ') << "^\n";
OS << "error: Macro instance '" << MacroExpTracker.MacroUnexpanded
<< "' has different values in this header, depending on how it was "
"included.\n";
for (auto IMT = MacroExpTracker.MacroExpansionInstances.begin(),
EMT = MacroExpTracker.MacroExpansionInstances.end();
IMT != EMT; ++IMT) {
MacroExpansionInstance &MacroInfo = *IMT;
OS << " '" << MacroExpTracker.MacroUnexpanded << "' expanded to: '"
<< MacroInfo.MacroExpanded
<< "' with respect to these inclusion paths:\n";
for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
EIP = MacroInfo.InclusionPathHandles.end();
IIP != EIP; ++IIP) {
const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
auto Count = (int)ip.size();
for (int Index = 0; Index < Count; ++Index) {
HeaderHandle H = ip[Index];
OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
<< "\n";
}
}
if (MacroInfo.DefinitionLocation.Line != ItemKey.Line) {
OS << MacroInfo.DefinitionSourceLine;
if (MacroInfo.DefinitionLocation.Column > 0)
OS << std::string(MacroInfo.DefinitionLocation.Column - 1, ' ')
<< "^\n";
OS << "Macro defined here.\n";
} else
OS << "(no macro definition)"
<< "\n";
}
}
return ReturnValue;
}
bool reportInconsistentConditionals(llvm::raw_ostream &OS) override {
bool ReturnValue = false;
for (auto I = ConditionalExpansions.begin(),
E = ConditionalExpansions.end();
I != E; ++I) {
const PPItemKey &ItemKey = I->first;
ConditionalTracker &CondTracker = I->second;
if (!CondTracker.hasMismatch())
continue;
ReturnValue = true;
OS << HeaderPaths[ItemKey.File] << ":" << ItemKey.Line << ":"
<< ItemKey.Column << "\n";
OS << "#" << getDirectiveSpelling(CondTracker.DirectiveKind) << " "
<< CondTracker.ConditionUnexpanded << "\n";
OS << "^\n";
OS << "error: Conditional expression instance '"
<< CondTracker.ConditionUnexpanded
<< "' has different values in this header, depending on how it was "
"included.\n";
for (auto IMT = CondTracker.ConditionalExpansionInstances.begin(),
EMT = CondTracker.ConditionalExpansionInstances.end();
IMT != EMT; ++IMT) {
ConditionalExpansionInstance &MacroInfo = *IMT;
OS << " '" << CondTracker.ConditionUnexpanded << "' expanded to: '"
<< ConditionValueKindStrings[MacroInfo.ConditionValue]
<< "' with respect to these inclusion paths:\n";
for (auto IIP = MacroInfo.InclusionPathHandles.begin(),
EIP = MacroInfo.InclusionPathHandles.end();
IIP != EIP; ++IIP) {
const std::vector<HeaderHandle> &ip = getInclusionPath(*IIP);
auto Count = (int)ip.size();
for (int Index = 0; Index < Count; ++Index) {
HeaderHandle H = ip[Index];
OS << std::string((Index * 2) + 4, ' ') << getHeaderFilePath(H)
<< "\n";
}
}
}
}
return ReturnValue;
}
static const char *getDirectiveSpelling(clang::tok::PPKeywordKind kind) {
switch (kind) {
case clang::tok::pp_if:
return "if";
case clang::tok::pp_elif:
return "elif";
case clang::tok::pp_ifdef:
return "ifdef";
case clang::tok::pp_ifndef:
return "ifndef";
default:
return "(unknown)";
}
}
private:
llvm::SmallVector<std::string, 32> HeaderList;
bool BlockCheckHeaderListOnly;
llvm::StringSet<> Strings;
std::vector<StringHandle> HeaderPaths;
std::vector<HeaderHandle> HeaderStack;
std::vector<HeaderInclusionPath> InclusionPaths;
InclusionPathHandle CurrentInclusionPathHandle;
llvm::SmallSet<HeaderHandle, 32> HeadersInThisCompile;
std::vector<PPItemKey> IncludeDirectives;
MacroExpansionMap MacroExpansions;
ConditionalExpansionMap ConditionalExpansions;
bool InNestedHeader;
};
}
PreprocessorTracker::~PreprocessorTracker() {}
PreprocessorTracker *PreprocessorTracker::create(
llvm::SmallVector<std::string, 32> &Headers,
bool DoBlockCheckHeaderListOnly) {
return new PreprocessorTrackerImpl(Headers, DoBlockCheckHeaderListOnly);
}
void PreprocessorCallbacks::InclusionDirective(
clang::SourceLocation HashLoc, const clang::Token &IncludeTok,
llvm::StringRef FileName, bool IsAngled,
clang::CharSourceRange FilenameRange, clang::OptionalFileEntryRef File,
llvm::StringRef SearchPath, llvm::StringRef RelativePath,
const clang::Module *SuggestedModule, bool ModuleImported,
clang::SrcMgr::CharacteristicKind FileType) {
int DirectiveLine, DirectiveColumn;
std::string HeaderPath = getSourceLocationFile(PP, HashLoc);
getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn);
PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn,
FileName);
}
void PreprocessorCallbacks::FileChanged(
clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason,
clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) {
switch (Reason) {
case EnterFile:
PPTracker.handleHeaderEntry(PP, getSourceLocationFile(PP, Loc));
break;
case ExitFile: {
clang::OptionalFileEntryRef F =
PP.getSourceManager().getFileEntryRefForID(PrevFID);
if (F)
PPTracker.handleHeaderExit(F->getName());
} break;
case SystemHeaderPragma:
case RenameFile:
break;
}
}
void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD,
clang::SourceRange Range,
const clang::MacroArgs *Args) {
clang::SourceLocation Loc = Range.getBegin();
if (!Loc.isFileID())
return;
clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
const clang::MacroInfo *MI = MD.getMacroInfo();
std::string MacroName = II->getName().str();
std::string Unexpanded(getMacroUnexpandedString(Range, PP, MacroName, MI));
std::string Expanded(getMacroExpandedString(PP, MacroName, MI, Args));
PPTracker.addMacroExpansionInstance(
PP, PPTracker.getCurrentHeaderHandle(), Loc, MI->getDefinitionLoc(), II,
Unexpanded, Expanded, PPTracker.getCurrentInclusionPathHandle());
}
void PreprocessorCallbacks::Defined(const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD,
clang::SourceRange Range) {
clang::SourceLocation Loc(Range.getBegin());
clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo();
const clang::MacroInfo *MI = MD.getMacroInfo();
std::string MacroName = II->getName().str();
std::string Unexpanded(getSourceString(PP, Range));
PPTracker.addMacroExpansionInstance(
PP, PPTracker.getCurrentHeaderHandle(), Loc,
(MI ? MI->getDefinitionLoc() : Loc), II, Unexpanded,
(MI ? "true" : "false"), PPTracker.getCurrentInclusionPathHandle());
}
void PreprocessorCallbacks::If(clang::SourceLocation Loc,
clang::SourceRange ConditionRange,
clang::PPCallbacks::ConditionValueKind ConditionResult) {
std::string Unexpanded(getSourceString(PP, ConditionRange));
PPTracker.addConditionalExpansionInstance(
PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_if,
ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
}
void PreprocessorCallbacks::Elif(clang::SourceLocation Loc,
clang::SourceRange ConditionRange,
clang::PPCallbacks::ConditionValueKind ConditionResult,
clang::SourceLocation IfLoc) {
std::string Unexpanded(getSourceString(PP, ConditionRange));
PPTracker.addConditionalExpansionInstance(
PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_elif,
ConditionResult, Unexpanded, PPTracker.getCurrentInclusionPathHandle());
}
void PreprocessorCallbacks::Ifdef(clang::SourceLocation Loc,
const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD) {
clang::PPCallbacks::ConditionValueKind IsDefined =
(MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
PPTracker.addConditionalExpansionInstance(
PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifdef,
IsDefined, PP.getSpelling(MacroNameTok),
PPTracker.getCurrentInclusionPathHandle());
}
void PreprocessorCallbacks::Ifndef(clang::SourceLocation Loc,
const clang::Token &MacroNameTok,
const clang::MacroDefinition &MD) {
clang::PPCallbacks::ConditionValueKind IsNotDefined =
(!MD ? clang::PPCallbacks::CVK_True : clang::PPCallbacks::CVK_False );
PPTracker.addConditionalExpansionInstance(
PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef,
IsNotDefined, PP.getSpelling(MacroNameTok),
PPTracker.getCurrentInclusionPathHandle());
}
}