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 b5c48105bb5..155bb20913a 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 @@ -30,6 +30,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration; import org.eclipse.cdt.core.index.IIndexFileSet; import org.eclipse.cdt.core.parser.util.CharArrayObjectMap; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; /** * Models the scope represented by an unknown type (e.g.: typeof(template type parameter)). @@ -147,14 +148,14 @@ public class CPPUnknownTypeScope implements ICPPInternalUnknownScope { @Override public final IBinding[] getBindings(ScopeLookupData lookup) { if (lookup.isPrefixLookup()) { - if (fScopeType instanceof ICPPDeferredClassInstance) { - ICPPDeferredClassInstance instance = (ICPPDeferredClassInstance) fScopeType; - IScope scope = instance.getClassTemplate().getCompositeScope(); - if (scope != null) { - return scope.getBindings(lookup); - } - } - return IBinding.EMPTY_BINDING_ARRAY; + // If name lookup is performed for the purpose of code completion in a dependent context, + // try to give some useful results heuristically. + IScope scope = CPPSemantics.heuristicallyFindConcreteScopeForType(fScopeType, + lookup.getLookupPoint()); + if (scope != null) { + return scope.getBindings(lookup); + } + return IBinding.EMPTY_BINDING_ARRAY; } IASTName lookupName= lookup.getLookupName(); if (lookupName != null) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java index 5e68ab6bd5e..1d7f79cf64d 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/semantics/AccessContext.java @@ -22,7 +22,6 @@ import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplateInstance; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope; -import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplatePartialSpecialization; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; @@ -278,9 +277,9 @@ public class AccessContext { if (scopeType instanceof ICPPDeferredClassInstance) { return ((ICPPDeferredClassInstance) scopeType).getClassTemplate(); } - } else { - scope = CPPSemantics.getParentScope(scope, data.getTranslationUnit()); } + + scope = CPPSemantics.getParentScope(scope, data.getTranslationUnit()); } if (scope instanceof ICPPClassScope) { return ((ICPPClassScope) scope).getClassType(); 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 40951dc337b..ea25c117c41 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 @@ -83,6 +83,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTypeId; import org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression; import org.eclipse.cdt.core.dom.ast.IASTTypeIdInitializerExpression; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; +import org.eclipse.cdt.core.dom.ast.IField; import org.eclipse.cdt.core.dom.ast.IBasicType.Kind; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.ICompositeType; @@ -3951,4 +3952,48 @@ public class CPPSemantics { return binding; } + + /** + * Given a dependent type, heuristically try to find a concrete scope (i.e. not an unknown scope) for it. + * @param point the point of instantiation for name lookups + */ + public static IScope heuristicallyFindConcreteScopeForType(IType type, IASTNode point) { + if (type instanceof ICPPDeferredClassInstance) { + // If this scope is for a deferred-class-instance, use the scope of the primary template. + ICPPDeferredClassInstance instance = (ICPPDeferredClassInstance) type; + return instance.getClassTemplate().getCompositeScope(); + } else if (type instanceof TypeOfDependentExpression) { + // If this scope is for the id-expression of a field reference, and the field owner + // is a deferred-class-instance, look up the field in the scope of the primary template, + // and use the scope of the resulting field type. + ICPPEvaluation evaluation = ((TypeOfDependentExpression) type).getEvaluation(); + if (evaluation instanceof EvalID) { + EvalID evalId = (EvalID) evaluation; + ICPPEvaluation fieldOwner = evalId.getFieldOwner(); + if (fieldOwner != null) { + IType fieldOwnerType = fieldOwner.getTypeOrFunctionSet(point); + if (fieldOwnerType instanceof ICPPDeferredClassInstance) { + ICPPDeferredClassInstance instance = (ICPPDeferredClassInstance) fieldOwnerType; + IScope scope = instance.getClassTemplate().getCompositeScope(); + LookupData lookup = new LookupData(evalId.getName(), evalId.getTemplateArgs(), point); + lookup.qualified = evalId.isQualified(); + try { + CPPSemantics.lookup(lookup, scope); + } catch (DOMException e) { + return null; + } + IBinding[] bindings = lookup.getFoundBindings(); + if (bindings.length == 1 && bindings[0] instanceof IField) { + IType fieldType = ((IField) bindings[0]).getType(); + if (fieldType instanceof ICompositeType) { + return ((ICompositeType) fieldType).getCompositeScope(); + } + } + } + } + } + } + // TODO(nathanridge): Handle more cases. + return null; + } } diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java index ca392da5c0e..451a7441b8c 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/contentassist2/CompletionTests.java @@ -1668,4 +1668,22 @@ public class CompletionTests extends AbstractContentAssistTest { final String[] expectedDisplay = { "other_tpl>" }; assertContentAssistResults(fCursorOffset, expectedDisplay, true, DISPLAY); } + + // struct A { + // void foo(); + // }; + // + // template + // struct B { + // A val; + // }; + // + // template + // void test(B b) { + // b.val./*cursor*/ + // } + public void testFieldOfDeferredClassInstance_Bug402617() throws Exception { + final String[] expected = { "A", "foo(void)" }; + assertContentAssistResults(fCursorOffset, expected, true, ID); + } }