[cfe-commits] r101274 - in /cfe/trunk: lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaDecl.cpp lib/Sema/SemaDeclCXX.cpp lib/Sema/SemaDeclObjC.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaExprObjC.cpp lib/Sema/SemaInit.cpp lib/Sema/SemaLookup.cpp lib/Sema/SemaTemplate.cpp test/FixIt/fixit-unrecoverable.c test/FixIt/fixit-unrecoverable.cpp test/FixIt/typo.m

Douglas Gregor dgregor at apple.com
Wed Apr 14 13:04:41 PDT 2010


Author: dgregor
Date: Wed Apr 14 15:04:41 2010
New Revision: 101274

URL: http://llvm.org/viewvc/llvm-project?rev=101274&view=rev
Log:
Teach typo correction about various language keywords. We can't
generally recover from typos in keywords (since we would effectively
have to mangle the token stream). However, there are still benefits to
typo-correcting with keywords:
  - We don't make stupid suggestions when the user typed something
  that is similar to a keyword. 
  - We can suggest the keyword in a diagnostic (did you mean
  "static_cast"?), even if we can't recover and therefore don't have
  a fix-it.


Added:
    cfe/trunk/test/FixIt/fixit-unrecoverable.c
    cfe/trunk/test/FixIt/fixit-unrecoverable.cpp
Modified:
    cfe/trunk/lib/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDeclObjC.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprObjC.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaLookup.cpp
    cfe/trunk/lib/Sema/SemaTemplate.cpp
    cfe/trunk/test/FixIt/typo.m

Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Apr 14 15:04:41 2010
@@ -1431,9 +1431,33 @@
   void LookupVisibleDecls(DeclContext *Ctx, LookupNameKind Kind,
                           VisibleDeclConsumer &Consumer);
 
