From 9ea8efaf92570d708494ea352d3e5c21f0fe0ea7 Mon Sep 17 00:00:00 2001 From: Nick Lewycky Date: Mon, 23 Jun 2014 22:57:51 +0000 Subject: [PATCH] Propagate isAddressOfMember into typo correction so that we don't correct &qualified-id into &unqualified-id. Also make sure to set the naming class when we find the qualified-id in a different class than the nested name specifier specified so far. Fixes PR19681! llvm-svn: 211551 --- clang/include/clang/Sema/TypoCorrection.h | 5 +++-- clang/lib/Parse/ParseExpr.cpp | 4 +++- clang/lib/Sema/SemaExpr.cpp | 14 ++++++++++++ clang/lib/Sema/SemaLookup.cpp | 25 ++++++++++++++++------ clang/test/SemaCXX/typo-correction-pt2.cpp | 13 +++++++++++ 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Sema/TypoCorrection.h b/clang/include/clang/Sema/TypoCorrection.h index e5f8f4dfa29b..6cab59c93efc 100644 --- a/clang/include/clang/Sema/TypoCorrection.h +++ b/clang/include/clang/Sema/TypoCorrection.h @@ -250,8 +250,8 @@ public: CorrectionCandidateCallback() : WantTypeSpecifiers(true), WantExpressionKeywords(true), WantCXXNamedCasts(true), WantRemainingKeywords(true), - WantObjCSuper(false), - IsObjCIvarLookup(false) {} + WantObjCSuper(false), IsObjCIvarLookup(false), + IsAddressOfOperand(false) {} virtual ~CorrectionCandidateCallback() {} @@ -287,6 +287,7 @@ public: // Temporary hack for the one case where a CorrectTypoContext enum is used // when looking up results. bool IsObjCIvarLookup; + bool IsAddressOfOperand; }; /// @brief Simple template class for restricting typo correction candidates diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index b2381576a2b4..3fea464c688e 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -435,7 +435,8 @@ class CastExpressionIdValidator : public CorrectionCandidateCallback { if (isa(ND)) return WantTypeSpecifiers; - return AllowNonTypes; + return AllowNonTypes && + CorrectionCandidateCallback::ValidateCandidate(candidate); } private: @@ -813,6 +814,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, SourceLocation TemplateKWLoc; CastExpressionIdValidator Validator(isTypeCast != NotTypeCast, isTypeCast != IsTypeCast); + Validator.IsAddressOfOperand = isAddressOfOperand; Name.setIdentifier(&II, ILoc); Res = Actions.ActOnIdExpression(getCurScope(), ScopeSpec, TemplateKWLoc, Name, Tok.is(tok::l_paren), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index bda80cd205d7..c7ea55219ac7 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1883,6 +1883,17 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, } } R.addDecl(ND); + if (getLangOpts().CPlusPlus && ND->isCXXClassMember()) { + CXXRecordDecl *Record = nullptr; + if (Corrected.getCorrectionSpecifier()) { + const Type *Ty = Corrected.getCorrectionSpecifier()->getAsType(); + Record = Ty->getAsCXXRecordDecl(); + } + if (!Record) + Record = cast( + ND->getDeclContext()->getRedeclContext()); + R.setNamingClass(Record); + } AcceptableWithRecovery = isa(ND) || isa(ND); @@ -2100,6 +2111,9 @@ ExprResult Sema::ActOnIdExpression(Scope *S, // If this name wasn't predeclared and if this is not a function // call, diagnose the problem. CorrectionCandidateCallback DefaultValidator; + DefaultValidator.IsAddressOfOperand = IsAddressOfOperand; + assert((!CCC || CCC->IsAddressOfOperand == IsAddressOfOperand) && + "Typo correction callback misconfigured"); if (DiagnoseEmptyLookup(S, SS, R, CCC ? *CCC : DefaultValidator)) return ExprError(); diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 9abff2c45ded..c7be6c931500 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -4454,14 +4454,27 @@ bool CorrectionCandidateCallback::ValidateCandidate(const TypoCorrection &candid return WantTypeSpecifiers || WantExpressionKeywords || WantCXXNamedCasts || WantRemainingKeywords || WantObjCSuper; - for (TypoCorrection::const_decl_iterator CDecl = candidate.begin(), - CDeclEnd = candidate.end(); - CDecl != CDeclEnd; ++CDecl) { - if (!isa(*CDecl)) - return true; + bool HasNonType = false; + bool HasStaticMethod = false; + bool HasNonStaticMethod = false; + for (Decl *D : candidate) { + if (FunctionTemplateDecl *FTD = dyn_cast(D)) + D = FTD->getTemplatedDecl(); + if (CXXMethodDecl *Method = dyn_cast(D)) { + if (Method->isStatic()) + HasStaticMethod = true; + else + HasNonStaticMethod = true; + } + if (!isa(D)) + HasNonType = true; } - return WantTypeSpecifiers; + if (IsAddressOfOperand && HasNonStaticMethod && !HasStaticMethod && + !candidate.getCorrectionSpecifier()) + return false; + + return WantTypeSpecifiers || HasNonType; } FunctionCallFilterCCC::FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs, diff --git a/clang/test/SemaCXX/typo-correction-pt2.cpp b/clang/test/SemaCXX/typo-correction-pt2.cpp index cdef7e7500bf..88a7073f9946 100644 --- a/clang/test/SemaCXX/typo-correction-pt2.cpp +++ b/clang/test/SemaCXX/typo-correction-pt2.cpp @@ -287,3 +287,16 @@ void test() { if (p) // expected-error-re {{use of undeclared identifier 'p'{{$}}}} return; } + +namespace PR19681 { + struct TypoA {}; + struct TypoB { + void test(); + private: + template void private_memfn(T); // expected-note{{declared here}} + }; + void TypoB::test() { + // FIXME: should suggest 'PR19681::TypoB::private_memfn' instead of '::PR19681::TypoB::private_memfn' + (void)static_cast(&TypoA::private_memfn); // expected-error{{no member named 'private_memfn' in 'PR19681::TypoA'; did you mean '::PR19681::TypoB::private_memfn'?}} + } +}