Skip to content

Conversation

qiongsiwu
Copy link
Contributor

@qiongsiwu qiongsiwu commented Jul 15, 2025

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

@qiongsiwu qiongsiwu self-assigned this Jul 15, 2025
@qiongsiwu
Copy link
Contributor Author

A different approach we can take is to deduplicate link libraries per module silently, and avoid adding any error checks. I think that is probably fine since there are no correctness issues. But I think it is also beneficial to report to the user that their modulemap contains duplicating link declarations. @Bigcheese @jansvoboda11 what do you think?

@jansvoboda11
Copy link
Contributor

I think a hard error is a bit too much initially, maybe let's introduce a warning first and then tighten the screws later?

…an error whe link declarations are used in submodules.
…ure as indicated by the test case clang/test/Modules/autolink.m
…s a feature as indicated by the test case clang/test/Modules/autolink.m"

This reverts commit 3286622.
@qiongsiwu
Copy link
Contributor Author

I think a hard error is a bit too much initially, maybe let's introduce a warning first and then tighten the screws later?

Thanks for the comment! While I agree that having a hard error initially may be harsh, I think we should go with an error for two reasons:

  1. I am not seeing duplicates in SDKs.
  2. Having an error will prevent duplicates getting accidentally added to the SDKs.

I'd like to keep this an error if there are no objections.

@qiongsiwu qiongsiwu marked this pull request as ready for review July 24, 2025 18:53
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 24, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 24, 2025

@llvm/pr-subscribers-clang

Author: Qiongsi Wu (qiongsiwu)

Changes

This PR teaches the modulemap parsing logic to report 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.

rdar://155880064


Full diff: https://github.com/llvm/llvm-project/pull/148959.diff

3 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticLexKinds.td (+4)
  • (modified) clang/lib/Lex/ModuleMapFile.cpp (+30-4)
  • (added) clang/test/ClangScanDeps/link-libraries-diag-dup.c (+57)
diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td
index 723f5d48b4f5f..b4dcb35454585 100644
--- a/clang/include/clang/Basic/DiagnosticLexKinds.td
+++ b/clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -909,6 +909,10 @@ def err_mmap_nested_submodule_id : Error<
   "qualified module name can only be used to define modules at the top level">;
 def err_mmap_expected_feature : Error<"expected a feature name">;
 def err_mmap_expected_attribute : Error<"expected an attribute name">;
+def err_mmap_link_redecalration : Error<"redeclaration of link library '%0'">;
+def note_mmap_prev_link_declaration : Note<"previously declared here">;
+def err_mmap_submodule_link_decl
+    : Error<"link declaration is not allowed in submodules">;
 def warn_mmap_unknown_attribute : Warning<"unknown attribute '%0'">,
   InGroup<IgnoredAttributes>;
 def warn_mmap_mismatched_private_submodule : Warning<
