llvm-project/clang/test/SemaCXX/using-hiding.cpp
John Brawn 9e11a6d8fd [Sema] Fix handling of functions that hide classes
When a function is declared in the same scope as a class with the same
name then the function hides that class. Currently this is done by a
single check after the main loop in LookupResult::resolveKind, but
this can give the wrong result when we have a using declaration in
multiple namespace scopes in two different ways:

 * When the using declaration is hidden in one namespace but not the
   other we can end up considering only the hidden one when deciding
   if the result is ambiguous, causing an incorrect "not ambiguous"
   result.

 * When two classes with the same name in different namespace scopes
   are both hidden by using declarations this can result in
   incorrectly deciding the result is ambiguous. There's currently a
   comment saying this is expected, but I don't think that's correct.

Solve this by checking each Decl to see if it's hidden by some other
Decl in the same scope. This means we have to delay removing anything
from Decls until after the main loop, in case a Decl is hidden by
another that is removed due to being non-unique.

Differential Revision: https://reviews.llvm.org/D154503
2023-08-18 12:39:37 +01:00

374 lines
7.0 KiB
C++

// RUN: %clang_cc1 -fsyntax-only -verify %s
namespace A {
class X { }; // expected-note{{candidate found by name lookup is 'A::X'}}
// expected-note@-1{{candidate found by name lookup is 'A::X'}}
}
namespace B {
void X(int); // expected-note{{candidate found by name lookup is 'B::X'}}
// expected-note@-1{{candidate found by name lookup is 'B::X'}}
}
// Using directive doesn't cause A::X to be hidden, so X is ambiguous.
namespace Test1a {
using namespace A;
using namespace B;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test1b {
using namespace B;
using namespace A;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
// The behaviour here should be the same as using namespaces A and B directly
namespace Test2a {
namespace C {
using A::X; // expected-note{{candidate found by name lookup is 'Test2a::C::X'}}
}
namespace D {
using B::X; // expected-note{{candidate found by name lookup is 'Test2a::D::X'}}
}
using namespace C;
using namespace D;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test2b {
namespace C {
using A::X; // expected-note{{candidate found by name lookup is 'Test2b::C::X'}}
}
namespace D {
using B::X; // expected-note{{candidate found by name lookup is 'Test2b::D::X'}}
}
using namespace D;
using namespace C;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
// Defining a function X inside C should hide using A::X in C but not D, so the result is ambiguous.
namespace Test3a {
namespace C {
using A::X;
void X(int); // expected-note{{candidate found by name lookup is 'Test3a::C::X'}}
}
namespace D {
using A::X; // expected-note{{candidate found by name lookup is 'Test3a::D::X'}}
}
using namespace C;
using namespace D;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test3b {
namespace C {
using A::X;
void X(int); // expected-note{{candidate found by name lookup is 'Test3b::C::X'}}
}
namespace D {
using A::X; // expected-note{{candidate found by name lookup is 'Test3b::D::X'}}
}
using namespace D;
using namespace C;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test3c {
namespace C {
void X(int); // expected-note{{candidate found by name lookup is 'Test3c::C::X'}}
using A::X;
}
namespace D {
using A::X; // expected-note{{candidate found by name lookup is 'Test3c::D::X'}}
}
using namespace C;
using namespace D;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test3d {
namespace C {
void X(int); // expected-note{{candidate found by name lookup is 'Test3d::C::X'}}
using A::X;
}
namespace D {
using A::X; // expected-note{{candidate found by name lookup is 'Test3d::D::X'}}
}
using namespace D;
using namespace C;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
// A::X hidden in both C and D by overloaded function, so the result is not ambiguous.
namespace Test4a {
namespace C {
using A::X;
void X(int);
}
namespace D {
using A::X;
void X(int, int);
}
using namespace C;
using namespace D;
void f() {
X(1);
}
}
namespace Test4b {
namespace C {
using A::X;
void X(int);
}
namespace D {
using A::X;
void X(int, int);
}
using namespace D;
using namespace C;
void f() {
X(1);
}
}
namespace Test4c {
namespace C {
void X(int);
using A::X;
}
namespace D {
void X(int, int);
using A::X;
}
using namespace C;
using namespace D;
void f() {
X(1);
}
}
namespace Test4d {
namespace C {
void X(int);
using A::X;
}
namespace D {
void X(int, int);
using A::X;
}
using namespace D;
using namespace C;
void f() {
X(1);
}
}
// B::X hides class X in C, so the the result is not ambiguous
namespace Test5a {
namespace C {
using B::X;
class X { };
}
namespace D {
using B::X;
}
using namespace C;
using namespace D;
void f() {
X(1);
}
}
namespace Test5b {
namespace C {
using B::X;
class X { };
}
namespace D {
using B::X;
}
using namespace D;
using namespace C;
void f() {
X(1);
}
}
namespace Test5c {
namespace C {
class X { };
using B::X;
}
namespace D {
using B::X;
}
using namespace C;
using namespace D;
void f() {
X(1);
}
}
namespace Test5d {
namespace C {
class X { };
using B::X;
}
namespace D {
using B::X;
}
using namespace D;
using namespace C;
void f() {
X(1);
}
}
// B::X hides class X declared in both C and D, so the result is not ambiguous.
namespace Test6a {
namespace C {
class X { };
using B::X;
}
namespace D {
class X { };
using B::X;
}
using namespace C;
using namespace D;
void f() {
X(1);
}
}
namespace Test6b {
namespace C {
class X { };
using B::X;
}
namespace D {
class X { };
using B::X;
}
using namespace D;
using namespace C;
void f() {
X(1);
}
}
namespace Test6c {
namespace C {
using B::X;
class X { };
}
namespace D {
using B::X;
class X { };
}
using namespace C;
using namespace D;
void f() {
X(1);
}
}
namespace Test6d {
namespace C {
using B::X;
class X { };
}
namespace D {
using B::X;
class X { };
}
using namespace D;
using namespace C;
void f() {
X(1);
}
}
// function X inside C should hide class X in C but not D.
namespace Test7a {
namespace C {
class X;
void X(int); // expected-note{{candidate found by name lookup is 'Test7a::C::X'}}
}
namespace D {
class X; // expected-note{{candidate found by name lookup is 'Test7a::D::X'}}
}
using namespace C;
using namespace D;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test7b {
namespace C {
class X;
void X(int); // expected-note{{candidate found by name lookup is 'Test7b::C::X'}}
}
namespace D {
class X; // expected-note{{candidate found by name lookup is 'Test7b::D::X'}}
}
using namespace D;
using namespace C;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test7c {
namespace C {
void X(int); // expected-note{{candidate found by name lookup is 'Test7c::C::X'}}
class X;
}
namespace D {
class X; // expected-note{{candidate found by name lookup is 'Test7c::D::X'}}
}
using namespace C;
using namespace D;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}
namespace Test7d {
namespace C {
void X(int); // expected-note{{candidate found by name lookup is 'Test7d::C::X'}}
class X;
}
namespace D {
class X; // expected-note{{candidate found by name lookup is 'Test7d::D::X'}}
}
using namespace D;
using namespace C;
void f() {
X(1); // expected-error{{reference to 'X' is ambiguous}}
}
}