]> granicus.if.org Git - clang/commitdiff
Implement typo correction for id-expressions, e.g.,
authorDouglas Gregor <dgregor@apple.com>
Thu, 31 Dec 2009 05:20:13 +0000 (05:20 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 31 Dec 2009 05:20:13 +0000 (05:20 +0000)
typo.cpp:22:10: error: use of undeclared identifier 'radious'; did
      you mean 'radius'?
  return radious * pi;
         ^~~~~~~
         radius

This was super-easy, since we already had decent recovery by looking
for names in dependent base classes.

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

include/clang/Basic/Diagnostic.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/Sema.h
lib/Sema/SemaExpr.cpp
lib/Sema/SemaLookup.cpp
lib/Sema/SemaOverload.cpp
test/FixIt/typo.cpp
test/Sema/var-redecl.c

index 9c69b59c6c52acf5e8b475b5c331ab6ce9991c78..a2ccea7525ac4856ee992bd5c36275e33811c75f 100644 (file)
@@ -45,7 +45,7 @@ namespace clang {
       DIAG_START_PARSE    = DIAG_START_LEX      +  300,
       DIAG_START_AST      = DIAG_START_PARSE    +  300,
       DIAG_START_SEMA     = DIAG_START_AST      +  100,
-      DIAG_START_ANALYSIS = DIAG_START_SEMA     + 1100,
+      DIAG_START_ANALYSIS = DIAG_START_SEMA     + 1500,
       DIAG_UPPER_LIMIT    = DIAG_START_ANALYSIS +  100
     };
 
index 5ab522069fe75cbc0f6bf785d55517c7b7ceda07..ffe328c3706f985c495d20f2c023f901f4e64f37 100644 (file)
@@ -2552,6 +2552,11 @@ def err_unknown_typename_suggest : Error<
   "unknown type name %0; did you mean %1?">;
 def err_unknown_nested_typename_suggest : Error<
   "no type named %0 in %1; did you mean %2?">;
+def err_no_member_suggest : Error<"no member named %0 in %1; did you mean %2?">;
+def err_undeclared_use_suggest : Error<
+  "use of undeclared %0; did you mean %1?">;
+def err_undeclared_var_use_suggest : Error<
+  "use of undeclared identifier %0; did you mean %1?">;
 
 }
 
index 06927b6d1d435d8ffc2e8e78360fe1ad627a0c84..b92bd4fcead1dcf11f792b903a7b97d62901b538 100644 (file)
@@ -1209,8 +1209,7 @@ public:
                           VisibleDeclConsumer &Consumer);
 
   bool CorrectTypo(LookupResult &R, Scope *S, const CXXScopeSpec *SS,
-                        bool AllowBuiltinCreation = false,
-                        bool EnteringContext = false);
+                   bool EnteringContext = false);
 
   void FindAssociatedClassesAndNamespaces(Expr **Args, unsigned NumArgs,
                                    AssociatedNamespaceSet &AssociatedNamespaces,
@@ -1458,7 +1457,7 @@ public:
                                              bool HasTrailingLParen,
                                              bool IsAddressOfOperand);
 
