From 04ea41c39a65b17fe8e5c4f8ba4caa9f3841f9cf Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Fri, 12 Oct 2012 20:00:44 +0000 Subject: [PATCH] Fix typo correction of one qualified name to another. When suggesting "foo::bar" as a correction for "fob::bar" we mistakenly replaced only "bar" with "foo::bar" producing "fob::foo::bar" which was broken. This corrects that replacement in as many places as I could find & provides test cases for all those cases I could find a test case for. There are a couple that don't seem to be reachable (one looks entirely dead, the other just doesn't seem to ever get called with a namespace to namespace change). Review by Richard Smith ( http://llvm-reviews.chandlerc.com/D57 ). llvm-svn: 165817 --- clang/include/clang/Sema/TypoCorrection.h | 12 +++++++++ clang/lib/Sema/SemaCXXScopeSpec.cpp | 3 ++- clang/lib/Sema/SemaDecl.cpp | 12 ++++++--- clang/lib/Sema/SemaDeclCXX.cpp | 3 ++- clang/lib/Sema/SemaExpr.cpp | 3 ++- clang/lib/Sema/SemaExprMember.cpp | 3 ++- clang/lib/Sema/SemaLookup.cpp | 8 ++++-- clang/lib/Sema/SemaTemplate.cpp | 3 ++- clang/test/FixIt/typo.cpp | 42 +++++++++++++++++++++++++++++-- 9 files changed, 77 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Sema/TypoCorrection.h b/clang/include/clang/Sema/TypoCorrection.h index a8f6e11..2b4a9e6 100644 --- a/clang/include/clang/Sema/TypoCorrection.h +++ b/clang/include/clang/Sema/TypoCorrection.h @@ -170,6 +170,17 @@ public: return CorrectionDecls.size() > 1; } + void setCorrectionRange(CXXScopeSpec* SS, + const DeclarationNameInfo &TypoName) { + CorrectionRange.setBegin(CorrectionNameSpec && SS ? SS->getBeginLoc() + : TypoName.getLoc()); + CorrectionRange.setEnd(TypoName.getLoc()); + } + + SourceRange getCorrectionRange() const { + return CorrectionRange; + } + typedef llvm::SmallVector::iterator decl_iterator; decl_iterator begin() { return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); @@ -193,6 +204,7 @@ private: unsigned CharDistance; unsigned QualifierDistance; unsigned CallbackDistance; + SourceRange CorrectionRange; }; /// @brief Base class for callback objects used by Sema::CorrectTypo to check diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 0de9dd5..15bfd1c 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -520,7 +520,8 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, if (LookupCtx) Diag(Found.getNameLoc(), diag::err_no_member_suggest) << Name << LookupCtx << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); else Diag(Found.getNameLoc(), diag::err_undeclared_var_use_suggest) << Name << CorrectedQuotedStr diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index aec5f01..ee246b81 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -435,7 +435,8 @@ bool Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II, else if (DeclContext *DC = computeDeclContext(*SS, false)) Diag(IILoc, diag::err_unknown_nested_typename_suggest) << II << DC << CorrectedQuotedStr << SS->getRange() - << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); else llvm_unreachable("could not have corrected a typo here"); @@ -684,11 +685,12 @@ Corrected: Diag(NameLoc, UnqualifiedDiag) << Name << CorrectedQuotedStr << FixItHint::CreateReplacement(NameLoc, CorrectedStr); - else + else // FIXME: is this even reachable? Test it. Diag(NameLoc, QualifiedDiag) << Name << computeDeclContext(SS, false) << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(NameLoc, CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); // Update the name, so that the caller has the new name. Name = Corrected.getCorrectionAsIdentifierInfo(); @@ -4894,6 +4896,10 @@ static NamedDecl* DiagnoseInvalidRedeclaration( } if (Correction) { + // FIXME: use Correction.getCorrectionRange() instead of computing the range + // here. This requires passing in the CXXScopeSpec to CorrectTypo which in + // turn causes the correction to fully qualify the name. If we fix + // CorrectTypo to minimally qualify then this change should be good. SourceRange FixItLoc(NewFD->getLocation()); CXXScopeSpec &SS = ExtraArgs.D.getCXXScopeSpec(); if (Correction.getCorrectionSpecifier() && SS.isValid()) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 12452b2..8d891a3 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -5811,7 +5811,8 @@ static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, if (DeclContext *DC = S.computeDeclContext(SS, false)) S.Diag(IdentLoc, diag::err_using_directive_member_suggest) << Ident << DC << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(IdentLoc, CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); else S.Diag(IdentLoc, diag::err_using_directive_suggest) << Ident << CorrectedQuotedStr diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 74ee870..3d66baa 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1640,7 +1640,8 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, Diag(R.getNameLoc(), diag::err_no_member_suggest) << Name << computeDeclContext(SS, false) << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); if (ND) Diag(ND->getLocation(), diag::note_previous_decl) << CorrectedQuotedStr; diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index a44fbf2..2b202ed 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -606,7 +606,8 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, R.addDecl(ND); SemaRef.Diag(R.getNameLoc(), diag::err_no_member_suggest) << Name << DC << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); SemaRef.Diag(ND->getLocation(), diag::note_previous_decl) << ND->getDeclName(); } diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index d5efd75..e4dab05 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4056,7 +4056,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, if (IsUnqualifiedLookup) UnqualifiedTyposCorrected[Typo] = Result; - return Result; + TypoCorrection TC = Result; + TC.setCorrectionRange(SS, TypoName); + return TC; } else if (BestResults.size() > 1 // Ugly hack equivalent to CTC == CTC_ObjCMessageReceiver; @@ -4076,7 +4078,9 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, if (IsUnqualifiedLookup) UnqualifiedTyposCorrected[Typo] = BestResults["super"].front(); - return BestResults["super"].front(); + TypoCorrection TC = BestResults["super"].front(); + TC.setCorrectionRange(SS, TypoName); + return TC; } // If this was an unqualified lookup and we believe the callback object did diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 0efe710..f56b054 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -333,7 +333,8 @@ void Sema::LookupTemplateName(LookupResult &Found, if (LookupCtx) Diag(Found.getNameLoc(), diag::err_no_member_template_suggest) << Name << LookupCtx << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr); + << FixItHint::CreateReplacement(Corrected.getCorrectionRange(), + CorrectedStr); else Diag(Found.getNameLoc(), diag::err_no_template_suggest) << Name << CorrectedQuotedStr diff --git a/clang/test/FixIt/typo.cpp b/clang/test/FixIt/typo.cpp index 3d40da8..b3568a5 100644 --- a/clang/test/FixIt/typo.cpp +++ b/clang/test/FixIt/typo.cpp @@ -5,7 +5,8 @@ // RUN: grep test_string %t namespace std { - template class basic_string { // expected-note 2{{'basic_string' declared here}} + template class basic_string { // expected-note 2{{'basic_string' declared here}} \ + // expected-note {{'otherstd::basic_string' declared here}} public: int find(const char *substr); // expected-note{{'find' declared here}} static const int npos = -1; // expected-note{{'npos' declared here}} @@ -76,13 +77,50 @@ int foo() { } namespace nonstd { - typedef std::basic_string yarn; // expected-note{{'nonstd::yarn' declared here}} + typedef std::basic_string yarn; // expected-note 2 {{'nonstd::yarn' declared here}} + int narf; // expected-note{{'nonstd::narf' declared here}} } yarn str4; // expected-error{{unknown type name 'yarn'; did you mean 'nonstd::yarn'?}} +wibble::yarn str5; // expected-error{{no type named 'yarn' in namespace 'otherstd'; did you mean 'nonstd::yarn'?}} + +int poit() { + nonstd::basic_string str; // expected-error{{no template named 'basic_string' in namespace 'nonstd'; did you mean 'otherstd::basic_string'?}} + return wibble::narf; // expected-error{{no member named 'narf' in namespace 'otherstd'; did you mean 'nonstd::narf'?}} +} namespace check_bool { void f() { Bool b; // expected-error{{use of undeclared identifier 'Bool'; did you mean 'bool'?}} } } + +namespace outr { +} +namespace outer { + namespace inner { // expected-note{{'outer::inner' declared here}} \ + // expected-note{{namespace 'outer::inner' defined here}} \ + // expected-note{{'inner' declared here}} + int i; + } +} + +using namespace outr::inner; // expected-error{{no namespace named 'inner' in namespace 'outr'; did you mean 'outer::inner'?}} + +void func() { + outr::inner::i = 3; // expected-error{{no member named 'inner' in namespace 'outr'; did you mean 'outer::inner'?}} + outer::innr::i = 4; // expected-error{{no member named 'innr' in namespace 'outer'; did you mean 'inner'?}} +} + +struct base { +}; +struct derived : base { + int i; +}; + +void func2() { + derived d; + // FIXME: we should offer a fix here. We do if the 'i' is misspelled, but we don't do name qualification changes + // to replace base::i with derived::i as we would for other qualified name misspellings. + // d.base::i = 3; +} -- 2.7.4