[llvm-branch-commits] [clang] c4fb772 - PR48339: Improve diagnostics for invalid dependent unqualified function calls.
Richard Smith via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Dec 2 18:00:16 PST 2020
Author: Richard Smith
Date: 2020-12-02T17:54:55-08:00
New Revision: c4fb7720ceb30f25c38d994fb375e4d1978de144
URL: https://github.com/llvm/llvm-project/commit/c4fb7720ceb30f25c38d994fb375e4d1978de144
DIFF: https://github.com/llvm/llvm-project/commit/c4fb7720ceb30f25c38d994fb375e4d1978de144.diff
LOG: PR48339: Improve diagnostics for invalid dependent unqualified function calls.
Fix bogus diagnostics that would get confused and think a "no viable
fuctions" case was an "undeclared identifiers" case, resulting in an
incorrect diagnostic preceding the correct one. Use overload resolution
to determine which function we should select when we can find call
candidates from a dependent base class. Make the diagnostics for a call
that could call a function from a dependent base class more specific,
and use a different diagnostic message for the case where the call
target is instead declared later in the same class. Plus some minor
diagnostic wording improvements.
Added:
Modified:
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaOverload.cpp
clang/test/CXX/drs/dr2xx.cpp
clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp
clang/test/SemaTemplate/cxx1z-using-declaration.cpp
clang/test/SemaTemplate/dependent-names.cpp
clang/test/SemaTemplate/dependent-typos-recovery.cpp
clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp
clang/test/SemaTemplate/recovery-crash.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3067c077ddb2..44195cc9db45 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5204,11 +5204,17 @@ def ext_undeclared_unqual_id_with_dependent_base : ExtWarn<
"use of undeclared identifier %0; "
"unqualified lookup into dependent bases of class template %1 is a Microsoft extension">,
InGroup<MicrosoftTemplate>;
-def ext_found_via_dependent_bases_lookup : ExtWarn<"use of identifier %0 "
+def err_found_in_dependent_base : Error<
+ "explicit qualification required to use member %0 from dependent base class">;
+def ext_found_in_dependent_base : ExtWarn<"use of member %0 "
"found via unqualified lookup into dependent bases of class templates is a "
"Microsoft extension">, InGroup<MicrosoftTemplate>;
-def note_dependent_var_use : Note<"must qualify identifier to find this "
- "declaration in dependent base class">;
+def err_found_later_in_class : Error<"member %0 used before its declaration">;
+def ext_found_later_in_class : ExtWarn<
+ "use of member %0 before its declaration is a Microsoft extension">,
+ InGroup<MicrosoftTemplate>;
+def note_dependent_member_use : Note<
+ "must qualify identifier to find this declaration in dependent base class">;
def err_not_found_by_two_phase_lookup : Error<"call to function %0 that is neither "
"visible in the template definition nor found by argument-dependent lookup">;
def note_not_found_by_two_phase_lookup : Note<"%0 should be declared prior to the "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 775dd1793dea..8b4a18ab11d6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3676,6 +3676,9 @@ class Sema final {
ArrayRef<Expr *> Args,
OverloadCandidateSet &CandidateSet,
bool PartialOverloading = false);
+ void AddOverloadedCallCandidates(
+ LookupResult &R, TemplateArgumentListInfo *ExplicitTemplateArgs,
+ ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet);
// An enum used to represent the
diff erent possible results of building a
// range-based for loop.
@@ -4958,6 +4961,8 @@ class Sema final {
DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *&TemplateArgs);
+ bool DiagnoseDependentMemberLookup(LookupResult &R);
+
bool
DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
CorrectionCandidateCallback &CCC,
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 7da854f4fb9e..6da530c245fe 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -255,7 +255,7 @@ static ParsedType recoverFromTypeInKnownDependentBase(Sema &S,
// We found some types in dependent base classes. Recover as if the user
// wrote 'typename MyClass::II' instead of 'II'. We'll fully resolve the
// lookup during template instantiation.
- S.Diag(NameLoc, diag::ext_found_via_dependent_bases_lookup) << &II;
+ S.Diag(NameLoc, diag::ext_found_in_dependent_base) << &II;
ASTContext &Context = S.Context;
auto *NNS = NestedNameSpecifier::Create(Context, nullptr, false,
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 527605ac4fb8..3183b09402ca 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2134,6 +2134,73 @@ static void emitEmptyLookupTypoDiagnostic(
SemaRef.PDiag(NoteID));
}
+/// Diagnose a lookup that found results in an enclosing class during error
+/// recovery. This usually indicates that the results were found in a dependent
+/// base class that could not be searched as part of a template definition.
+/// Always issues a diagnostic (though this may be only a warning in MS
+/// compatibility mode).
+///
+/// Return \c true if the error is unrecoverable, or \c false if the caller
+/// should attempt to recover using these lookup results.
+bool Sema::DiagnoseDependentMemberLookup(LookupResult &R) {
+ // During a default argument instantiation the CurContext points
+ // to a CXXMethodDecl; but we can't apply a this-> fixit inside a
+ // function parameter list, hence add an explicit check.
+ bool isDefaultArgument =
+ !CodeSynthesisContexts.empty() &&
+ CodeSynthesisContexts.back().Kind ==
+ CodeSynthesisContext::DefaultFunctionArgumentInstantiation;
+ CXXMethodDecl *CurMethod = dyn_cast<CXXMethodDecl>(CurContext);
+ bool isInstance = CurMethod && CurMethod->isInstance() &&
+ R.getNamingClass() == CurMethod->getParent() &&
+ !isDefaultArgument;
+
+ // There are two ways we can find a class-scope declaration during template
+ // instantiation that we did not find in the template definition: if it is a
+ // member of a dependent base class, or if it is declared after the point of
+ // use in the same class. Distinguish these by comparing the class in which
+ // the member was found to the naming class of the lookup.
+ unsigned DiagID = diag::err_found_in_dependent_base;
+ unsigned NoteID = diag::note_member_declared_at;
+ if (R.getRepresentativeDecl()->getDeclContext()->Equals(R.getNamingClass())) {
+ DiagID = getLangOpts().MSVCCompat ? diag::ext_found_later_in_class
+ : diag::err_found_later_in_class;
+ } else if (getLangOpts().MSVCCompat) {
+ DiagID = diag::ext_found_in_dependent_base;
+ NoteID = diag::note_dependent_member_use;
+ }
+
+ if (isInstance) {
+ // Give a code modification hint to insert 'this->'.
+ Diag(R.getNameLoc(), DiagID)
+ << R.getLookupName()
+ << FixItHint::CreateInsertion(R.getNameLoc(), "this->");
+ CheckCXXThisCapture(R.getNameLoc());
+ } else {
+ // FIXME: Add a FixItHint to insert 'Base::' or 'Derived::' (assuming
+ // they're not shadowed).
+ Diag(R.getNameLoc(), DiagID) << R.getLookupName();
+ }
+
+ for (NamedDecl *D : R)
+ Diag(D->getLocation(), NoteID);
+
+ // Return true if we are inside a default argument instantiation
+ // and the found name refers to an instance member function, otherwise
+ // the caller will try to create an implicit member call and this is wrong
+ // for default arguments.
+ //
+ // FIXME: Is this special case necessary? We could allow the caller to
+ // diagnose this.
+ if (isDefaultArgument && ((*R.begin())->isCXXInstanceMember())) {
+ Diag(R.getNameLoc(), diag::err_member_call_without_object);
+ return true;
+ }
+
+ // Tell the callee to try to recover.
+ return false;
+}
+
/// Diagnose an empty lookup.
///
/// \return false if new lookup candidates were found
@@ -2165,46 +2232,20 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
// Don't give errors about ambiguities in this lookup.
R.suppressDiagnostics();
- // During a default argument instantiation the CurContext points
- // to a CXXMethodDecl; but we can't apply a this-> fixit inside a
- // function parameter list, hence add an explicit check.
- bool isDefaultArgument =
- !CodeSynthesisContexts.empty() &&
- CodeSynthesisContexts.back().Kind ==
- CodeSynthesisContext::DefaultFunctionArgumentInstantiation;
- CXXMethodDecl *CurMethod = dyn_cast<CXXMethodDecl>(CurContext);
- bool isInstance = CurMethod &&
- CurMethod->isInstance() &&
- DC == CurMethod->getParent() && !isDefaultArgument;
-
- // Give a code modification hint to insert 'this->'.
- // TODO: fixit for inserting 'Base<T>::' in the other cases.
- // Actually quite
diff icult!
- if (getLangOpts().MSVCCompat)
- diagnostic = diag::ext_found_via_dependent_bases_lookup;
- if (isInstance) {
- Diag(R.getNameLoc(), diagnostic) << Name
- << FixItHint::CreateInsertion(R.getNameLoc(), "this->");
- CheckCXXThisCapture(R.getNameLoc());
- } else {
- Diag(R.getNameLoc(), diagnostic) << Name;
- }
-
- // Do we really want to note all of these?
- for (NamedDecl *D : R)
- Diag(D->getLocation(), diag::note_dependent_var_use);
-
- // Return true if we are inside a default argument instantiation
- // and the found name refers to an instance member function, otherwise
- // the function calling DiagnoseEmptyLookup will try to create an
- // implicit member call and this is wrong for default argument.
- if (isDefaultArgument && ((*R.begin())->isCXXInstanceMember())) {
- Diag(R.getNameLoc(), diag::err_member_call_without_object);
- return true;
+ // If there's a best viable function among the results, only mention
+ // that one in the notes.
+ OverloadCandidateSet Candidates(R.getNameLoc(),
+ OverloadCandidateSet::CSK_Normal);
+ AddOverloadedCallCandidates(R, ExplicitTemplateArgs, Args, Candidates);
+ OverloadCandidateSet::iterator Best;
+ if (Candidates.BestViableFunction(*this, R.getNameLoc(), Best) ==
+ OR_Success) {
+ R.clear();
+ R.addDecl(Best->FoundDecl.getDecl(), Best->FoundDecl.getAccess());
+ R.resolveKind();
}
- // Tell the callee to try to recover.
- return false;
+ return DiagnoseDependentMemberLookup(R);
}
R.clear();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 20a7bd08443d..8db31c5fef12 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -12750,6 +12750,16 @@ void Sema::AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE,
CandidateSet, PartialOverloading);
}
+/// Add the call candidates from the given set of lookup results to the given
+/// overload set. Non-function lookup results are ignored.
+void Sema::AddOverloadedCallCandidates(
+ LookupResult &R, TemplateArgumentListInfo *ExplicitTemplateArgs,
+ ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet) {
+ for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
+ AddOverloadedCallCandidate(*this, I.getPair(), ExplicitTemplateArgs, Args,
+ CandidateSet, false, /*KnownValid*/ false);
+}
+
/// Determine whether a declaration with the specified name could be moved into
/// a
diff erent namespace.
static bool canBeDeclaredInNamespace(const DeclarationName &Name) {
@@ -12769,13 +12779,11 @@ static bool canBeDeclaredInNamespace(const DeclarationName &Name) {
/// correctly implement two-stage name lookup.
///
/// Returns true if a viable candidate was found and a diagnostic was issued.
-static bool
-DiagnoseTwoPhaseLookup(Sema &SemaRef, SourceLocation FnLoc,
- const CXXScopeSpec &SS, LookupResult &R,
- OverloadCandidateSet::CandidateSetKind CSK,
- TemplateArgumentListInfo *ExplicitTemplateArgs,
- ArrayRef<Expr *> Args,
- bool *DoDiagnoseEmptyLookup = nullptr) {
+static bool DiagnoseTwoPhaseLookup(
+ Sema &SemaRef, SourceLocation FnLoc, const CXXScopeSpec &SS,
+ LookupResult &R, OverloadCandidateSet::CandidateSetKind CSK,
+ TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
+ CXXRecordDecl **FoundInClass = nullptr) {
if (!SemaRef.inTemplateInstantiation() || !SS.isEmpty())
return false;
@@ -12788,26 +12796,32 @@ DiagnoseTwoPhaseLookup(Sema &SemaRef, SourceLocation FnLoc,
if (!R.empty()) {
R.suppressDiagnostics();
- if (isa<CXXRecordDecl>(DC)) {
- // Don't diagnose names we find in classes; we get much better
- // diagnostics for these from DiagnoseEmptyLookup.
- R.clear();
- if (DoDiagnoseEmptyLookup)
- *DoDiagnoseEmptyLookup = true;
- return false;
- }
-
OverloadCandidateSet Candidates(FnLoc, CSK);
- for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I)
- AddOverloadedCallCandidate(SemaRef, I.getPair(),
- ExplicitTemplateArgs, Args,
- Candidates, false, /*KnownValid*/ false);
+ SemaRef.AddOverloadedCallCandidates(R, ExplicitTemplateArgs, Args,
+ Candidates);
OverloadCandidateSet::iterator Best;
- if (Candidates.BestViableFunction(SemaRef, FnLoc, Best) != OR_Success) {
- // No viable functions. Don't bother the user with notes for functions
- // which don't work and shouldn't be found anyway.
- R.clear();
+ OverloadingResult OR =
+ Candidates.BestViableFunction(SemaRef, FnLoc, Best);
+
+ if (auto *RD = dyn_cast<CXXRecordDecl>(DC)) {
+ // We either found non-function declarations or a best viable function
+ // at class scope. A class-scope lookup result disables ADL. Don't
+ // look past this, but let the caller know that we found something that
+ // either is, or might be, usable in this class.
+ if (FoundInClass) {
+ *FoundInClass = RD;
+ if (OR == OR_Success) {
+ R.clear();
+ R.addDecl(Best->FoundDecl.getDecl(), Best->FoundDecl.getAccess());
+ R.resolveKind();
+ }
+ }
+ return false;
+ }
+
+ if (OR != OR_Success) {
+ // There wasn't a unique best function or function template.
return false;
}
@@ -12902,6 +12916,12 @@ class BuildRecoveryCallExprRAII {
}
/// Attempts to recover from a call where no functions were found.
+///
+/// This function will do one of three things:
+/// * Diagnose, recover, and return a recovery expression.
+/// * Diagnose, fail to recover, and return ExprError().
+/// * Do not diagnose, do not recover, and return ExprResult(). The caller is
+/// expected to diagnose as appropriate.
static ExprResult
BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
UnresolvedLookupExpr *ULE,
@@ -12914,9 +12934,8 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
//
// template <typename T> auto foo(T t) -> decltype(foo(t)) {}
// template <typename T> auto foo(T t) -> decltype(foo(&t)) {}
- //
if (SemaRef.IsBuildingRecoveryCallExpr)
- return ExprError();
+ return ExprResult();
BuildRecoveryCallExprRAII RCE(SemaRef);
CXXScopeSpec SS;
@@ -12932,10 +12951,14 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
LookupResult R(SemaRef, ULE->getName(), ULE->getNameLoc(),
Sema::LookupOrdinaryName);
- bool DoDiagnoseEmptyLookup = EmptyLookup;
- if (!DiagnoseTwoPhaseLookup(
- SemaRef, Fn->getExprLoc(), SS, R, OverloadCandidateSet::CSK_Normal,
- ExplicitTemplateArgs, Args, &DoDiagnoseEmptyLookup)) {
+ CXXRecordDecl *FoundInClass = nullptr;
+ if (DiagnoseTwoPhaseLookup(SemaRef, Fn->getExprLoc(), SS, R,
+ OverloadCandidateSet::CSK_Normal,
+ ExplicitTemplateArgs, Args, &FoundInClass)) {
+ // OK, diagnosed a two-phase lookup issue.
+ } else if (EmptyLookup) {
+ // Try to recover from an empty lookup with typo correction.
+ R.clear();
NoTypoCorrectionCCC NoTypoValidator{};
FunctionCallFilterCCC FunctionCallValidator(SemaRef, Args.size(),
ExplicitTemplateArgs != nullptr,
@@ -12944,12 +12967,24 @@ BuildRecoveryCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
AllowTypoCorrection
? static_cast<CorrectionCandidateCallback &>(FunctionCallValidator)
: static_cast<CorrectionCandidateCallback &>(NoTypoValidator);
- if (!DoDiagnoseEmptyLookup ||
- SemaRef.DiagnoseEmptyLookup(S, SS, R, Validator, ExplicitTemplateArgs,
+ if (SemaRef.DiagnoseEmptyLookup(S, SS, R, Validator, ExplicitTemplateArgs,
Args))
return ExprError();
+ } else if (FoundInClass && SemaRef.getLangOpts().MSVCCompat) {
+ // We found a usable declaration of the name in a dependent base of some
+ // enclosing class.
+ // FIXME: We should also explain why the candidates found by name lookup
+ // were not viable.
+ if (SemaRef.DiagnoseDependentMemberLookup(R))
+ return ExprError();
+ } else {
+ // We had viable candidates and couldn't recover; let the caller diagnose
+ // this.
+ return ExprResult();
}
+ // If we get here, we should have issued a diagnostic and formed a recovery
+ // lookup result.
assert(!R.empty() && "lookup results empty despite recovery");
// If recovery created an ambiguity, just bail out.
@@ -13110,11 +13145,6 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
OverloadCandidateSet::iterator *Best,
OverloadingResult OverloadResult,
bool AllowTypoCorrection) {
- if (CandidateSet->empty())
- return BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc, Args,
- RParenLoc, /*EmptyLookup=*/true,
- AllowTypoCorrection);
-
switch (OverloadResult) {
case OR_Success: {
FunctionDecl *FDecl = (*Best)->Function;
@@ -13132,9 +13162,9 @@ static ExprResult FinishOverloadedCallExpr(Sema &SemaRef, Scope *S, Expr *Fn,
// have meant to call.
ExprResult Recovery = BuildRecoveryCallExpr(SemaRef, S, Fn, ULE, LParenLoc,
Args, RParenLoc,
- /*EmptyLookup=*/false,
+ CandidateSet->empty(),
AllowTypoCorrection);
- if (!Recovery.isInvalid())
+ if (Recovery.isInvalid() || Recovery.isUsable())
return Recovery;
// If the user passes in a function that we can't take the address of, we
diff --git a/clang/test/CXX/drs/dr2xx.cpp b/clang/test/CXX/drs/dr2xx.cpp
index 8cf4a19d91e4..c02b7a8f3634 100644
--- a/clang/test/CXX/drs/dr2xx.cpp
+++ b/clang/test/CXX/drs/dr2xx.cpp
@@ -82,12 +82,12 @@ namespace dr213 { // dr213: yes
template <class T> struct A : T {
void h(T t) {
char &r1 = f(t);
- int &r2 = g(t); // expected-error {{undeclared}}
+ int &r2 = g(t); // expected-error {{explicit qualification required to use member 'g' from dependent base class}}
}
};
struct B {
int &f(B);
- int &g(B); // expected-note {{in dependent base class}}
+ int &g(B); // expected-note {{here}}
};
char &f(B);
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp
index 0e948ce00031..b59cc175a197 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.general/p3-0x.cpp
@@ -150,7 +150,7 @@ namespace rdar13473493 {
{
public:
template <typename... Args>
- auto operator()(Args&&... args) const -> decltype(wrapped(args...)) // expected-note{{candidate template ignored: substitution failure [with Args = <int>]: use of undeclared identifier 'wrapped'}}
+ auto operator()(Args&&... args) const -> decltype(wrapped(args...)) // expected-note{{candidate template ignored: substitution failure [with Args = <int>]: member 'wrapped' used before its declaration}}
{
return wrapped(args...);
}
diff --git a/clang/test/SemaTemplate/cxx1z-using-declaration.cpp b/clang/test/SemaTemplate/cxx1z-using-declaration.cpp
index ba5918ce0989..84e3bff15982 100644
--- a/clang/test/SemaTemplate/cxx1z-using-declaration.cpp
+++ b/clang/test/SemaTemplate/cxx1z-using-declaration.cpp
@@ -4,14 +4,14 @@
template<typename ...T> struct Unexpanded : T... {
using T::f; // expected-error {{unexpanded}}
using typename T::type; // expected-error {{unexpanded}}
- template<typename ...U> void g(U ...u) { f(u...); } // expected-error {{undeclared identifier 'f'}}
+ template<typename ...U> void g(U ...u) { f(u...); } // expected-error {{explicit qualification required to use member 'f' from dependent base class}}
void h() {
Unexpanded<type...> *p; // expected-error {{undeclared identifier 'type'}}
}
};
void test_Unexpanded() {
- struct A { void f(); }; // expected-note {{must qualify}}
- struct B { void f(int); }; // expected-note {{must qualify}}
+ struct A { void f(); };
+ struct B { void f(int); }; // expected-note {{here}}
Unexpanded<A, B>().g(0); // expected-note {{instantiation of}}
}
diff --git a/clang/test/SemaTemplate/dependent-names.cpp b/clang/test/SemaTemplate/dependent-names.cpp
index 689398cb2739..9d12c41e81bb 100644
--- a/clang/test/SemaTemplate/dependent-names.cpp
+++ b/clang/test/SemaTemplate/dependent-names.cpp
@@ -91,12 +91,12 @@ namespace test0 {
namespace test1 {
template <class T> struct Base {
- void foo(T); // expected-note {{must qualify identifier to find this declaration in dependent base class}}
+ void foo(T); // expected-note {{member is declared here}}
};
template <class T> struct Derived : Base<T> {
void doFoo(T v) {
- foo(v); // expected-error {{use of undeclared identifier}}
+ foo(v); // expected-error {{explicit qualification required to use member 'foo' from dependent base class}}
}
};
diff --git a/clang/test/SemaTemplate/dependent-typos-recovery.cpp b/clang/test/SemaTemplate/dependent-typos-recovery.cpp
index d05b7144d908..3f89ca1dbec9 100644
--- a/clang/test/SemaTemplate/dependent-typos-recovery.cpp
+++ b/clang/test/SemaTemplate/dependent-typos-recovery.cpp
@@ -8,3 +8,16 @@ struct B {
auto a = bilder.f<int>(); // expected-error{{undeclared identifier 'bilder'; did you mean}}
auto b = (*(&bilder+0)).f<int>(); // expected-error{{undeclared identifier 'bilder'; did you mean}}
+
+struct X {
+ struct type {};
+};
+
+namespace PR48339 {
+ struct S {
+ template <typename T> static void g(typename T::type) {} // expected-note {{couldn't infer template argument 'T'}}
+ template <typename T> void f() { g(typename T::type{}); } // expected-error {{no matching function for call to 'g'}}
+ };
+
+ void f() { S{}.f<X>(); } // expected-note {{in instantiation of}}
+}
diff --git a/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp
index 14e43868e83d..93b07d1cd06d 100644
--- a/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp
+++ b/clang/test/SemaTemplate/ms-lookup-template-base-classes.cpp
@@ -14,8 +14,8 @@ class B : public A<T> {
public:
void z(T a)
{
- f(a); // expected-warning 2{{use of identifier 'f' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
- g(); // expected-warning 2{{use of identifier 'g' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ f(a); // expected-warning 2{{use of member 'f' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ g(); // expected-warning 2{{use of member 'g' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
}
};
@@ -89,8 +89,8 @@ template <class T>
class B : public A<T> {
public:
static void z2(){
- static_func(); // expected-warning {{use of identifier 'static_func' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
- func(); // expected-warning {{use of identifier 'func' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-error {{call to non-static member function without an object argument}}
+ static_func(); // expected-warning {{use of member 'static_func' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ func(); // expected-warning {{use of member 'func' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-error {{call to non-static member function without an object argument}}
}
};
template class B<int>; // expected-note {{requested here}}
@@ -111,8 +111,8 @@ class A {
template<class T>
class B : public A<T> {
public:
- void g1(int p = f1());// expected-warning {{use of identifier 'f1' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
- void g2(int p = f2());// expected-warning {{use of identifier 'f2' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-error {{call to non-static member function without an object argument}}
+ void g1(int p = f1());// expected-warning {{use of member 'f1' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ void g2(int p = f2());// expected-warning {{use of member 'f2' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-error {{call to non-static member function without an object argument}}
};
void foo()
@@ -137,7 +137,7 @@ template <class T>
class A : public B<T> {
public:
friend void foo(A<T> p){
- g(); // expected-warning {{use of identifier 'g' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ g(); // expected-warning {{use of member 'g' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
}
};
@@ -186,7 +186,7 @@ class Container : public Base<T> {
public:
template <typename S>
bool operator=(const Container<S>& rhs) {
- return base_fun(rhs); // expected-warning {{use of identifier 'base_fun' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ return base_fun(rhs); // expected-warning {{use of member 'base_fun' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
}
};
@@ -307,7 +307,7 @@ namespace two_types_in_base {
template <typename T> struct A { typedef T NameFromBase; }; // expected-note {{member type 'int' found by ambiguous name lookup}}
template <typename T> struct B { struct NameFromBase { T m; }; }; // expected-note {{member type 'two_types_in_base::B<int>::NameFromBase' found by ambiguous name lookup}}
template <typename T> struct C : A<T>, B<T> {
- NameFromBase m; // expected-error {{member 'NameFromBase' found in multiple base classes of
diff erent types}} expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ NameFromBase m; // expected-error {{member 'NameFromBase' found in multiple base classes of
diff erent types}} expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
};
static_assert(sizeof(C<int>) != 0, ""); // expected-note {{in instantiation of template class 'two_types_in_base::C<int>' requested here}}
}
@@ -387,7 +387,7 @@ struct A { typedef int NameFromBase; };
template <typename T>
struct B : A {};
template <typename T>
-struct C : B<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+struct C : B<T> { NameFromBase m; }; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
}
namespace type_in_second_dependent_base {
@@ -396,7 +396,7 @@ struct A {};
template<typename T>
struct B { typedef T NameFromBase; };
template <typename T>
-struct D : A<T>, B<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+struct D : A<T>, B<T> { NameFromBase m; }; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
}
namespace type_in_second_non_dependent_base {
@@ -405,7 +405,7 @@ struct B { typedef int NameFromBase; };
template<typename T>
struct C : A, B {};
template <typename T>
-struct D : C<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+struct D : C<T> { NameFromBase m; }; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
}
namespace type_in_virtual_base_of_dependent_base {
@@ -414,7 +414,7 @@ struct A { typedef T NameFromBase; };
template <typename T>
struct B : virtual A<T> {};
template <typename T>
-struct C : B<T>, virtual A<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+struct C : B<T>, virtual A<T> { NameFromBase m; }; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
C<int> c;
}
@@ -424,7 +424,7 @@ struct A { typedef T NameFromBase; };
template <typename T>
struct B : public A<T> {};
template <typename T>
-struct C : B<T>, public A<T> { NameFromBase m; }; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-warning {{direct base 'A<int>' is inaccessible due to ambiguity:}}
+struct C : B<T>, public A<T> { NameFromBase m; }; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}} expected-warning {{direct base 'A<int>' is inaccessible due to ambiguity:}}
C<int> c; // expected-note {{in instantiation of template class 'type_in_base_of_multiple_dependent_bases::C<int>' requested here}}
}
@@ -434,14 +434,14 @@ template<typename T> struct B : A<T> {
struct C;
template<typename TT>
struct D : C {
- NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ NameFromBase m; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
};
struct E : C {
- NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ NameFromBase m; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
};
};
template<typename T> struct B<T>::C : B {
- NameFromBase m; // expected-warning {{use of identifier 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
+ NameFromBase m; // expected-warning {{use of member 'NameFromBase' found via unqualified lookup into dependent bases of class templates is a Microsoft extension}}
};
template<typename T> struct F : B<T>::C {
NameFromBase m; // expected-error {{unknown type name 'NameFromBase'}}
diff --git a/clang/test/SemaTemplate/recovery-crash.cpp b/clang/test/SemaTemplate/recovery-crash.cpp
index ec44747ce5a0..a451d9d108da 100644
--- a/clang/test/SemaTemplate/recovery-crash.cpp
+++ b/clang/test/SemaTemplate/recovery-crash.cpp
@@ -5,12 +5,12 @@
// Clang used to crash trying to recover while adding 'this->' before Work(x);
template <typename> struct A {
- static void Work(int); // expected-note{{must qualify identifier}}
+ static void Work(int); // expected-note{{here}}
};
template <typename T> struct B : public A<T> {
template <typename T2> B(T2 x) {
- Work(x); // expected-error{{use of undeclared identifier}}
+ Work(x); // expected-error{{explicit qualification required}}
}
};
@@ -58,10 +58,10 @@ namespace test1 {
}
template <class T>
void NonTemplateClass::MemberFuncTemplate(ArraySlice<T> resource_data, int) {
- // expected-error at +1 {{use of undeclared identifier 'UndeclaredMethod'}}
+ // expected-error at +1 {{member 'UndeclaredMethod' used before its declaration}}
UndeclaredMethod(resource_data);
}
// expected-error at +2 {{out-of-line definition of 'UndeclaredMethod' does not match any declaration}}
- // expected-note at +1 {{must qualify identifier to find this declaration in dependent base class}}
+ // expected-note at +1 {{member is declared here}}
void NonTemplateClass::UndeclaredMethod() {}
}
More information about the llvm-branch-commits
mailing list