From 538e9e8ebd09233b3900ed2dfd23e4e1ca5c9fc0 Mon Sep 17 00:00:00 2001 From: Qiongsi Wu Date: Fri, 22 Aug 2025 09:18:01 -0700 Subject: [clang][Modules] Reporting Errors for Duplicating Link Declarations in `modulemap`s (#148959) This PR teaches the modulemap parsing logic to report warnings that default to errors if the parsing logic sees duplicating link declarations in the same module. Specifically, duplicating link declarations means multiple link declarations with the same string-literal in the same module. No errors are reported if a same link declaration exist in a submodule and its enclosing module. The warning can be disabled with `-Wno-module-link-redeclaration`. rdar://155880064 --- clang/lib/Lex/ModuleMapFile.cpp | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'clang/lib/Lex/ModuleMapFile.cpp') diff --git a/clang/lib/Lex/ModuleMapFile.cpp b/clang/lib/Lex/ModuleMapFile.cpp index 183e919d14c2..f0cd9d2bee82 100644 --- a/clang/lib/Lex/ModuleMapFile.cpp +++ b/clang/lib/Lex/ModuleMapFile.cpp @@ -118,7 +118,8 @@ struct ModuleMapFileParser { std::optional parseExcludeDecl(clang::SourceLocation LeadingLoc); std::optional parseUmbrellaDirDecl(SourceLocation UmbrellaLoc); - std::optional parseLinkDecl(); + std::optional + parseLinkDecl(llvm::StringMap &SeenLinkDecl, bool Allowed); SourceLocation consumeToken(); void skipUntil(MMToken::TokenKind K); @@ -325,6 +326,7 @@ std::optional ModuleMapFileParser::parseModuleDecl(bool TopLevel) { SourceLocation LBraceLoc = consumeToken(); bool Done = false; + llvm::StringMap SeenLinkDecl; do { std::optional SubDecl; switch (Tok.Kind) { @@ -405,7 +407,9 @@ std::optional ModuleMapFileParser::parseModuleDecl(bool TopLevel) { break; case MMToken::LinkKeyword: - SubDecl = parseLinkDecl(); + // Link decls are only allowed in top level modules or explicit + // submodules. + SubDecl = parseLinkDecl(SeenLinkDecl, TopLevel || MDecl.Explicit); break; default: @@ -822,7 +826,8 @@ ModuleMapFileParser::parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc) { /// /// module-declaration: /// 'link' 'framework'[opt] string-literal -std::optional ModuleMapFileParser::parseLinkDecl() { +std::optional ModuleMapFileParser::parseLinkDecl( + llvm::StringMap &SeenLinkDecl, bool Allowed) { assert(Tok.is(MMToken::LinkKeyword)); LinkDecl LD; LD.Location = consumeToken(); @@ -838,12 +843,33 @@ std::optional ModuleMapFileParser::parseLinkDecl() { if (!Tok.is(MMToken::StringLiteral)) { Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name) << LD.Framework << SourceRange(LD.Location); + consumeToken(); HadError = true; return std::nullopt; } - LD.Library = Tok.getString(); + StringRef Library = Tok.getString(); + + LD.Library = Library; consumeToken(); + + // Make sure we eat all the tokens when we report the errors so parsing + // can continue. + if (!Allowed) { + Diags.Report(LD.Location, diag::err_mmap_submodule_link_decl); + HadError = true; + return std::nullopt; + } + + auto [It, Inserted] = + SeenLinkDecl.insert(std::make_pair(Library, LD.Location)); + if (!Inserted) { + Diags.Report(LD.Location, diag::warn_mmap_link_redeclaration) << Library; + Diags.Report(It->second, diag::note_mmap_prev_link_declaration); + HadError = true; + return std::nullopt; + } + return std::move(LD); } -- cgit v1.2.3