From 1f70bda2234224622bc5e4800abdb87add12f698 Mon Sep 17 00:00:00 2001 From: Vincenzo Eduardo Padulano Date: Thu, 9 Apr 2026 15:07:26 +0200 Subject: [PATCH] [io] Add type mismatch check for TObject-derived cases Adds a check for type mismatch in TFileMerger for the particular case of having a TObject and trying to merge it against other objects of different types. Importantly, this includes also the check when the other object is of a non-TObject-derived type, which would previously lead to a segfault. --- io/io/src/TFileMerger.cxx | 7 ++++++ io/io/test/TFileMergerTests.cxx | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/io/io/src/TFileMerger.cxx b/io/io/src/TFileMerger.cxx index 270515168ed96..fbc7bfbf24e5a 100644 --- a/io/io/src/TFileMerger.cxx +++ b/io/io/src/TFileMerger.cxx @@ -661,6 +661,13 @@ Bool_t TFileMerger::MergeOne(TDirectory *target, TList *sourcelist, Int_t type, if (!hobj) { TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname); if (key2) { + if (strcmp(key2->GetClassName(), keyclassname) != 0) { + Error("MergeRecursive", + "Object type mismatch for key '%s' in file '%s': expected '%s' but found '%s'.", keyname, + nextsource->GetName(), keyclassname, key2->GetClassName()); + nextsource = (TFile *)sourcelist->After(nextsource); + return kFALSE; + } hobj = key2->ReadObj(); if (!hobj) { switch (fErrBehavior) { diff --git a/io/io/test/TFileMergerTests.cxx b/io/io/test/TFileMergerTests.cxx index b32c6e321c3af..db58eedb1a209 100644 --- a/io/io/test/TFileMergerTests.cxx +++ b/io/io/test/TFileMergerTests.cxx @@ -18,6 +18,8 @@ #include "gtest/gtest.h" +#include + static void CreateATuple(TMemFile &file, const char *name, double value) { auto mytree = new TTree(name, "A tree"); @@ -396,3 +398,45 @@ TEST(TFileMerger, MergeSelectiveTutorial) EXPECT_NE(file.Get("ntuple"), nullptr); } } + +TEST(TFileMerger, TypeMismatchErrorTObjectWithNonTObject) +{ + struct PathRAII { + std::string fPath; + PathRAII(const char *path) : fPath(path) {} + ~PathRAII() { std::remove(fPath.c_str()); } + }; + + PathRAII input1("ErrorIfTypeMismatch_input1.root"); + const auto objname{"myobj"}; + PathRAII input2("ErrorIfTypeMismatch_input2.root"); + PathRAII outputfile("ErrorIfTypeMismatch_output.root"); + + // Create two files with objects of different types, one must be a TObject-derived type and other a + // non-TObject-derived type + { + auto f = std::make_unique(input1.fPath.c_str(), "RECREATE"); + auto t = std::make_unique(objname, objname); + f->Write(); + } + + { + auto f = std::make_unique(input2.fPath.c_str(), "RECREATE"); + std::vector v{1, 2, 3}; + f->WriteObject(&v, objname); + } + + // Ensure that TFileMerger detects the type mismatch and errors out. + { + ROOT::TestSupport::CheckDiagsRAII diagRAII; + diagRAII.requiredDiag(kError, "TFileMerger::MergeRecursive", "expected 'TTree' but found 'vector'", + /*matchFullMessage*/ false); + diagRAII.requiredDiag(kError, "TFileMerger::Merge", "error during merge of your ROOT files"); + + TFileMerger fm; + fm.OutputFile(outputfile.fPath.c_str()); + fm.AddFile(input1.fPath.c_str(), /*cpProgress*/ false); + fm.AddFile(input2.fPath.c_str(), /*cpProgress*/ false); + fm.PartialMerge(); + } +}