+  /// \brief The context in which typo-correction occurs.
+  ///
+  /// The typo-correction context affects which keywords (if any) are 
+  /// considered when trying to correct for typos.
+  enum CorrectTypoContext {
+    /// \brief An unknown context, where any keyword might be valid.
+    CTC_Unknown,
+    /// \brief A context where no keywords are used (e.g. we expect an actual
+    /// name).
+    CTC_NoKeywords,
+    /// \brief A context where we're correcting a type name.
+    CTC_Type,
+    /// \brief An expression context.
+    CTC_Expression,
+    /// \brief A type cast, or anything else that can be followed by a '<'. 
+    CTC_CXXCasts,
+    /// \brief A member lookup context.
+    CTC_MemberLookup,
+    /// \brief The receiver of an Objective-C message send within an 
+    /// Objective-C method where 'super' is a valid keyword.
+    CTC_ObjCMessageReceiver
+  };
+  
   DeclarationName CorrectTypo(LookupResult &R, Scope *S, CXXScopeSpec *SS,
                               DeclContext *MemberContext = 0,
                               bool EnteringContext = false,
+                              CorrectTypoContext CTC = CTC_Unknown,
                               const ObjCObjectPointerType *OPT = 0);
 
   void FindAssociatedClassesAndNamespaces(Expr **Args, unsigned NumArgs,

Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Wed Apr 14 15:04:41 2010
@@ -483,7 +483,8 @@
     // We haven't found anything, and we're not recovering from a
     // different kind of error, so look for typos.
     DeclarationName Name = Found.getLookupName();
-    if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext) &&
+    if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext,  
+                    CTC_NoKeywords) &&
         Found.isSingleResult() &&
         isAcceptableNestedNameSpecifier(Found.getAsSingle<NamedDecl>())) {
       if (LookupCtx)

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Apr 14 15:04:41 2010
@@ -246,32 +246,36 @@
   LookupResult Lookup(*this, &II, IILoc, LookupOrdinaryName, 
                       NotForRedeclaration);
 
-  // FIXME: It would be nice if we could correct for typos in built-in
-  // names, such as "itn" for "int".
-
-  if (CorrectTypo(Lookup, S, SS) && Lookup.isSingleResult()) {
-    NamedDecl *Result = Lookup.getAsSingle<NamedDecl>();
-    if ((isa<TypeDecl>(Result) || isa<ObjCInterfaceDecl>(Result)) &&
-        !Result->isInvalidDecl()) {
-      // We found a similarly-named type or interface; suggest that.
-      if (!SS || !SS->isSet())
-        Diag(IILoc, diag::err_unknown_typename_suggest)
-          << &II << Lookup.getLookupName()
-          << FixItHint::CreateReplacement(SourceRange(IILoc),
-                                          Result->getNameAsString());
-      else if (DeclContext *DC = computeDeclContext(*SS, false))
-        Diag(IILoc, diag::err_unknown_nested_typename_suggest) 
-          << &II << DC << Lookup.getLookupName() << SS->getRange()
-          << FixItHint::CreateReplacement(SourceRange(IILoc),
-                                          Result->getNameAsString());
-      else
-        llvm_unreachable("could not have corrected a typo here");
+  if (DeclarationName Corrected = CorrectTypo(Lookup, S, SS, 0, 0, CTC_Type)) {
+    if (NamedDecl *Result = Lookup.getAsSingle<NamedDecl>()) {
+      if ((isa<TypeDecl>(Result) || isa<ObjCInterfaceDecl>(Result)) &&
+          !Result->isInvalidDecl()) {
+        // We found a similarly-named type or interface; suggest that.
+        if (!SS || !SS->isSet())
+          Diag(IILoc, diag::err_unknown_typename_suggest)
+            << &II << Lookup.getLookupName()
+            << FixItHint::CreateReplacement(SourceRange(IILoc),
+                                            Result->getNameAsString());
+        else if (DeclContext *DC = computeDeclContext(*SS, false))
+          Diag(IILoc, diag::err_unknown_nested_typename_suggest) 
+            << &II << DC << Lookup.getLookupName() << SS->getRange()
+            << FixItHint::CreateReplacement(SourceRange(IILoc),
+                                            Result->getNameAsString());
+        else
+          llvm_unreachable("could not have corrected a typo here");
 
-      Diag(Result->getLocation(), diag::note_previous_decl)
-        << Result->getDeclName();
-      
-      SuggestedType = getTypeName(*Result->getIdentifier(), IILoc, S, SS);
-      return true;
+        Diag(Result->getLocation(), diag::note_previous_decl)
+          << Result->getDeclName();
+        
+        SuggestedType = getTypeName(*Result->getIdentifier(), IILoc, S, SS);
+        return true;
+      }
+    } else if (Lookup.empty()) {
+      // We corrected to a keyword.
+      // FIXME: Actually recover with the keyword we suggest, and emit a fix-it.
+      Diag(IILoc, diag::err_unknown_typename_suggest)
+        << &II << Corrected;
+      return true;      
     }
   }
 
@@ -605,7 +609,7 @@
     // Perform typo correction at the given location, but only if we
     // find an Objective-C class name.
     LookupResult R(*this, Id, RecoverLoc, LookupOrdinaryName);
-    if (CorrectTypo(R, TUScope, 0) &&
+    if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
         (IDecl = R.getAsSingle<ObjCInterfaceDecl>())) {
       Diag(RecoverLoc, diag::err_undef_interface_suggest)
         << Id << IDecl->getDeclName() 

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Apr 14 15:04:41 2010
@@ -1099,7 +1099,8 @@
 
       // If no results were found, try to correct typos.
       if (R.empty() && BaseType.isNull() &&
-          CorrectTypo(R, S, &SS, ClassDecl) && R.isSingleResult()) {
+          CorrectTypo(R, S, &SS, ClassDecl, 0, CTC_NoKeywords) && 
+          R.isSingleResult()) {
         if (FieldDecl *Member = R.getAsSingle<FieldDecl>()) {
           if (Member->getDeclContext()->getLookupContext()->Equals(ClassDecl)) {
             // We have found a non-static data member with a similar

Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Wed Apr 14 15:04:41 2010
@@ -120,7 +120,7 @@
     if (!PrevDecl) {
       // Try to correct for a typo in the superclass name.
       LookupResult R(*this, SuperName, SuperLoc, LookupOrdinaryName);
-      if (CorrectTypo(R, TUScope, 0) &&
+      if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
           (PrevDecl = R.getAsSingle<ObjCInterfaceDecl>())) {
         Diag(SuperLoc, diag::err_undef_superclass_suggest)
           << SuperName << ClassName << PrevDecl->getDeclName();
@@ -317,7 +317,7 @@
     if (!PDecl) {
       LookupResult R(*this, ProtocolId[i].first, ProtocolId[i].second,
                      LookupObjCProtocolName);
-      if (CorrectTypo(R, TUScope, 0) &&
+      if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
           (PDecl = R.getAsSingle<ObjCProtocolDecl>())) {
         Diag(ProtocolId[i].second, diag::err_undeclared_protocol_suggest)
           << ProtocolId[i].first << R.getLookupName();
@@ -554,7 +554,7 @@
     // We did not find anything with the name ClassName; try to correct for 
     // typos in the class name.
     LookupResult R(*this, ClassName, ClassLoc, LookupOrdinaryName);
-    if (CorrectTypo(R, TUScope, 0) &&
+    if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) &&
         (IDecl = R.getAsSingle<ObjCInterfaceDecl>())) {
       // Suggest the (potentially) correct interface name. However, put the
       // fix-it hint itself in a separate note, since changing the name in 

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Wed Apr 14 15:04:41 2010
@@ -946,43 +946,55 @@
   }
 
   // We didn't find anything, so try to correct for a typo.
-  if (S && CorrectTypo(R, S, &SS)) {
-    if (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin())) {
-      if (SS.isEmpty())
-        Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName()
-          << FixItHint::CreateReplacement(R.getNameLoc(),
-                                          R.getLookupName().getAsString());
-      else
-        Diag(R.getNameLoc(), diag::err_no_member_suggest)
-          << Name << computeDeclContext(SS, false) << R.getLookupName()
-          << SS.getRange()
-          << FixItHint::CreateReplacement(R.getNameLoc(),
-                                          R.getLookupName().getAsString());
-      if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
-        Diag(ND->getLocation(), diag::note_previous_decl)
-          << ND->getDeclName();
+  DeclarationName Corrected;
+  if (S && (Corrected = CorrectTypo(R, S, &SS))) {
+    if (!R.empty()) {
+      if (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin())) {
+        if (SS.isEmpty())
+          Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName()
+            << FixItHint::CreateReplacement(R.getNameLoc(),
+                                            R.getLookupName().getAsString());
+        else
+          Diag(R.getNameLoc(), diag::err_no_member_suggest)
+            << Name << computeDeclContext(SS, false) << R.getLookupName()
+            << SS.getRange()
+            << FixItHint::CreateReplacement(R.getNameLoc(),
+                                            R.getLookupName().getAsString());
+        if (NamedDecl *ND = R.getAsSingle<NamedDecl>())
+          Diag(ND->getLocation(), diag::note_previous_decl)
+            << ND->getDeclName();
 
-      // Tell the callee to try to recover.
-      return false;
-    }
+        // Tell the callee to try to recover.
+        return false;
+      }
+    
+      if (isa<TypeDecl>(*R.begin()) || isa<ObjCInterfaceDecl>(*R.begin())) {
+        // FIXME: If we ended up with a typo for a type name or
+        // Objective-C class name, we're in trouble because the parser
+        // is in the wrong place to recover. Suggest the typo
+        // correction, but don't make it a fix-it since we're not going
+        // to recover well anyway.
+        if (SS.isEmpty())
+          Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName();
+        else
+          Diag(R.getNameLoc(), diag::err_no_member_suggest)
+            << Name << computeDeclContext(SS, false) << R.getLookupName()
+            << SS.getRange();
 
-    if (isa<TypeDecl>(*R.begin()) || isa<ObjCInterfaceDecl>(*R.begin())) {
-      // FIXME: If we ended up with a typo for a type name or
-      // Objective-C class name, we're in trouble because the parser
-      // is in the wrong place to recover. Suggest the typo
-      // correction, but don't make it a fix-it since we're not going
-      // to recover well anyway.
+        // Don't try to recover; it won't work.
+        return true;
+      }
+    } else {
+      // FIXME: We found a keyword. Suggest it, but don't provide a fix-it 
+      // because we aren't able to recover.
       if (SS.isEmpty())
-        Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName();
+        Diag(R.getNameLoc(), diagnostic_suggest) << Name << Corrected;
       else
         Diag(R.getNameLoc(), diag::err_no_member_suggest)
-          << Name << computeDeclContext(SS, false) << R.getLookupName()
-          << SS.getRange();
-
-      // Don't try to recover; it won't work.
+        << Name << computeDeclContext(SS, false) << Corrected
+        << SS.getRange();
       return true;
     }
-
     R.clear();
   }
 
@@ -2575,7 +2587,8 @@
   // We didn't find anything with the given name, so try to correct
   // for typos.
   DeclarationName Name = R.getLookupName();
-  if (SemaRef.CorrectTypo(R, 0, &SS, DC) &&
+  if (SemaRef.CorrectTypo(R, 0, &SS, DC, false, Sema::CTC_MemberLookup) && 
+      !R.empty() &&
       (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin()))) {
     SemaRef.Diag(R.getNameLoc(), diag::err_no_member_suggest)
       << Name << DC << R.getLookupName() << SS.getRange()
@@ -3032,7 +3045,7 @@
         // Attempt to correct for typos in ivar names.
         LookupResult Res(*this, R.getLookupName(), R.getNameLoc(),
                          LookupMemberName);
-        if (CorrectTypo(Res, 0, 0, IDecl) &&
+        if (CorrectTypo(Res, 0, 0, IDecl, false, CTC_MemberLookup) &&
             (IV = Res.getAsSingle<ObjCIvarDecl>())) {
           Diag(R.getNameLoc(),
                diag::err_typecheck_member_reference_ivar_suggest)

Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Wed Apr 14 15:04:41 2010
@@ -372,7 +372,7 @@
 
   // Attempt to correct for typos in property names.
   LookupResult Res(*this, MemberName, MemberLoc, LookupOrdinaryName);
-  if (CorrectTypo(Res, 0, 0, IFace, false, OPT) &&
+  if (CorrectTypo(Res, 0, 0, IFace, false, CTC_NoKeywords, OPT) &&
       Res.getAsSingle<ObjCPropertyDecl>()) {
     DeclarationName TypoResult = Res.getLookupName();
     Diag(MemberLoc, diag::err_property_not_found_suggest)
@@ -523,18 +523,37 @@
   }
   }
 
-  if (CorrectTypo(Result, S, 0) && Result.isSingleResult()) {
-    NamedDecl *ND = Result.getFoundDecl();
-    if (isa<ObjCInterfaceDecl>(ND)) {
-      Diag(NameLoc, diag::err_unknown_receiver_suggest)
-        << Name << Result.getLookupName()
-        << FixItHint::CreateReplacement(SourceRange(NameLoc),
-                                        ND->getNameAsString());
-      Diag(ND->getLocation(), diag::note_previous_decl)
-        << ND->getDeclName();
+  // Determine our typo-correction context.
+  CorrectTypoContext CTC = CTC_Expression;
+  if (ObjCMethodDecl *Method = getCurMethodDecl())
+    if (Method->getClassInterface() &&
+        Method->getClassInterface()->getSuperClass())
+      CTC = CTC_ObjCMessageReceiver;
+      
+  if (DeclarationName Corrected = CorrectTypo(Result, S, 0, 0, false, CTC)) {
+    if (Result.isSingleResult()) {
+      // If we found a declaration, correct when it refers to an Objective-C
+      // class.
+      NamedDecl *ND = Result.getFoundDecl();
+      if (isa<ObjCInterfaceDecl>(ND)) {
+        Diag(NameLoc, diag::err_unknown_receiver_suggest)
+          << Name << Result.getLookupName()
+          << FixItHint::CreateReplacement(SourceRange(NameLoc),
+                                          ND->getNameAsString());
+        Diag(ND->getLocation(), diag::note_previous_decl)
+          << Corrected;
 
-      Name = ND->getIdentifier();
-      return ObjCClassMessage;
+        Name = ND->getIdentifier();
+        return ObjCClassMessage;
+      }
+    } else if (Result.empty() && Corrected.getAsIdentifierInfo() &&
+               Corrected.getAsIdentifierInfo()->isStr("super")) {
+      // If we've found the keyword "super", this is a send to super.
+      Diag(NameLoc, diag::err_unknown_receiver_suggest)
+        << Name << Corrected
+        << FixItHint::CreateReplacement(SourceRange(NameLoc), "super");
+      Name = Corrected.getAsIdentifierInfo();
+      return ObjCSuperMessage;
     }
   }
   

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Wed Apr 14 15:04:41 2010
@@ -1354,7 +1354,8 @@
         // was a typo for another field name.
         LookupResult R(SemaRef, FieldName, D->getFieldLoc(), 
                        Sema::LookupMemberName);
-        if (SemaRef.CorrectTypo(R, /*Scope=*/0, /*SS=*/0, RT->getDecl()) &&
+        if (SemaRef.CorrectTypo(R, /*Scope=*/0, /*SS=*/0, RT->getDecl(), false,
+                                Sema::CTC_NoKeywords) && 
             (ReplacementField = R.getAsSingle<FieldDecl>()) &&
             ReplacementField->getDeclContext()->getLookupContext()
                                                       ->Equals(RT->getDecl())) {

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Wed Apr 14 15:04:41 2010
@@ -2421,6 +2421,9 @@
   /// found (so far) with the typo name.
   llvm::SmallVector<NamedDecl *, 4> BestResults;
 
+  /// \brief The keywords that have the smallest edit distance.
+  llvm::SmallVector<IdentifierInfo *, 4> BestKeywords;
+  
   /// \brief The best edit distance found so far.
   unsigned BestEditDistance;
   
@@ -2429,13 +2432,23 @@
     : Typo(Typo->getName()) { }
 
   virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass);
+  void addKeywordResult(ASTContext &Context, llvm::StringRef Keyword);
 
   typedef llvm::SmallVector<NamedDecl *, 4>::const_iterator iterator;
   iterator begin() const { return BestResults.begin(); }
   iterator end() const { return BestResults.end(); }
-  bool empty() const { return BestResults.empty(); }
+  void clear_decls() { BestResults.clear(); }
+  
+  bool empty() const { return BestResults.empty() && BestKeywords.empty(); }
 
-  unsigned getBestEditDistance() const { return BestEditDistance; }
+  typedef llvm::SmallVector<IdentifierInfo *, 4>::const_iterator
+    keyword_iterator;
+  keyword_iterator keyword_begin() const { return BestKeywords.begin(); }
+  keyword_iterator keyword_end() const { return BestKeywords.end(); }
+  bool keyword_empty() const { return BestKeywords.empty(); }
+  unsigned keyword_size() const { return BestKeywords.size(); }
+  
+  unsigned getBestEditDistance() const { return BestEditDistance; }  
 };
 
 }
@@ -2457,11 +2470,12 @@
   // entity. If this edit distance is not worse than the best edit
   // distance we've seen so far, add it to the list of results.
   unsigned ED = Typo.edit_distance(Name->getName());
-  if (!BestResults.empty()) {
+  if (!BestResults.empty() || !BestKeywords.empty()) {
     if (ED < BestEditDistance) {
       // This result is better than any we've seen before; clear out
       // the previous results.
       BestResults.clear();
+      BestKeywords.clear();
       BestEditDistance = ED;
     } else if (ED > BestEditDistance) {
       // This result is worse than the best results we've seen so far;
@@ -2474,6 +2488,28 @@
   BestResults.push_back(ND);
 }
 
+void TypoCorrectionConsumer::addKeywordResult(ASTContext &Context, 
+                                              llvm::StringRef Keyword) {
+  // Compute the edit distance between the typo and this keyword.
+  // If this edit distance is not worse than the best edit
+  // distance we've seen so far, add it to the list of results.
+  unsigned ED = Typo.edit_distance(Keyword);
+  if (!BestResults.empty() || !BestKeywords.empty()) {
+    if (ED < BestEditDistance) {
+      BestResults.clear();
+      BestKeywords.clear();
+      BestEditDistance = ED;
+    } else if (ED > BestEditDistance) {
+      // This result is worse than the best results we've seen so far;
+      // ignore it.
+      return;
+    }
+  } else
+    BestEditDistance = ED;
+  
+  BestKeywords.push_back(&Context.Idents.get(Keyword));
+}
+
 /// \brief Try to "correct" a typo in the source code by finding
 /// visible declarations whose names are similar to the name that was
 /// present in the source code.
@@ -2494,6 +2530,9 @@
 /// \param EnteringContext whether we're entering the context described by 
 /// the nested-name-specifier SS.
 ///
+/// \param CTC The context in which typo correction occurs, which impacts the
+/// set of keywords permitted.
+///
 /// \param OPT when non-NULL, the search for visible declarations will
 /// also walk the protocols in the qualified interfaces of \p OPT.
 ///
@@ -2502,8 +2541,10 @@
 /// may contain the results of name lookup for the correct name or it may be
 /// empty.
 DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS,
-                       DeclContext *MemberContext, bool EnteringContext,
-                       const ObjCObjectPointerType *OPT) {
+                                  DeclContext *MemberContext, 
+                                  bool EnteringContext,
+                                  CorrectTypoContext CTC,
+                                  const ObjCObjectPointerType *OPT) {
   if (Diags.hasFatalErrorOccurred())
     return DeclarationName();
 
@@ -2529,8 +2570,10 @@
   // instantiation.
   if (!ActiveTemplateInstantiations.empty())
     return DeclarationName();
-
+  
   TypoCorrectionConsumer Consumer(Typo);
+  
+  // Perform name lookup to find visible, similarly-named entities.
   if (MemberContext) {
     LookupVisibleDecls(MemberContext, Res.getLookupKind(), Consumer);
 
@@ -2551,37 +2594,231 @@
     LookupVisibleDecls(S, Res.getLookupKind(), Consumer);
   }
 
+  // Add context-dependent keywords.
+  bool WantTypeSpecifiers = false;
+  bool WantExpressionKeywords = false;
+  bool WantCXXNamedCasts = false;
+  bool WantRemainingKeywords = false;
+  switch (CTC) {
+    case CTC_Unknown:
+      WantTypeSpecifiers = true;
+      WantExpressionKeywords = true;
+      WantCXXNamedCasts = true;
+      WantRemainingKeywords = true;
+      break;
+  
+    case CTC_NoKeywords:
+      break;
+  
+    case CTC_Type:
+      WantTypeSpecifiers = true;
+      break;
+      
+    case CTC_ObjCMessageReceiver:
+      Consumer.addKeywordResult(Context, "super");
+      // Fall through to handle message receivers like expressions.
+      
+    case CTC_Expression:
+      if (getLangOptions().CPlusPlus)
+        WantTypeSpecifiers = true;
+      WantExpressionKeywords = true;
+      // Fall through to get C++ named casts.
+      
+    case CTC_CXXCasts:
+      WantCXXNamedCasts = true;
+      break;
+      
+    case CTC_MemberLookup:
+      if (getLangOptions().CPlusPlus)
+        Consumer.addKeywordResult(Context, "template");
+      break;
+  }
+
+  if (WantTypeSpecifiers) {
+    // Add type-specifier keywords to the set of results.
+    const char *CTypeSpecs[] = {
+      "char", "const", "double", "enum", "float", "int", "long", "short",
+      "signed", "struct", "union", "unsigned", "void", "volatile", "_Bool",
+      "_Complex", "_Imaginary",
+      // storage-specifiers as well
+      "extern", "inline", "static", "typedef"
+    };
+    
+    const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]);
+    for (unsigned I = 0; I != NumCTypeSpecs; ++I)
+      Consumer.addKeywordResult(Context, CTypeSpecs[I]);
+    
+    if (getLangOptions().C99)
+      Consumer.addKeywordResult(Context, "restrict");
+    if (getLangOptions().Bool || getLangOptions().CPlusPlus)
+      Consumer.addKeywordResult(Context, "bool");
+    
+    if (getLangOptions().CPlusPlus) {
+      Consumer.addKeywordResult(Context, "class");
+      Consumer.addKeywordResult(Context, "typename");
+      Consumer.addKeywordResult(Context, "wchar_t");
+      
+      if (getLangOptions().CPlusPlus0x) {
+        Consumer.addKeywordResult(Context, "char16_t");
+        Consumer.addKeywordResult(Context, "char32_t");
+        Consumer.addKeywordResult(Context, "constexpr");
+        Consumer.addKeywordResult(Context, "decltype");
+        Consumer.addKeywordResult(Context, "thread_local");
+      }      
+    }
+        
+    if (getLangOptions().GNUMode)
+      Consumer.addKeywordResult(Context, "typeof");
+  }
+  
+  if (WantCXXNamedCasts) {
+    Consumer.addKeywordResult(Context, "const_cast");
+    Consumer.addKeywordResult(Context, "dynamic_cast");
+    Consumer.addKeywordResult(Context, "reinterpret_cast");
+    Consumer.addKeywordResult(Context, "static_cast");
+  }
+  
+  if (WantExpressionKeywords) {
+    Consumer.addKeywordResult(Context, "sizeof");
+    if (getLangOptions().Bool || getLangOptions().CPlusPlus) {
+      Consumer.addKeywordResult(Context, "false");
+      Consumer.addKeywordResult(Context, "true");
+    }
+    
+    if (getLangOptions().CPlusPlus) {
+      const char *CXXExprs[] = { 
+        "delete", "new", "operator", "throw", "typeid" 
+      };
+      const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]);
+      for (unsigned I = 0; I != NumCXXExprs; ++I)
+        Consumer.addKeywordResult(Context, CXXExprs[I]);
+      
+      if (isa<CXXMethodDecl>(CurContext) &&
+          cast<CXXMethodDecl>(CurContext)->isInstance())
+        Consumer.addKeywordResult(Context, "this");
+      
+      if (getLangOptions().CPlusPlus0x) {
+        Consumer.addKeywordResult(Context, "alignof");
+        Consumer.addKeywordResult(Context, "nullptr");
+      }
+    }
+  }
+  
+  if (WantRemainingKeywords) {
+    if (getCurFunctionOrMethodDecl() || getCurBlock()) {
+      // Statements.
+      const char *CStmts[] = {
+        "do", "else", "for", "goto", "if", "return", "switch", "while" };
+      const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]);
+      for (unsigned I = 0; I != NumCStmts; ++I)
+        Consumer.addKeywordResult(Context, CStmts[I]);
+      
+      if (getLangOptions().CPlusPlus) {
+        Consumer.addKeywordResult(Context, "catch");
+        Consumer.addKeywordResult(Context, "try");
+      }
+      
+      if (S && S->getBreakParent())
+        Consumer.addKeywordResult(Context, "break");
+      
+      if (S && S->getContinueParent())
+        Consumer.addKeywordResult(Context, "continue");
+      
+      if (!getSwitchStack().empty()) {
+        Consumer.addKeywordResult(Context, "case");
+        Consumer.addKeywordResult(Context, "default");
+      }
+    } else {
+      if (getLangOptions().CPlusPlus) {
+        Consumer.addKeywordResult(Context, "namespace");
+        Consumer.addKeywordResult(Context, "template");
+      }
+
+      if (S && S->isClassScope()) {
+        Consumer.addKeywordResult(Context, "explicit");
+        Consumer.addKeywordResult(Context, "friend");
+        Consumer.addKeywordResult(Context, "mutable");
+        Consumer.addKeywordResult(Context, "private");
+        Consumer.addKeywordResult(Context, "protected");
+        Consumer.addKeywordResult(Context, "public");
+        Consumer.addKeywordResult(Context, "virtual");
+      }
+    }
+        
+    if (getLangOptions().CPlusPlus) {
+      Consumer.addKeywordResult(Context, "using");
+
+      if (getLangOptions().CPlusPlus0x)
+        Consumer.addKeywordResult(Context, "static_assert");
+    }
+  }
+  
+  // If we haven't found anything, we're done.
   if (Consumer.empty())
     return DeclarationName();
 
   // Only allow a single, closest name in the result set (it's okay to
   // have overloads of that name, though).
-  TypoCorrectionConsumer::iterator I = Consumer.begin();
-  DeclarationName BestName = (*I)->getDeclName();
-
-  // If we've found an Objective-C ivar or property, don't perform
-  // name lookup again; we'll just return the result directly.
-  NamedDecl *FoundBest = 0;
-  if (isa<ObjCIvarDecl>(*I) || isa<ObjCPropertyDecl>(*I))
-    FoundBest = *I;
-  ++I;
-  for(TypoCorrectionConsumer::iterator IEnd = Consumer.end(); I != IEnd; ++I) {
-    if (BestName != (*I)->getDeclName())
+  DeclarationName BestName;
+  NamedDecl *BestIvarOrPropertyDecl = 0;
+  bool FoundIvarOrPropertyDecl = false;
+  
+  // Check all of the declaration results to find the best name so far.
+  for (TypoCorrectionConsumer::iterator I = Consumer.begin(), 
+                                     IEnd = Consumer.end();
+       I != IEnd; ++I) {
+    if (!BestName)
+      BestName = (*I)->getDeclName();
+    else if (BestName != (*I)->getDeclName())
       return DeclarationName();
 
-    // FIXME: If there are both ivars and properties of the same name,
-    // don't return both because the callee can't handle two
-    // results. We really need to separate ivar lookup from property
-    // lookup to avoid this problem.
-    FoundBest = 0;
+    // \brief Keep track of either an Objective-C ivar or a property, but not
+    // both.
+    if (isa<ObjCIvarDecl>(*I) || isa<ObjCPropertyDecl>(*I)) {
+      if (FoundIvarOrPropertyDecl)
+        BestIvarOrPropertyDecl = 0;
+      else {
+        BestIvarOrPropertyDecl = *I;
+        FoundIvarOrPropertyDecl = true;
+      }
+    }
   }
 
+  // Now check all of the keyword results to find the best name. 
+  switch (Consumer.keyword_size()) {
+    case 0:
+      // No keywords matched.
+      break;
+      
+    case 1:
+      // If we already have a name
+      if (!BestName) {
+        // We did not have anything previously, 
+        BestName = *Consumer.keyword_begin();
+      } else if (BestName.getAsIdentifierInfo() == *Consumer.keyword_begin()) {
+        // We have a declaration with the same name as a context-sensitive
+        // keyword. The keyword takes precedence.
+        BestIvarOrPropertyDecl = 0;
+        FoundIvarOrPropertyDecl = false;
+        Consumer.clear_decls();
+      } else {
+        // Name collision; we will not correct typos.
+        return DeclarationName();
+      }
+      break;
+      
+    default:
+      // Name collision; we will not correct typos.
+      return DeclarationName();
+  }
+  
   // BestName is the closest viable name to what the user
   // typed. However, to make sure that we don't pick something that's
   // way off, make sure that the user typed at least 3 characters for
   // each correction.
   unsigned ED = Consumer.getBestEditDistance();
-  if (ED == 0 || (BestName.getAsIdentifierInfo()->getName().size() / ED) < 3)
+  if (ED == 0 || !BestName.getAsIdentifierInfo() ||
+      (BestName.getAsIdentifierInfo()->getName().size() / ED) < 3)
     return DeclarationName();
 
   // Perform name lookup again with the name we chose, and declare
@@ -2591,11 +2828,14 @@
 
   // If we found an ivar or property, add that result; no further
   // lookup is required.
-  if (FoundBest)
-    Res.addDecl(FoundBest);  
+  if (BestIvarOrPropertyDecl)
+    Res.addDecl(BestIvarOrPropertyDecl);  
   // If we're looking into the context of a member, perform qualified
   // name lookup on the best name.
-  else if (MemberContext)
+  else if (!Consumer.keyword_empty()) {
+    // The best match was a keyword. Return it.
+    return BestName;
+  } else if (MemberContext)
     LookupQualifiedName(Res, MemberContext);
   // Perform lookup as if we had just parsed the best name.
   else

Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Wed Apr 14 15:04:41 2010
@@ -250,7 +250,8 @@
   if (Found.empty() && !isDependent) {
     // If we did not find any names, attempt to correct any typos.
     DeclarationName Name = Found.getLookupName();
-    if (CorrectTypo(Found, S, &SS, LookupCtx)) {
+    if (DeclarationName Corrected = CorrectTypo(Found, S, &SS, LookupCtx, 
+                                                 false, CTC_CXXCasts)) {
       FilterAcceptableTemplateNames(Context, Found);
       if (!Found.empty() && isa<TemplateDecl>(*Found.begin())) {
         if (LookupCtx)

Added: cfe/trunk/test/FixIt/fixit-unrecoverable.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-unrecoverable.c?rev=101274&view=auto
==============================================================================
--- cfe/trunk/test/FixIt/fixit-unrecoverable.c (added)
+++ cfe/trunk/test/FixIt/fixit-unrecoverable.c Wed Apr 14 15:04:41 2010
@@ -0,0 +1,12 @@
+/* FIXME: This is a file containing various typos for which we can
+   suggest corrections but are unable to actually recover from
+   them. Ideally, we would eliminate all such cases and move these
+   tests elsewhere. */
+
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+// FIXME: Sadly, the following doesn't work within a function.
+
+unsinged x = 17; // expected-error{{unknown type name 'unsinged'; did you mean 'unsigned'?}}
+
+

Added: cfe/trunk/test/FixIt/fixit-unrecoverable.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-unrecoverable.cpp?rev=101274&view=auto
==============================================================================
--- cfe/trunk/test/FixIt/fixit-unrecoverable.cpp (added)
+++ cfe/trunk/test/FixIt/fixit-unrecoverable.cpp Wed Apr 14 15:04:41 2010
@@ -0,0 +1,11 @@
+/* FIXME: This is a file containing various typos for which we can
+   suggest corrections but are unable to actually recover from
+   them. Ideally, we would eliminate all such cases and move these
+   tests elsewhere. */
+
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+float f(int y) {
+  return static_cst<float>(y); // expected-error{{use of undeclared identifier 'static_cst'; did you mean 'static_cast'?}}
+}
+

Modified: cfe/trunk/test/FixIt/typo.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/typo.m?rev=101274&r1=101273&r2=101274&view=diff
==============================================================================
--- cfe/trunk/test/FixIt/typo.m (original)
+++ cfe/trunk/test/FixIt/typo.m Wed Apr 14 15:04:41 2010
@@ -87,3 +87,18 @@
 
 @interface IPv6 <Network_Socket> // expected-error{{cannot find protocol declaration for 'Network_Socket'; did you mean 'NetworkSocket'?}}
 @end
+
+ at interface Super
+- (int)method;
+ at end
+
+ at interface Sub : Super
+- (int)method;
+ at end
+
+ at implementation Sub
+- (int)method {
+  return [supper method]; // expected-error{{unknown receiver 'supper'; did you mean 'super'?}}
+}
+  
+ at end





More information about the cfe-commits mailing list