diff --git a/core/metacling/src/TCling.cxx b/core/metacling/src/TCling.cxx index 998e29cec2d64..2762f350d862e 100644 --- a/core/metacling/src/TCling.cxx +++ b/core/metacling/src/TCling.cxx @@ -2651,7 +2651,7 @@ Longptr_t TCling::ProcessLine(const char* line, EErrorCode* error/*=0*/) } } else { // neither ACLiC nor run shared-library (.x) - size_t unnamedMacroOpenCurly; + size_t unnamedMacroOpenCurly, unnamedMacroCloseCurly; { std::string code; std::string codeline; @@ -2662,14 +2662,15 @@ Longptr_t TCling::ProcessLine(const char* line, EErrorCode* error/*=0*/) std::getline(in, codeline); code += codeline + "\n"; } - unnamedMacroOpenCurly - = cling::utils::isUnnamedMacro(code, fInterpreter->getCI()->getLangOpts()); + TString dirName = gSystem->DirName(fname); + std::tie(unnamedMacroOpenCurly, unnamedMacroCloseCurly) + = cling::utils::isUnnamedMacro(code, fInterpreter->getCI()->getSourceManager(), fInterpreter->getCI()->getPreprocessor(), dirName.Data()); } fCurExecutingMacros.push_back(fname); - if (unnamedMacroOpenCurly != std::string::npos) { + if (unnamedMacroOpenCurly != std::string::npos && unnamedMacroCloseCurly != std::string::npos) { compRes = fMetaProcessor->readInputFromFile(fname.Data(), &result, - unnamedMacroOpenCurly); + unnamedMacroOpenCurly, false, unnamedMacroCloseCurly); } else { // No DynLookup for .x, .L of named macros. fInterpreter->enableDynamicLookup(false); diff --git a/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h b/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h index bdabee747feac..e479a258c3551 100644 --- a/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h +++ b/interpreter/cling/include/cling/MetaProcessor/MetaProcessor.h @@ -139,14 +139,15 @@ namespace cling { /// execution of the last statement ///\param [in] posOpenCurly - position of the opening '{'; -1 if no curly. ///\param [in] lineByLine - Process each line individually. + ///\param [in] posCloseCurly - position of the closing '}'; -1 if no curly. /// ///\returns result of the compilation. /// Interpreter::CompilationResult - readInputFromFile(llvm::StringRef filename, - Value* result, + readInputFromFile(llvm::StringRef filename, Value* result, size_t posOpenCurly = (size_t)(-1), - bool lineByLine = false); + bool lineByLine = false, + size_t posCloseCurly = (size_t)(-1)); ///\brief Set the stdout and stderr stream to the appropriate file. /// diff --git a/interpreter/cling/include/cling/Utils/SourceNormalization.h b/interpreter/cling/include/cling/Utils/SourceNormalization.h index dde8cb326cba0..8cc1ef31b0835 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,23 @@ 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. + /// \param extraIncludePath - additional path where to search headers + /// \return the pair of positions of the unnamed macro's opening '{' and + /// closing '}'; or std::string::npos if this is not an unnamed macro. + std::pair isUnnamedMacro(llvm::StringRef source, + clang::SourceManager& sm, + clang::Preprocessor& pp, + llvm::StringRef extraIncludePath); + ///\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..928c7a102bef4 100644 --- a/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp +++ b/interpreter/cling/lib/MetaProcessor/MetaProcessor.cpp @@ -365,10 +365,9 @@ namespace cling { } Interpreter::CompilationResult - MetaProcessor::readInputFromFile(llvm::StringRef filename, - Value* result, - size_t posOpenCurly, - bool lineByLine) { + MetaProcessor::readInputFromFile(llvm::StringRef filename, Value* result, + size_t posOpenCurly, bool lineByLine, + size_t posCloseCurly) { // FIXME: This will fail for Unicode BOMs (and seems really weird) { @@ -424,7 +423,6 @@ namespace cling { if (in.fail()) return reportIOErr(filename, "read"); - static const char whitespace[] = " \t\r\n"; if (content.length() > 2 && content[0] == '#' && content[1] == '!') { // Convert shebang line to comment. That's nice because it doesn't // change the content size, leaving posOpenCurly untouched. @@ -433,48 +431,18 @@ 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 '}' - size_t posCloseCurly = content.find_last_not_of(whitespace); - if (posCloseCurly != std::string::npos) { - if (content[posCloseCurly] == ';' && content[posCloseCurly-1] == '}') { - content[posCloseCurly--] = ' '; // replace ';' and enter next if - } - if (content[posCloseCurly] == '}') { - content[posCloseCurly] = ' '; // replace '}' - } else { - std::string::size_type posBlockClose = content.find_last_of('}'); - if (posBlockClose != std::string::npos) { - content[posBlockClose] = ' '; // replace '}' - } - std::string::size_type posComment - = content.find_first_not_of(whitespace, posBlockClose); - if (posComment != std::string::npos - && content[posComment] == '/' && content[posComment+1] == '/') { - // More text (comments) are okay after the last '}', but - // we can not easily find it to remove it (so we need to upgrade - // this code to better handle the case with comments or - // preprocessor code before and after the leading { and - // trailing }) - while (posComment <= posCloseCurly) { - content[posComment++] = ' '; // replace '}' and comment - } - } else { - content[posCloseCurly] = '{'; - // By putting the '{' back, we keep the code as consistent as - // the user wrote it ... but we should still warn that we not - // goint to treat this file an unamed macro. - cling::errs() - << "Warning in cling::MetaProcessor: can not find the closing '}', " - << llvm::sys::path::filename(filename) - << " is not handled as an unamed script!\n"; - } // did not find "//" - } // remove comments after the trailing '}' - } // find '}' - } // ignore outermost block + } + if (posCloseCurly != (size_t)-1 && !content.empty()) { + assert(posCloseCurly < content.length() && + content[posCloseCurly] == '}' && + "No curly at claimed position of closing curly!"); + // hide the curly brace: + content[posCloseCurly] = ' '; + } m_CurrentlyExecutingFile = filename; bool topmost = !m_TopExecutingFile.data(); diff --git a/interpreter/cling/lib/Utils/SourceNormalization.cpp b/interpreter/cling/lib/Utils/SourceNormalization.cpp index c2f1251d1946e..66c68d49a4ea0 100644 --- a/interpreter/cling/lib/Utils/SourceNormalization.cpp +++ b/interpreter/cling/lib/Utils/SourceNormalization.cpp @@ -11,7 +11,10 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/HeaderSearchOptions.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" #include @@ -443,7 +446,54 @@ cling::utils::isUnnamedMacro(llvm::StringRef source, return std::string::npos; } - +std::pair +cling::utils::isUnnamedMacro(llvm::StringRef source, clang::SourceManager& sm, + clang::Preprocessor& pp, + llvm::StringRef extraIncludePath) { + std::unique_ptr buf = + llvm::MemoryBuffer::getMemBufferCopy(source, + "unnamed_macro_candidate_buffer"); + clang::FileID fid = sm.createFileID(std::move(buf)); + + auto HeaderOpts = pp.getHeaderSearchInfo().getHeaderSearchOpts(); + + clang::HeaderSearch& HS = pp.getHeaderSearchInfo(); + if (auto fileEntry = sm.getFileEntryRefForID(fid)) + HS.getFileInfo(*fileEntry); + if (!extraIncludePath.empty()) { + if (auto dirRef = + sm.getFileManager().getOptionalDirectoryRef(extraIncludePath)) { + HS.AddSearchPath({*dirRef, clang::SrcMgr::C_User, /*isFramework=*/false}, + /*isAngled=*/false); + } + } + clang::TrivialModuleLoader trivialLoader; + auto PPOpts = pp.getPreprocessorOpts(); + clang::Preprocessor localPP(PPOpts, pp.getDiagnostics(), pp.getLangOpts(), sm, + HS, trivialLoader, + /*IILookup=*/nullptr, /*OwnsHeaderSearch=*/false); + localPP.Initialize(pp.getTargetInfo(), pp.getAuxTargetInfo()); + localPP.setPredefines(pp.getPredefines()); + sm.setMainFileID(fid); + localPP.EnterMainSourceFile(); + + clang::Token tok; + localPP.Lex(tok); // expands macros, removes comments and skips false #if + // paths automatically + if (tok.is(tok::l_brace)) { + const size_t openBrace = sm.getFileOffset(sm.getFileLoc(tok.getLocation())); + size_t closeBrace = std::string::npos; + do { + localPP.Lex(tok); + if (tok.is(tok::r_brace)) { + closeBrace = sm.getFileOffset(sm.getFileLoc(tok.getLocation())); + } + } while (tok.isNot(tok::eof) && tok.isNot(tok::annot_repl_input_end)); + return {openBrace, closeBrace}; + } else { + return {std::string::npos, std::string::npos}; + } +} size_t cling::utils::getWrapPoint(std::string& source, const clang::LangOptions& LangOpts) {