diff --git a/clang/lib/Lex/ModuleMapFile.cpp b/clang/lib/Lex/ModuleMapFile.cpp
index 183e919d14c22..08bba8c16e2d6 100644
--- a/clang/lib/Lex/ModuleMapFile.cpp
+++ b/clang/lib/Lex/ModuleMapFile.cpp
@@ -118,7 +118,8 @@ struct ModuleMapFileParser {
   std::optional<ExcludeDecl> parseExcludeDecl(clang::SourceLocation LeadingLoc);
   std::optional<UmbrellaDirDecl>
   parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
-  std::optional<LinkDecl> parseLinkDecl();
+  std::optional<LinkDecl>
+  parseLinkDecl(llvm::StringMap<SourceLocation> &SeenLinkDecl, bool Allowed);
 
   SourceLocation consumeToken();
   void skipUntil(MMToken::TokenKind K);
@@ -325,6 +326,7 @@ std::optional<ModuleDecl> ModuleMapFileParser::parseModuleDecl(bool TopLevel) {
   SourceLocation LBraceLoc = consumeToken();
 
   bool Done = false;
+  llvm::StringMap<SourceLocation> SeenLinkDecl;
   do {
     std::optional<Decl> SubDecl;
     switch (Tok.Kind) {
@@ -405,7 +407,9 @@ std::optional<ModuleDecl> 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<LinkDecl> ModuleMapFileParser::parseLinkDecl() {
+std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl(
+    llvm::StringMap<SourceLocation> &SeenLinkDecl, bool Allowed) {
   assert(Tok.is(MMToken::LinkKeyword));
   LinkDecl LD;
   LD.Location = consumeToken();
@@ -838,12 +843,33 @@ std::optional<LinkDecl> 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::err_mmap_link_redecalration) << Library;
+    Diags.Report(It->second, diag::note_mmap_prev_link_declaration);
+    HadError = true;
+    return std::nullopt;
+  }
+
   return std::move(LD);
 }
 
diff --git a/clang/test/ClangScanDeps/link-libraries-diag-dup.c b/clang/test/ClangScanDeps/link-libraries-diag-dup.c
new file mode 100644
index 0000000000000..6f9d4be4447a6
--- /dev/null
+++ b/clang/test/ClangScanDeps/link-libraries-diag-dup.c
@@ -0,0 +1,57 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+
+//--- module.modulemap
+module A {
+  umbrella header "A.h"
+
+  module B {
+    header "B.h"
+    link "libraryB"
+  }
+
+  explicit module D {
+    header "D.h"
+    link "libraryD"
+  }
+
+  link "libraryA"
+  link "libraryA"
+}
+
+module C {
+  header "C.h"
+  link "libraryA"
+}
+
+//--- A.h
+#include "B.h"
+//--- B.h
+// empty
+//--- C.h
+// empty
+//--- D.h
+// empty
+//--- TU.c
+#include "A.h"
+#include "C.h"
+#include "D.h"
+
+//--- cdb.json.template
+[{
+  "file": "DIR/TU.c",
+  "directory": "DIR",
+  "command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -c DIR/TU.c"
+}]
+
+// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
+// RUN: not clang-scan-deps -compilation-database %t/cdb.json -format \
+// RUN:   experimental-full 2>&1 | FileCheck %s
+
+// Note that module D does not report an error because it is explicit.
+// Therefore we can use CHECK-NEXT for the redeclaration error on line 15.
+// CHECK:      module.modulemap:6:5: error: link declaration is not allowed in submodules
+// CHECK-NEXT: module.modulemap:15:3: error: redeclaration of link library 'libraryA'
+// CHECK-NEXT: module.modulemap:14:3: note: previously declared here
+// CHECK-NOT:  module.modulemap:20:3: error: redeclaration of link library 'libraryA'

@qiongsiwu
Copy link
Contributor Author

Gentle ping for review. Thanks!

@qiongsiwu
Copy link
Contributor Author

Ping for review. Thank you!

Copy link
Contributor

@Bigcheese Bigcheese left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to make this a warning that defaults to error to start with. That provides an easy way to disable it if anyone does hit it, but it still errors by default.

Otherwise the change looks good.

@qiongsiwu qiongsiwu requested a review from Bigcheese August 18, 2025 14:39
@qiongsiwu qiongsiwu merged commit 538e9e8 into llvm:main Aug 22, 2025
9 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Aug 22, 2025

LLVM Buildbot has detected a new failure on builder clang-aarch64-quick running on linaro-clang-aarch64-quick while building clang at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/21626

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'LLVM-Unit :: Support/./SupportTests/188/385' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/unittests/Support/./SupportTests-LLVM-Unit-3861641-188-385.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=385 GTEST_SHARD_INDEX=188 /home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/unittests/Support/./SupportTests
--

Script:
--
/home/tcwg-buildbot/worker/clang-aarch64-quick/stage1/unittests/Support/./SupportTests --gtest_filter=ProgramEnvTest.TestLockFileExclusive
--
../llvm/llvm/unittests/Support/ProgramTest.cpp:648: Failure
Value of: Finished
  Actual: false
Expected: true


../llvm/llvm/unittests/Support/ProgramTest.cpp:648
Value of: Finished
  Actual: false
Expected: true



********************


@qiongsiwu
Copy link
Contributor Author

LLVM Buildbot has detected a new failure on builder clang-aarch64-quick running on linaro-clang-aarch64-quick while building clang at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/65/builds/21626

Here is the relevant piece of the build log for the reference

This seems to be an intermittent failure. The following build https://lab.llvm.org/buildbot/#/builders/65/builds/21627 succeeded.

qiongsiwu added a commit to qiongsiwu/llvm-project that referenced this pull request Aug 27, 2025
…n `modulemap`s (llvm#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
(cherry picked from commit 538e9e8)
qiongsiwu added a commit to swiftlang/llvm-project that referenced this pull request Aug 27, 2025
…n `modulemap`s (llvm#148959) (#11265)

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
(cherry picked from commit 538e9e8)
qiongsiwu added a commit to qiongsiwu/llvm-project that referenced this pull request Sep 2, 2025
qiongsiwu added a commit to swiftlang/llvm-project that referenced this pull request Sep 2, 2025
…ations in `modulemap`s (llvm#148959) (#11265)" (#11310)

This reverts commit b11f60b.

We found a problem with the link decl checks when testing for Windows rebranching. Temporarily revert the checks. 

rdar://159467837
qiongsiwu added a commit to qiongsiwu/llvm-project that referenced this pull request Sep 5, 2025
qiongsiwu added a commit that referenced this pull request Sep 6, 2025
#157154)

…ations in `modulemap`s (#148959)"

This reverts commit 538e9e8 for two
reasons.

1. Link decls in submodules can make sense even if the submodule is not
explicit. We need to review the error check. This PR reverts the check
so we still allow link decls in submodules.
2. It is not a fatal error to have duplicating link decls. The linker
deduplicates them anyways.

rdar://159467837
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants