From 85bfa7323f900fb706d66879deba884cee969234 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Fri, 24 Jul 2026 18:07:13 +0200 Subject: [PATCH 1/3] [cling] preprocess source before deciding if script is unnamed macro Fixes https://github.com/root-project/root/issues/15888 Google-AI helped to learn how to use the Lexer --- core/metacling/src/TCling.cxx | 2 +- .../include/cling/Utils/SourceNormalization.h | 16 +++++++++++++++ .../cling/lib/MetaProcessor/MetaProcessor.cpp | 4 ++-- .../cling/lib/Utils/SourceNormalization.cpp | 20 ++++++++++++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 904b85242bc9d..a8df36467e8c1 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -2663,7 +2663,7 @@ Longptr_t TCling::ProcessLine(const char* line, EErrorCode* error/*=0*/) code += codeline + "\n"; } unnamedMacroOpenCurly - = cling::utils::isUnnamedMacro(code, fInterpreter->getCI()->getLangOpts()); + = cling::utils::isUnnamedMacro(code, fInterpreter->getCI()->getSourceManager(), fInterpreter->getCI()->getPreprocessor()); } fCurExecutingMacros.push_back(fname); diff --git a/interpreter/cling/include/cling/Utils/SourceNormalization.h b/interpreter/cling/include/cling/Utils/SourceNormalization.h index dde8cb326cba0..8a5e5a9a752ec 100644 --- a/interpreter/cling/include/cling/Utils/SourceNormalization.h +++ b/interpreter/cling/include/cling/Utils/SourceNormalization.h @@ -20,6 +20,7 @@ namespace clang { class LangOptions; class SourceLocation; class SourceManager; + class Preprocessor; } namespace cling { @@ -29,6 +30,7 @@ namespace utils { /// Unnamed macros contain no function definition, but "prompt-style" code /// surrounded by a set of curly braces. /// + /// \note Preprocessing macros are ignored /// \param source The source code to analyze. /// \param LangOpts - LangOptions to use for lexing. /// \return the position of the unnamed macro's opening '{'; or @@ -36,6 +38,20 @@ namespace utils { size_t isUnnamedMacro(llvm::StringRef source, clang::LangOptions& LangOpts); + ///\brief Determine whether the source is an unnamed macro. + /// + /// Unnamed macros contain no function definition, but "prompt-style" code + /// surrounded by a set of curly braces. + /// + /// \note Preprocessing macros are fully evaluated + /// \param source The source code to analyze. + /// \param sm - source manager to use for lexing. + /// \param pp - preprocessor to use for lexing. + /// \return the position of the unnamed macro's opening '{'; or + /// std::string::npos if this is not an unnamed macro. + size_t isUnnamedMacro(llvm::StringRef source, clang::SourceManager& sm, + clang::Preprocessor& pp); + ///\brief Determine whether the source needs to be moved into a function. /// /// If so, move possible includes directives out of the future body of the diff --git a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp index 701c5555998f0..e233d1a79edf7 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp @@ -433,8 +433,8 @@ namespace cling { } if (posOpenCurly != (size_t)-1 && !content.empty()) { - assert(content[posOpenCurly] == '{' - && "No curly at claimed position of opening curly!"); + assert(posOpenCurly < content.length() && content[posOpenCurly] == '{' && + "No curly at claimed position of opening curly!"); // hide the curly brace: content[posOpenCurly] = ' '; // and the matching closing '}' diff --git a/interpreter/cling/lib/Utils/SourceNormalization.cpp b/interpreter/cling/lib/Utils/SourceNormalization.cpp index c2f1251d1946e..48d813d234472 100644 --- a/interpreter/cling/lib/Utils/SourceNormalization.cpp +++ b/interpreter/cling/lib/Utils/SourceNormalization.cpp @@ -12,6 +12,7 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" #include @@ -443,7 +444,24 @@ cling::utils::isUnnamedMacro(llvm::StringRef source, return std::string::npos; } - +size_t cling::utils::isUnnamedMacro(llvm::StringRef source, + clang::SourceManager& sm, + clang::Preprocessor& pp) { + std::unique_ptr buf = + llvm::MemoryBuffer::getMemBufferCopy(source, + "unnamed_macro_candidate_buffer"); + clang::FileID fid = sm.createFileID(std::move(buf)); + pp.EnterSourceFile(fid, nullptr, clang::SourceLocation()); + clang::Token tok; + do { + pp.Lex(tok); // Lexes while expanding macros and skipping false #if paths + // automatically + if (tok.is(clang::tok::l_brace)) { + return sm.getFileOffset(sm.getFileLoc(tok.getLocation())); + } + } while (tok.isNot(clang::tok::eof)); + return std::string::npos; +} size_t cling::utils::getWrapPoint(std::string& source, const clang::LangOptions& LangOpts) { From c46412b22e6b15b62060663f3e5d540a909c5817 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Fri, 24 Jul 2026 18:49:21 +0200 Subject: [PATCH 2/3] Update MetaProcessor.cpp --- interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp index e233d1a79edf7..555a52dc686ca 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp @@ -438,7 +438,7 @@ namespace cling { // hide the curly brace: content[posOpenCurly] = ' '; // and the matching closing '}' - size_t posCloseCurly = content.find_last_not_of(whitespace); + size_t posCloseCurly = content.find_last_not_of(whitespace); // TODO replace instead with preprocessed one, isUnnamedMacro should return tuple start and stop, rather than researching here if (posCloseCurly != std::string::npos) { if (content[posCloseCurly] == ';' && content[posCloseCurly-1] == '}') { content[posCloseCurly--] = ' '; // replace ';' and enter next if From 9f8c29dac1396a287780454b4c26eb2ec724ab6f Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Sat, 25 Jul 2026 11:30:27 +0200 Subject: [PATCH 3/3] Update SourceNormalization.cpp --- interpreter/cling/lib/Utils/SourceNormalization.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interpreter/cling/lib/Utils/SourceNormalization.cpp b/interpreter/cling/lib/Utils/SourceNormalization.cpp index 48d813d234472..6e95a8e251ace 100644 --- a/interpreter/cling/lib/Utils/SourceNormalization.cpp +++ b/interpreter/cling/lib/Utils/SourceNormalization.cpp @@ -456,10 +456,14 @@ size_t cling::utils::isUnnamedMacro(llvm::StringRef source, do { pp.Lex(tok); // Lexes while expanding macros and skipping false #if paths // automatically - if (tok.is(clang::tok::l_brace)) { + if (tok.is(tok::comment)) + continue; // ignore comments + if (tok.is(tok::l_brace)) { return sm.getFileOffset(sm.getFileLoc(tok.getLocation())); + } else { + return std::string::npos; } - } while (tok.isNot(clang::tok::eof)); + } while (tok.isNot(tok::eof)); return std::string::npos; }