diff options
author | John McCall <rjmccall@apple.com> | 2009-12-16 08:11:27 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2009-12-16 08:11:27 +0000 |
commit | 578b69b186d9cba0a6ae1dd7f4c04cd6a49f0aac (patch) | |
tree | ede0c169beeed54c863512d74bae423fdb1b51b3 | |
parent | e8e4a1c0bf2e3dd26dd0721a102307bbb589e96a (diff) |
Introduce a centralized routine in Sema for diagnosing failed lookups (when
used as expressions). In dependent contexts, try to recover by doing a lookup
in previously-dependent base classes. We get better diagnostics out, but
unfortunately the recovery fails: we need to turn it into a method call
expression, not a bare call expression. Thus this is still a WIP.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@91525 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaExpr.cpp | 78 | ||||
-rw-r--r-- | lib/Sema/SemaOverload.cpp | 50 | ||||
-rw-r--r-- | test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp | 2 | ||||
-rw-r--r-- | test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3.cpp | 2 | ||||
-rw-r--r-- | test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/conversion-function.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/decltype-crash.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/no-implicit-builtin-decls.cpp | 2 | ||||
-rw-r--r-- | test/SemaCXX/type-dependent-exprs.cpp | 2 | ||||
-rw-r--r-- | test/SemaTemplate/constructor-template.cpp | 6 | ||||
-rw-r--r-- | test/SemaTemplate/dependent-names.cpp | 15 | ||||
-rw-r--r-- | test/SemaTemplate/instantiate-call.cpp | 2 | ||||
-rw-r--r-- | test/SemaTemplate/recursive-template-instantiation.cpp | 2 |
15 files changed, 148 insertions, 25 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 9a2a334479..37d80e15e4 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1279,7 +1279,9 @@ def err_unexpected_typedef : Error< def err_unexpected_namespace : Error< "unexpected namespace name %0: expected expression">; def err_undeclared_var_use : Error<"use of undeclared identifier %0">; -def err_undeclared_use : Error<"use of undeclared '%0'">; +def note_dependent_var_use : Note<"must qualify identifier to find this " + "declaration in dependent base class">; +def err_undeclared_use : Error<"use of undeclared %0">; def warn_deprecated : Warning<"%0 is deprecated">, InGroup<DiagGroup<"deprecated-declarations">>; def warn_unavailable : Warning<"%0 is unavailable">, diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 28a6bba7f4..f189790944 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1479,6 +1479,8 @@ public: bool HasTrailingLParen, bool IsAddressOfOperand); + bool DiagnoseEmptyLookup(const CXXScopeSpec &SS, LookupResult &R); + OwningExprResult LookupInObjCMethod(LookupResult &R, Scope *S, IdentifierInfo *II); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 77c2636ca7..ac3703eac9 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -917,6 +917,68 @@ static void DiagnoseInstanceReference(Sema &SemaRef, SemaRef.Diag(Loc, diag::err_member_call_without_object) << Range; } +/// Diagnose an empty lookup. +/// +/// \return false if new lookup candidates were found +bool Sema::DiagnoseEmptyLookup(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; + if (Name.getNameKind() == DeclarationName::CXXOperatorName || + Name.getNameKind() == DeclarationName::CXXLiteralOperatorName || + Name.getNameKind() == DeclarationName::CXXConversionFunctionName) + diagnostic = diag::err_undeclared_use; + + // 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 (isa<CXXRecordDecl>(DC)) { + LookupQualifiedName(R, DC); + + if (!R.empty()) { + // Don't give errors about ambiguities in this lookup. + R.suppressDiagnostics(); + + CXXMethodDecl *CurMethod = dyn_cast<CXXMethodDecl>(CurContext); + bool isInstance = CurMethod && + CurMethod->isInstance() && + DC == CurMethod->getParent(); + + // Give a code modification hint to insert 'this->'. + // TODO: fixit for inserting 'Base<T>::' in the other cases. + // Actually quite difficult! + if (isInstance) + Diag(R.getNameLoc(), diagnostic) << Name + << CodeModificationHint::CreateInsertion(R.getNameLoc(), + "this->"); + else + Diag(R.getNameLoc(), diagnostic) << Name; + + // Do we really want to note all of these? + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) + Diag((*I)->getLocation(), diag::note_dependent_var_use); + + // Tell the callee to try to recover. + return false; + } + } + } + + // Give up, we can't recover. + Diag(R.getNameLoc(), diagnostic) << Name; + return true; +} + Sema::OwningExprResult Sema::ActOnIdExpression(Scope *S, const CXXScopeSpec &SS, UnqualifiedId &Id, @@ -989,17 +1051,11 @@ 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 (!SS.isEmpty()) - return ExprError(Diag(NameLoc, diag::err_no_member) - << Name << computeDeclContext(SS, false) - << SS.getRange()); - else if (Name.getNameKind() == DeclarationName::CXXOperatorName || - Name.getNameKind() == DeclarationName::CXXLiteralOperatorName || - Name.getNameKind() == DeclarationName::CXXConversionFunctionName) - return ExprError(Diag(NameLoc, diag::err_undeclared_use) - << Name); - else - return ExprError(Diag(NameLoc, diag::err_undeclared_var_use) << Name); + if (DiagnoseEmptyLookup(SS, R)) + return ExprError(); + + assert(!R.empty() && + "DiagnoseEmptyLookup returned false but added no results"); } } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 23598bf605..d01bca718d 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2622,7 +2622,14 @@ Sema::AddTemplateOverloadCandidate(FunctionTemplateDecl *FunctionTemplate, Args, NumArgs, Specialization, Info)) { // FIXME: Record what happened with template argument deduction, so // that we can give the user a beautiful diagnostic. - (void)Result; + (void) Result; + + CandidateSet.push_back(OverloadCandidate()); + OverloadCandidate &Candidate = CandidateSet.back(); + Candidate.Function = FunctionTemplate->getTemplatedDecl(); + Candidate.Viable = false; + Candidate.IsSurrogate = false; + Candidate.IgnoreObjectArgument = false; return; } @@ -4637,6 +4644,34 @@ void Sema::AddOverloadedCallCandidates(llvm::SmallVectorImpl<NamedDecl*> &Fns, CandidateSet, PartialOverloading); } + +/// Attempts to recover from a call where no functions were found. +/// +/// Returns true if new candidates were found. +static bool AddRecoveryCallCandidates(Sema &SemaRef, Expr *Fn, + const TemplateArgumentListInfo *ExplicitTemplateArgs, + Expr **Args, unsigned NumArgs, + OverloadCandidateSet &CandidateSet) { + UnresolvedLookupExpr *ULE + = cast<UnresolvedLookupExpr>(Fn->IgnoreParenCasts()); + + CXXScopeSpec SS; + if (ULE->getQualifier()) { + SS.setScopeRep(ULE->getQualifier()); + SS.setRange(ULE->getQualifierRange()); + } + + LookupResult R(SemaRef, ULE->getName(), ULE->getNameLoc(), + Sema::LookupOrdinaryName); + if (SemaRef.DiagnoseEmptyLookup(SS, R)) + return false; + + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + AddOverloadedCallCandidate(SemaRef, *I, ExplicitTemplateArgs, + Args, NumArgs, CandidateSet, false); + } + return true; +} /// ResolveOverloadedCallFn - Given the call expression that calls Fn /// (which eventually refers to the declaration Func) and the call @@ -4661,6 +4696,19 @@ FunctionDecl *Sema::ResolveOverloadedCallFn(Expr *Fn, AddOverloadedCallCandidates(Fns, UnqualifiedName, ArgumentDependentLookup, ExplicitTemplateArgs, Args, NumArgs, CandidateSet); + + // If we found nothing, try to recover. + // AddRecoveryCallCandidates diagnoses the error itself, so we just + // bailout out if it fails. + if (CandidateSet.empty() && + !AddRecoveryCallCandidates(*this, Fn, ExplicitTemplateArgs, + Args, NumArgs, CandidateSet)) { + Fn->Destroy(Context); + for (unsigned Arg = 0; Arg < NumArgs; ++Arg) + Args[Arg]->Destroy(Context); + return 0; + } + OverloadCandidateSet::iterator Best; switch (BestViableFunction(CandidateSet, Fn->getLocStart(), Best)) { case OR_Success: diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp index 456a785587..b59e6ca320 100644 --- a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp +++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp @@ -32,7 +32,7 @@ namespace D { namespace Test { void test() { func(A::A()); - func(B::B()); // expected-error {{ no matching function for call to 'func' }} + func(B::B()); // expected-error {{use of undeclared identifier 'func'}} func(C::C()); A::A() + A::A(); B::B() + B::B(); diff --git a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3.cpp b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3.cpp index d0cb42da6e..2530f128a4 100644 --- a/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -template<class X, class Y, class Z> X f(Y,Z); +template<class X, class Y, class Z> X f(Y,Z); // expected-note {{candidate function}} void g() { f<int,char*,double>("aa",3.0); diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp index d03e346d6f..bcfb71c987 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp @@ -15,7 +15,7 @@ void test_f1(int *ip, float fv) { f1(ip, fv); } -template<typename T> void f2(T*, T*); +template<typename T> void f2(T*, T*); // expected-note 2 {{candidate function}} struct ConvToIntPtr { operator int*() const; diff --git a/test/SemaCXX/conversion-function.cpp b/test/SemaCXX/conversion-function.cpp index d74bd2e391..997e19c388 100644 --- a/test/SemaCXX/conversion-function.cpp +++ b/test/SemaCXX/conversion-function.cpp @@ -9,7 +9,7 @@ public: } float g() { - return operator float(); // expected-error{{no matching function for call to 'operator float'}} + return operator float(); // expected-error{{use of undeclared 'operator float'}} } }; diff --git a/test/SemaCXX/decltype-crash.cpp b/test/SemaCXX/decltype-crash.cpp index fa98e751ff..f94ba453ff 100644 --- a/test/SemaCXX/decltype-crash.cpp +++ b/test/SemaCXX/decltype-crash.cpp @@ -3,5 +3,5 @@ int& a(); void f() { - decltype(a()) c; // expected-error {{no matching function for call to 'decltype'}} + decltype(a()) c; // expected-error {{use of undeclared identifier 'decltype'}} } diff --git a/test/SemaCXX/no-implicit-builtin-decls.cpp b/test/SemaCXX/no-implicit-builtin-decls.cpp index 18c603a06f..d82f7f18bf 100644 --- a/test/SemaCXX/no-implicit-builtin-decls.cpp +++ b/test/SemaCXX/no-implicit-builtin-decls.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s void f() { - void *p = malloc(sizeof(int) * 10); // expected-error{{no matching function for call to 'malloc'}} + void *p = malloc(sizeof(int) * 10); // expected-error{{use of undeclared identifier 'malloc'}} } int malloc(double); diff --git a/test/SemaCXX/type-dependent-exprs.cpp b/test/SemaCXX/type-dependent-exprs.cpp index ba11581fbb..abe1b4d730 100644 --- a/test/SemaCXX/type-dependent-exprs.cpp +++ b/test/SemaCXX/type-dependent-exprs.cpp @@ -19,6 +19,6 @@ T f(T x) { return g(x); h(x); // h is a dependent name g(1, 1); // expected-error{{no matching function for call}} - h(1); // expected-error{{no matching function for call to 'h'}} + h(1); // expected-error{{use of undeclared identifier 'h'}} return 0; } diff --git a/test/SemaTemplate/constructor-template.cpp b/test/SemaTemplate/constructor-template.cpp index 6fed9ed64c..68aaa646fb 100644 --- a/test/SemaTemplate/constructor-template.cpp +++ b/test/SemaTemplate/constructor-template.cpp @@ -1,11 +1,11 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s struct X0 { // expected-note{{candidate}} X0(int); // expected-note{{candidate}} - template<typename T> X0(T); - template<typename T, typename U> X0(T*, U*); + template<typename T> X0(T); // expected-note {{candidate}} + template<typename T, typename U> X0(T*, U*); // expected-note {{candidate}} // PR4761 - template<typename T> X0() : f0(T::foo) {} + template<typename T> X0() : f0(T::foo) {} // expected-note {{candidate}} int f0; }; diff --git a/test/SemaTemplate/dependent-names.cpp b/test/SemaTemplate/dependent-names.cpp index f73af595fd..fbe78e2703 100644 --- a/test/SemaTemplate/dependent-names.cpp +++ b/test/SemaTemplate/dependent-names.cpp @@ -84,3 +84,18 @@ namespace test0 { d2.test3(); // expected-note {{in instantiation of member function}} } } + +namespace test1 { + template <class T> struct Base { + void foo(T); // expected-note {{must qualify identifier to find this declaration in dependent base class}} + }; + + template <class T> struct Derived : Base<T> { + void doFoo(T v) { + // FIXME: the second error here is from a broken recovery attempt + foo(v); // expected-error {{use of undeclared identifier}} expected-error {{call to non-static member function without an object}} + } + }; + + template struct Derived<int>; // expected-note {{requested here}} +} diff --git a/test/SemaTemplate/instantiate-call.cpp b/test/SemaTemplate/instantiate-call.cpp index a40364a147..ad06be33aa 100644 --- a/test/SemaTemplate/instantiate-call.cpp +++ b/test/SemaTemplate/instantiate-call.cpp @@ -24,7 +24,7 @@ namespace N3 { template<typename T, typename Result> struct call_f0 { void test_f0(T t) { - Result &result = f0(t); // expected-error 2{{no matching}} + Result &result = f0(t); // expected-error 2{{undeclared identifier}} } }; } diff --git a/test/SemaTemplate/recursive-template-instantiation.cpp b/test/SemaTemplate/recursive-template-instantiation.cpp index e86196cbb5..0ddedaf235 100644 --- a/test/SemaTemplate/recursive-template-instantiation.cpp +++ b/test/SemaTemplate/recursive-template-instantiation.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -template<typename T> void f(T* t) { +template<typename T> void f(T* t) { // expected-note{{candidate function}} f(*t); // expected-error{{no matching function}}\ // expected-note 3{{requested here}} } |