Support non-identifier module names when preprocessing modules.

llvm-svn: 305758
This commit is contained in:
Richard Smith 2017-06-19 23:09:36 +00:00
parent 8361b0a9bb
commit 9565c75b29
8 changed files with 119 additions and 60 deletions

View File

@ -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.

View File

@ -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<std::string, SourceLocation> &IdComponent) {
return IdComponent.first;
}
static StringRef getModuleNameFromComponent(StringRef R) { return R; }
template<typename InputIter>
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<typename Container>
static void printModuleId(raw_ostream &OS, const Container &C) {
return printModuleId(OS, C.begin(), C.end());
}
std::string Module::getFullModuleName(bool AllowStringLiterals) const {
SmallVector<StringRef, 2> 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<StringRef>::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<Module *> &Exported) const {
// All non-explicit submodules are exported.
for (std::vector<Module *>::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";

View File

@ -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,

View File

@ -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();
}

View File

@ -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(),

View File

@ -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.

View File

@ -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<IdentifierInfo *, SourceLocation> &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<std::pair<IdentifierInfo *, SourceLocation>>
&ModuleName) {
while (true) {
std::pair<IdentifierInfo*, SourceLocation> 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<IdentifierInfo *, SourceLocation> 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<std::pair<IdentifierInfo *, SourceLocation>>
&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;

View File

@ -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"