summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn McCall <rjmccall@apple.com>2009-12-16 08:11:27 +0000
committerJohn McCall <rjmccall@apple.com>2009-12-16 08:11:27 +0000
commit578b69b186d9cba0a6ae1dd7f4c04cd6a49f0aac (patch)
treeede0c169beeed54c863512d74bae423fdb1b51b3
parente8e4a1c0bf2e3dd26dd0721a102307bbb589e96a (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.td4
-rw-r--r--lib/Sema/Sema.h2
-rw-r--r--lib/Sema/SemaExpr.cpp78
-rw-r--r--lib/Sema/SemaOverload.cpp50
-rw-r--r--test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp2
-rw-r--r--test/CXX/temp/temp.fct.spec/temp.arg.explicit/p3.cpp2
-rw-r--r--test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/basic.cpp2
-rw-r--r--test/SemaCXX/conversion-function.cpp2
-rw-r--r--test/SemaCXX/decltype-crash.cpp2
-rw-r--r--test/SemaCXX/no-implicit-builtin-decls.cpp2
-rw-r--r--test/SemaCXX/type-dependent-exprs.cpp2
-rw-r--r--test/SemaTemplate/constructor-template.cpp6
-rw-r--r--test/SemaTemplate/dependent-names.cpp15
-rw-r--r--test/SemaTemplate/instantiate-call.cpp2
-rw-r--r--test/SemaTemplate/recursive-template-instantiation.cpp2
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}}
}