diff options
author | John Thompson <John.Thompson.JTSoftware@gmail.com> | 2013-09-18 18:19:43 +0000 |
---|---|---|
committer | John Thompson <John.Thompson.JTSoftware@gmail.com> | 2013-09-18 18:19:43 +0000 |
commit | 740839260b15c75ef1503683cac19c40e9bd1b66 (patch) | |
tree | 042862ea58573abe22e4698c751587220d3fd984 /clang-tools-extra/modularize/PreprocessorTracker.cpp | |
parent | 0742ce9cf7f6b1cb60db2cca9cb551b4b98bedf3 (diff) | |
download | bcm5719-llvm-740839260b15c75ef1503683cac19c40e9bd1b66.tar.gz bcm5719-llvm-740839260b15c75ef1503683cac19c40e9bd1b66.zip |
Check for #include in extern and namespace blocks.
llvm-svn: 190950
Diffstat (limited to 'clang-tools-extra/modularize/PreprocessorTracker.cpp')
-rw-r--r-- | clang-tools-extra/modularize/PreprocessorTracker.cpp | 152 |
1 files changed, 149 insertions, 3 deletions
diff --git a/clang-tools-extra/modularize/PreprocessorTracker.cpp b/clang-tools-extra/modularize/PreprocessorTracker.cpp index c0f20043479..391afbe0151 100644 --- a/clang-tools-extra/modularize/PreprocessorTracker.cpp +++ b/clang-tools-extra/modularize/PreprocessorTracker.cpp @@ -7,7 +7,7 @@ // //===--------------------------------------------------------------------===// // -// The Basic Idea +// The Basic Idea (Macro and Conditional Checking) // // Basically we install a PPCallbacks-derived object to track preprocessor // activity, namely when a header file is entered/exited, when a macro @@ -49,7 +49,17 @@ // ^ // Macro defined here. // -// Design and Implementation Details +// The Basic Idea ('Extern "C/C++" {}' Or 'namespace {}') With Nested +// '#include' Checking) +// +// To check for '#include' directives nested inside 'Extern "C/C++" {}' +// or 'namespace {}' blocks, we keep track of the '#include' directives +// while running the preprocessor, and later during a walk of the AST +// we call a function to check for any '#include' directies inside +// an 'Extern "C/C++" {}' or 'namespace {}' block, given its source +// range. +// +// Design and Implementation Details (Macro and Conditional Checking) // // A PreprocessorTrackerImpl class implements the PreprocessorTracker // interface. It uses a PreprocessorCallbacks class derived from PPCallbacks @@ -205,6 +215,22 @@ // to make clearer the separate reporting phases, I could add an output // message marking the phases. // +// Design and Implementation Details ('Extern "C/C++" {}' Or +// 'namespace {}') With Nested '#include' Checking) +// +// We override the InclusionDirective in PPCallbacks to record information +// about each '#include' directive encountered during preprocessing. +// We co-opt the PPItemKey class to store the information about each +// '#include' directive, including the source file name containing the +// directive, the name of the file being included, and the source line +// and column of the directive. We store these object in a vector, +// after first check to see if an entry already exists. +// +// Later, while the AST is being walked for other checks, we provide +// visit handlers for 'extern "C/C++" {}' and 'namespace (name) {}' +// blocks, checking to see if any '#include' directives occurred +// within the blocks, reporting errors if any found. +// // Future Directions // // We probably should add options to disable any of the checks, in case @@ -285,7 +311,7 @@ std::string getSourceString(clang::Preprocessor &PP, clang::SourceRange Range) { return llvm::StringRef(BeginPtr, Length).trim().str(); } -// Retrieve source line from file image. +// Retrieve source line from file image given a location. std::string getSourceLine(clang::Preprocessor &PP, clang::SourceLocation Loc) { const llvm::MemoryBuffer *MemBuffer = PP.getSourceManager().getBuffer(PP.getSourceManager().getFileID(Loc)); @@ -310,6 +336,39 @@ std::string getSourceLine(clang::Preprocessor &PP, clang::SourceLocation Loc) { return llvm::StringRef(BeginPtr, Length).str(); } +// Retrieve source line from file image given a file ID and line number. +std::string getSourceLine(clang::Preprocessor &PP, clang::FileID FileID, + int Line) { + const llvm::MemoryBuffer *MemBuffer = PP.getSourceManager().getBuffer(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(); +} + // 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 @@ -765,6 +824,14 @@ public: ~PreprocessorCallbacks() {} // Overridden handlers. + void InclusionDirective(clang::SourceLocation HashLoc, + const clang::Token &IncludeTok, + llvm::StringRef FileName, bool IsAngled, + clang::CharSourceRange FilenameRange, + const clang::FileEntry *File, + llvm::StringRef SearchPath, + llvm::StringRef RelativePath, + const clang::Module *Imported); void FileChanged(clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, clang::SrcMgr::CharacteristicKind FileType, @@ -821,6 +888,70 @@ public: // Handle exiting a preprocessing session. void handlePreprocessorExit() { HeaderStack.clear(); } + // Handle include directive. + // This function is called every time an include directive is seen by the + // preprocessor, for the purpose of later checking for 'extern "" {}' or + // "namespace {}" blocks containing #include directives. + void handleIncludeDirective(llvm::StringRef DirectivePath, int DirectiveLine, + int DirectiveColumn, llvm::StringRef TargetPath) { + HeaderHandle CurrentHeaderHandle = findHeaderHandle(DirectivePath); + StringHandle IncludeHeaderHandle = addString(TargetPath); + for (std::vector<PPItemKey>::const_iterator I = IncludeDirectives.begin(), + E = IncludeDirectives.end(); + I != E; ++I) { + // If we already have an entry for this directive, return now. + if ((I->File == CurrentHeaderHandle) && (I->Line == DirectiveLine)) + return; + } + PPItemKey IncludeDirectiveItem(IncludeHeaderHandle, CurrentHeaderHandle, + DirectiveLine, DirectiveColumn); + IncludeDirectives.push_back(IncludeDirectiveItem); + } + + // Check for include directives within the given source line range. + // Report errors if any found. Returns true if no include directives + // found in block. + bool checkForIncludesInBlock(clang::Preprocessor &PP, + clang::SourceRange BlockSourceRange, + const char *BlockIdentifierMessage, + llvm::raw_ostream &OS) { + clang::SourceLocation BlockStartLoc = BlockSourceRange.getBegin(); + clang::SourceLocation BlockEndLoc = BlockSourceRange.getEnd(); + // Use block location to get FileID of both the include directive + // and block statement. + clang::FileID FileID = PP.getSourceManager().getFileID(BlockStartLoc); + std::string SourcePath = getSourceLocationFile(PP, BlockStartLoc); + HeaderHandle SourceHandle = findHeaderHandle(SourcePath); + 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 we find an entry within the block, report an error. + 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; + } + // Handle entering a header source file. void handleHeaderEntry(clang::Preprocessor &PP, llvm::StringRef HeaderPath) { // Ignore <built-in> and <command-line> to reduce message clutter. @@ -1176,6 +1307,7 @@ private: std::vector<HeaderInclusionPath> InclusionPaths; InclusionPathHandle CurrentInclusionPathHandle; llvm::SmallSet<HeaderHandle, 128> HeadersInThisCompile; + std::vector<PPItemKey> IncludeDirectives; MacroExpansionMap MacroExpansions; ConditionalExpansionMap ConditionalExpansions; bool InNestedHeader; @@ -1193,6 +1325,20 @@ PreprocessorTracker *PreprocessorTracker::create() { // Preprocessor callbacks for modularize. +// Handle include directive. +void PreprocessorCallbacks::InclusionDirective( + clang::SourceLocation HashLoc, const clang::Token &IncludeTok, + llvm::StringRef FileName, bool IsAngled, + clang::CharSourceRange FilenameRange, const clang::FileEntry *File, + llvm::StringRef SearchPath, llvm::StringRef RelativePath, + const clang::Module *Imported) { + int DirectiveLine, DirectiveColumn; + std::string HeaderPath = getSourceLocationFile(PP, HashLoc); + getSourceLocationLineAndColumn(PP, HashLoc, DirectiveLine, DirectiveColumn); + PPTracker.handleIncludeDirective(HeaderPath, DirectiveLine, DirectiveColumn, + FileName); +} + // Handle file entry/exit. void PreprocessorCallbacks::FileChanged( clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, |