Don't consider 'LinkageSpec' when calculating DeclContext 'Encloses'

We don't properly handle lookup through using directives when there is
a linkage spec in the common chain.  This is because `CppLookupName` and
`CppNamespaceLookup` end up skipping `LinkageSpec`'s (correctly, as they
are not lookup scopes), but the `UnqualUsingDirectiveSet` does not.

I discovered that when we are calculating the `CommonAncestor` for a
using-directive, we were coming up with the `LinkageSpec`, instead of
the `LinkageSpec`'s parent.  Then, when we use
`UnqualUsingDirectiveSet::getNamespacesFor` a scope, we don't end up
finding any that were in the `LinkageSpec` (again, since `CppLookupName`
skips linkage specs), so those don't end up participating in the lookup.

The function `UnqualUsingDirectiveSet::addUsingDirective` calculates
this common ancestor via a loop through the the `DeclSpec::Encloses`
function.

Changing this Encloses function to believe that a `LinkageSpec`
`Encloses` nothing ends up fixing the problem without breaking any other tests,
so I opted to do that.  A less aggressive patch could perhaps change only
the `addUsingDirective`, but my examination of all uses of `Encloses`
showed that it seems to be used exclusively in lookup, which makes me think
this is correct everywhere.

Differential Revision: https://reviews.llvm.org/D113709
This commit is contained in:
Erich Keane 2021-11-11 12:55:45 -08:00
parent 5e96553608
commit 90010c2e1d
2 changed files with 34 additions and 1 deletions

View File

@ -1212,7 +1212,7 @@ bool DeclContext::Encloses(const DeclContext *DC) const {
return getPrimaryContext()->Encloses(DC);
for (; DC; DC = DC->getParent())
if (DC->getPrimaryContext() == this)
if (!isa<LinkageSpecDecl>(DC) && DC->getPrimaryContext() == this)
return true;
return false;
}

View File

@ -0,0 +1,33 @@
// RUN: %clang_cc1 %s -verify
// expected-no-diagnostics
extern "C++" {
namespace A {
namespace B {
int bar;
}
} // namespace A
namespace C {
void foo() {
using namespace A;
(void)B::bar;
}
} // namespace C
}
extern "C" {
extern "C++" {
namespace D {
namespace E {
int bar;
}
} // namespace A
namespace F {
void foo() {
using namespace D;
(void)E::bar;
}
} // namespace C
}
}