summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/modularize/PreprocessorTracker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/modularize/PreprocessorTracker.cpp')
-rw-r--r--clang-tools-extra/modularize/PreprocessorTracker.cpp152
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,
OpenPOWER on IntegriCloud