summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaelyn Takata <rikka@google.com>2014-10-27 18:07:46 +0000
committerKaelyn Takata <rikka@google.com>2014-10-27 18:07:46 +0000
commitfe408a77f63e390daf0be62d49ae9b9e1520424a (patch)
tree3f3b8d2e0ed1d7debcdb18a1cfd4db3d5a340bbc
parent8363f04ee626280fefa6d2848751859820a8d4c9 (diff)
downloadbcm5719-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.cpp47
-rw-r--r--clang/lib/Sema/SemaExprMember.cpp114
-rw-r--r--clang/test/SemaCXX/arrow-operator.cpp5
-rw-r--r--clang/test/SemaCXX/typo-correction-delayed.cpp44
-rw-r--r--clang/test/SemaCXX/typo-correction-pt2.cpp2
-rw-r--r--clang/test/SemaCXX/typo-correction.cpp10
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() {
OpenPOWER on IntegriCloud