|
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"
|
@@ -78,6 +79,87 @@ llvm::Regex swift::getSwiftInterfaceCompilerVersionRegex() {
|
78 | 79 | ": (.+)$", llvm::Regex::Newline);
|
79 | 80 | }
|
80 | 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 | + |
81 | 163 | // MARK: Import statements
|
82 | 164 |
|
83 | 165 | /// Diagnose any scoped imports in \p imports, i.e. those with a non-empty
|
@@ -175,6 +257,8 @@ static void printImports(raw_ostream &out,
|
175 | 257 | }
|
176 | 258 |
|
177 | 259 | out << "\n";
|
| 260 | + |
| 261 | + diagnoseIfModuleImportsShadowingDecl(Opts, importedModule, M); |
178 | 262 | }
|
179 | 263 | }
|
180 | 264 |
|
@@ -596,6 +680,8 @@ bool swift::emitSwiftInterface(raw_ostream &out,
|
596 | 680 |
|
597 | 681 | D->print(out, printOptions);
|
598 | 682 | out << "\n";
|
| 683 | + |
| 684 | + diagnoseIfDeclShadowsKnownModule(Opts, const_cast<Decl *>(D), M); |
599 | 685 | }
|
600 | 686 |
|
601 | 687 | // Print dummy extensions for any protocols that were indirectly conformed to.
|
|
0 commit comments