diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java index 59becb789f7..d3cd6746235 100644 --- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java +++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/core/parser/tests/ast2/AST2CPPTests.java @@ -10586,6 +10586,20 @@ public class AST2CPPTests extends AST2TestBase { public void testFriendConstructorDestructor_400940() throws Exception { parseAndCheckBindings(); } + + // namespace Hugo { + // class C { + // friend class Waldo; + // }; + // } + // using namespace Hugo; + // class Waldo {}; + // void foo() { + // Waldo c; // error here + // } + public void testFriendClassLookup_512932() throws Exception { + parseAndCheckBindings(); + } // struct S { // virtual void mFuncDecl() final; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IScope.java index 458a148b189..5f7324e6cb8 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IScope.java @@ -114,6 +114,7 @@ public interface IScope { private boolean fResolve= true; private boolean fPrefixLookup; private boolean fIgnorePointOfDeclaration; + private boolean fArgumentDependent; public ScopeLookupData(IASTName name, boolean resolve, boolean prefixLookup) { if (name == null) @@ -162,6 +163,11 @@ public interface IScope { public final void setIgnorePointOfDeclaration(boolean ignorePointOfDeclaration) { fIgnorePointOfDeclaration = ignorePointOfDeclaration; } + + /** @since 6.3 */ + public final void setArgumentDependent(boolean argumentDependent) { + fArgumentDependent = argumentDependent; + } public final void setLookupKey(char[] key) { fLookupKey= key; @@ -187,6 +193,11 @@ public interface IScope { return fIgnorePointOfDeclaration; } + /** @since 6.3 */ + public final boolean isArgumentDependent() { + return fArgumentDependent; + } + public final IIndexFileSet getIncludedFiles() { return fTu == null ? IIndexFileSet.EMPTY : fTu.getIndexFileSet(); } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTInternal.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTInternal.java index a59a0152ce7..2895ce1c154 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTInternal.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ASTInternal.java @@ -91,8 +91,12 @@ public class ASTInternal { } public static void addName(IScope scope, IASTName name) { + addName(scope, name, /* adlOnly = */ false); + } + + public static void addName(IScope scope, IASTName name, boolean adlOnly) { if (scope instanceof IASTInternalScope) { - ((IASTInternalScope) scope).addName(name); + ((IASTInternalScope) scope).addName(name, adlOnly); } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/IASTInternalScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/IASTInternalScope.java index e8ff4c19d51..b22e5d9211d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/IASTInternalScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/IASTInternalScope.java @@ -31,9 +31,13 @@ public interface IASTInternalScope extends IScope { public void addBinding(IBinding binding); /** - * Adds an IASTName to be cached in this scope + * Adds an IASTName to be cached in this scope. + * @param adlOnly whether this declaration of this name only makes the name visible to + * argument-dependent lookup + * + * Implementation note: only CPPNamespaceScope cares about "adlOnly". */ - public void addName(IASTName name); + public void addName(IASTName name, boolean adlOnly); /** * Can be called during ambiguity resolution to populate a scope without considering diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java index 5c875bb8f45..a6e3c032c09 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/ProblemBinding.java @@ -192,7 +192,7 @@ public class ProblemBinding extends PlatformObject implements IProblemBinding, I } @Override - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CScope.java index faa972f7b85..9635347ccde 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/c/CScope.java @@ -194,7 +194,7 @@ public class CScope implements ICScope, IASTInternalScope { } @Override - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { final char[] nchars = name.toCharArray(); if (nchars.length == 0) return; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassScope.java index 53e47990dc8..e2f04b05142 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassScope.java @@ -212,7 +212,7 @@ public class CPPClassScope extends CPPScope implements ICPPClassScope { } @Override - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { // Don't add names from inactive code branches. if (!name.isActive()) return; @@ -251,7 +251,7 @@ public class CPPClassScope extends CPPScope implements ICPPClassScope { return; } } - super.addName(name); + super.addName(name, adlOnly); } @SuppressWarnings({ "rawtypes", "unchecked" }) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassSpecializationScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassSpecializationScope.java index 5fba88908de..068dd2a782e 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassSpecializationScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPClassSpecializationScope.java @@ -29,7 +29,7 @@ public class CPPClassSpecializationScope extends AbstractCPPClassSpecializationS // This scope does not cache its own names @Override - public void addName(IASTName name) {} + public void addName(IASTName name, boolean adlOnly) {} @Override public IASTNode getPhysicalNode() { return null; } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNamespaceScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNamespaceScope.java index a82e28c6b34..91612722509 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNamespaceScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPNamespaceScope.java @@ -40,6 +40,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDirective; import org.eclipse.cdt.core.index.IIndex; import org.eclipse.cdt.core.index.IIndexFileSet; import org.eclipse.cdt.core.index.IIndexName; +import org.eclipse.cdt.core.parser.util.CharArraySet; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.ASTInternal; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPScopeMapper.InlineNamespaceDirective; @@ -59,6 +60,9 @@ public class CPPNamespaceScope extends CPPScope implements ICPPInternalNamespace private ICPPNamespaceScope[] fEnclosingNamespaceSet; private List fInlineNamespaceDefinitions; private ICPPInternalNamespaceScope[] fInlineNamespaces; + + // The set of names declared in this scope that are currently only visible to argument-dependent lookup. + private CharArraySet fVisibleToAdlOnly = new CharArraySet(0); public CPPNamespaceScope(IASTNode physicalNode) { super(physicalNode); @@ -154,10 +158,23 @@ public class CPPNamespaceScope extends CPPScope implements ICPPInternalNamespace } @Override - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { if (name instanceof ICPPASTQualifiedName && !canDenoteNamespaceMember((ICPPASTQualifiedName) name)) return; - super.addName(name); + super.addName(name, adlOnly); + if (adlOnly) { + fVisibleToAdlOnly.put(name.getLookupKey()); + } else { + fVisibleToAdlOnly.remove(name.getLookupKey()); + } + } + + @Override + protected boolean nameIsVisibleToLookup(ScopeLookupData lookup) { + if (lookup.isArgumentDependent()) { + return true; + } + return !fVisibleToAdlOnly.containsKey(lookup.getLookupKey()); } public boolean canDenoteNamespaceMember(ICPPASTQualifiedName name) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPScope.java index 0152fb0b17a..badcb044d2b 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPScope.java @@ -90,7 +90,7 @@ abstract public class CPPScope implements ICPPASTInternalScope { @Override @SuppressWarnings({ "unchecked" }) - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { // Don't add inactive names to the scope. if (!name.isActive()) return; @@ -224,11 +224,18 @@ abstract public class CPPScope implements ICPPASTInternalScope { return ArrayUtil.trim(IBinding.class, result); } + + protected boolean nameIsVisibleToLookup(ScopeLookupData lookup) { + return true; + } public IBinding[] getBindingsInAST(ScopeLookupData lookup) { populateCache(); final char[] c = lookup.getLookupKey(); IBinding[] result = IBinding.EMPTY_BINDING_ARRAY; + if (!nameIsVisibleToLookup(lookup)) { + return result; + } Object obj = null; if (lookup.isPrefixLookup()) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPUnknownTypeScope.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPUnknownTypeScope.java index c9696f4f204..e308094f3df 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPUnknownTypeScope.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPUnknownTypeScope.java @@ -178,7 +178,7 @@ public class CPPUnknownTypeScope implements ICPPInternalUnknownScope { } @Override - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { } protected IBinding getOrCreateBinding(final char[] name, int idx) { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java index 1a37a1e4e1e..0cba0c43894 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPSemantics.java @@ -631,6 +631,7 @@ public class CPPSemantics { // don't ascend into enclosing scopes. boolean originalQualified = data.qualified; data.qualified = true; + data.setArgumentDependent(true); Set friendFns = new HashSet<>(2); Set associated = getAssociatedScopes(data, friendFns); for (ICPPNamespaceScope scope : associated) { @@ -643,6 +644,7 @@ public class CPPSemantics { new NameMatcherPredicate(data.getLookupKey())).toArray(); mergeResults(data, matchingFriendFns, false); data.qualified = originalQualified; + data.setArgumentDependent(false); } private static class NameMatcherPredicate implements IUnaryPredicate { diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java index 013da7a4359..4b3672c0570 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/CPPVisitor.java @@ -550,7 +550,14 @@ public class CPPVisitor extends ASTQueries { binding = new CPPClassType(name, binding); } // Name may live in a different scope, so make sure to add it to the owner scope as well. - ASTInternal.addName(scope, elabType.getName()); + // [namespace.memdef] p3: + // "If a friend declaration in a non-local class first declares a + // class, function, class template or function template the friend + // is a member of the innermost enclosing namespace. The friend + // declaration does not by itself make the name visible to + // unqualified lookup or qualified lookup." + boolean visibleToAdlOnly = isFriend; + ASTInternal.addName(scope, elabType.getName(), visibleToAdlOnly); } } catch (DOMException e) { binding = e.getProblem(); diff --git a/lrparser/org.eclipse.cdt.core.lrparser/old/org/eclipse/cdt/internal/core/dom/lrparser/c99/bindings/C99Scope.java b/lrparser/org.eclipse.cdt.core.lrparser/old/org/eclipse/cdt/internal/core/dom/lrparser/c99/bindings/C99Scope.java index 33eb1b9f545..5c550591085 100644 --- a/lrparser/org.eclipse.cdt.core.lrparser/old/org/eclipse/cdt/internal/core/dom/lrparser/c99/bindings/C99Scope.java +++ b/lrparser/org.eclipse.cdt.core.lrparser/old/org/eclipse/cdt/internal/core/dom/lrparser/c99/bindings/C99Scope.java @@ -96,7 +96,7 @@ public class C99Scope implements IC99Scope, IASTInternalScope { } @Override - public void addName(IASTName name) { + public void addName(IASTName name, boolean adlOnly) { throw new UnsupportedOperationException(); }