From baf48166579ffb963a2f5eefc5f86c05b375ef51 Mon Sep 17 00:00:00 2001 From: Markus Schorn Date: Thu, 22 Jan 2009 13:56:25 +0000 Subject: [PATCH] Resolution of friends, bug 86368. --- .../core/parser/tests/ast2/AST2CPPTests.java | 68 +++++++++++++++++++ .../parser/cpp/CPPASTAmbiguityResolver.java | 2 +- .../parser/cpp/semantics/CPPSemantics.java | 66 ++++++++++++------ .../dom/parser/cpp/semantics/CPPVisitor.java | 11 ++- .../parser/cpp/semantics/FriendCollector.java | 67 ++++++++++++++++++ 5 files changed, 185 insertions(+), 29 deletions(-) create mode 100644 core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FriendCollector.java 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 cdd1a5a7ed6..5a9382e02e0 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 @@ -6433,4 +6433,72 @@ public class AST2CPPTests extends AST2BaseTest { ITypedef s2= (ITypedef) ((IPointerType) d.getType()).getType(); assertInstance(((IFunctionType) s2.getType()).getParameterTypes()[0], IBasicType.class); } + + // namespace A { + // class X { + // friend void f(int); + // class Y { + // friend void g(int); + // }; + // }; + // void test() { + // f(1); + // g(1); + // } + // } + public void testFriendFunctionResolution_86368_1() throws Exception { + final String code= getAboveComment(); + parseAndCheckBindings(code); + + BindingAssertionHelper bh= new BindingAssertionHelper(code, true); + IFunction f1= bh.assertNonProblem("f(int)", 1); + IFunction f2= bh.assertNonProblem("f(1)", 1); + assertSame(f1, f2); + IFunction g1= bh.assertNonProblem("g(int)", 1); + IFunction g2= bh.assertNonProblem("g(1)", 1); + assertSame(g1, g2); + + bh= new BindingAssertionHelper(code, true); + f2= bh.assertNonProblem("f(1)", 1); + f1= bh.assertNonProblem("f(int)", 1); + assertSame(f1, f2); + g2= bh.assertNonProblem("g(1)", 1); + g1= bh.assertNonProblem("g(int)", 1); + assertSame(g1, g2); + } + + // namespace A { + // void f(int); + // } + // using A::f; + // namespace A { + // void f(char); // openReferences fails + // } + // void foo() { + // f('i'); + // } + // void bar() { + // using A::f; + // f('c'); + // } + public void testFriendFunctionResolution_86368_2() throws Exception { + final String code= getAboveComment(); + parseAndCheckBindings(code); + + BindingAssertionHelper bh= new BindingAssertionHelper(code, true); + IFunction f1= bh.assertNonProblem("f(int)", 1); + IFunction f2= bh.assertNonProblem("f('i')", 1); + assertSame(f1, f2); + IFunction g1= bh.assertNonProblem("f(char)", 1); + IFunction g2= bh.assertNonProblem("f('c')", 1); + assertSame(g1, g2); + + bh= new BindingAssertionHelper(code, true); + f2= bh.assertNonProblem("f('i')", 1); + f1= bh.assertNonProblem("f(int)", 1); + assertSame(f1, f2); + g2= bh.assertNonProblem("f('c')", 1); + g1= bh.assertNonProblem("f(char)", 1); + assertSame(g1, g2); + } } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java index 780843abfd5..a66240f9c5e 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/CPPASTAmbiguityResolver.java @@ -166,7 +166,7 @@ final class CPPASTAmbiguityResolver extends ASTVisitor { private void repopulateScope(IASTDeclaration declaration) { IScope scope= CPPVisitor.getContainingScope(declaration); if (scope instanceof ICPPASTInternalScope) { - CPPSemantics.populateCache((ICPPASTInternalScope) scope, declaration, false); + CPPSemantics.populateCache((ICPPASTInternalScope) scope, declaration); } } 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 2c0487297e9..c57cfdc8295 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 @@ -25,6 +25,7 @@ import java.util.Set; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.EScopeKind; import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTCastExpression; @@ -52,6 +53,7 @@ import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; import org.eclipse.cdt.core.dom.ast.IASTReturnStatement; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTStatement; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTTypeId; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; @@ -1171,6 +1173,15 @@ public class CPPSemantics { } else if (parent instanceof ICPPASTTemplateDeclaration) { ICPPASTTemplateDeclaration template = (ICPPASTTemplateDeclaration) parent; nodes = template.getTemplateParameters(); + } else if (parent instanceof ICPPASTForStatement) { + ICPPASTForStatement forStatement = (ICPPASTForStatement) parent; + final IASTDeclaration conditionDeclaration = forStatement.getConditionDeclaration(); + IASTStatement initDeclaration= forStatement.getInitializerStatement(); + if (conditionDeclaration != null) { + nodes= new IASTNode[] {initDeclaration, conditionDeclaration}; + } else { + nodes= new IASTNode[] {initDeclaration}; + } } int idx = -1; @@ -1216,7 +1227,7 @@ public class CPPSemantics { } } } else { - populateCache(scope, item, (item == parent)); + populateCache(scope, item); } if (nodes != null && ++idx < nodes.length) { @@ -1269,7 +1280,7 @@ public class CPPSemantics { } } - public static void populateCache(ICPPASTInternalScope scope, IASTNode node, boolean checkAux) { + public static void populateCache(ICPPASTInternalScope scope, IASTNode node) { IASTDeclaration declaration = null; if (node instanceof ICPPASTTemplateDeclaration) { declaration = ((ICPPASTTemplateDeclaration)node).getDeclaration(); @@ -1279,18 +1290,7 @@ public class CPPSemantics { declaration = ((IASTDeclarationStatement)node).getDeclaration(); } else if (node instanceof ICPPASTCatchHandler) { declaration = ((ICPPASTCatchHandler)node).getDeclaration(); - } else if (node instanceof ICPPASTForStatement && checkAux) { - ICPPASTForStatement forStatement = (ICPPASTForStatement) node; - if (forStatement.getConditionDeclaration() == null) { - if (forStatement.getInitializerStatement() instanceof IASTDeclarationStatement) - declaration = ((IASTDeclarationStatement)forStatement.getInitializerStatement()).getDeclaration(); - } else { - if (forStatement.getInitializerStatement() instanceof IASTDeclarationStatement) { - populateCache(scope, forStatement.getInitializerStatement(), checkAux); - } - declaration = forStatement.getConditionDeclaration(); - } - } else if (node instanceof ICPPASTSwitchStatement) { + } else if (node instanceof ICPPASTSwitchStatement) { declaration = ((ICPPASTSwitchStatement)node).getControllerDeclaration(); } else if (node instanceof ICPPASTIfStatement) { declaration = ((ICPPASTIfStatement)node).getConditionDeclaration(); @@ -1323,7 +1323,19 @@ public class CPPSemantics { IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) simpleDeclaration.getDeclSpecifier(); IASTDeclarator[] declarators = simpleDeclaration.getDeclarators(); - if (!declSpec.isFriend()) { + IScope dtorScope= scope; + if (declSpec.isFriend()) { + // friends are added to an enclosing scope. They have to be added such that they are + // picked up when this scope is re-populated during ambiguity resolution, while the + // enclosing scope is left as it is. + try { + while (dtorScope.getKind() == EScopeKind.eClassType) + dtorScope= dtorScope.getParent(); + } catch (DOMException e) { + dtorScope= null; + } + } + if (dtorScope != null) { for (IASTDeclarator declarator : declarators) { IASTDeclarator innermost= null; while (declarator != null) { @@ -1336,28 +1348,38 @@ public class CPPSemantics { } if (innermost != null) { IASTName declaratorName = innermost.getName(); - ASTInternal.addName(scope, declaratorName); + ASTInternal.addName(dtorScope, declaratorName); } } } // declSpec - IASTName specName = null; if (declSpec instanceof IASTElaboratedTypeSpecifier) { - if (declarators.length == 0 || scope.getPhysicalNode() instanceof IASTTranslationUnit) + if (declarators.length == 0 || scope.getPhysicalNode() instanceof IASTTranslationUnit) { specName = ((IASTElaboratedTypeSpecifier)declSpec).getName(); + } } else if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { ICPPASTCompositeTypeSpecifier compSpec = (ICPPASTCompositeTypeSpecifier) declSpec; specName = compSpec.getName(); - // anonymous union? //GCC supports anonymous structs too - if (declarators.length == 0 && /*compSpec.getKey() == IASTCompositeTypeSpecifier.k_union &&*/ - specName.getLookupKey().length == 0) { + // anonymous union or struct (GCC supports anonymous structs too) + if (declarators.length == 0 && specName.getLookupKey().length == 0) { IASTDeclaration[] decls = compSpec.getMembers(); for (IASTDeclaration decl : decls) { - populateCache(scope, decl, checkAux); + populateCache(scope, decl); } + } else { + // collect friends enclosed in nested classes + switch (scope.getKind()) { + case eLocal: + case eGlobal: + case eNamespace: + compSpec.accept(new FriendCollector(scope)); + break; + default: + break; + } } } else if (declSpec instanceof IASTEnumerationSpecifier) { IASTEnumerationSpecifier enumeration = (IASTEnumerationSpecifier) declSpec; 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 74622d2a12a..ec5873df7d5 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 @@ -18,6 +18,7 @@ import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUti import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.EScopeKind; import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator; import org.eclipse.cdt.core.dom.ast.IASTArrayModifier; import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; @@ -568,9 +569,11 @@ public class CPPVisitor extends ASTQueries { if (parent instanceof IASTSimpleDeclaration && scope instanceof ICPPClassScope) { ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) ((IASTSimpleDeclaration) parent).getDeclSpecifier(); if (declSpec.isFriend()) { + isFriendDecl= true; try { - scope = (ICPPScope) getParentScope(scope, name.getTranslationUnit()); - isFriendDecl= true; + while (scope.getKind() == EScopeKind.eClassType) { + scope = (ICPPScope) getParentScope(scope, name.getTranslationUnit()); + } } catch (DOMException e1) { } } @@ -661,10 +664,6 @@ public class CPPVisitor extends ASTQueries { } else { binding = template ? (ICPPFunction) new CPPFunctionTemplate(name) : new CPPFunction((ICPPASTFunctionDeclarator) funcDeclarator); - // friend functions may be declared in a different scope than the owner scope - if (simpleDecl != null && ((ICPPASTDeclSpecifier) simpleDecl.getDeclSpecifier()).isFriend()) { - ASTInternal.addName(scope, name); - } } } else if (parent instanceof IASTSimpleDeclaration) { IType t1 = null, t2 = null; diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FriendCollector.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FriendCollector.java new file mode 100644 index 00000000000..49a5f5f94b0 --- /dev/null +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/FriendCollector.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2009 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; + +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier; +import org.eclipse.cdt.internal.core.dom.parser.ASTInternal; +import org.eclipse.cdt.internal.core.dom.parser.IASTAmbiguousDeclarator; +import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPASTInternalScope; + +/** + * Utility class to populate scope with friend declarations hidden in nested classes + */ +class FriendCollector extends ASTVisitor { + + private final ICPPASTInternalScope fScope; + + public FriendCollector(ICPPASTInternalScope scope) { + fScope= scope; + shouldVisitDeclarations= true; + } + + @Override + public int visit(IASTDeclaration declaration) { + if (declaration instanceof IASTSimpleDeclaration) { + IASTSimpleDeclaration simpleDeclaration = (IASTSimpleDeclaration) declaration; + ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) simpleDeclaration.getDeclSpecifier(); + if (declSpec.isFriend()) { + IASTDeclarator[] declarators= simpleDeclaration.getDeclarators(); + for (IASTDeclarator declarator : declarators) { + IASTDeclarator innermost= null; + while (declarator != null) { + if (declarator instanceof IASTAmbiguousDeclarator) { + innermost= null; + break; + } + innermost= declarator; + declarator= declarator.getNestedDeclarator(); + } + if (innermost != null) { + IASTName declaratorName = innermost.getName(); + ASTInternal.addName(fScope, declaratorName); + } + } + return PROCESS_SKIP; + } + + if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { + return PROCESS_CONTINUE; + } + } + return PROCESS_SKIP; + } +}