]> granicus.if.org Git - clang/commitdiff
Objective-C: Provide fixit with suggested spelling correction
authorFariborz Jahanian <fjahanian@apple.com>
Wed, 5 Jun 2013 18:46:14 +0000 (18:46 +0000)
committerFariborz Jahanian <fjahanian@apple.com>
Wed, 5 Jun 2013 18:46:14 +0000 (18:46 +0000)
for -Wundeclared-selector warnings. // rdar://14039037

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183331 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/SemaDeclObjC.cpp
lib/Sema/SemaExprObjC.cpp
test/FixIt/selector-fixit.m [new file with mode: 0644]

index a97e1cc35558f1fcc7f046549584175528b12b04..6e682e8954e511370913570e4e61b51d4cdf018d 100644 (file)
@@ -811,6 +811,9 @@ def error_dealloc_bad_result_type : Error<
   "instead of %0">;
 def warn_undeclared_selector : Warning<
   "undeclared selector %0">, InGroup<UndeclaredSelector>, DefaultIgnore;
+def warn_undeclared_selector_with_typo : Warning<
+  "undeclared selector %0; did you mean %1?">,
+  InGroup<UndeclaredSelector>, DefaultIgnore;
 def warn_implicit_atomic_property : Warning<
   "property is assumed atomic by default">, InGroup<ImplicitAtomic>, DefaultIgnore;
 def note_auto_readonly_iboutlet_fixup_suggest : Note<
index d5873864902c16ca0edb0eccb52621d95a246e66..3c4cbae7909ab7d6c5a2adc32d1c64da8744da2e 100644 (file)
@@ -2669,6 +2669,8 @@ public:
                                     warn, /*instance*/false);
   }
 
+  const ObjCMethodDecl *SelectorsForTypoCorrection(Selector Sel);
+  
   /// DiagnoseMismatchedMethodsInGlobalPool - This routine goes through list of
   /// methods in global pool and issues diagnostic on identical selectors which
   /// have mismathched types.
index f1ce4bce5f6581401a81c4080692598f703f3e28..b2387d3c7e162b1e5222d19e5c519582b81718fa 100644 (file)
@@ -2271,6 +2271,57 @@ ObjCMethodDecl *Sema::LookupImplementedMethodInGlobalPool(Selector Sel) {
   return 0;
 }
 
+static void
+HelperSelectorsForTypoCorrection(
+                      SmallVectorImpl<const ObjCMethodDecl *> &BestMethod,
+                      StringRef Typo, const ObjCMethodDecl * Method) {
+  const unsigned MaxEditDistance = 1;
+  unsigned BestEditDistance = MaxEditDistance + 1;
+  StringRef MethodName = Method->getSelector().getAsString();
+  
+  unsigned MinPossibleEditDistance = abs((int)MethodName.size() - (int)Typo.size());
+  if (MinPossibleEditDistance > 0 &&
+      Typo.size() / MinPossibleEditDistance < 1)
+    return;
+  unsigned EditDistance = Typo.edit_distance(MethodName, true, MaxEditDistance);
+  if (EditDistance > MaxEditDistance)
+    return;
+  if (EditDistance == BestEditDistance)
+    BestMethod.push_back(Method);
+  else if (EditDistance < BestEditDistance) {
+    BestMethod.clear();
+    BestMethod.push_back(Method);
+    BestEditDistance = EditDistance;
+  }
+}
+
+const ObjCMethodDecl *
+Sema::SelectorsForTypoCorrection(Selector Sel) {
+  unsigned NumArgs = Sel.getNumArgs();
+  SmallVector<const ObjCMethodDecl *, 8> Methods;
+  
+  for (GlobalMethodPool::iterator b = MethodPool.begin(),
+       e = MethodPool.end(); b != e; b++) {
+    // instance methods
+    for (ObjCMethodList *M = &b->second.first; M; M=M->getNext())
+      if (M->Method &&
+          (M->Method->getSelector().getNumArgs() == NumArgs))
+        Methods.push_back(M->Method);
+    // class methods
+    for (ObjCMethodList *M = &b->second.second; M; M=M->getNext())
+      if (M->Method &&
+          (M->Method->getSelector().getNumArgs() == NumArgs))
+        Methods.push_back(M->Method);
+  }
+  
+  SmallVector<const ObjCMethodDecl *, 8> SelectedMethods;
+  for (unsigned i = 0, e = Methods.size(); i < e; i++) {
+    HelperSelectorsForTypoCorrection(SelectedMethods,
+                                     Sel.getAsString(), Methods[i]);
+  }
+  return (SelectedMethods.size() == 1) ? SelectedMethods[0] : NULL;
+}
+
 static void
 HelperToDiagnoseMismatchedMethodsInGlobalPool(Sema &S,
                                               ObjCMethodList &MethList) {
index 23116d84addd619a4d2da03af3988d806b430607..3b84acece278874a42ce9b601f9806d6b2b6b8fc 100644 (file)
@@ -968,8 +968,18 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel,
   if (!Method)
     Method = LookupFactoryMethodInGlobalPool(Sel,
                                           SourceRange(LParenLoc, RParenLoc));
-  if (!Method)
-    Diag(SelLoc, diag::warn_undeclared_selector) << Sel;
+  if (!Method) {
+    if (const ObjCMethodDecl *OM = SelectorsForTypoCorrection(Sel)) {
+      Selector MatchedSel = OM->getSelector();
+      SourceRange SelectorRange(LParenLoc.getLocWithOffset(1),
+                                RParenLoc.getLocWithOffset(-1));
+      Diag(SelLoc, diag::warn_undeclared_selector_with_typo)
+        << Sel << MatchedSel
+        << FixItHint::CreateReplacement(SelectorRange, MatchedSel.getAsString());
+      
+    } else
+        Diag(SelLoc, diag::warn_undeclared_selector) << Sel;
+  }
   
   if (!Method ||
       Method->getImplementationControl() != ObjCMethodDecl::Optional) {
diff --git a/test/FixIt/selector-fixit.m b/test/FixIt/selector-fixit.m
new file mode 100644 (file)
index 0000000..a8b38c6
--- /dev/null
@@ -0,0 +1,30 @@
+// RUN: cp %s %t
+// RUN: %clang_cc1 -x objective-c -Wundeclared-selector -fixit %t
+// RUN: %clang_cc1 -x objective-c -Wundeclared-selector -Werror %t
+// rdar://14039037
+
+@interface NSObject @end
+
+@interface LogoutController : NSObject
+- (void)close;
+- (void)closed;
+- (void) open : (id) file_id;
+@end
+
+@implementation LogoutController
+
+- (void)close  { }
+- (void)closed  { }
+
+- (SEL)Meth
+{
+  return @selector(cloze);
+}
+- (void) open : (id) file_id {}
+
+- (SEL)Meth1
+{
+  return @selector(ope:);
+}
+
+@end