[ClangImporter] Don't import compatibility methods named 'print'. (#10928)

...because they make things harder for people trying to use
Swift.print. Before:

  error: 'print' has been renamed to 'printDocument(_:)'

After:

  error: use of 'print' nearly matches global function
    'print(_:separator:terminator:)' in module 'Swift'
    rather than instance method 'print(_:extra:)'

(This actually occurs with AppKit's NSDocument, so it's not just a
hypothetical concern.)

rdar://problem/32839733
This commit is contained in:
Jordan Rose
2017-07-13 15:44:39 -07:00
committed by GitHub
parent 16b7de05a3
commit 663c4f9524
4 changed files with 136 additions and 9 deletions

View File

@@ -1868,6 +1868,23 @@ applyPropertyOwnership(VarDecl *prop,
} }
} }
/// Does this name refer to a method that might shadow Swift.print?
///
/// As a heuristic, methods that have a base name of 'print' but more than
/// one argument are left alone. These can still shadow Swift.print but are
/// less likely to be confused for it, at least.
static bool isPrintLikeMethod(DeclName name, const DeclContext *dc) {
if (!name || name.isSpecial() || name.isSimpleName())
return false;
if (name.getBaseIdentifier().str() != "print")
return false;
if (!dc->isTypeContext())
return false;
if (name.getArgumentNames().size() > 1)
return false;
return true;
}
using MirroredMethodEntry = using MirroredMethodEntry =
std::pair<const clang::ObjCMethodDecl*, ProtocolDecl*>; std::pair<const clang::ObjCMethodDecl*, ProtocolDecl*>;
@@ -3681,6 +3698,14 @@ namespace {
if (forceClassMethod && decl->hasRelatedResultType()) if (forceClassMethod && decl->hasRelatedResultType())
return nullptr; return nullptr;
// Hack: avoid importing methods named "print" that aren't available in
// the current version of Swift. We'd rather just let the user use
// Swift.print in that case.
if (!isActiveSwiftVersion() &&
isPrintLikeMethod(importedName.getDeclName(), dc)) {
return nullptr;
}
// Add the implicit 'self' parameter patterns. // Add the implicit 'self' parameter patterns.
bool isInstance = decl->isInstanceMethod() && !forceClassMethod; bool isInstance = decl->isInstanceMethod() && !forceClassMethod;
SmallVector<ParameterList *, 4> bodyParams; SmallVector<ParameterList *, 4> bodyParams;
@@ -7114,15 +7139,10 @@ void ClangImporter::Implementation::importAttributes(
// Hack: mark any method named "print" with less than two parameters as // Hack: mark any method named "print" with less than two parameters as
// warn_unqualified_access. // warn_unqualified_access.
if (auto MD = dyn_cast<FuncDecl>(MappedDecl)) { if (auto MD = dyn_cast<FuncDecl>(MappedDecl)) {
if (!MD->getName().empty() && MD->getName().str() == "print" && if (isPrintLikeMethod(MD->getFullName(), MD->getDeclContext())) {
MD->getDeclContext()->isTypeContext()) { // Use a non-implicit attribute so it shows up in the generated
auto *formalParams = MD->getParameterList(1); // interface.
if (formalParams->size() <= 1) { MD->getAttrs().add(new (C) WarnUnqualifiedAccessAttr(/*implicit*/false));
// Use a non-implicit attribute so it shows up in the generated
// interface.
MD->getAttrs().add(
new (C) WarnUnqualifiedAccessAttr(/*implicit*/false));
}
} }
} }

View File

@@ -2,6 +2,31 @@ Name: APINotesFrameworkTest
Classes: Classes:
- Name: A - Name: A
SwiftObjCMembers: true SwiftObjCMembers: true
- Name: PrintingInterference
Methods:
- Selector: 'print:'
MethodKind: Instance
SwiftName: 'printDocument(_:)'
- Name: PrintingRenamed
Methods:
- Selector: 'print'
MethodKind: Instance
SwiftName: 'printDocument()'
- Selector: 'print:'
MethodKind: Instance
SwiftName: 'printDocument(_:)'
- Selector: 'print:options:'
MethodKind: Instance
SwiftName: 'printDocument(_:options:)'
- Selector: 'print'
MethodKind: Class
SwiftName: 'printDocument()'
- Selector: 'print:'
MethodKind: Class
SwiftName: 'printDocument(_:)'
- Selector: 'print:options:'
MethodKind: Class
SwiftName: 'printDocument(_:options:)'
- Name: TestProperties - Name: TestProperties
Properties: Properties:
- Name: accessorsOnly - Name: accessorsOnly
@@ -43,6 +68,31 @@ Tags:
SwiftVersions: SwiftVersions:
- Version: 3.0 - Version: 3.0
Classes: Classes:
- Name: PrintingInterference
Methods:
- Selector: 'print:'
MethodKind: Instance
SwiftName: 'print(_:)'
- Name: PrintingRenamed
Methods:
- Selector: 'print'
MethodKind: Instance
SwiftName: 'print()'
- Selector: 'print:'
MethodKind: Instance
SwiftName: 'print(_:)'
- Selector: 'print:options:'
MethodKind: Instance
SwiftName: 'print(_:options:)'
- Selector: 'print'
MethodKind: Class
SwiftName: 'print()'
- Selector: 'print:'
MethodKind: Class
SwiftName: 'print(_:)'
- Selector: 'print:options:'
MethodKind: Class
SwiftName: 'print(_:options:)'
- Name: TestProperties - Name: TestProperties
Methods: Methods:
- Selector: accessorsOnlyRenamedRetyped - Selector: accessorsOnlyRenamedRetyped

View File

@@ -17,5 +17,20 @@
@property (nullable) id importantInstanceProperty __attribute__((swift_name("finalInstanceProperty"))); @property (nullable) id importantInstanceProperty __attribute__((swift_name("finalInstanceProperty")));
@end @end
@interface PrintingRenamed : Base
- (void)print;
- (void)print:(id)thing;
- (void)print:(id)thing options:(id)options;
+ (void)print;
+ (void)print:(id)thing;
+ (void)print:(id)thing options:(id)options;
@end
@interface PrintingInterference : Base
- (void)print:(id)thing; // Only this one gets renamed.
- (void)print:(id)thing extra:(id)options;
@end
#pragma clang assume_nonnull end #pragma clang assume_nonnull end
#endif // __OBJC__ #endif // __OBJC__

View File

@@ -140,4 +140,46 @@ func testRenamedProtocolMembers(obj: ProtoWithManyRenames) {
// CHECK-DIAGS-4-NOT: :[[@LINE-1]]:{{[0-9]+}}: // CHECK-DIAGS-4-NOT: :[[@LINE-1]]:{{[0-9]+}}:
} }
extension PrintingRenamed {
func testDroppingRenamedPrints() {
// CHECK-DIAGS-3: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method
print()
// CHECK-DIAGS-4-NOT: [[@LINE-1]]:{{[0-9]+}}:
// CHECK-DIAGS-3: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method
print(self)
// CHECK-DIAGS-4-NOT: [[@LINE-1]]:{{[0-9]+}}:
// CHECK-DIAGS-3-NOT: [[@LINE+1]]:{{[0-9]+}}:
print(self, options: self)
// CHECK-DIAGS-4: [[@LINE-1]]:{{[0-9]+}}: error: argument labels '(_:, options:)' do not match any available overloads
}
static func testDroppingRenamedPrints() {
// CHECK-DIAGS-3: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to class method
print()
// CHECK-DIAGS-4-NOT: [[@LINE-1]]:{{[0-9]+}}:
// CHECK-DIAGS-3: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to class method
print(self)
// CHECK-DIAGS-4-NOT: [[@LINE-1]]:{{[0-9]+}}:
// CHECK-DIAGS-3-NOT: [[@LINE+1]]:{{[0-9]+}}:
print(self, options: self)
// CHECK-DIAGS-4: [[@LINE-1]]:{{[0-9]+}}: error: argument labels '(_:, options:)' do not match any available overloads
}
}
extension PrintingInterference {
func testDroppingRenamedPrints() {
// CHECK-DIAGS-3: [[@LINE+1]]:{{[0-9]+}}: warning: use of 'print' treated as a reference to instance method
print(self)
// CHECK-DIAGS-4: [[@LINE-1]]:{{[0-9]+}}: error: use of 'print' nearly matches global function 'print(_:separator:terminator:)' in module 'Swift' rather than instance method 'print(_:extra:)'
// CHECK-DIAGS-3-NOT: [[@LINE+1]]:{{[0-9]+}}:
print(self, extra: self)
// CHECK-DIAGS-4-NOT: [[@LINE-1]]:{{[0-9]+}}:
}
}
let unrelatedDiagnostic: Int = nil let unrelatedDiagnostic: Int = nil