Skip to content

Commit 538e9e8

Browse files
authored
[clang][Modules] Reporting Errors for Duplicating Link Declarations in modulemaps (#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
1 parent c984132 commit 538e9e8

File tree

3 files changed

+92
-4
lines changed

3 files changed

+92
-4
lines changed

clang/include/clang/Basic/DiagnosticLexKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,11 @@ def err_mmap_nested_submodule_id : Error<
912912
"qualified module name can only be used to define modules at the top level">;
913913
def err_mmap_expected_feature : Error<"expected a feature name">;
914914
def err_mmap_expected_attribute : Error<"expected an attribute name">;
915+
def warn_mmap_link_redeclaration : Warning<"redeclaration of link library '%0'">,
916+
InGroup<DiagGroup<"module-link-redeclaration">>, DefaultError;
917+
def note_mmap_prev_link_declaration : Note<"previously declared here">;
918+
def err_mmap_submodule_link_decl
919+
: Error<"link declaration is not allowed in submodules">;
915920
def warn_mmap_unknown_attribute : Warning<"unknown attribute '%0'">,
916921
InGroup<IgnoredAttributes>;
917922
def warn_mmap_mismatched_private_submodule : Warning<

clang/lib/Lex/ModuleMapFile.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ struct ModuleMapFileParser {
118118
std::optional<ExcludeDecl> parseExcludeDecl(clang::SourceLocation LeadingLoc);
119119
std::optional<UmbrellaDirDecl>
120120
parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
121-
std::optional<LinkDecl> parseLinkDecl();
121+
std::optional<LinkDecl>
122+
parseLinkDecl(llvm::StringMap<SourceLocation> &SeenLinkDecl, bool Allowed);
122123

123124
SourceLocation consumeToken();
124125
void skipUntil(MMToken::TokenKind K);
@@ -325,6 +326,7 @@ std::optional<ModuleDecl> ModuleMapFileParser::parseModuleDecl(bool TopLevel) {
325326
SourceLocation LBraceLoc = consumeToken();
326327

327328
bool Done = false;
329+
llvm::StringMap<SourceLocation> SeenLinkDecl;
328330
do {
329331
std::optional<Decl> SubDecl;
330332
switch (Tok.Kind) {
@@ -405,7 +407,9 @@ std::optional<ModuleDecl> ModuleMapFileParser::parseModuleDecl(bool TopLevel) {
405407
break;
406408

407409
case MMToken::LinkKeyword:
408-
SubDecl = parseLinkDecl();
410+
// Link decls are only allowed in top level modules or explicit
411+
// submodules.
412+
SubDecl = parseLinkDecl(SeenLinkDecl, TopLevel || MDecl.Explicit);
409413
break;
410414

411415
default:
@@ -822,7 +826,8 @@ ModuleMapFileParser::parseUmbrellaDirDecl(clang::SourceLocation UmbrellaLoc) {
822826
///
823827
/// module-declaration:
824828
/// 'link' 'framework'[opt] string-literal
825-
std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl() {
829+
std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl(
830+
llvm::StringMap<SourceLocation> &SeenLinkDecl, bool Allowed) {
826831
assert(Tok.is(MMToken::LinkKeyword));
827832
LinkDecl LD;
828833
LD.Location = consumeToken();
@@ -838,12 +843,33 @@ std::optional<LinkDecl> ModuleMapFileParser::parseLinkDecl() {
838843
if (!Tok.is(MMToken::StringLiteral)) {
839844
Diags.Report(Tok.getLocation(), diag::err_mmap_expected_library_name)
840845
<< LD.Framework << SourceRange(LD.Location);
846+
consumeToken();
841847
HadError = true;
842848
return std::nullopt;
843849
}
844850

845-
LD.Library = Tok.getString();
851+
StringRef Library = Tok.getString();
852+
853+
LD.Library = Library;
846854
consumeToken();
855+
856+
// Make sure we eat all the tokens when we report the errors so parsing
857+
// can continue.
858+
if (!Allowed) {
859+
Diags.Report(LD.Location, diag::err_mmap_submodule_link_decl);
860+
HadError = true;
861+
return std::nullopt;
862+
}
863+
864+
auto [It, Inserted] =
865+
SeenLinkDecl.insert(std::make_pair(Library, LD.Location));
866+
if (!Inserted) {
867+
Diags.Report(LD.Location, diag::warn_mmap_link_redeclaration) << Library;
868+
Diags.Report(It->second, diag::note_mmap_prev_link_declaration);
869+
HadError = true;
870+
return std::nullopt;
871+
}
872+
847873
return std::move(LD);
848874
}
849875

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir %t
3+
// RUN: split-file %s %t
4+
5+
//--- module.modulemap
6+
module A {
7+
umbrella header "A.h"
8+
9+
module B {
10+
header "B.h"
11+
link "libraryB"
12+
}
13+
14+
explicit module D {
15+
header "D.h"
16+
link "libraryD"
17+
}
18+
19+
link "libraryA"
20+
link "libraryA"
21+
}
22+
23+
module C {
24+
header "C.h"
25+
link "libraryA"
26+
}
27+
28+
//--- A.h
29+
#include "B.h"
30+
//--- B.h
31+
// empty
32+
//--- C.h
33+
// empty
34+
//--- D.h
35+
// empty
36+
//--- TU.c
37+
#include "A.h"
38+
#include "C.h"
39+
#include "D.h"
40+
41+
//--- cdb.json.template
42+
[{
43+
"file": "DIR/TU.c",
44+
"directory": "DIR",
45+
"command": "clang -fmodules -fmodules-cache-path=DIR/cache -I DIR -c DIR/TU.c"
46+
}]
47+
48+
// RUN: sed "s|DIR|%/t|g" %t/cdb.json.template > %t/cdb.json
49+
// RUN: not clang-scan-deps -compilation-database %t/cdb.json -format \
50+
// RUN: experimental-full 2>&1 | FileCheck %s
51+
52+
// Note that module D does not report an error because it is explicit.
53+
// Therefore we can use CHECK-NEXT for the redeclaration error on line 15.
54+
// CHECK: module.modulemap:6:5: error: link declaration is not allowed in submodules
55+
// CHECK-NEXT: module.modulemap:15:3: error: redeclaration of link library 'libraryA' [-Wmodule-link-redeclaration]
56+
// CHECK-NEXT: module.modulemap:14:3: note: previously declared here
57+
// CHECK-NOT: module.modulemap:20:3: error: redeclaration of link library 'libraryA'

0 commit comments

Comments
 (0)