Teach global selector lookup to ignore hidden methods, which occur
when the methods are declared in a submodule that has not yet been
imported. Part of <rdar://problem/10634711>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172635 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index 870c52b..e6c5d92 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -2167,61 +2167,79 @@
if (Pos == MethodPool.end())
return 0;
+ // Gather the non-hidden methods.
ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second;
+ llvm::SmallVector<ObjCMethodDecl *, 4> Methods;
+ for (ObjCMethodList *M = &MethList; M; M = M->Next) {
+ if (M->Method && !M->Method->isHidden()) {
+ // If we're not supposed to warn about mismatches, we're done.
+ if (!warn)
+ return M->Method;
- if (warn && MethList.Method && MethList.Next) {
- bool issueDiagnostic = false, issueError = false;
-
- // We support a warning which complains about *any* difference in
- // method signature.
- bool strictSelectorMatch =
- (receiverIdOrClass && warn &&
- (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl,
- R.getBegin()) !=
- DiagnosticsEngine::Ignored));
- if (strictSelectorMatch)
- for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) {
- if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method,
- MMS_strict)) {
- issueDiagnostic = true;
- break;
- }
- }
-
- // If we didn't see any strict differences, we won't see any loose
- // differences. In ARC, however, we also need to check for loose
- // mismatches, because most of them are errors.
- if (!strictSelectorMatch ||
- (issueDiagnostic && getLangOpts().ObjCAutoRefCount))
- for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) {
- // This checks if the methods differ in type mismatch.
- if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method,
- MMS_loose) &&
- !isAcceptableMethodMismatch(MethList.Method, Next->Method)) {
- issueDiagnostic = true;
- if (getLangOpts().ObjCAutoRefCount)
- issueError = true;
- break;
- }
- }
-
- if (issueDiagnostic) {
- if (issueError)
- Diag(R.getBegin(), diag::err_arc_multiple_method_decl) << Sel << R;
- else if (strictSelectorMatch)
- Diag(R.getBegin(), diag::warn_strict_multiple_method_decl) << Sel << R;
- else
- Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R;
-
- Diag(MethList.Method->getLocStart(),
- issueError ? diag::note_possibility : diag::note_using)
- << MethList.Method->getSourceRange();
- for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next)
- Diag(Next->Method->getLocStart(), diag::note_also_found)
- << Next->Method->getSourceRange();
+ Methods.push_back(M->Method);
}
}
- return MethList.Method;
+
+ // If there aren't any visible methods, we're done.
+ // FIXME: Recover if there are any known-but-hidden methods?
+ if (Methods.empty())
+ return 0;
+
+ if (Methods.size() == 1)
+ return Methods[0];
+
+ // We found multiple methods, so we may have to complain.
+ bool issueDiagnostic = false, issueError = false;
+
+ // We support a warning which complains about *any* difference in
+ // method signature.
+ bool strictSelectorMatch =
+ (receiverIdOrClass && warn &&
+ (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl,
+ R.getBegin())
+ != DiagnosticsEngine::Ignored));
+ if (strictSelectorMatch) {
+ for (unsigned I = 1, N = Methods.size(); I != N; ++I) {
+ if (!MatchTwoMethodDeclarations(Methods[0], Methods[I], MMS_strict)) {
+ issueDiagnostic = true;
+ break;
+ }
+ }
+ }
+
+ // If we didn't see any strict differences, we won't see any loose
+ // differences. In ARC, however, we also need to check for loose
+ // mismatches, because most of them are errors.
+ if (!strictSelectorMatch ||
+ (issueDiagnostic && getLangOpts().ObjCAutoRefCount))
+ for (unsigned I = 1, N = Methods.size(); I != N; ++I) {
+ // This checks if the methods differ in type mismatch.
+ if (!MatchTwoMethodDeclarations(Methods[0], Methods[I], MMS_loose) &&
+ !isAcceptableMethodMismatch(Methods[0], Methods[I])) {
+ issueDiagnostic = true;
+ if (getLangOpts().ObjCAutoRefCount)
+ issueError = true;
+ break;
+ }
+ }
+
+ if (issueDiagnostic) {
+ if (issueError)
+ Diag(R.getBegin(), diag::err_arc_multiple_method_decl) << Sel << R;
+ else if (strictSelectorMatch)
+ Diag(R.getBegin(), diag::warn_strict_multiple_method_decl) << Sel << R;
+ else
+ Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R;
+
+ Diag(Methods[0]->getLocStart(),
+ issueError ? diag::note_possibility : diag::note_using)
+ << Methods[0]->getSourceRange();
+ for (unsigned I = 1, N = Methods.size(); I != N; ++I) {
+ Diag(Methods[I]->getLocStart(), diag::note_also_found)
+ << Methods[I]->getSourceRange();
+ }
+ }
+ return Methods[0];
}
ObjCMethodDecl *Sema::LookupImplementedMethodInGlobalPool(Selector Sel) {
diff --git a/test/Modules/Inputs/MethodPoolASub.h b/test/Modules/Inputs/MethodPoolASub.h
new file mode 100644
index 0000000..e0bd238
--- /dev/null
+++ b/test/Modules/Inputs/MethodPoolASub.h
@@ -0,0 +1,3 @@
+@interface A (Sub)
+- (char)method3;
+@end
diff --git a/test/Modules/Inputs/MethodPoolBSub.h b/test/Modules/Inputs/MethodPoolBSub.h
new file mode 100644
index 0000000..3404ce9
--- /dev/null
+++ b/test/Modules/Inputs/MethodPoolBSub.h
@@ -0,0 +1,3 @@
+@interface B (Sub)
+- (char *)method3;
+@end
diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map
index 702f5c1..42c6c2b 100644
--- a/test/Modules/Inputs/module.map
+++ b/test/Modules/Inputs/module.map
@@ -107,9 +107,17 @@
}
module MethodPoolA {
header "MethodPoolA.h"
+
+ explicit module Sub {
+ header "MethodPoolASub.h"
+ }
}
module MethodPoolB {
header "MethodPoolB.h"
+
+ explicit module Sub {
+ header "MethodPoolBSub.h"
+ }
}
module import_decl {
header "import-decl.h"
diff --git a/test/Modules/method_pool.m b/test/Modules/method_pool.m
index 9574caa..1ba5abd 100644
--- a/test/Modules/method_pool.m
+++ b/test/Modules/method_pool.m
@@ -28,3 +28,21 @@
void testMethod2Again(id object) {
[object method2:1]; // expected-warning{{multiple methods named 'method2:' found}}
}
+
+void testMethod3(id object) {
+ [object method3]; // expected-warning{{instance method '-method3' not found (return type defaults to 'id')}}
+}
+
+@import MethodPoolB.Sub;
+
+void testMethod3Again(id object) {
+ char *str = [object method3]; // okay: only found in MethodPoolB.Sub
+}
+
+@import MethodPoolA.Sub;
+
+void testMethod3AgainAgain(id object) {
+ [object method3]; // expected-warning{{multiple methods named 'method3' found}}
+ // expected-note@2{{using}}
+ // expected-note@2{{also found}}
+}