[cfe-commits] PATCH: Add typo correction for the (non-template) type name in C++ "new" statements. (issue 5014044)

rikka at google.com rikka at google.com
Tue Sep 13 17:16:25 PDT 2011


Reviewers: cfe-commits_cs.uiuc.edu,



Please review this at http://codereview.appspot.com/5014044/

Affected files:
   M include/clang/Parse/Parser.h
   M include/clang/Sema/Sema.h
   M lib/Parse/ParseDecl.cpp
   M lib/Parse/ParseExprCXX.cpp
   M lib/Parse/Parser.cpp
   M lib/Sema/SemaDecl.cpp
   M test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp


-------------- next part --------------
Index: include/clang/Parse/Parser.h
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 906958f323539e5d16e35050f0f7efdde89acba4..3b7540dc039e31fdee8b4985aaba06a18432e593 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -436,7 +436,8 @@ private:
       Tok.setAnnotationValue(ER.get());
   }
 
-  bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false);
+  bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false,
+                                   bool TryTypoCorrection = false);
   bool TryAnnotateCXXScopeToken(bool EnteringContext = false);
 
   /// TryAltiVecToken - Check for context-sensitive AltiVec identifier tokens,
@@ -1312,7 +1313,7 @@ private:
   /// simple-type-specifier.
   void ParseCXXSimpleTypeSpecifier(DeclSpec &DS);
 
-  bool ParseCXXTypeSpecifierSeq(DeclSpec &DS);
+  bool ParseCXXTypeSpecifierSeq(DeclSpec &DS, bool TryTypoCorrection=false);
 
   //===--------------------------------------------------------------------===//
   // C++ 5.3.4 and 5.3.5: C++ new and delete
@@ -1499,7 +1500,8 @@ private:
                                   const char *&PrevSpec,
                                   unsigned &DiagID,
                const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
-                                  bool SuppressDeclarations = false);
+                                  bool SuppressDeclarations = false,
+                                  bool TryTypoCorrection = false);
 
   void ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none);
 
Index: include/clang/Sema/Sema.h
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index c08244490afdb1e8a4c5d71f182b75dff24f9570..0eaa59fbaa9aa0f33353ab61d866d25a968b8de4 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -863,7 +863,8 @@ public:
                          bool isClassName = false,
                          bool HasTrailingDot = false,
                          ParsedType ObjectType = ParsedType(),
-                         bool WantNontrivialTypeSourceInfo = false);
+                         bool WantNontrivialTypeSourceInfo = false,
+                         IdentifierInfo **CorrectedII = 0);
   TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S);
   bool isMicrosoftMissingTypename(const CXXScopeSpec *SS);
   bool DiagnoseUnknownTypeName(const IdentifierInfo &II,
Index: lib/Parse/ParseDecl.cpp
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 16c25a47904fb37dffeb999f81158728ca4804ea..5fcb318ffda3ba82f2d533da399cd40cf8a88eeb 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -2215,7 +2215,8 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
                                         const char *&PrevSpec,
                                         unsigned &DiagID,
                                         const ParsedTemplateInfo &TemplateInfo,
-                                        bool SuppressDeclarations) {
+                                        bool SuppressDeclarations,
+                                        bool TryTypoCorrection) {
   SourceLocation Loc = Tok.getLocation();
 
   switch (Tok.getKind()) {
@@ -2232,12 +2233,12 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
   case tok::kw_typename:  // typename foo::bar
     // Annotate typenames and C++ scope specifiers.  If we get one, just
     // recurse to handle whatever we get.
-    if (TryAnnotateTypeOrScopeToken())
+    if (TryAnnotateTypeOrScopeToken(false, TryTypoCorrection))
       return true;
     if (Tok.is(tok::identifier))
       return false;
     return ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID,
-                                      TemplateInfo, SuppressDeclarations);
+                                      TemplateInfo, SuppressDeclarations, TryTypoCorrection);
   case tok::coloncolon:   // ::foo::bar
     if (NextToken().is(tok::kw_new) ||    // ::new
         NextToken().is(tok::kw_delete))   // ::delete
@@ -2245,10 +2246,10 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
 
     // Annotate typenames and C++ scope specifiers.  If we get one, just
     // recurse to handle whatever we get.
-    if (TryAnnotateTypeOrScopeToken())
+    if (TryAnnotateTypeOrScopeToken(false, TryTypoCorrection))
       return true;
     return ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID,
