|
18 | 18 | #include "swift/AST/ExistentialLayout.h"
|
19 | 19 | #include "swift/AST/FileSystem.h"
|
20 | 20 | #include "swift/AST/Module.h"
|
| 21 | +#include "swift/AST/ModuleNameLookup.h" |
21 | 22 | #include "swift/AST/ProtocolConformance.h"
|
22 | 23 | #include "swift/Basic/STLExtras.h"
|
23 | 24 | #include "swift/Frontend/Frontend.h"
|
|
36 | 37 |
|
37 | 38 | using namespace swift;
|
38 | 39 |
|
39 |
| -version::Version swift::InterfaceFormatVersion({1, 0}); |
| 40 | +// MARK: Module interface header comments |
40 | 41 |
|
41 |
| -/// Diagnose any scoped imports in \p imports, i.e. those with a non-empty |
42 |
| -/// access path. These are not yet supported by module interfaces, since the |
43 |
| -/// information about the declaration kind is not preserved through the binary |
44 |
| -/// serialization that happens as an intermediate step in non-whole-module |
45 |
| -/// builds. |
46 |
| -/// |
47 |
| -/// These come from declarations like `import class FooKit.MainFooController`. |
48 |
| -static void diagnoseScopedImports(DiagnosticEngine &diags, |
49 |
| - ArrayRef<ImportedModule> imports){ |
50 |
| - for (const ImportedModule &importPair : imports) { |
51 |
| - if (importPair.accessPath.empty()) |
52 |
| - continue; |
53 |
| - diags.diagnose(importPair.accessPath.front().Loc, |
54 |
| - diag::module_interface_scoped_import_unsupported); |
55 |
| - } |
56 |
| -} |
| 42 | +version::Version swift::InterfaceFormatVersion({1, 0}); |
57 | 43 |
|
58 | 44 | /// Prints to \p out a comment containing a format version number, tool version
|
59 | 45 | /// string as well as any relevant command-line flags in \p Opts used to
|
@@ -93,6 +79,106 @@ llvm::Regex swift::getSwiftInterfaceCompilerVersionRegex() {
|
93 | 79 | ": (.+)$", llvm::Regex::Newline);
|
94 | 80 | }
|
95 | 81 |
|
| 82 | +// MARK: Module name shadowing warnings (SR-898) |
| 83 | +// |
| 84 | +// When swiftc emits a module interface, it qualifies most types with their |
| 85 | +// module name. This usually makes the interface less ambiguous, but if a type |
| 86 | +// exists with the same name as a module, then references to that module will |
| 87 | +// incorrectly look inside the type instead. This breakage is not obvious until |
| 88 | +// someone tries to load the module interface, and may sometimes only occur in |
| 89 | +// clients' module interfaces. |
| 90 | +// |
| 91 | +// Truly fixing this will require a new module-qualification syntax which |
| 92 | +// completely ignores shadowing. In lieu of that, we detect and warn about three |
| 93 | +// common examples which are relatively actionable: |
| 94 | +// |
| 95 | +// 1. An `import` statement written into the module interface will |
| 96 | +// (transitively) import a type with the module interface's name. |
| 97 | +// |
| 98 | +// 2. The module interface declares a type with the same name as the module the |
| 99 | +// interface is for. |
| 100 | +// |
| 101 | +// 3. The module interface declares a type with the same name as a module it has |
| 102 | +// (transitively) imported without `@_implementationOnly`. |
| 103 | +// |
| 104 | +// We do not check for shadowing between imported module names and imported |
| 105 | +// declarations; this is both much rarer and much more difficult to solve. |
| 106 | +// We silence these warnings if you use the temporary workaround flag, |
| 107 | +// '-module-interface-preserve-types-as-written'. |
| 108 | + |
| 109 | +/// Emit a warning explaining that \p shadowingDecl will interfere with |
| 110 | +/// references to types in \p shadowedModule in the module interfaces of |
| 111 | +/// \p brokenModule and its clients. |
| 112 | +static void |
| 113 | +diagnoseDeclShadowsModule(ModuleInterfaceOptions const &Opts, |
| 114 | + TypeDecl *shadowingDecl, ModuleDecl *shadowedModule, |
| 115 | + ModuleDecl *brokenModule) { |
| 116 | + if (Opts.PreserveTypesAsWritten || shadowingDecl == shadowedModule) |
| 117 | + return; |
| 118 | + |
| 119 | + shadowingDecl->diagnose( |
| 120 | + diag::warning_module_shadowing_may_break_module_interface, |
| 121 | + shadowingDecl->getDescriptiveKind(), |
| 122 | + FullyQualified<Type>(shadowingDecl->getDeclaredInterfaceType()), |
| 123 | + shadowedModule, brokenModule); |
| 124 | +} |
| 125 | + |
| 126 | +/// Check whether importing \p importedModule will bring in any declarations |
| 127 | +/// that will shadow \p importingModule, and diagnose them if so. |
| 128 | +static void |
| 129 | +diagnoseIfModuleImportsShadowingDecl(ModuleInterfaceOptions const &Opts, |
| 130 | + ModuleDecl *importedModule, |
| 131 | + ModuleDecl *importingModule) { |
| 132 | + using namespace namelookup; |
| 133 | + |
| 134 | + SmallVector<ValueDecl *, 4> decls; |
| 135 | + lookupInModule(importedModule, importingModule->getName(), decls, |
| 136 | + NLKind::UnqualifiedLookup, ResolutionKind::TypesOnly, |
| 137 | + importedModule, |
| 138 | + NL_UnqualifiedDefault | NL_IncludeUsableFromInline); |
| 139 | + for (auto decl : decls) |
| 140 | + diagnoseDeclShadowsModule(Opts, cast<TypeDecl>(decl), importingModule, |
| 141 | + importingModule); |
| 142 | +} |
| 143 | + |
| 144 | +/// Check whether \p D will shadow any modules imported by \p M, and diagnose |
| 145 | +/// them if so. |
| 146 | +static void diagnoseIfDeclShadowsKnownModule(ModuleInterfaceOptions const &Opts, |
| 147 | + Decl *D, ModuleDecl *M) { |
| 148 | + ASTContext &ctx = M->getASTContext(); |
| 149 | + |
| 150 | + // We only care about types (and modules, which are a subclass of TypeDecl); |
| 151 | + // when the grammar expects a type name, it ignores non-types during lookup. |
| 152 | + TypeDecl *TD = dyn_cast<TypeDecl>(D); |
| 153 | + if (!TD) |
| 154 | + return; |
| 155 | + |
| 156 | + ModuleDecl *shadowedModule = ctx.getLoadedModule(TD->getName()); |
| 157 | + if (!shadowedModule || M->isImportedImplementationOnly(shadowedModule)) |
| 158 | + return; |
| 159 | + |
| 160 | + diagnoseDeclShadowsModule(Opts, TD, shadowedModule, M); |
| 161 | +} |
| 162 | + |
| 163 | +// MARK: Import statements |
| 164 | + |
| 165 | +/// Diagnose any scoped imports in \p imports, i.e. those with a non-empty |
| 166 | +/// access path. These are not yet supported by module interfaces, since the |
| 167 | +/// information about the declaration kind is not preserved through the binary |
| 168 | +/// serialization that happens as an intermediate step in non-whole-module |
| 169 | +/// builds. |
| 170 | +/// |
| 171 | +/// These come from declarations like `import class FooKit.MainFooController`. |
| 172 | +static void diagnoseScopedImports(DiagnosticEngine &diags, |
| 173 | + ArrayRef<ImportedModule> imports){ |
| 174 | + for (const ImportedModule &importPair : imports) { |
| 175 | + if (importPair.accessPath.empty()) |
| 176 | + continue; |
| 177 | + diags.diagnose(importPair.accessPath.front().Loc, |
| 178 | + diag::module_interface_scoped_import_unsupported); |
| 179 | + } |
| 180 | +} |
| 181 | + |
96 | 182 | /// Prints the imported modules in \p M to \p out in the form of \c import
|
97 | 183 | /// source declarations.
|
98 | 184 | static void printImports(raw_ostream &out,
|
@@ -171,9 +257,13 @@ static void printImports(raw_ostream &out,
|
171 | 257 | }
|
172 | 258 |
|
173 | 259 | out << "\n";
|
| 260 | + |
| 261 | + diagnoseIfModuleImportsShadowingDecl(Opts, importedModule, M); |
174 | 262 | }
|
175 | 263 | }
|
176 | 264 |
|
| 265 | +// MARK: Dummy protocol conformances |
| 266 | + |
177 | 267 | // FIXME: Copied from ASTPrinter.cpp...
|
178 | 268 | static bool isPublicOrUsableFromInline(const ValueDecl *VD) {
|
179 | 269 | AccessScope scope =
|
@@ -553,6 +643,8 @@ const StringLiteral InheritedProtocolCollector::DummyProtocolName =
|
553 | 643 | "_ConstraintThatIsNotPartOfTheAPIOfThisLibrary";
|
554 | 644 | } // end anonymous namespace
|
555 | 645 |
|
| 646 | +// MARK: Interface emission |
| 647 | + |
556 | 648 | bool swift::emitSwiftInterface(raw_ostream &out,
|
557 | 649 | ModuleInterfaceOptions const &Opts,
|
558 | 650 | ModuleDecl *M) {
|
@@ -580,6 +672,8 @@ bool swift::emitSwiftInterface(raw_ostream &out,
|
580 | 672 |
|
581 | 673 | D->print(out, printOptions);
|
582 | 674 | out << "\n";
|
| 675 | + |
| 676 | + diagnoseIfDeclShadowsKnownModule(Opts, const_cast<Decl *>(D), M); |
583 | 677 | }
|
584 | 678 |
|
585 | 679 | // Print dummy extensions for any protocols that were indirectly conformed to.
|
|
0 commit comments