-  bool DiagnoseEmptyLookup(const CXXScopeSpec &SS, LookupResult &R);
+  bool DiagnoseEmptyLookup(Scope *S, const CXXScopeSpec &SS, LookupResult &R);
 
   OwningExprResult LookupInObjCMethod(LookupResult &R,
                                       Scope *S,
index 77ddbc29dda761cf3a125272ab872b923793f0f5..c87a274122b6e21641f5c36aff4b40fd1aa1e5a4 100644 (file)
@@ -879,28 +879,25 @@ static void DiagnoseInstanceReference(Sema &SemaRef,
 /// Diagnose an empty lookup.
 ///
 /// \return false if new lookup candidates were found
-bool Sema::DiagnoseEmptyLookup(const CXXScopeSpec &SS,
+bool Sema::DiagnoseEmptyLookup(Scope *S, const CXXScopeSpec &SS,
                                LookupResult &R) {
   DeclarationName Name = R.getLookupName();
 
-  // We don't know how to recover from bad qualified lookups.
-  if (!SS.isEmpty()) {
-    Diag(R.getNameLoc(), diag::err_no_member)
-      << Name << computeDeclContext(SS, false)
-      << SS.getRange();
-    return true;
-  }
-
   unsigned diagnostic = diag::err_undeclared_var_use;
+  unsigned diagnostic_suggest = diag::err_undeclared_var_use_suggest;
   if (Name.getNameKind() == DeclarationName::CXXOperatorName ||
       Name.getNameKind() == DeclarationName::CXXLiteralOperatorName ||
-      Name.getNameKind() == DeclarationName::CXXConversionFunctionName)
+      Name.getNameKind() == DeclarationName::CXXConversionFunctionName) {
     diagnostic = diag::err_undeclared_use;
+    diagnostic_suggest = diag::err_undeclared_use_suggest;
+  }
 
-  // Fake an unqualified lookup.  This is useful when (for example)
-  // the original lookup would not have found something because it was
-  // a dependent name.
-  for (DeclContext *DC = CurContext; DC; DC = DC->getParent()) {
+  // If the original lookup was an unqualified lookup, fake an
+  // unqualified lookup.  This is useful when (for example) the
+  // original lookup would not have found something because it was a
+  // dependent name.
+  for (DeclContext *DC = SS.isEmpty()? CurContext : 0;
+       DC; DC = DC->getParent()) {
     if (isa<CXXRecordDecl>(DC)) {
       LookupQualifiedName(R, DC);
 
@@ -933,6 +930,33 @@ bool Sema::DiagnoseEmptyLookup(const CXXScopeSpec &SS,
     }
   }
 
+  // We didn't find anything, so try to correct for a typo.
+  if (S && CorrectTypo(R, S, &SS) && 
+      (isa<ValueDecl>(*R.begin()) || isa<TemplateDecl>(*R.begin()))) {
+    if (SS.isEmpty())
+      Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName()
+        << CodeModificationHint::CreateReplacement(R.getNameLoc(),
+                                              R.getLookupName().getAsString());
+    else 
+      Diag(R.getNameLoc(), diag::err_no_member_suggest)
+        << Name << computeDeclContext(SS, false) << R.getLookupName()
+        << SS.getRange()
+        << CodeModificationHint::CreateReplacement(R.getNameLoc(),
+                                              R.getLookupName().getAsString());
+
+    // Tell the callee to try to recover.
+    return false;
+  }
+
+  // Emit a special diagnostic for failed member lookups.
+  // FIXME: computing the declaration context might fail here (?)
+  if (!SS.isEmpty()) {
+    Diag(R.getNameLoc(), diag::err_no_member)
+      << Name << computeDeclContext(SS, false)
+      << SS.getRange();
+    return true;
+  }
+
   // Give up, we can't recover.
   Diag(R.getNameLoc(), diagnostic) << Name;
   return true;
@@ -1010,7 +1034,7 @@ Sema::OwningExprResult Sema::ActOnIdExpression(Scope *S,
     // If this name wasn't predeclared and if this is not a function
     // call, diagnose the problem.
     if (R.empty()) {
-      if (DiagnoseEmptyLookup(SS, R))
+      if (DiagnoseEmptyLookup(S, SS, R))
         return ExprError();
 
       assert(!R.empty() &&
index 72779c339d011d39d44af7e66311a8612d4c91fc..9abbd575dcca76a0a21a530afd3b8545fc80d18a 100644 (file)
@@ -2134,11 +2134,14 @@ void TypoCorrectionConsumer::FoundDecl(NamedDecl *ND, NamedDecl *Hiding) {
 /// \param SS the nested-name-specifier that precedes the name we're
 /// looking for, if present.
 ///
+/// \param EnteringContext whether we're entering the context described by 
+/// the nested-name-specifier SS.
+///
 /// \returns true if the typo was corrected, in which case the \p Res
 /// structure will contain the results of name lookup for the
 /// corrected name. Otherwise, returns false.
 bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
-                       bool AllowBuiltinCreation, bool EnteringContext) {
+                       bool EnteringContext) {
   // We only attempt to correct typos for identifiers.
   IdentifierInfo *Typo = Res.getLookupName().getAsIdentifierInfo();
   if (!Typo)
@@ -2190,6 +2193,12 @@ bool Sema::CorrectTypo(LookupResult &Res, Scope *S, const CXXScopeSpec *SS,
   // success if we found something that was not ambiguous.
   Res.clear();
   Res.setLookupName(BestName);
-  LookupParsedName(Res, S, SS, AllowBuiltinCreation, EnteringContext);
-  return Res.getResultKind() != LookupResult::NotFound && !Res.isAmbiguous();
+  LookupParsedName(Res, S, SS, /*AllowBuiltinCreation=*/false, EnteringContext);
+
+  if (Res.isAmbiguous()) {
+    Res.suppressDiagnostics();
+    return false;
+  }
+
+  return Res.getResultKind() != LookupResult::NotFound;
 }
index 99dbaba37be796fcdd21695c8cb3427f9f9ba078..5892081736697cf8a6d3959fbea331205dca7ed8 100644 (file)
@@ -4822,7 +4822,7 @@ BuildRecoveryCallExpr(Sema &SemaRef, Expr *Fn,
 
   LookupResult R(SemaRef, ULE->getName(), ULE->getNameLoc(),
                  Sema::LookupOrdinaryName);
-  if (SemaRef.DiagnoseEmptyLookup(SS, R))
+  if (SemaRef.DiagnoseEmptyLookup(/*Scope=*/0, SS, R))
     return Destroy(SemaRef, Fn, Args, NumArgs);
 
   assert(!R.empty() && "lookup results empty despite recovery");
index b884ffa7555da0c6ff932b57cf7b5ae0c546088a..6c232f74038f54c807e3889cf1a1301aed0337cb 100644 (file)
@@ -1,7 +1,11 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
 // RUN: %clang_cc1 -fsyntax-only -fixit -o - | %clang_cc1 -fsyntax-only -pedantic -Werror -x c++ -
 namespace std {
-  template<typename T> class basic_string { };
+  template<typename T> class basic_string { 
+    int find(const char *substr);
+    static const int npos = -1;
+  };
+
   typedef basic_string<char> string;
 }
 
@@ -13,3 +17,11 @@ using namespace std;
 
 otherstd::strng str1; // expected-error{{no type named 'strng' in namespace 'otherstd'; did you mean 'string'?}}
 tring str2; // expected-error{{unknown type name 'tring'; did you mean 'string'?}}
+
+float area(float radius, float pi) {
+  return radious * pi; // expected-error{{use of undeclared identifier 'radious'; did you mean 'radius'?}}
+}
+
+bool test_string(std::string s) {
+  return s.find("hello") == std::string::pos; // expected-error{{no member named 'pos' in 'class std::basic_string<char>'; did you mean 'npos'?}}
+}
index d4d900d302876db0e6876f2d2dd881078856971b..e67499bcc4c98104d8c55c304eaec8a094359957 100644 (file)
@@ -53,7 +53,8 @@ void outer_shadowing_test() {
 void g18(void) {
   extern int g19;
 }
-int *p=&g19; // expected-error{{use of undeclared identifier 'g19'}}
+int *p=&g19; // expected-error{{use of undeclared identifier 'g19'}} \
+             // expected-warning{{incompatible pointer types}}
 
 // PR3645
 static int a;