diff options
| author | Kaelyn Takata <rikka@google.com> | 2014-10-27 18:07:46 +0000 |
|---|---|---|
| committer | Kaelyn Takata <rikka@google.com> | 2014-10-27 18:07:46 +0000 |
| commit | fe408a77f63e390daf0be62d49ae9b9e1520424a (patch) | |
| tree | 3f3b8d2e0ed1d7debcdb18a1cfd4db3d5a340bbc | |
| parent | 8363f04ee626280fefa6d2848751859820a8d4c9 (diff) | |
| download | bcm5719-llvm-fe408a77f63e390daf0be62d49ae9b9e1520424a.tar.gz bcm5719-llvm-fe408a77f63e390daf0be62d49ae9b9e1520424a.zip | |
Wire up LookupMemberExpr to use the new TypoExpr.
This includes adding the new TypoExpr-based lazy typo correction to
LookupMemberExprInRecord as an alternative to the existing eager typo
correction.
llvm-svn: 220698
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 47 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprMember.cpp | 114 | ||||
| -rw-r--r-- | clang/test/SemaCXX/arrow-operator.cpp | 5 | ||||
| -rw-r--r-- | clang/test/SemaCXX/typo-correction-delayed.cpp | 44 | ||||
| -rw-r--r-- | clang/test/SemaCXX/typo-correction-pt2.cpp | 2 | ||||
| -rw-r--r-- | clang/test/SemaCXX/typo-correction.cpp | 10 |
6 files changed, 203 insertions, 19 deletions
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 1d9df2d4d45..9e05e82e9c9 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5921,6 +5921,7 @@ class TransformTypos : public TreeTransform<TransformTypos> { llvm::SmallSetVector<TypoExpr *, 2> TypoExprs; llvm::SmallDenseMap<TypoExpr *, ExprResult, 2> TransformCache; + llvm::SmallDenseMap<OverloadExpr *, Expr *, 4> OverloadResolution; /// \brief Emit diagnostics for all of the TypoExprs encountered. /// If the TypoExprs were successfully corrected, then the diagnostics should @@ -5930,8 +5931,21 @@ class TransformTypos : public TreeTransform<TransformTypos> { for (auto E : TypoExprs) { TypoExpr *TE = cast<TypoExpr>(E); auto &State = SemaRef.getTypoExprState(TE); - if (State.DiagHandler) - State.DiagHandler(State.Consumer->getCurrentCorrection()); + if (State.DiagHandler) { + TypoCorrection TC = State.Consumer->getCurrentCorrection(); + ExprResult Replacement = TransformCache[TE]; + + // Extract the NamedDecl from the transformed TypoExpr and add it to the + // TypoCorrection, replacing the existing decls. This ensures the right + // NamedDecl is used in diagnostics e.g. in the case where overload + // resolution was used to select one from several possible decls that + // had been stored in the TypoCorrection. + if (auto *ND = getDeclFromExpr( + Replacement.isInvalid() ? nullptr : Replacement.get())) + TC.setCorrectionDecl(ND); + + State.DiagHandler(TC); + } SemaRef.clearDelayedTypo(TE); } } @@ -5956,9 +5970,38 @@ class TransformTypos : public TreeTransform<TransformTypos> { return false; } + NamedDecl *getDeclFromExpr(Expr *E) { + if (auto *OE = dyn_cast_or_null<OverloadExpr>(E)) + E = OverloadResolution[OE]; + + if (!E) + return nullptr; + if (auto *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getDecl(); + if (auto *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + // FIXME: Add any other expr types that could be be seen by the delayed typo + // correction TreeTransform for which the corresponding TypoCorrection could + // contain multple decls. + return nullptr; + } + public: TransformTypos(Sema &SemaRef) : BaseTransform(SemaRef) {} + ExprResult RebuildCallExpr(Expr *Callee, SourceLocation LParenLoc, + MultiExprArg Args, + SourceLocation RParenLoc, + Expr *ExecConfig = nullptr) { + auto Result = BaseTransform::RebuildCallExpr(Callee, LParenLoc, Args, + RParenLoc, ExecConfig); + if (auto *OE = dyn_cast<OverloadExpr>(Callee)) { + if (!Result.isInvalid() && Result.get()) + OverloadResolution[OE] = cast<CallExpr>(Result.get())->getCallee(); + } + return Result; + } + ExprResult TransformLambdaExpr(LambdaExpr *E) { return Owned(E); } ExprResult Transform(Expr *E) { diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index aca3df1ea2b..0d7f3c641d3 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -10,6 +10,7 @@ // This file implements semantic analysis member access expressions. // //===----------------------------------------------------------------------===// +#include "clang/Sema/Overload.h" #include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTLambda.h" #include "clang/AST/DeclCXX.h" @@ -569,13 +570,47 @@ class RecordMemberExprValidatorCCC : public CorrectionCandidateCallback { const RecordDecl *const Record; }; +class MemberTypoDiags { + Sema &SemaRef; + DeclContext *Ctx; + DeclarationName Typo; + SourceLocation TypoLoc; + SourceRange BaseRange; + SourceRange ScopeSpecLoc; + unsigned DiagnosticID; + unsigned NoSuggestDiagnosticID; + +public: + MemberTypoDiags(Sema &SemaRef, DeclContext *Ctx, DeclarationName Typo, + SourceLocation TypoLoc, SourceRange BaseRange, + CXXScopeSpec &SS, unsigned DiagnosticID, + unsigned NoSuggestDiagnosticID) + : SemaRef(SemaRef), Ctx(Ctx), Typo(Typo), TypoLoc(TypoLoc), BaseRange(BaseRange), + ScopeSpecLoc(SS.getRange()), DiagnosticID(DiagnosticID), + NoSuggestDiagnosticID(NoSuggestDiagnosticID) {} + + void operator()(const TypoCorrection &TC) { + if (TC) { + assert(!TC.isKeyword() && "Got a keyword as a correction for a member!"); + bool DroppedSpecifier = + TC.WillReplaceSpecifier() && + Typo.getAsString() == TC.getAsString(SemaRef.getLangOpts()); + SemaRef.diagnoseTypo(TC, SemaRef.PDiag(DiagnosticID) << Typo << Ctx + << DroppedSpecifier + << ScopeSpecLoc); + } else { + SemaRef.Diag(TypoLoc, NoSuggestDiagnosticID) << Typo << Ctx << BaseRange; + } + } +}; + } -static bool -LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, - SourceRange BaseRange, const RecordType *RTy, - SourceLocation OpLoc, CXXScopeSpec &SS, - bool HasTemplateArgs) { +static bool LookupMemberExprInRecord( + Sema &SemaRef, LookupResult &R, SourceRange BaseRange, + const RecordType *RTy, SourceLocation OpLoc, CXXScopeSpec &SS, + bool HasTemplateArgs, TypoExpr **TE = nullptr, + Sema::TypoRecoveryCallback TRC = nullptr) { RecordDecl *RDecl = RTy->getDecl(); if (!SemaRef.isThisOutsideMemberFunctionBody(QualType(RTy, 0)) && SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), @@ -619,6 +654,20 @@ LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, if (!R.empty()) return false; + if (TE && SemaRef.getLangOpts().CPlusPlus) { + // TODO: C cannot handle TypoExpr nodes because the C code paths do not know + // what to do with dependent types e.g. on the LHS of an assigment. + *TE = SemaRef.CorrectTypoDelayed( + R.getLookupNameInfo(), R.getLookupKind(), nullptr, &SS, + llvm::make_unique<RecordMemberExprValidatorCCC>(RTy), + MemberTypoDiags(SemaRef, DC, R.getLookupName(), R.getNameLoc(), + BaseRange, SS, diag::err_no_member_suggest, + diag::err_no_member), + TRC, Sema::CTK_ErrorRecovery, DC); + R.clear(); + return false; + } + // We didn't find anything with the given name, so try to correct // for typos. DeclarationName Name = R.getLookupName(); @@ -1148,6 +1197,48 @@ Sema::PerformMemberExprBaseConversion(Expr *Base, bool IsArrow) { return CheckPlaceholderExpr(Base); } +namespace { + +class MemberExprTypoRecovery { + Expr *BaseExpr; + CXXScopeSpec SS; + SourceLocation OpLoc; + bool IsArrow; + +public: + MemberExprTypoRecovery(Expr *BE, CXXScopeSpec &SS, SourceLocation OpLoc, + bool IsArrow) + : BaseExpr(BE), SS(SS), + OpLoc(OpLoc), IsArrow(IsArrow) {} + + ExprResult operator()(Sema &SemaRef, TypoExpr *TE, TypoCorrection TC) { + if (TC.isKeyword()) + return ExprError(); + + LookupResult R(SemaRef, TC.getCorrection(), + TC.getCorrectionRange().getBegin(), + SemaRef.getTypoExprState(TE) + .Consumer->getLookupResult() + .getLookupKind()); + R.suppressDiagnostics(); + + QualType BaseType; + if (auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr)) + BaseType = DRE->getDecl()->getType(); + else if (auto *CE = dyn_cast<CallExpr>(BaseExpr)) + BaseType = CE->getCallReturnType(); + + for (NamedDecl *ND : TC) + R.addDecl(ND); + R.resolveKind(); + return SemaRef.BuildMemberReferenceExpr( + BaseExpr, BaseExpr->getType(), OpLoc, IsArrow, SS, SourceLocation(), + nullptr, R, nullptr); + } +}; + +} + /// Look up the given member of the given non-type-dependent /// expression. This can return in one of two ways: /// * If it returns a sentinel null-but-valid result, the caller will @@ -1210,13 +1301,18 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, // Handle field access to simple records. if (const RecordType *RTy = BaseType->getAs<RecordType>()) { - if (LookupMemberExprInRecord(S, R, BaseExpr.get()->getSourceRange(), - RTy, OpLoc, SS, HasTemplateArgs)) + TypoExpr *TE = nullptr; + if (LookupMemberExprInRecord( + S, R, BaseExpr.get()->getSourceRange(), RTy, OpLoc, SS, + HasTemplateArgs, &TE, + MemberExprTypoRecovery(BaseExpr.get(), SS, OpLoc, IsArrow))) return ExprError(); // Returning valid-but-null is how we indicate to the caller that - // the lookup result was filled in. - return ExprResult((Expr *)nullptr); + // the lookup result was filled in. If typo correction was attempted and + // failed, the lookup result will have been cleared--that combined with the + // valid-but-null ExprResult will trigger the appropriate diagnostics. + return ExprResult(TE); } // Handle ivar access to Objective-C objects. diff --git a/clang/test/SemaCXX/arrow-operator.cpp b/clang/test/SemaCXX/arrow-operator.cpp index 173ff729fc1..3e32a6ba33e 100644 --- a/clang/test/SemaCXX/arrow-operator.cpp +++ b/clang/test/SemaCXX/arrow-operator.cpp @@ -52,14 +52,15 @@ class wrapped_ptr { class Worker { public: - void DoSomething(); + void DoSomething(); // expected-note {{'DoSomething' declared here}} void Chuck(); }; void test() { wrapped_ptr<Worker> worker(new Worker); worker.DoSomething(); // expected-error {{no member named 'DoSomething' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'; did you mean to use '->' instead of '.'?}} - worker.DoSamething(); // expected-error {{no member named 'DoSamething' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'}} + worker.DoSamething(); // expected-error {{no member named 'DoSamething' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'; did you mean to use '->' instead of '.'?}} \ + // expected-error {{no member named 'DoSamething' in 'arrow_suggest::Worker'; did you mean 'DoSomething'?}} worker.Chuck(); // expected-error {{no member named 'Chuck' in 'arrow_suggest::wrapped_ptr<arrow_suggest::Worker>'; did you mean 'Check'?}} } diff --git a/clang/test/SemaCXX/typo-correction-delayed.cpp b/clang/test/SemaCXX/typo-correction-delayed.cpp new file mode 100644 index 00000000000..c79fe45629f --- /dev/null +++ b/clang/test/SemaCXX/typo-correction-delayed.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions %s + +struct A {}; +struct B {}; +struct D { + A fizbin; // expected-note 2 {{declared here}} + A foobar; // expected-note 2 {{declared here}} + B roxbin; // expected-note 2 {{declared here}} + B toobad; // expected-note 2 {{declared here}} + void BooHoo(); + void FoxBox(); +}; + +void something(A, B); +void test() { + D obj; + something(obj.fixbin, // expected-error {{did you mean 'fizbin'?}} + obj.toobat); // expected-error {{did you mean 'toobad'?}} + something(obj.toobat, // expected-error {{did you mean 'foobar'?}} + obj.fixbin); // expected-error {{did you mean 'roxbin'?}} + something(obj.fixbin, // expected-error {{did you mean 'fizbin'?}} + obj.fixbin); // expected-error {{did you mean 'roxbin'?}} + something(obj.toobat, // expected-error {{did you mean 'foobar'?}} + obj.toobat); // expected-error {{did you mean 'toobad'?}} + // Both members could be corrected to methods, but that isn't valid. + something(obj.boohoo, // expected-error-re {{no member named 'boohoo' in 'D'{{$}}}} + obj.foxbox); // expected-error-re {{no member named 'foxbox' in 'D'{{$}}}} + // The first argument has a usable correction but the second doesn't. + something(obj.boobar, // expected-error-re {{no member named 'boobar' in 'D'{{$}}}} + obj.foxbox); // expected-error-re {{no member named 'foxbox' in 'D'{{$}}}} +} + +// Ensure the delayed typo correction does the right thing when trying to +// recover using a seemingly-valid correction for which a valid expression to +// replace the TypoExpr cannot be created (but which does have a second +// correction candidate that would be a valid and usable correction). +class Foo { +public: + template <> void testIt(); // expected-error {{no function template matches}} + void textIt(); // expected-note {{'textIt' declared here}} +}; +void testMemberExpr(Foo *f) { + f->TestIt(); // expected-error {{no member named 'TestIt' in 'Foo'; did you mean 'textIt'?}} +} diff --git a/clang/test/SemaCXX/typo-correction-pt2.cpp b/clang/test/SemaCXX/typo-correction-pt2.cpp index d300764e645..de12da77d1a 100644 --- a/clang/test/SemaCXX/typo-correction-pt2.cpp +++ b/clang/test/SemaCXX/typo-correction-pt2.cpp @@ -28,7 +28,7 @@ struct A { }; struct B : A { using A::CreateFoo; - void CreateFoo(int, int); + void CreateFoo(int, int); // expected-note {{'CreateFoo' declared here}} }; void f(B &x) { x.Createfoo(0,0); // expected-error {{no member named 'Createfoo' in 'PR13387::B'; did you mean 'CreateFoo'?}} diff --git a/clang/test/SemaCXX/typo-correction.cpp b/clang/test/SemaCXX/typo-correction.cpp index e8160b066de..a4f517436aa 100644 --- a/clang/test/SemaCXX/typo-correction.cpp +++ b/clang/test/SemaCXX/typo-correction.cpp @@ -247,7 +247,7 @@ namespace b6956809_test1 { struct S1 { void method(A*); // no note here - void method(B*); + void method(B*); // expected-note{{'method' declared here}} }; void test1() { @@ -258,15 +258,15 @@ namespace b6956809_test1 { struct S2 { S2(); - void method(A*) const; // expected-note{{candidate function not viable}} + void method(A*) const; private: - void method(B*); // expected-note{{candidate function not viable}} + void method(B*); }; void test2() { B b; const S2 s; - s.methodd(&b); // expected-error{{no member named 'methodd' in 'b6956809_test1::S2'; did you mean 'method'}} expected-error{{no matching member function for call to 'method'}} + s.methodd(&b); // expected-error-re{{no member named 'methodd' in 'b6956809_test1::S2'{{$}}}} } } @@ -274,7 +274,7 @@ namespace b6956809_test2 { template<typename T> struct Err { typename T::error n; }; // expected-error{{type 'void *' cannot be used prior to '::' because it has no members}} struct S { template<typename T> typename Err<T>::type method(T); // expected-note{{in instantiation of template class 'b6956809_test2::Err<void *>' requested here}} - template<typename T> int method(T *); + template<typename T> int method(T *); // expected-note{{'method' declared here}} }; void test() { |