-                                      TemplateInfo, SuppressDeclarations);
+                                      TemplateInfo, SuppressDeclarations, TryTypoCorrection);
 
   // simple-type-specifier:
   case tok::annot_typename: {
Index: lib/Parse/ParseExprCXX.cpp
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 1faeebc37cd4edd0390832d4e12990f2644a5259..233226a37effb4c35af182315c6eb961e429c915 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -1382,7 +1382,7 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
 ///   type-specifier-seq: [C++ 8.1]
 ///     type-specifier type-specifier-seq[opt]
 ///
-bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
+bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, bool TryTypoCorrection) {
   DS.SetRangeStart(Tok.getLocation());
   const char *PrevSpec = 0;
   unsigned DiagID;
@@ -1390,7 +1390,7 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
 
   // Parse one or more of the type specifiers.
   if (!ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID,
-      ParsedTemplateInfo(), /*SuppressDeclarations*/true)) {
+      ParsedTemplateInfo(), /*SuppressDeclarations*/true, TryTypoCorrection)) {
     Diag(Tok, diag::err_expected_type);
     return true;
   }
@@ -2046,7 +2046,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
                                                 TypeIdParens.getBegin()));
       } else {
         MaybeParseGNUAttributes(DeclaratorInfo);
-        if (ParseCXXTypeSpecifierSeq(DS))
+        if (ParseCXXTypeSpecifierSeq(DS, true))
           DeclaratorInfo.setInvalidType(true);
         else {
           DeclaratorInfo.SetSourceRange(DS.getSourceRange());
@@ -2059,7 +2059,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
     // A new-type-id is a simplified type-id, where essentially the
     // direct-declarator is replaced by a direct-new-declarator.
     MaybeParseGNUAttributes(DeclaratorInfo);
-    if (ParseCXXTypeSpecifierSeq(DS))
+    if (ParseCXXTypeSpecifierSeq(DS, true))
       DeclaratorInfo.setInvalidType(true);
     else {
       DeclaratorInfo.SetSourceRange(DS.getSourceRange());
Index: lib/Parse/Parser.cpp
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index c31e1634a0a6243e920ab8cae39f634301fec5e8..38cbd80c615e476d492cd86ca4132188a530a71e 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -1204,7 +1204,8 @@ TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) {
 ///
 /// Note that this routine emits an error if you call it with ::new or ::delete
 /// as the current tokens, so only call it in contexts where these are invalid.
-bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
+bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext,
+                                         bool TryTypoCorrection) {
   assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)
           || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)) &&
          "Cannot be a type or scope token!");
