From 9565c75b29826f48a0923e401a885f391b6c1dc3 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 19 Jun 2017 23:09:36 +0000 Subject: [PATCH] Support non-identifier module names when preprocessing modules. llvm-svn: 305758 --- clang/include/clang/Basic/Module.h | 4 +- clang/lib/Basic/Module.cpp | 63 +++++++++++------ clang/lib/Frontend/CompilerInstance.cpp | 11 ++- .../lib/Frontend/PrintPreprocessedOutput.cpp | 6 +- .../lib/Frontend/Rewrite/FrontendActions.cpp | 11 ++- .../Frontend/Rewrite/InclusionRewriter.cpp | 10 +-- clang/lib/Lex/Pragma.cpp | 70 ++++++++++++------- clang/test/Modules/string_names.cpp | 4 ++ 8 files changed, 119 insertions(+), 60 deletions(-) diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h index 1e52b29367b2..177175eae965 100644 --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -393,7 +393,9 @@ public: /// \brief Retrieve the full name of this module, including the path from /// its top-level module. - std::string getFullModuleName() const; + /// \param AllowStringLiterals If \c true, components that might not be + /// lexically valid as identifiers will be emitted as string literals. + std::string getFullModuleName(bool AllowStringLiterals = false) const; /// \brief Whether the full name of this module is equal to joining /// \p nameParts with "."s. diff --git a/clang/lib/Basic/Module.cpp b/clang/lib/Basic/Module.cpp index 83c524877ab0..1d96afd476ef 100644 --- a/clang/lib/Basic/Module.cpp +++ b/clang/lib/Basic/Module.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/Module.h" +#include "clang/Basic/CharInfo.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/TargetInfo.h" @@ -125,7 +126,36 @@ const Module *Module::getTopLevelModule() const { return Result; } -std::string Module::getFullModuleName() const { +static StringRef getModuleNameFromComponent( + const std::pair &IdComponent) { + return IdComponent.first; +} +static StringRef getModuleNameFromComponent(StringRef R) { return R; } + +template +static void printModuleId(raw_ostream &OS, InputIter Begin, InputIter End, + bool AllowStringLiterals = true) { + for (InputIter It = Begin; It != End; ++It) { + if (It != Begin) + OS << "."; + + StringRef Name = getModuleNameFromComponent(*It); + if (!AllowStringLiterals || isValidIdentifier(Name)) + OS << Name; + else { + OS << '"'; + OS.write_escaped(Name); + OS << '"'; + } + } +} + +template +static void printModuleId(raw_ostream &OS, const Container &C) { + return printModuleId(OS, C.begin(), C.end()); +} + +std::string Module::getFullModuleName(bool AllowStringLiterals) const { SmallVector Names; // Build up the set of module names (from innermost to outermost). @@ -133,15 +163,11 @@ std::string Module::getFullModuleName() const { Names.push_back(M->Name); std::string Result; - for (SmallVectorImpl::reverse_iterator I = Names.rbegin(), - IEnd = Names.rend(); - I != IEnd; ++I) { - if (!Result.empty()) - Result += '.'; - - Result += *I; - } - + + llvm::raw_string_ostream Out(Result); + printModuleId(Out, Names.rbegin(), Names.rend(), AllowStringLiterals); + Out.flush(); + return Result; } @@ -240,14 +266,6 @@ Module *Module::findSubmodule(StringRef Name) const { return SubModules[Pos->getValue()]; } -static void printModuleId(raw_ostream &OS, const ModuleId &Id) { - for (unsigned I = 0, N = Id.size(); I != N; ++I) { - if (I) - OS << "."; - OS << Id[I].first; - } -} - void Module::getExportedModules(SmallVectorImpl &Exported) const { // All non-explicit submodules are exported. for (std::vector::const_iterator I = SubModules.begin(), @@ -334,7 +352,8 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS << "framework "; if (IsExplicit) OS << "explicit "; - OS << "module " << Name; + OS << "module "; + printModuleId(OS, &Name, &Name + 1); if (IsSystem || IsExternC) { OS.indent(Indent + 2); @@ -434,7 +453,7 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { OS.indent(Indent + 2); OS << "export "; if (Module *Restriction = Exports[I].getPointer()) { - OS << Restriction->getFullModuleName(); + OS << Restriction->getFullModuleName(true); if (Exports[I].getInt()) OS << ".*"; } else { @@ -455,7 +474,7 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { for (unsigned I = 0, N = DirectUses.size(); I != N; ++I) { OS.indent(Indent + 2); OS << "use "; - OS << DirectUses[I]->getFullModuleName(); + OS << DirectUses[I]->getFullModuleName(true); OS << "\n"; } @@ -488,7 +507,7 @@ void Module::print(raw_ostream &OS, unsigned Indent) const { for (unsigned I = 0, N = Conflicts.size(); I != N; ++I) { OS.indent(Indent + 2); OS << "conflict "; - OS << Conflicts[I].Other->getFullModuleName(); + OS << Conflicts[I].Other->getFullModuleName(true); OS << ", \""; OS.write_escaped(Conflicts[I].Message); OS << "\"\n"; diff --git a/clang/lib/Frontend/CompilerInstance.cpp b/clang/lib/Frontend/CompilerInstance.cpp index 72a8c3818093..e5da2ae4f22e 100644 --- a/clang/lib/Frontend/CompilerInstance.cpp +++ b/clang/lib/Frontend/CompilerInstance.cpp @@ -11,6 +11,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/Basic/CharInfo.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/MemoryBufferCache.h" @@ -1902,17 +1903,23 @@ CompilerInstance::loadModule(SourceLocation ImportLoc, void CompilerInstance::loadModuleFromSource(SourceLocation ImportLoc, StringRef ModuleName, StringRef Source) { + // Avoid creating filenames with special characters. + SmallString<128> CleanModuleName(ModuleName); + for (auto &C : CleanModuleName) + if (!isAlphanumeric(C)) + C = '_'; + // FIXME: Using a randomized filename here means that our intermediate .pcm // output is nondeterministic (as .pcm files refer to each other by name). // Can this affect the output in any way? SmallString<128> ModuleFileName; if (std::error_code EC = llvm::sys::fs::createTemporaryFile( - ModuleName, "pcm", ModuleFileName)) { + CleanModuleName, "pcm", ModuleFileName)) { getDiagnostics().Report(ImportLoc, diag::err_fe_unable_to_open_output) << ModuleFileName << EC.message(); return; } - std::string ModuleMapFileName = (ModuleName + ".map").str(); + std::string ModuleMapFileName = (CleanModuleName + ".map").str(); FrontendInputFile Input( ModuleMapFileName, diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index 832eaf2926f0..5336de1f7468 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -349,7 +349,7 @@ void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, case tok::pp_include_next: startNewLineIfNeeded(); MoveToLine(HashLoc); - OS << "#pragma clang module import " << Imported->getFullModuleName() + OS << "#pragma clang module import " << Imported->getFullModuleName(true) << " /* clang -E: implicit import for " << "#" << PP.getSpelling(IncludeTok) << " " << (IsAngled ? '<' : '"') << FileName << (IsAngled ? '>' : '"') @@ -378,14 +378,14 @@ void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, /// Handle entering the scope of a module during a module compilation. void PrintPPOutputPPCallbacks::BeginModule(const Module *M) { startNewLineIfNeeded(); - OS << "#pragma clang module begin " << M->getFullModuleName(); + OS << "#pragma clang module begin " << M->getFullModuleName(true); setEmittedDirectiveOnThisLine(); } /// Handle leaving the scope of a module during a module compilation. void PrintPPOutputPPCallbacks::EndModule(const Module *M) { startNewLineIfNeeded(); - OS << "#pragma clang module end /*" << M->getFullModuleName() << "*/"; + OS << "#pragma clang module end /*" << M->getFullModuleName(true) << "*/"; setEmittedDirectiveOnThisLine(); } diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp index 45feffbcb5b5..2ccdc263951b 100644 --- a/clang/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -9,6 +9,7 @@ #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/AST/ASTConsumer.h" +#include "clang/Basic/CharInfo.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -224,7 +225,15 @@ public: auto OS = Out.lock(); assert(OS && "loaded module file after finishing rewrite action?"); - (*OS) << "#pragma clang module build " << MF->ModuleName << "\n"; + (*OS) << "#pragma clang module build "; + if (isValidIdentifier(MF->ModuleName)) + (*OS) << MF->ModuleName; + else { + (*OS) << '"'; + OS->write_escaped(MF->ModuleName); + (*OS) << '"'; + } + (*OS) << '\n'; // Rewrite the contents of the module in a separate compiler instance. CompilerInstance Instance(CI.getPCHContainerOperations(), diff --git a/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp b/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp index 3564cebba8a8..e0477069b340 100644 --- a/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp +++ b/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -140,7 +140,7 @@ void InclusionRewriter::WriteLineInfo(StringRef Filename, int Line, } void InclusionRewriter::WriteImplicitModuleImport(const Module *Mod) { - OS << "#pragma clang module import " << Mod->getFullModuleName() + OS << "#pragma clang module import " << Mod->getFullModuleName(true) << " /* clang -frewrite-includes: implicit import */" << MainEOL; } @@ -471,15 +471,15 @@ void InclusionRewriter::Process(FileID FileId, else if (const IncludedFile *Inc = FindIncludeAtLocation(Loc)) { const Module *Mod = FindEnteredModule(Loc); if (Mod) - OS << "#pragma clang module begin " << Mod->getFullModuleName() - << "\n"; + OS << "#pragma clang module begin " + << Mod->getFullModuleName(true) << "\n"; // Include and recursively process the file. Process(Inc->Id, Inc->FileType); if (Mod) - OS << "#pragma clang module end /*" << Mod->getFullModuleName() - << "*/\n"; + OS << "#pragma clang module end /*" + << Mod->getFullModuleName(true) << "*/\n"; // Add line marker to indicate we're returning from an included // file. diff --git a/clang/lib/Lex/Pragma.cpp b/clang/lib/Lex/Pragma.cpp index c16478dd2be4..bf2363a0a6f4 100644 --- a/clang/lib/Lex/Pragma.cpp +++ b/clang/lib/Lex/Pragma.cpp @@ -20,6 +20,7 @@ #include "clang/Basic/TokenKinds.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/LiteralSupport.h" #include "clang/Lex/MacroInfo.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" @@ -754,15 +755,52 @@ void Preprocessor::HandlePragmaIncludeAlias(Token &Tok) { getHeaderSearchInfo().AddIncludeAlias(OriginalSource, ReplaceFileName); } +// Lex a component of a module name: either an identifier or a string literal; +// for components that can be expressed both ways, the two forms are equivalent. +static bool LexModuleNameComponent( + Preprocessor &PP, Token &Tok, + std::pair &ModuleNameComponent, + bool First) { + PP.LexUnexpandedToken(Tok); + if (Tok.is(tok::string_literal) && !Tok.hasUDSuffix()) { + StringLiteralParser Literal(Tok, PP); + if (Literal.hadError) + return true; + ModuleNameComponent = std::make_pair( + PP.getIdentifierInfo(Literal.GetString()), Tok.getLocation()); + } else if (!Tok.isAnnotation() && Tok.getIdentifierInfo()) { + ModuleNameComponent = + std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()); + } else { + PP.Diag(Tok.getLocation(), diag::err_pp_expected_module_name) << First; + return true; + } + return false; +} + +static bool LexModuleName( + Preprocessor &PP, Token &Tok, + llvm::SmallVectorImpl> + &ModuleName) { + while (true) { + std::pair NameComponent; + if (LexModuleNameComponent(PP, Tok, NameComponent, ModuleName.empty())) + return true; + ModuleName.push_back(NameComponent); + + PP.LexUnexpandedToken(Tok); + if (Tok.isNot(tok::period)) + return false; + } +} + void Preprocessor::HandlePragmaModuleBuild(Token &Tok) { SourceLocation Loc = Tok.getLocation(); - LexUnexpandedToken(Tok); - if (Tok.isAnnotation() || !Tok.getIdentifierInfo()) { - Diag(Tok.getLocation(), diag::err_pp_expected_module_name) << true; + std::pair ModuleNameLoc; + if (LexModuleNameComponent(*this, Tok, ModuleNameLoc, true)) return; - } - IdentifierInfo *ModuleName = Tok.getIdentifierInfo(); + IdentifierInfo *ModuleName = ModuleNameLoc.first; LexUnexpandedToken(Tok); if (Tok.isNot(tok::eod)) { @@ -1383,26 +1421,6 @@ public: } }; -static bool LexModuleName( - Preprocessor &PP, Token &Tok, - llvm::SmallVectorImpl> - &ModuleName) { - while (true) { - PP.LexUnexpandedToken(Tok); - if (Tok.isAnnotation() || !Tok.getIdentifierInfo()) { - PP.Diag(Tok.getLocation(), diag::err_pp_expected_module_name) - << ModuleName.empty(); - return true; - } - - ModuleName.emplace_back(Tok.getIdentifierInfo(), Tok.getLocation()); - - PP.LexUnexpandedToken(Tok); - if (Tok.isNot(tok::period)) - return false; - } -} - /// Handle the clang \#pragma module import extension. The syntax is: /// \code /// #pragma clang module import some.module.name @@ -1473,7 +1491,7 @@ struct PragmaModuleBeginHandler : public PragmaHandler { // be loaded or implicitly loadable. // FIXME: We could create the submodule here. We'd need to know whether // it's supposed to be explicit, but not much else. - Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule(Current); + Module *M = PP.getHeaderSearchInfo().lookupModule(Current); if (!M) { PP.Diag(ModuleName.front().second, diag::err_pp_module_begin_no_module_map) << Current; diff --git a/clang/test/Modules/string_names.cpp b/clang/test/Modules/string_names.cpp index 43068f13c012..a6503d048d6b 100644 --- a/clang/test/Modules/string_names.cpp +++ b/clang/test/Modules/string_names.cpp @@ -1,6 +1,10 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -fmodules-decluse -I %S/Inputs/string_names %s -fmodule-name="my/module-a" -verify +// Check that we can preprocess with string module names. +// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/string_names %s -fmodule-name="my/module-a" -E -frewrite-imports -o %t/test.ii +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-decluse -I %S/Inputs/string_names %t/test.ii -fmodule-name="my/module-a" + #include "a.h" #include "b.h" // expected-error {{does not depend on a module exporting}} #include "c.h"