Skip to content

Commit 1b3b69f

Browse files
committed
Warning for framework include violation from Headers to PrivateHeaders
Framework vendors usually layout their framework headers in the following way: Foo.framework/Headers -> "public" headers Foo.framework/PrivateHeader -> "private" headers Since both headers in both directories can be found with #import <Foo/some-header.h>, it's easy to make mistakes and include headers in Foo.framework/PrivateHeader from headers in Foo.framework/Headers, which usually configures a layering violation on Darwin ecosystems. One of the problem this causes is dep cycles when modules are used, since it's very common for "private" modules to include from the "public" ones; adding an edge the other way around will trigger cycles. Add a warning to catch those cases such that: ./A.framework/Headers/A.h:1:10: warning: public framework header includes private framework header 'A/APriv.h' #include <A/APriv.h> ^ rdar://problem/38712182 llvm-svn: 335542
1 parent 9bca748 commit 1b3b69f

File tree

16 files changed

+147
-5
lines changed

16 files changed

+147
-5
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def Availability : DiagGroup<"availability">;
3232
def Section : DiagGroup<"section">;
3333
def AutoImport : DiagGroup<"auto-import">;
3434
def FrameworkHdrQuotedInclude : DiagGroup<"quoted-include-in-framework-header">;
35+
def FrameworkIncludePrivateFromPublic :
36+
DiagGroup<"framework-include-private-from-public">;
3537
def CXX14BinaryLiteral : DiagGroup<"c++14-binary-literal">;
3638
def CXXPre14CompatBinaryLiteral : DiagGroup<"c++98-c++11-compat-binary-literal">;
3739
def GNUBinaryLiteral : DiagGroup<"gnu-binary-literal">;

clang/include/clang/Basic/DiagnosticLexKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,9 @@ def warn_quoted_include_in_framework_header : Warning<
718718
"double-quoted include \"%0\" in framework header, "
719719
"expected angle-bracketed instead"
720720
>, InGroup<FrameworkHdrQuotedInclude>, DefaultIgnore;
721+
def warn_framework_include_private_from_public : Warning<
722+
"public framework header includes private framework header '%0'"
723+
>, InGroup<FrameworkIncludePrivateFromPublic>;
721724

722725
def warn_auto_module_import : Warning<
723726
"treating #%select{include|import|include_next|__include_macros}0 as an "

clang/lib/Lex/HeaderSearch.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,12 @@ static const char *copyString(StringRef Str, llvm::BumpPtrAllocator &Alloc) {
621621
return CopyStr;
622622
}
623623

624-
static bool isFrameworkStylePath(StringRef Path,
624+
static bool isFrameworkStylePath(StringRef Path, bool &IsPrivateHeader,
625625
SmallVectorImpl<char> &FrameworkName) {
626626
using namespace llvm::sys;
627627
path::const_iterator I = path::begin(Path);
628628
path::const_iterator E = path::end(Path);
629+
IsPrivateHeader = false;
629630

630631
// Detect different types of framework style paths:
631632
//
@@ -637,12 +638,16 @@ static bool isFrameworkStylePath(StringRef Path,
637638
// and some other variations among these lines.
638639
int FoundComp = 0;
639640
while (I != E) {
641+
if (*I == "Headers")
642+
++FoundComp;
640643
if (I->endswith(".framework")) {
641644
FrameworkName.append(I->begin(), I->end());
642645
++FoundComp;
643646
}
644-
if (*I == "Headers" || *I == "PrivateHeaders")
647+
if (*I == "PrivateHeaders") {
645648
++FoundComp;
649+
IsPrivateHeader = true;
650+
}
646651
++I;
647652
}
648653

@@ -654,11 +659,13 @@ diagnoseFrameworkInclude(DiagnosticsEngine &Diags, SourceLocation IncludeLoc,
654659
StringRef Includer, StringRef IncludeFilename,
655660
const FileEntry *IncludeFE, bool isAngled = false,
656661
bool FoundByHeaderMap = false) {
662+
bool IsIncluderPrivateHeader = false;
657663
SmallString<128> FromFramework, ToFramework;
658-
if (!isFrameworkStylePath(Includer, FromFramework))
664+
if (!isFrameworkStylePath(Includer, IsIncluderPrivateHeader, FromFramework))
659665
return;
660-
bool IsIncludeeInFramework =
661-
isFrameworkStylePath(IncludeFE->getName(), ToFramework);
666+
bool IsIncludeePrivateHeader = false;
667+
bool IsIncludeeInFramework = isFrameworkStylePath(
668+
IncludeFE->getName(), IsIncludeePrivateHeader, ToFramework);
662669

663670
if (!isAngled && !FoundByHeaderMap) {
664671
SmallString<128> NewInclude("<");
@@ -672,6 +679,14 @@ diagnoseFrameworkInclude(DiagnosticsEngine &Diags, SourceLocation IncludeLoc,
672679
<< IncludeFilename
673680
<< FixItHint::CreateReplacement(IncludeLoc, NewInclude);
674681
}
682+
683+
// Headers in Foo.framework/Headers should not include headers
684+
// from Foo.framework/PrivateHeaders, since this violates public/private
685+
// API boundaries and can cause modular dependency cycles.
686+
if (!IsIncluderPrivateHeader && IsIncludeeInFramework &&
687+
IsIncludeePrivateHeader && FromFramework == ToFramework)
688+
Diags.Report(IncludeLoc, diag::warn_framework_include_private_from_public)
689+
<< IncludeFilename;
675690
}
676691

677692
/// LookupFile - Given a "foo" or \<foo> reference, look up the indicated file,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#include <A/APriv.h>
2+
#include "APriv2.h"
3+
#include <Z/Z.h>
4+
int foo();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// framework-public-includes-private/A.framework/Modules/module.modulemap
2+
framework module A {
3+
header "A.h"
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// framework-public-includes-private/A.framework/Modules/module.private.modulemap
2+
framework module A_Private {
3+
header "APriv.h"
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// framework-public-includes-private/A.framework/PrivateHeaders/APriv.h
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// framework-public-includes-private/A.framework/PrivateHeaders/APriv2.h
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
{
3+
"mappings" :
4+
{
5+
"A.h" : "A/A.h",
6+
"APriv2.h" : "A/APriv2.h"
7+
}
8+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// framework-public-includes-private/flat-header-path/Z.h
2+
#import "ZPriv.h"

0 commit comments

Comments
 (0)