@@ -1278,13 +1279,19 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
       return true;
 
   if (Tok.is(tok::identifier)) {
+    IdentifierInfo *CorrectedII = NULL;
     // Determine whether the identifier is a type name.
     if (ParsedType Ty = Actions.getTypeName(*Tok.getIdentifierInfo(),
                                             Tok.getLocation(), getCurScope(),
                                             &SS, false, 
                                             NextToken().is(tok::period),
                                             ParsedType(),
-                                            /*NonTrivialTypeSourceInfo*/true)) {
+                                            /*NonTrivialTypeSourceInfo*/true,
+                                            TryTypoCorrection ? &CorrectedII
+                                                              : NULL)) {
+      // A FixIt was applied as a result of typo correction
+      if (CorrectedII)
+        Tok.setIdentifierInfo(CorrectedII);
       // This is a typename. Replace the current token in-place with an
       // annotation type token.
       Tok.setKind(tok::annot_typename);
Index: lib/Sema/SemaDecl.cpp
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 96531d41459bc1e78f4ac234297be0925a970329..5961ea1ea8cb86fa556e1cbcb542e924103e9299 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -70,7 +70,8 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
                              Scope *S, CXXScopeSpec *SS,
                              bool isClassName, bool HasTrailingDot,
                              ParsedType ObjectTypePtr,
-                             bool WantNontrivialTypeSourceInfo) {
+                             bool WantNontrivialTypeSourceInfo,
+                             IdentifierInfo **CorrectedII) {
   // Determine where we will perform name lookup.
   DeclContext *LookupCtx = 0;
   if (ObjectTypePtr) {
@@ -145,6 +146,40 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
   switch (Result.getResultKind()) {
   case LookupResult::NotFound:
   case LookupResult::NotFoundInCurrentInstantiation:
+    if (CorrectedII) {
+      TypoCorrection Corr = CorrectTypo(Result.getLookupNameInfo(),
+                                        Sema::LookupOrdinaryName,
+                                        S, SS, NULL, false,
+                                        Sema::CTC_Type);
+      IdentifierInfo *NewII = Corr.getCorrectionAsIdentifierInfo();
+      TemplateTy Template;
+      bool MemberOfUnknownSpecialization;
+      UnqualifiedId TemplateName;
+      TemplateName.setIdentifier(NewII, NameLoc);
+      if (Corr && (Corr.getCorrectionSpecifier() || NewII != &II) &&
+          // Ignore a correction to a template type as the to-be-corrected
+          // identifier is not a template (typo correction for template names is
+          // handled elsewhere).
+          !isTemplateName(S, *SS, false, TemplateName, ParsedType(), false,
+                          Template, MemberOfUnknownSpecialization)) {
+        std::string CorrectedStr(Corr.getAsString(getLangOptions()));
+        std::string CorrectedQuotedStr(Corr.getQuoted(getLangOptions()));
+        Diag(NameLoc, diag::err_unknown_typename_suggest)
+            << Result.getLookupName() << CorrectedQuotedStr
+            << FixItHint::CreateReplacement(SourceRange(NameLoc), CorrectedStr);
+        if (NamedDecl *FirstDecl = Corr.getCorrectionDecl())
+          Diag(FirstDecl->getLocation(), diag::note_previous_decl)
+            << CorrectedQuotedStr;
+
+        if (NestedNameSpecifier *NNS = Corr.getCorrectionSpecifier())
+          SS->MakeTrivial(Context, NNS, SourceRange(NameLoc));
+        *CorrectedII = NewII;
+        return getTypeName(**CorrectedII, NameLoc, S, SS, isClassName,
+                           HasTrailingDot, ObjectTypePtr,
+                           WantNontrivialTypeSourceInfo);
+      }
+    }
+    // If typo correction failed or was not performed, fall through
   case LookupResult::FoundOverloaded:
   case LookupResult::FoundUnresolvedValue:
     Result.suppressDiagnostics();
Index: test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
diff --git a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
index 85e3e7ed08fe66f92fb10082b0c5ac2adabf55b2..76ceea1d4d5b89eea31bf90111e6d0c59369b5b1 100644
--- a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
+++ b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
@@ -1,8 +1,10 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++0x-extensions %s
 
-namespace fizbin { class Foobar; } // expected-note{{'fizbin::Foobar' declared here}}
+namespace fizbin { class Foobar {}; } // expected-note 2 {{'fizbin::Foobar' declared here}} \
+                                      // expected-note {{'Foobar' declared here}}
 Foobar *my_bar  // expected-error{{unknown type name 'Foobar'; did you mean 'fizbin::Foobar'?}}
-    = new Foobar;  // expected-error{{expected a type}}
+    = new Foobar; // expected-error{{unknown type name 'Foobar'; did you mean 'fizbin::Foobar'?}}
+fizbin::Foobar *my_foo = new fizbin::FooBar; // expected-error{{unknown type name 'FooBar'; did you mean 'Foobar'?}}
 
 namespace barstool { int toFoobar() { return 1; } } // expected-note 3 {{'barstool::toFoobar' declared here}}
 int Double(int x) { return x + x; }
@@ -62,11 +64,13 @@ void f() {
 
 // Test case from http://llvm.org/bugs/show_bug.cgi?id=10318
 namespace llvm {
- template <typename T> class GraphWriter {}; // expected-note{{'llvm::GraphWriter' declared here}}
+ template <typename T> class GraphWriter {}; // expected-note {{'llvm::GraphWriter' declared here}} \
+                                             // expected-note {{'GraphWriter' declared here}}
 }
 
 struct S {};
 void bar() {
  GraphWriter<S> x; //expected-error{{no template named 'GraphWriter'; did you mean 'llvm::GraphWriter'?}}
-
+ (void)new llvm::GraphWriter; // expected-error {{expected a type}}
+ (void)new llvm::Graphwriter<S>; // expected-error {{no template named 'Graphwriter' in namespace 'llvm'; did you mean 'GraphWriter'?}}
 }


More information about the cfe-commits mailing list