@@ -225,6 +225,7 @@ const char *rootClingHelp =
225225#include " clang/Basic/MemoryBufferCache.h"
226226#include " clang/Frontend/CompilerInstance.h"
227227#include " clang/Frontend/FrontendActions.h"
228+ #include " clang/Frontend/FrontendDiagnostic.h"
228229#include " clang/Lex/HeaderSearch.h"
229230#include " clang/Lex/Preprocessor.h"
230231#include " clang/Lex/ModuleMap.h"
@@ -3853,6 +3854,105 @@ static bool FileExists(const char *file)
38533854 return (stat (file, &buf) == 0 );
38543855}
38553856
3857+ // //////////////////////////////////////////////////////////////////////////////
3858+ // / Custom diag client for clang that verifies that each implicitly build module
3859+ // / is a system module. If not, it will let the current rootcling invocation
3860+ // / fail with an error. All other diags beside module build remarks will be
3861+ // / forwarded to the passed child diag client.
3862+ // /
3863+ // / The reason why we need this is that if we built implicitly a C++ module
3864+ // / that belongs to a ROOT dictionary, then we will miss information generated
3865+ // / by rootcling in this file (e.g. the source code comments to annotation
3866+ // / attributes transformation will be missing in the module file).
3867+ class CheckModuleBuildClient : public clang ::DiagnosticConsumer {
3868+ clang::DiagnosticConsumer *fChild ;
3869+ bool fOwnsChild ;
3870+ clang::ModuleMap &fMap ;
3871+
3872+ public:
3873+ CheckModuleBuildClient (clang::DiagnosticConsumer *Child, bool OwnsChild, clang::ModuleMap &Map)
3874+ : fChild (Child), fOwnsChild (OwnsChild), fMap (Map)
3875+ {
3876+ }
3877+
3878+ ~CheckModuleBuildClient ()
3879+ {
3880+ if (fOwnsChild )
3881+ delete fChild ;
3882+ }
3883+
3884+ virtual void HandleDiagnostic (clang::DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) override
3885+ {
3886+ using namespace clang ::diag;
3887+
3888+ // This method catches the module_build remark from clang and checks if
3889+ // the implicitly built module is a system module or not. We only support
3890+ // building system modules implicitly.
3891+
3892+ std::string moduleName;
3893+ const clang::Module *module = nullptr ;
3894+
3895+ // Extract the module from the diag argument with index 0.
3896+ const auto &ID = Info.getID ();
3897+ if (ID == remark_module_build || ID == remark_module_build_done) {
3898+ moduleName = Info.getArgStdStr (0 );
3899+ module = fMap .findModule (moduleName);
3900+ // We should never be able to build a module without having it in the
3901+ // modulemap. Still, let's print a warning that we at least tell the
3902+ // user that this could lead to problems.
3903+ if (!module ) {
3904+ ROOT::TMetaUtils::Warning (0 ,
3905+ " Couldn't find module %s in the available modulemaps. This"
3906+ " prevents us from correctly diagnosing wrongly built modules.\n " ,
3907+ moduleName.c_str ());
3908+ }
3909+ }
3910+
3911+ // Skip the diag only if we build a system module. We still print the diag
3912+ // when building a non-system module as we will print an error below and the
3913+ // user should see the detailed default clang diagnostic.
3914+ bool isSystemModuleDiag = module && module ->IsSystem ;
3915+ if (!isSystemModuleDiag)
3916+ fChild ->HandleDiagnostic (DiagLevel, Info);
3917+
3918+ if (ID == remark_module_build && !isSystemModuleDiag) {
3919+ ROOT::TMetaUtils::Error (0 ,
3920+ " Had to build non-system module %s implicitly. You first need to\n "
3921+ " generate the dictionary for %s or mark the C++ module as a system\n "
3922+ " module if you provided your own system modulemap file:\n "
3923+ " %s [system] { ... }\n " ,
3924+ moduleName.c_str (), moduleName.c_str (), moduleName.c_str ());
3925+ }
3926+ }
3927+
3928+ // All methods below just forward to the child and the default method.
3929+ virtual void clear () override
3930+ {
3931+ fChild ->clear ();
3932+ DiagnosticConsumer::clear ();
3933+ }
3934+
3935+ virtual void BeginSourceFile (const clang::LangOptions &LangOpts, const clang::Preprocessor *PP) override
3936+ {
3937+ fChild ->BeginSourceFile (LangOpts, PP);
3938+ DiagnosticConsumer::BeginSourceFile (LangOpts, PP);
3939+ }
3940+
3941+ virtual void EndSourceFile () override
3942+ {
3943+ fChild ->EndSourceFile ();
3944+ DiagnosticConsumer::EndSourceFile ();
3945+ }
3946+
3947+ virtual void finish () override
3948+ {
3949+ fChild ->finish ();
3950+ DiagnosticConsumer::finish ();
3951+ }
3952+
3953+ virtual bool IncludeInDiagnosticCounts () const override { return fChild ->IncludeInDiagnosticCounts (); }
3954+ };
3955+
38563956// //////////////////////////////////////////////////////////////////////////////
38573957
38583958int RootClingMain (int argc,
@@ -4292,10 +4392,6 @@ int RootClingMain(int argc,
42924392 // the shared library.
42934393 clingArgsInterpreter.push_back (" -fmodules-cache-path=" +
42944394 llvm::sys::path::parent_path (sharedLibraryPathName).str ());
4295-
4296- // We enable remarks in clang for building on-demand modules. This is
4297- // useful to figure out when and why on-demand modules are built by clang.
4298- clingArgsInterpreter.push_back (" -Rmodule-build" );
42994395 }
43004396
43014397 // Convert arguments to a C array and check if they are sane
@@ -4342,9 +4438,27 @@ int RootClingMain(int argc,
43424438 interpPtr = owningInterpPtr.get ();
43434439 }
43444440 cling::Interpreter &interp = *interpPtr;
4441+ clang::CompilerInstance *CI = interp.getCI ();
43454442 // FIXME: Remove this once we switch cling to use the driver. This would handle -fmodules-embed-all-files for us.
4346- interp.getCI ()->getFrontendOpts ().ModulesEmbedAllFiles = true ;
4347- interp.getCI ()->getSourceManager ().setAllFilesAreTransient (true );
4443+ CI->getFrontendOpts ().ModulesEmbedAllFiles = true ;
4444+ CI->getSourceManager ().setAllFilesAreTransient (true );
4445+
4446+ clang::Preprocessor &PP = CI->getPreprocessor ();
4447+ clang::HeaderSearch &headerSearch = PP.getHeaderSearchInfo ();
4448+ clang::ModuleMap &moduleMap = headerSearch.getModuleMap ();
4449+ auto &diags = interp.getDiagnostics ();
4450+
4451+ // Manually enable the module build remarks. We don't enable them via the
4452+ // normal clang command line arg because otherwise we would get remarks for
4453+ // building STL/libc when starting the interpreter in rootcling_stage1.
4454+ // We can't prevent these diags in any other way because we can only attach
4455+ // our own diag client now after the interpreter has already started.
4456+ diags.setSeverity (clang::diag::remark_module_build, clang::diag::Severity::Remark, clang::SourceLocation ());
4457+
4458+ // Attach our own diag client that listens to the module_build remarks from
4459+ // clang to check that we don't build dictionary C++ modules implicitly.
4460+ auto recordingClient = new CheckModuleBuildClient (diags.getClient (), diags.ownsClient (), moduleMap);
4461+ diags.setClient (recordingClient, true );
43484462
43494463 if (ROOT::TMetaUtils::GetErrorIgnoreLevel () == ROOT::TMetaUtils::kInfo ) {
43504464 ROOT::TMetaUtils::Info (0 , " \n " );
@@ -4537,7 +4651,6 @@ int RootClingMain(int argc,
45374651
45384652 // Ignore these #pragmas to suppress "unknown pragma" warnings.
45394653 // See LinkdefReader.cxx.
4540- clang::Preprocessor& PP = interp.getCI ()->getPreprocessor ();
45414654 PP.AddPragmaHandler (new IgnoringPragmaHandler (" link" ));
45424655 PP.AddPragmaHandler (new IgnoringPragmaHandler (" extra_include" ));
45434656 PP.AddPragmaHandler (new IgnoringPragmaHandler (" read" ));
@@ -4650,7 +4763,6 @@ int RootClingMain(int argc,
46504763 ROOT::TMetaUtils::RConstructorTypes constructorTypes;
46514764
46524765 // Select using DictSelection
4653- clang::CompilerInstance *CI = interp.getCI ();
46544766 const unsigned int selRulesInitialSize = selectionRules.Size ();
46554767 if (dictSelection && !onepcm)
46564768 DictSelectionReader dictSelReader (interp, selectionRules, CI->getASTContext (), normCtxt);
0 commit comments