Index: modularize/PreprocessorTracker.h =================================================================== --- modularize/PreprocessorTracker.h (revision 0) +++ modularize/PreprocessorTracker.h (revision 0) @@ -0,0 +1,53 @@ +//===- PreprocessorTracker.h - Tracks preprocessor activities -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +/// +/// \file +/// \brief Track preprocessor activities for modularize. +/// +//===--------------------------------------------------------------------===// + +#ifndef MODULARIZE_PREPROCESSOR_TRACKER_H +#define MODULARIZE_PREPROCESSOR_TRACKER_H + +#include "clang/Lex/Preprocessor.h" + +namespace Modularize { + +// Preprocessor tracker for modularize. +// +// This class stores information about all the headers processed in the +// course of running modularize. +class PreprocessorTracker { +public: + virtual ~PreprocessorTracker(); + + // Handle entering a preprocessing session. + // (Called after a Preprocessor object is created, but before preprocessing.) + virtual void handlePreprocessorEntry(clang::Preprocessor &PP, + llvm::StringRef RootHeaderFile) = 0; + // Handle exiting a preprocessing session. + // (Called after preprocessing is complete, but before the Preprocessor + // object is destroyed.) + virtual void handlePreprocessorExit() = 0; + + // Report on inconsistent macro instances. + // Returns true if any mismatches. + virtual bool reportInconsistentMacros(llvm::raw_ostream &OS) = 0; + + // Report on inconsistent conditional directive instances. + // Returns true if any mismatches. + virtual bool reportInconsistentConditionals(llvm::raw_ostream &OS) = 0; + + // Create instance of PreprocessorTracker. + static PreprocessorTracker *create(); +}; + +} // end namespace Modularize + +#endif Index: modularize/PreprocessorTracker.cpp =================================================================== --- modularize/PreprocessorTracker.cpp (revision 0) +++ modularize/PreprocessorTracker.cpp (revision 0) @@ -0,0 +1,1031 @@ +//=- PreprocessorTracker.cpp - Preprocessor tracking -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// + +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/MacroArgs.h" +#include "clang/Lex/PPCallbacks.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/StringPool.h" +#include "PreprocessorTracker.h" + +namespace Modularize { + +// Forwards. +class PreprocessorTrackerImpl; + +// Some handle types + +// String handle. +typedef llvm::PooledStringPtr StringHandle; + +// Header handle. +typedef int HeaderHandle; +const HeaderHandle HeaderHandleInvalid = -1; + +// Header inclusion path handle. +typedef int InclusionPathHandle; +const InclusionPathHandle InclusionPathHandleInvalid = -1; + +// Some utility functions. + +// Get a "file:line:column" source location string. +static std::string getSourceLocationString(clang::Preprocessor &PP, + clang::SourceLocation Loc) { + if (Loc.isInvalid()) + return std::string("(none)"); + else + return Loc.printToString(PP.getSourceManager()); +} + +// Get just the file name from a source location. +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); +} + +// Get just the line and column from a source location. +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(); +} + +// Retrieve source snippet from file image. +std::string getSourceString(clang::Preprocessor &PP, clang::SourceRange Range) { + clang::SourceLocation bLoc = Range.getBegin(); + clang::SourceLocation eLoc = Range.getEnd(); + const char *bPtr = PP.getSourceManager().getCharacterData(bLoc); + const char *ePtr = PP.getSourceManager().getCharacterData(eLoc); + size_t length = ePtr - bPtr; + return llvm::StringRef(bPtr, length).trim().str(); +} + +// Retrieve source line from file image. +std::string getSourceLine(clang::Preprocessor &PP, clang::SourceLocation Loc) { + const llvm::MemoryBuffer *memoryBuffer = + PP.getSourceManager().getBuffer(PP.getSourceManager().getFileID(Loc)); + const char *buffer = memoryBuffer->getBufferStart(); + const char *bufferEnd = memoryBuffer->getBufferEnd(); + const char *bPtr = PP.getSourceManager().getCharacterData(Loc); + const char *ePtr = bPtr; + while (bPtr > buffer) { + if (*bPtr == '\n') { + bPtr++; + break; + } + bPtr--; + } + while (ePtr < bufferEnd) { + if (*ePtr == '\n') { + break; + } + ePtr++; + } + size_t length = ePtr - bPtr; + return llvm::StringRef(bPtr, length).str(); +} + +// Get the string for the unexpanded macro instance. +// The soureRange is expected to end at the last token +// for the macro instance, which in the case of a function-style +// macro will be a ')', but for an object-style macro, it +// will be the macro name itself. +std::string getMacroUnexpandedString(clang::SourceRange Range, + clang::Preprocessor &PP, + llvm::StringRef MacroName, + const clang::MacroInfo *MI) { + clang::SourceLocation bLoc(Range.getBegin()); + const char *bPtr = PP.getSourceManager().getCharacterData(bLoc); + size_t length; + std::string unexpanded; + if (MI->isFunctionLike()) { + clang::SourceLocation eLoc(Range.getEnd()); + const char *ePtr = PP.getSourceManager().getCharacterData(eLoc) + 1; + length = (ePtr - bPtr) + 1; // +1 is ')' width. + } else + length = MacroName.size(); + return llvm::StringRef(bPtr, length).trim().str(); +} + +// Get the expansion for a macro instance, given the information +// provided by PPCallbacks. +std::string getMacroExpandedString(clang::Preprocessor &PP, + llvm::StringRef MacroName, + const clang::MacroInfo *MI, + const clang::MacroArgs *Args) { + std::string expanded; + // Walk over the macro tokens. + typedef clang::MacroInfo::tokens_iterator Iter; + for (Iter I = MI->tokens_begin(), E = MI->tokens_end(); I != E; ++I) { + clang::IdentifierInfo *II = I->getIdentifierInfo(); + int ArgNo = (II && Args ? MI->getArgumentNum(II) : -1); + if (ArgNo == -1) { + // This isn't an argument, just add it. + if (II == NULL) + expanded += PP.getSpelling((*I)); // Not an identifier. + else { + // Token is for an identifier. + std::string name = II->getName().str(); + // Check for nexted macro references. + clang::MacroInfo *mi = PP.getMacroInfo(II); + if (mi != NULL) + expanded += getMacroExpandedString(PP, name, mi, NULL); + else + expanded += name; + } + continue; + } + // We get here if it's a function-style macro with arguments. + const clang::Token *ResultArgToks; + const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo); + if (Args->ArgNeedsPreexpansion(ArgTok, PP)) + ResultArgToks = &(const_cast(Args)) + ->getPreExpArgument(ArgNo, MI, PP)[0]; + else + ResultArgToks = ArgTok; // Use non-preexpanded tokens. + // If the arg token didn't expand into anything, ignore it. + if (ResultArgToks->is(clang::tok::eof)) + continue; + unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks); + // Append the resulting argument expansions. + for (unsigned ai = 0; ai < NumToks; ++ai) { + const clang::Token &AT = ResultArgToks[ai]; + clang::IdentifierInfo *II = AT.getIdentifierInfo(); + if (II == NULL) + expanded += PP.getSpelling(AT); // Not an identifier. + else { + // It's an identifier. Check for further expansion. + std::string name = II->getName().str(); + clang::MacroInfo *mi = PP.getMacroInfo(II); + if (mi != NULL) + expanded += getMacroExpandedString(PP, name, mi, NULL); + else + expanded += name; + } + } + } + return expanded; +} + +// Get the string representing a vector of tokens. +std::string +getTokensSpellingString(clang::Preprocessor &PP, + llvm::SmallVectorImpl &tokens) { + std::string expanded; + // Walk over the macro tokens. + typedef llvm::SmallVectorImpl::iterator Iter; + for (Iter I = tokens.begin(), E = tokens.end(); I != E; ++I) + expanded += PP.getSpelling(*I); // Not an identifier. + return llvm::StringRef(expanded).trim().str(); +} + +// Get the expansion for a macro instance, given the information +// provided by PPCallbacks. +std::string getExpandedString(clang::Preprocessor &PP, + llvm::StringRef MacroName, + const clang::MacroInfo *MI, + const clang::MacroArgs *Args) { + std::string expanded; + // Walk over the macro tokens. + typedef clang::MacroInfo::tokens_iterator Iter; + for (Iter I = MI->tokens_begin(), E = MI->tokens_end(); I != E; ++I) { + clang::IdentifierInfo *II = I->getIdentifierInfo(); + int ArgNo = (II && Args ? MI->getArgumentNum(II) : -1); + if (ArgNo == -1) { + // This isn't an argument, just add it. + if (II == NULL) + expanded += PP.getSpelling((*I)); // Not an identifier. + else { + // Token is for an identifier. + std::string name = II->getName().str(); + // Check for nexted macro references. + clang::MacroInfo *mi = PP.getMacroInfo(II); + if (mi != NULL) + expanded += getMacroExpandedString(PP, name, mi, NULL); + else + expanded += name; + } + continue; + } + // We get here if it's a function-style macro with arguments. + const clang::Token *ResultArgToks; + const clang::Token *ArgTok = Args->getUnexpArgument(ArgNo); + if (Args->ArgNeedsPreexpansion(ArgTok, PP)) + ResultArgToks = &(const_cast(Args)) + ->getPreExpArgument(ArgNo, MI, PP)[0]; + else + ResultArgToks = ArgTok; // Use non-preexpanded tokens. + // If the arg token didn't expand into anything, ignore it. + if (ResultArgToks->is(clang::tok::eof)) + continue; + unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks); + // Append the resulting argument expansions. + for (unsigned ai = 0; ai < NumToks; ++ai) { + const clang::Token &AT = ResultArgToks[ai]; + clang::IdentifierInfo *II = AT.getIdentifierInfo(); + if (II == NULL) + expanded += PP.getSpelling(AT); // Not an identifier. + else { + // It's an identifier. Check for further expansion. + std::string name = II->getName().str(); + clang::MacroInfo *mi = PP.getMacroInfo(II); + if (mi != NULL) + expanded += getMacroExpandedString(PP, name, mi, NULL); + else + expanded += name; + } + } + } + return expanded; +} + +// We need some operator overloads for string handles. +bool operator==(const StringHandle &H1, const StringHandle &H2) { + const char *s1 = (H1 ? *H1 : ""); + const char *s2 = (H2 ? *H2 : ""); + int diff = strcmp(s1, s2); + return diff == 0; +} +bool operator!=(const StringHandle &H1, const StringHandle &H2) { + const char *s1 = (H1 ? *H1 : ""); + const char *s2 = (H2 ? *H2 : ""); + int diff = strcmp(s1, s2); + return diff != 0; +} +bool operator<(const StringHandle &H1, const StringHandle &H2) { + const char *s1 = (H1 ? *H1 : ""); + const char *s2 = (H2 ? *H2 : ""); + int diff = strcmp(s1, s2); + return diff < 0; +} +bool operator>(const StringHandle &H1, const StringHandle &H2) { + const char *s1 = (H1 ? *H1 : ""); + const char *s2 = (H2 ? *H2 : ""); + int diff = strcmp(s1, s2); + return diff > 0; +} + +// Preprocessor item key. +// +// This class represents a location in a source file, for use +// as a key representing a unique file/line/column triplet, +// which in this case is used to identify a macro expansion instance, +// but could be used for other things as well. +// The file is a header file handle, the line is a line number, +// and the column is a column number. +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; +}; + +// Header inclusion path. +class HeaderInclusionPath { +public: + HeaderInclusionPath(std::vector HeaderInclusionPath) + : Path(HeaderInclusionPath) {} + HeaderInclusionPath(const HeaderInclusionPath &Other) : Path(Other.Path) {} + HeaderInclusionPath() {} + std::vector Path; +}; + +// Macro expansion instance. +// +// This class represents an instance of a macro expansion with a +// unique value. It also stores the unique header inclusion paths +// for use in telling the user the nested include path f +class MacroExpansionInstance { +public: + MacroExpansionInstance(StringHandle MacroExpanded, + PPItemKey &DefinitionLocation, + StringHandle DefinitionSourceLine, + InclusionPathHandle H) + : MacroExpanded(MacroExpanded), DefinitionLocation(DefinitionLocation), + DefinitionSourceLine(DefinitionSourceLine) { + InclusionPathHandles.push_back(H); + } + MacroExpansionInstance() {} + + // Check for the presence of a header inclusion path handle entry. + // Return false if not found. + bool haveInclusionPathHandle(InclusionPathHandle H) { + for (std::vector::iterator + I = InclusionPathHandles.begin(), + E = InclusionPathHandles.end(); + I != E; ++I) { + if (*I == H) + return true; + } + return InclusionPathHandleInvalid; + } + // Add a new header inclusion path entry, if not already present. + void addInclusionPathHandle(InclusionPathHandle H) { + if (!haveInclusionPathHandle(H)) + InclusionPathHandles.push_back(H); + } + + // A string representing the macro instance after preprocessing. + StringHandle MacroExpanded; + // A file/line/column triplet representing the macro definition location. + PPItemKey DefinitionLocation; + // A place to save the macro definition line string. + StringHandle DefinitionSourceLine; + // The header inclusion path handles for all the instances. + std::vector InclusionPathHandles; +}; + +// Macro expansion instance tracker. +// +// This class represents one macro expansion, keyed by a PPItemKey. +// It stores a string representing the macro reference in the source, +// and a list of MacroExpansionInstance objects representing +// the unique value the macro expands to in instances of the header. +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() {} + + // Find a matching macro expansion instance. + MacroExpansionInstance * + findMacroExpansionInstance(StringHandle MacroExpanded, + PPItemKey &DefinitionLocation) { + for (std::vector::iterator + I = MacroExpansionInstances.begin(), + E = MacroExpansionInstances.end(); + I != E; ++I) { + if ((I->MacroExpanded == MacroExpanded) && + (I->DefinitionLocation == DefinitionLocation)) { + return &*I; // Found. + } + } + return NULL; // Not found. + } + + // Add a macro expansion instance. + void addMacroExpansionInstance(StringHandle MacroExpanded, + PPItemKey &DefinitionLocation, + StringHandle DefinitionSourceLine, + InclusionPathHandle InclusionPathHandle) { + MacroExpansionInstances.push_back( + MacroExpansionInstance(MacroExpanded, DefinitionLocation, + DefinitionSourceLine, InclusionPathHandle)); + } + + // Return true if there is a mismatch. + bool hasMismatch() { return MacroExpansionInstances.size() > 1; } + + // A string representing the macro instance without expansion. + StringHandle MacroUnexpanded; + // A place to save the macro instance source line string. + StringHandle InstanceSourceLine; + // The macro expansion instances. + // If all instances of the macro expansion expand to the same value, + // This vector will only have one instance. + std::vector MacroExpansionInstances; +}; + +// Conditional expansion instance. +// +// This class represents an instance of a macro expansion with a +// unique value. It also stores the unique header inclusion paths +// for use in telling the user the nested include path f +class ConditionalExpansionInstance { +public: + ConditionalExpansionInstance(bool ConditionValue, InclusionPathHandle H) + : ConditionValue(ConditionValue) { + InclusionPathHandles.push_back(H); + } + ConditionalExpansionInstance() {} + + // Check for the presence of a header inclusion path handle entry. + // Return false if not found. + bool haveInclusionPathHandle(InclusionPathHandle H) { + for (std::vector::iterator + I = InclusionPathHandles.begin(), + E = InclusionPathHandles.end(); + I != E; ++I) { + if (*I == H) + return true; + } + return InclusionPathHandleInvalid; + } + // Add a new header inclusion path entry, if not already present. + void addInclusionPathHandle(InclusionPathHandle H) { + if (!haveInclusionPathHandle(H)) + InclusionPathHandles.push_back(H); + } + + // A flag representing the evaluated condition value. + bool ConditionValue; + // The header inclusion path handles for all the instances. + std::vector InclusionPathHandles; +}; + +// Conditional directive instance tracker. +// +// This class represents one conditional directive, keyed by a PPItemKey. +// It stores a string representing the macro reference in the source, +// and a list of MacroExpansionInstance objects representing +// the unique value the macro expands to in instances of the header. +class ConditionalTracker { +public: + ConditionalTracker(clang::tok::PPKeywordKind DirectiveKind, + bool ConditionValue, StringHandle ConditionUnexpanded, + InclusionPathHandle InclusionPathHandle) + : DirectiveKind(DirectiveKind), ConditionUnexpanded(ConditionUnexpanded) { + addConditionalExpansionInstance(ConditionValue, InclusionPathHandle); + } + ConditionalTracker() {} + + // Find a matching condition expansion instance. + ConditionalExpansionInstance * + findConditionalExpansionInstance(bool ConditionValue) { + for (std::vector::iterator + I = ConditionalExpansionInstances.begin(), + E = ConditionalExpansionInstances.end(); + I != E; ++I) { + if (I->ConditionValue == ConditionValue) { + return &*I; // Found. + } + } + return NULL; // Not found. + } + + // Add a conditional expansion instance. + void + addConditionalExpansionInstance(bool ConditionValue, + InclusionPathHandle InclusionPathHandle) { + ConditionalExpansionInstances.push_back( + ConditionalExpansionInstance(ConditionValue, InclusionPathHandle)); + } + + // Return true if there is a mismatch. + bool hasMismatch() { return ConditionalExpansionInstances.size() > 1; } + + // The kind of directive. + clang::tok::PPKeywordKind DirectiveKind; + // A string representing the macro instance without expansion. + StringHandle ConditionUnexpanded; + // The condition expansion instances. + // If all instances of the conditional expression expand to the same value, + // This vector will only have one instance. + std::vector ConditionalExpansionInstances; +}; + +// Preprocessor callbacks for modularize. +// +// This class derives from the Clang PPCallbacks class to track preprocessor +// actions, such as changing files and handling preprocessor directives and +// macro expansions. It has to figure out when a new header file is entered +// and left, as the provided handler is not particularly clear about it. +class PreprocessorCallbacks : public clang::PPCallbacks { +public: + PreprocessorCallbacks(PreprocessorTrackerImpl &ppTracker, + clang::Preprocessor &pp, llvm::StringRef rootHeaderFile) + : PPTracker(ppTracker), PP(pp), RootHeaderFile(rootHeaderFile) {} + ~PreprocessorCallbacks() {} + + // Overidden handlers. + void FileChanged(clang::SourceLocation Loc, + clang::PPCallbacks::FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID = clang::FileID()); + void MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, clang::SourceRange Range, + const clang::MacroArgs *Args); + void Defined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, clang::SourceRange Range); + void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + bool ConditionResult); + void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + bool ConditionResult, clang::SourceLocation IfLoc); + void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDirective *MD); + void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDirective *MD); + +private: + PreprocessorTrackerImpl &PPTracker; + clang::Preprocessor &PP; + std::string RootHeaderFile; +}; + +// Preprocessor macro expansion item map types. +typedef std::map MacroExpansionMap; +typedef std::map::iterator +MacroExpansionMapIter; + +// Preprocessor conditional expansion item map types. +typedef std::map ConditionalExpansionMap; +typedef std::map::iterator +ConditionalExpansionMapIter; + +// Preprocessor tracker for modularize. +// +// This class stores information about all the headers processed in the +// course of running modularize. +class PreprocessorTrackerImpl : public PreprocessorTracker { +public: + PreprocessorTrackerImpl() + : CurrentInclusionPathHandle(InclusionPathHandleInvalid) {} + ~PreprocessorTrackerImpl() {} + + // Handle entering a preprocessing session. + void handlePreprocessorEntry(clang::Preprocessor &PP, + llvm::StringRef rootHeaderFile) { + assert((HeaderStack.size() == 0) && "Header stack should be empty."); + pushHeaderHandle(addHeader(rootHeaderFile)); + PP.addPPCallbacks(new PreprocessorCallbacks(*this, PP, rootHeaderFile)); + } + // Handle exiting a preprocessing session. + void handlePreprocessorExit() { HeaderStack.clear(); } + + // Handle entering a header source file. + void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) { + // Ignore and to reduce message clutter. + if (HeaderPath.startswith("<")) + return; + HeaderHandle h = addHeader(HeaderPath); + if (h != getCurrentHeaderHandle()) + pushHeaderHandle(h); + } + // Handle exiting a header source file. + void handleHeaderExit(llvm::StringRef HeaderPath) { + // Ignore and to reduce message clutter. + if (HeaderPath.startswith("<")) + return; + HeaderHandle h = findHeaderHandle(HeaderPath); + if (isHeaderHandleInStack(h)) { + while ((h != getCurrentHeaderHandle()) && (HeaderStack.size() != 0)) + popHeaderHandle(); + } + } + + // Lookup/add string. + StringHandle addString(llvm::StringRef Str) { return Strings.intern(Str); } + + // Get the handle of a header file entry. + // Return HeaderHandleInvalid if not found. + HeaderHandle findHeaderHandle(llvm::StringRef HeaderPath) const { + HeaderHandle h = 0; + for (std::vector::const_iterator I = HeaderPaths.begin(), + E = HeaderPaths.end(); + I != E; ++I, ++h) { + if (**I == HeaderPath) + return h; + } + return HeaderHandleInvalid; + } + + // Add a new header file entry, or return existing handle. + // Return the header handle. + HeaderHandle addHeader(llvm::StringRef HeaderPath) { + std::string canonicalPath(HeaderPath); + std::replace(canonicalPath.begin(), canonicalPath.end(), '\\', '/'); + HeaderHandle h = findHeaderHandle(canonicalPath); + if (h == HeaderHandleInvalid) { + h = HeaderPaths.size(); + HeaderPaths.push_back(addString(canonicalPath)); + } + return h; + } + + // Return a header file path string given its handle. + StringHandle getHeaderFilePath(HeaderHandle H) const { + if ((H >= 0) && (H < (HeaderHandle)HeaderPaths.size())) + return HeaderPaths[H]; + return StringHandle(); + } + + // Returns a handle to the inclusion path. + InclusionPathHandle pushHeaderHandle(HeaderHandle H) { + HeaderStack.push_back(H); + return CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack); + } + // Pops the last header handle from the stack; + void popHeaderHandle() { + // assert((HeaderStack.size() != 0) && "Header stack already empty."); + if (HeaderStack.size() != 0) { + HeaderStack.pop_back(); + CurrentInclusionPathHandle = addInclusionPathHandle(HeaderStack); + } + } + // Get the top handle on the header stack. + HeaderHandle getCurrentHeaderHandle() const { + if (HeaderStack.size() != 0) + return HeaderStack.back(); + return HeaderHandleInvalid; + } + + // Check for presence of header handle in the header stack. + bool isHeaderHandleInStack(HeaderHandle H) const { + for (std::vector::const_iterator I = HeaderStack.begin(), + E = HeaderStack.end(); + I != E; ++I) { + if (*I == H) + return true; + } + return false; + } + + // Get the handle of a header inclusion path entry. + // Return InclusionPathHandleInvalid if not found. + InclusionPathHandle + findInclusionPathHandle(const std::vector &Path) const { + InclusionPathHandle h = 0; + for (std::vector::const_iterator + I = InclusionPaths.begin(), + E = InclusionPaths.end(); + I != E; ++I, ++h) { + if (I->Path == Path) + return h; + } + return HeaderHandleInvalid; + } + // Add a new header inclusion path entry, or return existing handle. + // Return the header inclusion path entry handle. + InclusionPathHandle + addInclusionPathHandle(const std::vector &Path) { + InclusionPathHandle h = findInclusionPathHandle(Path); + if (h == HeaderHandleInvalid) { + h = InclusionPaths.size(); + InclusionPaths.push_back(HeaderInclusionPath(Path)); + } + return h; + } + // Return the current inclusion path handle. + InclusionPathHandle getCurrentInclusionPathHandle() const { + return CurrentInclusionPathHandle; + } + + // Return an inclusion path given its handle. + const std::vector & + getInclusionPath(InclusionPathHandle H) const { + if ((H >= 0) && (H <= (InclusionPathHandle)InclusionPaths.size())) + return InclusionPaths[H].Path; + static std::vector empty; + return empty; + } + + // Add a macro expansion instance. + void addMacroExpansionInstance(clang::Preprocessor &PP, HeaderHandle H, + clang::SourceLocation InstanceLoc, + clang::SourceLocation DefinitionLoc, + clang::IdentifierInfo *II, + llvm::StringRef MacroUnexpanded, + llvm::StringRef MacroExpanded, + InclusionPathHandle InclusionPathHandle) { + StringHandle macroName = addString(II->getName()); + PPItemKey instanceKey(PP, macroName, H, InstanceLoc); + PPItemKey definitionKey(PP, macroName, H, DefinitionLoc); + MacroExpansionMapIter 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 &mt = I->second; + MacroExpansionInstance *mi = mt.findMacroExpansionInstance( + addString(MacroExpanded), definitionKey); + if (mi != NULL) + mi->addInclusionPathHandle(InclusionPathHandle); + else { + std::string definitionSourceLine = + getSourceLocationString(PP, DefinitionLoc) + ":\n" + + getSourceLine(PP, DefinitionLoc) + "\n"; + mt.addMacroExpansionInstance(addString(MacroExpanded), definitionKey, + addString(definitionSourceLine), + InclusionPathHandle); + } + } + } + + // Add a conditional expansion instance. + void + addConditionalExpansionInstance(clang::Preprocessor &PP, HeaderHandle H, + clang::SourceLocation InstanceLoc, + clang::tok::PPKeywordKind DirectiveKind, + bool ConditionValue, + llvm::StringRef ConditionUnexpanded, + InclusionPathHandle InclusionPathHandle) { + StringHandle conditionUnexpanded(addString(ConditionUnexpanded)); + PPItemKey instanceKey(PP, conditionUnexpanded, H, InstanceLoc); + ConditionalExpansionMapIter I = ConditionalExpansions.find(instanceKey); + if (I == ConditionalExpansions.end()) { + std::string instanceSourceLine = + getSourceLocationString(PP, InstanceLoc) + ":\n" + + getSourceLine(PP, InstanceLoc) + "\n"; + ConditionalExpansions[instanceKey] = + ConditionalTracker(DirectiveKind, ConditionValue, conditionUnexpanded, + InclusionPathHandle); + } else { + ConditionalTracker &mt = I->second; + ConditionalExpansionInstance *mi = + mt.findConditionalExpansionInstance(ConditionValue); + if (mi != NULL) + mi->addInclusionPathHandle(InclusionPathHandle); + else { + mt.addConditionalExpansionInstance(ConditionValue, InclusionPathHandle); + } + } + } + + // Report on inconsistent macro instances. + // Returns true if any mismatches. + bool reportInconsistentMacros(llvm::raw_ostream &OS) { + bool returnValue = false; + for (MacroExpansionMapIter I = MacroExpansions.begin(), + E = MacroExpansions.end(); + I != E; ++I) { + const PPItemKey &ik = I->first; + MacroExpansionTracker &mt = I->second; + if (!mt.hasMismatch()) + continue; + returnValue = true; + OS << *mt.InstanceSourceLine; + if (ik.Column > 0) + OS << std::string(ik.Column - 1, ' ') << "^\n"; + OS << "error: Macro instance '" << *mt.MacroUnexpanded + << "' has different values in this header, depending on how it was " + "included.\n"; + for (std::vector::iterator + IMT = mt.MacroExpansionInstances.begin(), + EMT = mt.MacroExpansionInstances.end(); + IMT != EMT; ++IMT) { + MacroExpansionInstance &mi = *IMT; + OS << " '" << *mt.MacroUnexpanded << "' expanded to: '" + << *mi.MacroExpanded << "' with respect to these inclusion paths:\n"; + for (std::vector::iterator + IIP = mi.InclusionPathHandles.begin(), + EIP = mi.InclusionPathHandles.end(); + IIP != EIP; ++IIP) { + const std::vector &ip = getInclusionPath(*IIP); + int c = (int)ip.size(); + for (int i = 0; i < c; ++i) { + HeaderHandle h = ip[i]; + OS << std::string((i * 2) + 4, ' ') << *getHeaderFilePath(h) + << "\n"; + } + } + // For a macro that wasn't defined, we flag it by using the + // instance location. + // If there is a definition... + if (mi.DefinitionLocation.Line != ik.Line) { + OS << *mi.DefinitionSourceLine; + if (mi.DefinitionLocation.Column > 0) + OS << std::string(mi.DefinitionLocation.Column - 1, ' ') << "^\n"; + OS << "Macro defined here.\n"; + } else + OS << "(no macro definition)" + << "\n"; + } + } + return returnValue; + } + + // Report on inconsistent macro instances. + // Returns true if any mismatches. + bool reportInconsistentConditionals(llvm::raw_ostream &OS) { + bool returnValue = false; + for (ConditionalExpansionMapIter I = ConditionalExpansions.begin(), + E = ConditionalExpansions.end(); + I != E; ++I) { + const PPItemKey &ik = I->first; + ConditionalTracker &mt = I->second; + if (!mt.hasMismatch()) + continue; + returnValue = true; + OS << *HeaderPaths[ik.File] << ":" << ik.Line << ":" << ik.Column << "\n"; + OS << "#" << getDirectiveSpelling(mt.DirectiveKind) << " " + << *mt.ConditionUnexpanded << "\n"; + OS << "^\n"; + OS << "error: Conditional expression instance '" + << *mt.ConditionUnexpanded + << "' has different values in this header, depending on how it was " + "included.\n"; + for (std::vector::iterator + IMT = mt.ConditionalExpansionInstances.begin(), + EMT = mt.ConditionalExpansionInstances.end(); + IMT != EMT; ++IMT) { + ConditionalExpansionInstance &mi = *IMT; + OS << " '" << *mt.ConditionUnexpanded << "' expanded to: '" + << (mi.ConditionValue ? "true" : "false") + << "' with respect to these inclusion paths:\n"; + for (std::vector::iterator + IIP = mi.InclusionPathHandles.begin(), + EIP = mi.InclusionPathHandles.end(); + IIP != EIP; ++IIP) { + const std::vector &ip = getInclusionPath(*IIP); + int c = (int)ip.size(); + for (int i = 0; i < c; ++i) { + HeaderHandle h = ip[i]; + OS << std::string((i * 2) + 4, ' ') << *getHeaderFilePath(h) + << "\n"; + } + } + } + } + return returnValue; + } + + // Get directive spelling. + 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::StringPool Strings; + std::vector HeaderPaths; + std::vector HeaderStack; + std::vector InclusionPaths; + InclusionPathHandle CurrentInclusionPathHandle; + MacroExpansionMap MacroExpansions; + ConditionalExpansionMap ConditionalExpansions; +}; + +// PreprocessorTracker functions. + +// PreprocessorTracker desctructor. +PreprocessorTracker::~PreprocessorTracker() {} + +// Create instance of PreprocessorTracker. +PreprocessorTracker *PreprocessorTracker::create() { + return new PreprocessorTrackerImpl(); +} + +// Preprocessor callbacks for modularize. + +// Handle file entry/exit. +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: + if (PrevFID.isInvalid()) + PPTracker.handleHeaderExit(RootHeaderFile); + else + PPTracker.handleHeaderExit(getSourceLocationFile(PP, Loc)); + break; + case SystemHeaderPragma: + return; + case RenameFile: + return; + default: + return; + } +} + +// Handle macro expansion. +void PreprocessorCallbacks::MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, + clang::SourceRange Range, + const clang::MacroArgs *Args) { + clang::SourceLocation loc = Range.getBegin(); + clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); + const clang::MacroInfo *MI = PP.getMacroInfo(II); + 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::MacroDirective *MD, + clang::SourceRange Range) { + clang::SourceLocation loc(Range.getBegin()); + clang::IdentifierInfo *II = MacroNameTok.getIdentifierInfo(); + const clang::MacroInfo *MI = PP.getMacroInfo(II); + 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, + bool 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, + bool 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::MacroDirective *MD) { + bool isDefined = (MD != 0); + 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::MacroDirective *MD) { + bool isNotDefined = (MD == 0); + PPTracker.addConditionalExpansionInstance( + PP, PPTracker.getCurrentHeaderHandle(), Loc, clang::tok::pp_ifndef, + isNotDefined, PP.getSpelling(MacroNameTok), + PPTracker.getCurrentInclusionPathHandle()); +} +} Index: modularize/Modularize.cpp =================================================================== --- modularize/Modularize.cpp (revision 186675) +++ modularize/Modularize.cpp (working copy) @@ -90,10 +90,12 @@ #include #include #include +#include "PreprocessorTracker.h" using namespace clang::tooling; using namespace clang; using namespace llvm; +using namespace Modularize; // Option to specify a file name for a list of header files to check. cl::opt @@ -382,9 +384,15 @@ class CollectEntitiesConsumer : public ASTConsumer { public: - CollectEntitiesConsumer(EntityMap &Entities, Preprocessor &PP) - : Entities(Entities), PP(PP) {} + CollectEntitiesConsumer(EntityMap &Entities, + PreprocessorTracker &preprocessorTracker, + Preprocessor &PP, StringRef InFile) + : Entities(Entities), PPTracker(preprocessorTracker), PP(PP) { + PPTracker.handlePreprocessorEntry(PP, InFile); + } + ~CollectEntitiesConsumer() { PPTracker.handlePreprocessorExit(); } + virtual void HandleTranslationUnit(ASTContext &Ctx) { SourceManager &SM = Ctx.getSourceManager(); @@ -409,33 +417,41 @@ private: EntityMap &Entities; + PreprocessorTracker &PPTracker; Preprocessor &PP; }; class CollectEntitiesAction : public SyntaxOnlyAction { public: - CollectEntitiesAction(EntityMap &Entities) : Entities(Entities) {} + CollectEntitiesAction(EntityMap &Entities, + PreprocessorTracker &preprocessorTracker) + : Entities(Entities), PPTracker(preprocessorTracker) {} protected: virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return new CollectEntitiesConsumer(Entities, CI.getPreprocessor()); + return new CollectEntitiesConsumer(Entities, PPTracker, + CI.getPreprocessor(), InFile); } private: EntityMap &Entities; + PreprocessorTracker &PPTracker; }; class ModularizeFrontendActionFactory : public FrontendActionFactory { public: - ModularizeFrontendActionFactory(EntityMap &Entities) : Entities(Entities) {} + ModularizeFrontendActionFactory(EntityMap &Entities, + PreprocessorTracker &preprocessorTracker) + : Entities(Entities), PPTracker(preprocessorTracker) {} virtual CollectEntitiesAction *create() { - return new CollectEntitiesAction(Entities); + return new CollectEntitiesAction(Entities, PPTracker); } private: EntityMap &Entities; + PreprocessorTracker &PPTracker; }; int main(int argc, const char **argv) { @@ -464,10 +480,14 @@ Compilations.reset( new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); + // Create preprocessor tracker, to watch for macro and conditional problems. + OwningPtr PPTracker(PreprocessorTracker::create()); + // Parse all of the headers, detecting duplicates. EntityMap Entities; ClangTool Tool(*Compilations, Headers); - int HadErrors = Tool.run(new ModularizeFrontendActionFactory(Entities)); + int HadErrors = + Tool.run(new ModularizeFrontendActionFactory(Entities, *PPTracker)); // Create a place to save duplicate entity locations, separate bins per kind. typedef SmallVector LocationArray; @@ -515,6 +535,16 @@ } } + // Complain about macro instance in header files that differ based on how + // they are included. + if (PPTracker->reportInconsistentMacros(errs())) + HadErrors = 1; + + // Complain about preprocessor conditional directives in header files that + // differ based on how they are included. + if (PPTracker->reportInconsistentConditionals(errs())) + HadErrors = 1; + // Complain about any headers that have contents that differ based on how // they are included. // FIXME: Could we provide information about which preprocessor conditionals @@ -530,7 +560,7 @@ HadErrors = 1; errs() << "error: header '" << H->first->getName() - << "' has different contents depending on how it was included\n"; + << "' has different contents depending on how it was included.\n"; for (unsigned I = 0, N = H->second.size(); I != N; ++I) { errs() << "note: '" << H->second[I].Name << "' in " << H->second[I].Loc.File->getName() << " at " Index: modularize/CMakeLists.txt =================================================================== --- modularize/CMakeLists.txt (revision 186541) +++ modularize/CMakeLists.txt (working copy) @@ -7,6 +7,7 @@ add_clang_executable(modularize Modularize.cpp + PreprocessorTracker.cpp ) target_link_libraries(modularize Index: test/modularize/ProblemsInconsistent.modularize =================================================================== --- test/modularize/ProblemsInconsistent.modularize (revision 186541) +++ test/modularize/ProblemsInconsistent.modularize (working copy) @@ -4,9 +4,105 @@ Inputs/InconsistentHeader2.h # CHECK: error: macro 'SYMBOL' defined at multiple locations: -# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9 -# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:9 -# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9 +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9 +# CHECK-NEXT: error: macro 'FUNC_STYLE' defined at multiple locations: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9 +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9 +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:15:11: +# CHECK-NEXT: int var = FUNC_STYLE(1, 0); +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Macro instance 'FUNC_STYLE(1, 0);' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'FUNC_STYLE(1, 0);' expanded to: '1||0' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:4:9: +# CHECK-NEXT: #define FUNC_STYLE(a, b) a || b +# CHECK-NEXT: ^ +# CHECK-NEXT: Macro defined here. +# CHECK-NEXT: 'FUNC_STYLE(1, 0);' expanded to: '1&&0' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:8:9: +# CHECK-NEXT: #define FUNC_STYLE(a, b) a &&b +# CHECK-NEXT: ^ +# CHECK-NEXT: Macro defined here. +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:5: +# CHECK-NEXT: #if SYMBOL == 1 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Macro instance 'SYMBOL' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'SYMBOL' expanded to: '1' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:3:9: +# CHECK-NEXT: #define SYMBOL 1 +# CHECK-NEXT: ^ +# CHECK-NEXT: Macro defined here. +# CHECK-NEXT: 'SYMBOL' expanded to: '2' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:7:9: +# CHECK-NEXT: #define SYMBOL 2 +# CHECK-NEXT: ^ +# CHECK-NEXT: Macro defined here. +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:5: +# CHECK-NEXT: #if defined(SYMBOL1) +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Macro instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h:3:9: +# CHECK-NEXT: #define SYMBOL1 1 +# CHECK-NEXT: ^ +# CHECK-NEXT: Macro defined here. +# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: (no macro definition) +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:11:2 +# CHECK-NEXT: #if SYMBOL == 1 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Conditional expression instance 'SYMBOL == 1' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'SYMBOL == 1' expanded to: 'true' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: 'SYMBOL == 1' expanded to: 'false' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:2:2 +# CHECK-NEXT: #ifdef SYMBOL1 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Conditional expression instance 'SYMBOL1' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'SYMBOL1' expanded to: 'true' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: 'SYMBOL1' expanded to: 'false' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:6:2 +# CHECK-NEXT: #ifdef SYMBOL2 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Conditional expression instance 'SYMBOL2' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'SYMBOL2' expanded to: 'false' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: 'SYMBOL2' expanded to: 'true' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h:17:2 +# CHECK-NEXT: #if defined(SYMBOL1) +# CHECK-NEXT: ^ +# CHECK-NEXT: error: Conditional expression instance 'defined(SYMBOL1)' has different values in this header, depending on how it was included. +# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'true' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader1.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: 'defined(SYMBOL1)' expanded to: 'false' with respect to these inclusion paths: +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentHeader2.h +# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h +# CHECK-NEXT: error: header '{{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h' has different contents depending on how it was included. # CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 3:9 not always provided -# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 6:9 not always provided -# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 10:13 not always provided +# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 4:9 not always provided +# CHECK-NEXT: note: 'SYMBOL' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 7:9 not always provided +# CHECK-NEXT: note: 'FUNC_STYLE' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 8:9 not always provided +# CHECK-NEXT: note: 'TypeInt' in {{.*}}{{[/\\]}}Inputs{{[/\\]}}InconsistentSubHeader.h at 12:13 not always provided Index: test/modularize/Inputs/InconsistentSubHeader.h =================================================================== --- test/modularize/Inputs/InconsistentSubHeader.h (revision 186541) +++ test/modularize/Inputs/InconsistentSubHeader.h (working copy) @@ -1,11 +1,18 @@ // Set up so TypeInt only defined during InconsistentHeader1.h include. #ifdef SYMBOL1 #define SYMBOL 1 +#define FUNC_STYLE(a, b) a || b #endif #ifdef SYMBOL2 #define SYMBOL 2 +#define FUNC_STYLE(a, b) a &&b #endif #if SYMBOL == 1 typedef int TypeInt; #endif + +int var = FUNC_STYLE(1, 0); + +#if defined(SYMBOL1) +#endif