From 9464a08a743295d6aefaca1a751b5b4d371cf99c Mon Sep 17 00:00:00 2001 From: Fariborz Jahanian Date: Wed, 5 Jun 2013 18:46:14 +0000 Subject: [PATCH] Objective-C: Provide fixit with suggested spelling correction 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 | 3 ++ include/clang/Sema/Sema.h | 2 + lib/Sema/SemaDeclObjC.cpp | 51 ++++++++++++++++++++++ lib/Sema/SemaExprObjC.cpp | 14 +++++- test/FixIt/selector-fixit.m | 30 +++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 test/FixIt/selector-fixit.m diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a97e1cc355..6e682e8954 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -811,6 +811,9 @@ def error_dealloc_bad_result_type : Error< "instead of %0">; def warn_undeclared_selector : Warning< "undeclared selector %0">, InGroup, DefaultIgnore; +def warn_undeclared_selector_with_typo : Warning< + "undeclared selector %0; did you mean %1?">, + InGroup, DefaultIgnore; def warn_implicit_atomic_property : Warning< "property is assumed atomic by default">, InGroup, DefaultIgnore; def note_auto_readonly_iboutlet_fixup_suggest : Note< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d587386490..3c4cbae790 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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. diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index f1ce4bce5f..b2387d3c7e 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -2271,6 +2271,57 @@ ObjCMethodDecl *Sema::LookupImplementedMethodInGlobalPool(Selector Sel) { return 0; } +static void +HelperSelectorsForTypoCorrection( + SmallVectorImpl &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 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 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) { diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 23116d84ad..3b84acece2 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -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 index 0000000000..a8b38c6b9e --- /dev/null +++ b/test/FixIt/selector-fixit.m @@ -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 -- 2.40.0