зеркало из https://github.com/microsoft/clang.git
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
This commit is contained in:
Родитель
e8e4a1c0bf
Коммит
578b69b186
|
@ -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">,
|
||||
|
|
|
@ -1479,6 +1479,8 @@ public:
|
|||
bool HasTrailingLParen,
|
||||
bool IsAddressOfOperand);
|
||||
|
||||
bool DiagnoseEmptyLookup(const CXXScopeSpec &SS, LookupResult &R);
|
||||
|
||||
OwningExprResult LookupInObjCMethod(LookupResult &R,
|
||||
Scope *S,
|
||||
IdentifierInfo *II);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'}}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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'}}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче