[clang-tools-extra] r190950 - Check for #include in extern and namespace blocks.
John Thompson
John.Thompson.JTSoftware at gmail.com
Wed Sep 18 11:19:43 PDT 2013
Author: jtsoftware
Date: Wed Sep 18 13:19:43 2013
New Revision: 190950
URL: http://llvm.org/viewvc/llvm-project?rev=190950&view=rev
Log:
Check for #include in extern and namespace blocks.
Added:
clang-tools-extra/trunk/test/modularize/Inputs/Empty.h
clang-tools-extra/trunk/test/modularize/Inputs/IncludeInExtern.h
clang-tools-extra/trunk/test/modularize/Inputs/IncludeInNamespace.h
clang-tools-extra/trunk/test/modularize/ProblemsExternC.modularize
clang-tools-extra/trunk/test/modularize/ProblemsNamespace.modularize
Modified:
clang-tools-extra/trunk/modularize/Modularize.cpp
clang-tools-extra/trunk/modularize/PreprocessorTracker.cpp
clang-tools-extra/trunk/modularize/PreprocessorTracker.h
Modified: clang-tools-extra/trunk/modularize/Modularize.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/modularize/Modularize.cpp?rev=190950&r1=190949&r2=190950&view=diff
==============================================================================
--- clang-tools-extra/trunk/modularize/Modularize.cpp (original)
+++ clang-tools-extra/trunk/modularize/Modularize.cpp Wed Sep 18 13:19:43 2013
@@ -75,6 +75,19 @@
// ^
// Macro defined here.
//
+// Checks will also be performed for '#include' directives that are
+// nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
+// and can produce error message like the following:
+//
+// IncludeInExtern.h:2:3
+// #include "Empty.h"
+// ^
+// error: Include directive within extern "C" {}.
+// IncludeInExtern.h:1:1
+// extern "C" {
+// ^
+// The "extern "C" {}" block is here.
+//
// See PreprocessorTracker.cpp for additional details.
//
// Future directions:
@@ -84,18 +97,15 @@
//
// Some ideas:
//
-// 1. Check for and warn about "#include" directives inside 'extern "C/C++" {}'
-// and "namespace (name) {}" blocks.
-//
-// 2. Omit duplicate "not always provided" messages
+// 1. Omit duplicate "not always provided" messages
//
-// 3. Add options to disable any of the checks, in case
+// 2. Add options to disable any of the checks, in case
// there is some problem with them, or the messages get too verbose.
//
-// 4. Try to figure out the preprocessor conditional directives that
+// 3. Try to figure out the preprocessor conditional directives that
// contribute to problems and tie them to the inconsistent definitions.
//
-// 5. There are some legitimate uses of preprocessor macros that
+// 4. There are some legitimate uses of preprocessor macros that
// modularize will flag as errors, such as repeatedly #include'ing
// a file and using interleaving defined/undefined macros
// to change declarations in the included file. Is there a way
@@ -103,7 +113,7 @@
// to ignore. Otherwise you can just exclude the file, after checking
// for legitimate errors.
//
-// 6. What else?
+// 5. What else?
//
// General clean-up and refactoring:
//
@@ -453,8 +463,11 @@ private:
class CollectEntitiesVisitor
: public RecursiveASTVisitor<CollectEntitiesVisitor> {
public:
- CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities)
- : SM(SM), Entities(Entities) {}
+ CollectEntitiesVisitor(SourceManager &SM, EntityMap &Entities,
+ Preprocessor &PP, PreprocessorTracker &PPTracker,
+ int &HadErrors)
+ : SM(SM), Entities(Entities), PP(PP), PPTracker(PPTracker),
+ HadErrors(HadErrors) {}
bool TraverseStmt(Stmt *S) { return true; }
bool TraverseType(QualType T) { return true; }
@@ -478,6 +491,42 @@ public:
bool TraverseConstructorInitializer(CXXCtorInitializer *Init) { return true; }
bool TraverseLambdaCapture(LambdaExpr::Capture C) { return true; }
+ // Check 'extern "*" {}' block for #include directives.
+ bool VisitLinkageSpecDecl(LinkageSpecDecl *D) {
+ // Bail if not a block.
+ if (!D->hasBraces())
+ return true;
+ SourceRange BlockRange = D->getSourceRange();
+ const char *LinkageLabel;
+ switch (D->getLanguage()) {
+ case LinkageSpecDecl::lang_c:
+ LinkageLabel = "extern \"C\" {}";
+ break;
+ case LinkageSpecDecl::lang_cxx:
+ LinkageLabel = "extern \"C++\" {}";
+ break;
+ default:
+ LinkageLabel = "extern \"\" {}";
+ }
+ if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, LinkageLabel,
+ errs()))
+ HadErrors = 1;
+ return true;
+ }
+
+ // Check 'namespace (name) {}' block for #include directives.
+ bool VisitNamespaceDecl(const NamespaceDecl *D) {
+ SourceRange BlockRange = D->getSourceRange();
+ std::string Label("namespace ");
+ Label += D->getName();
+ Label += " {}";
+ if (!PPTracker.checkForIncludesInBlock(PP, BlockRange, Label.c_str(),
+ errs()))
+ HadErrors = 1;
+ return true;
+ }
+
+ // Collect definition entities.
bool VisitNamedDecl(NamedDecl *ND) {
// We only care about file-context variables.
if (!ND->getDeclContext()->isFileContext())
@@ -517,14 +566,18 @@ public:
private:
SourceManager &SM;
EntityMap &Entities;
+ Preprocessor &PP;
+ PreprocessorTracker &PPTracker;
+ int &HadErrors;
};
class CollectEntitiesConsumer : public ASTConsumer {
public:
CollectEntitiesConsumer(EntityMap &Entities,
PreprocessorTracker &preprocessorTracker,
- Preprocessor &PP, StringRef InFile)
- : Entities(Entities), PPTracker(preprocessorTracker), PP(PP) {
+ Preprocessor &PP, StringRef InFile, int &HadErrors)
+ : Entities(Entities), PPTracker(preprocessorTracker), PP(PP),
+ HadErrors(HadErrors) {
PPTracker.handlePreprocessorEntry(PP, InFile);
}
@@ -534,7 +587,7 @@ public:
SourceManager &SM = Ctx.getSourceManager();
// Collect declared entities.
- CollectEntitiesVisitor(SM, Entities)
+ CollectEntitiesVisitor(SM, Entities, PP, PPTracker, HadErrors)
.TraverseDecl(Ctx.getTranslationUnitDecl());
// Collect macro definitions.
@@ -556,39 +609,46 @@ private:
EntityMap &Entities;
PreprocessorTracker &PPTracker;
Preprocessor &PP;
+ int &HadErrors;
};
class CollectEntitiesAction : public SyntaxOnlyAction {
public:
CollectEntitiesAction(EntityMap &Entities,
- PreprocessorTracker &preprocessorTracker)
- : Entities(Entities), PPTracker(preprocessorTracker) {}
+ PreprocessorTracker &preprocessorTracker,
+ int &HadErrors)
+ : Entities(Entities), PPTracker(preprocessorTracker),
+ HadErrors(HadErrors) {}
protected:
virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) {
return new CollectEntitiesConsumer(Entities, PPTracker,
- CI.getPreprocessor(), InFile);
+ CI.getPreprocessor(), InFile, HadErrors);
}
private:
EntityMap &Entities;
PreprocessorTracker &PPTracker;
+ int &HadErrors;
};
class ModularizeFrontendActionFactory : public FrontendActionFactory {
public:
ModularizeFrontendActionFactory(EntityMap &Entities,
- PreprocessorTracker &preprocessorTracker)
- : Entities(Entities), PPTracker(preprocessorTracker) {}
+ PreprocessorTracker &preprocessorTracker,
+ int &HadErrors)
+ : Entities(Entities), PPTracker(preprocessorTracker),
+ HadErrors(HadErrors) {}
virtual CollectEntitiesAction *create() {
- return new CollectEntitiesAction(Entities, PPTracker);
+ return new CollectEntitiesAction(Entities, PPTracker, HadErrors);
}
private:
EntityMap &Entities;
PreprocessorTracker &PPTracker;
+ int &HadErrors;
};
int main(int Argc, const char **Argv) {
@@ -626,8 +686,9 @@ int main(int Argc, const char **Argv) {
EntityMap Entities;
ClangTool Tool(*Compilations, Headers);
Tool.appendArgumentsAdjuster(new AddDependenciesAdjuster(Dependencies));
- int HadErrors =
- Tool.run(new ModularizeFrontendActionFactory(Entities, *PPTracker));
+ int HadErrors = 0;
+ HadErrors |= Tool.run(
+ new ModularizeFrontendActionFactory(Entities, *PPTracker, HadErrors));
// Create a place to save duplicate entity locations, separate bins per kind.
typedef SmallVector<Location, 8> LocationArray;
Modified: clang-tools-extra/trunk/modularize/PreprocessorTracker.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/modularize/PreprocessorTracker.cpp?rev=190950&r1=190949&r2=190950&view=diff
==============================================================================
--- clang-tools-extra/trunk/modularize/PreprocessorTracker.cpp (original)
+++ clang-tools-extra/trunk/modularize/PreprocessorTracker.cpp Wed Sep 18 13:19:43 2013
@@ -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::Prepr
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::Preproc
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
// 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,
Modified: clang-tools-extra/trunk/modularize/PreprocessorTracker.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/modularize/PreprocessorTracker.h?rev=190950&r1=190949&r2=190950&view=diff
==============================================================================
--- clang-tools-extra/trunk/modularize/PreprocessorTracker.h (original)
+++ clang-tools-extra/trunk/modularize/PreprocessorTracker.h Wed Sep 18 13:19:43 2013
@@ -52,6 +52,22 @@ public:
// object is destroyed.)
virtual void handlePreprocessorExit() = 0;
+ // 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.
+ virtual void handleIncludeDirective(llvm::StringRef DirectivePath,
+ int DirectiveLine, int DirectiveColumn,
+ llvm::StringRef TargetPath) = 0;
+
+ // Check for include directives within the given source line range.
+ // Report errors if any found. Returns true if no include directives
+ // found in block.
+ virtual bool checkForIncludesInBlock(clang::Preprocessor &PP,
+ clang::SourceRange BlockSourceRange,
+ const char *BlockIdentifierMessage,
+ llvm::raw_ostream &OS) = 0;
+
// Report on inconsistent macro instances.
// Returns true if any mismatches.
virtual bool reportInconsistentMacros(llvm::raw_ostream &OS) = 0;
Added: clang-tools-extra/trunk/test/modularize/Inputs/Empty.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/modularize/Inputs/Empty.h?rev=190950&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/modularize/Inputs/Empty.h (added)
+++ clang-tools-extra/trunk/test/modularize/Inputs/Empty.h Wed Sep 18 13:19:43 2013
@@ -0,0 +1 @@
+// Empty header for testing #include directives in blocks.
Added: clang-tools-extra/trunk/test/modularize/Inputs/IncludeInExtern.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/modularize/Inputs/IncludeInExtern.h?rev=190950&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/modularize/Inputs/IncludeInExtern.h (added)
+++ clang-tools-extra/trunk/test/modularize/Inputs/IncludeInExtern.h Wed Sep 18 13:19:43 2013
@@ -0,0 +1,3 @@
+extern "C" {
+ #include "Empty.h"
+}
Added: clang-tools-extra/trunk/test/modularize/Inputs/IncludeInNamespace.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/modularize/Inputs/IncludeInNamespace.h?rev=190950&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/modularize/Inputs/IncludeInNamespace.h (added)
+++ clang-tools-extra/trunk/test/modularize/Inputs/IncludeInNamespace.h Wed Sep 18 13:19:43 2013
@@ -0,0 +1,3 @@
+namespace MyNamespace {
+ #include "Empty.h"
+}
Added: clang-tools-extra/trunk/test/modularize/ProblemsExternC.modularize
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/modularize/ProblemsExternC.modularize?rev=190950&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/modularize/ProblemsExternC.modularize (added)
+++ clang-tools-extra/trunk/test/modularize/ProblemsExternC.modularize Wed Sep 18 13:19:43 2013
@@ -0,0 +1,12 @@
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/IncludeInExtern.h
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInExtern.h:2:3
+# CHECK-NEXT: #include "Empty.h"
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Include directive within extern "C" {}.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInExtern.h:1:1
+# CHECK-NEXT: extern "C" {
+# CHECK-NEXT: ^
+# CHECK-NEXT: The "extern "C" {}" block is here.
Added: clang-tools-extra/trunk/test/modularize/ProblemsNamespace.modularize
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/modularize/ProblemsNamespace.modularize?rev=190950&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/modularize/ProblemsNamespace.modularize (added)
+++ clang-tools-extra/trunk/test/modularize/ProblemsNamespace.modularize Wed Sep 18 13:19:43 2013
@@ -0,0 +1,12 @@
+# RUN: not modularize %s -x c++ 2>&1 | FileCheck %s
+
+Inputs/IncludeInNamespace.h
+
+# CHECK: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInNamespace.h:2:3
+# CHECK-NEXT: #include "Empty.h"
+# CHECK-NEXT: ^
+# CHECK-NEXT: error: Include directive within namespace MyNamespace {}.
+# CHECK-NEXT: {{.*}}{{[/\\]}}Inputs{{[/\\]}}IncludeInNamespace.h:1:1
+# CHECK-NEXT: namespace MyNamespace {
+# CHECK-NEXT: ^
+# CHECK-NEXT: The "namespace MyNamespace {}" block is here.
More information about the cfe-commits
mailing list