diff --git a/core/foundation/src/TClassEdit.cxx b/core/foundation/src/TClassEdit.cxx index 280317fbef5cb..9c2ccf48c6969 100644 --- a/core/foundation/src/TClassEdit.cxx +++ b/core/foundation/src/TClassEdit.cxx @@ -1303,9 +1303,16 @@ string TClassEdit::CleanType(const char *typeDesc, int mode, const char **tail) const char* c; for(c=typeDesc;*c;c++) { - if (c[0]==' ') { + if (std::isspace(c[0])) { if (kbl) continue; if (!isalnum(c[ 1]) && c[ 1] !='_') continue; + if (c[0] != ' ') { + // Significant whitespace other than ' ' (e.g. \n in a multi-line + // class name from a selection XML) is replaced by a plain space. + result += ' '; + kbl = 1; + continue; + } } if (kbl && (mode>=2 || parensStack.empty())) { //remove "const' etc... int done = 0; diff --git a/core/foundation/test/testClassEdit.cxx b/core/foundation/test/testClassEdit.cxx index a9215bbbc8f15..f10ef09cc7745 100644 --- a/core/foundation/test/testClassEdit.cxx +++ b/core/foundation/test/testClassEdit.cxx @@ -375,3 +375,20 @@ TEST(TClassEdit, SplitType) auto si = (TStreamerInfo*) c->GetStreamerInfo(); si->ls("noaddr"); } + +TEST(TClassEdit, MultiLineName) +{ + // A class name in a selection XML can span multiple lines. Whitespace + // other than ' ' used to empty out the template arguments it preceded, + // e.g. "Foo<,vector,>" (https://github.com/root-project/root/issues/22359). + const char *name = "Foo<\n std::vector, std::vector,\n\tunsigned int,\n Bar ::Ref>>"; + TClassEdit::TSplitType split(name, (TClassEdit::EModType)(TClassEdit::kLong64 | TClassEdit::kDropStd)); + std::string out; + split.ShortType(out, TClassEdit::kLong64 | TClassEdit::kDropStd); + EXPECT_STREQ("Foo,vector,unsigned int,Bar::Ref > >", out.c_str()); + + // Whitespace that separates two identifiers is significant and must be + // kept as a single plain space. + EXPECT_STREQ("unsigned int", TClassEdit::CleanType("unsigned\nint").c_str()); + EXPECT_STREQ("Foo", TClassEdit::CleanType("\n Foo<\n int\n>").c_str()); +}