From 301de3d40ea15dfc84a90c227db62514cd2dc578 Mon Sep 17 00:00:00 2001 From: Nathan Ridge Date: Sun, 5 Nov 2017 21:06:42 -0500 Subject: [PATCH] Bug 522010 - Completion of non-type template parameter in ambiguous template argument This works around the fact that the optimization introduced in bug 316704 inteferes with the mechanism for offering completions for both alternatives in an ambiguous context. Change-Id: Ibe14c1b4f2f9c9b3394d4635c87424a25fbd7a53 --- .../cdt/core/dom/ast/ASTCompletionNode.java | 25 +++++++++++-- .../cdt/core/dom/ast/IASTCompletionNode.java | 37 +++++++++++++++++++ .../dom/parser/cpp/GNUCPPSourceParser.java | 9 +++++ .../text/contentassist2/CompletionTests.java | 8 ++++ .../DOMCompletionProposalComputer.java | 16 ++++++-- .../lrparser/action/ASTCompletionNode.java | 27 +++++++++++--- 6 files changed, 111 insertions(+), 11 deletions(-) diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTCompletionNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTCompletionNode.java index 607379e3d8f..733ecf946bc 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTCompletionNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/ASTCompletionNode.java @@ -20,7 +20,7 @@ import org.eclipse.cdt.core.parser.IToken; */ public class ASTCompletionNode implements IASTCompletionNode { private final IToken completionToken; - private final List names = new ArrayList<>(); + private final List entries = new ArrayList<>(); private final IASTTranslationUnit translationUnit; public ASTCompletionNode(IToken completionToken, IASTTranslationUnit translationUnit) { @@ -29,7 +29,7 @@ public class ASTCompletionNode implements IASTCompletionNode { } public void addName(IASTName name) { - names.add(name); + entries.add(new CompletionNameEntry(name, name.getParent())); } @Override @@ -42,9 +42,28 @@ public class ASTCompletionNode implements IASTCompletionNode { return completionToken.getLength(); } + @Override + public boolean containsName(IASTName name) { + for (CompletionNameEntry entry : entries) { + if (entry.fName == name) { + return true; + } + } + return false; + } + @Override public IASTName[] getNames() { - return names.toArray(new IASTName[names.size()]); + IASTName[] names = new IASTName[entries.size()]; + for (int i = 0; i < entries.size(); ++i) { + names[i] = entries.get(i).fName; + } + return names; + } + + @Override + public CompletionNameEntry[] getEntries() { + return entries.toArray(new CompletionNameEntry[entries.size()]); } @Override diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTCompletionNode.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTCompletionNode.java index 237b49d38ee..8c9d983ae32 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTCompletionNode.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ast/IASTCompletionNode.java @@ -25,6 +25,25 @@ package org.eclipse.cdt.core.dom.ast; * @noimplement This interface is not intended to be implemented by clients. */ public interface IASTCompletionNode { + /** + * Represents a name that fits in this context, and its parent. + * The parent is stored separately because two entries can have + * the same name but different parents. (This is due to the + * parser sometimes re-using nodes between alternatives in an + * ambiguous node.) + * + * @since 6.4 + */ + public class CompletionNameEntry { + public CompletionNameEntry(IASTName name, IASTNode parent) { + fName = name; + fParent = parent; + } + + public IASTName fName; + public IASTNode fParent; + } + /** * If the point of completion was at the end of a potential identifier, this * string contains the text of that identifier. @@ -38,10 +57,28 @@ public interface IASTCompletionNode { */ public int getLength(); + /** + * Returns true if this completion node contains a {@link CompletionNameEntry} + * with the given name. + * + * @since 6.4 + */ + public boolean containsName(IASTName name); + /** * Returns a list of names that fit in this context. + * If doing computations based on the name's parent, prefer calling getEntries() instead + * and obtaining the parent from there. */ public IASTName[] getNames(); + + /** + * Returns a list of names that fir in this context, along with their parents. + * See {@link CompletionNameEntry} for more details. + * + * @since 6.4 + */ + public CompletionNameEntry[] getEntries(); /** * Returns the translation unit for this completion. diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java index a2b01fec885..2fc22a307cb 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/parser/cpp/GNUCPPSourceParser.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.cdt.core.dom.ast.ASTCompletionNode; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTAlignmentSpecifier; @@ -785,6 +786,14 @@ public class GNUCPPSourceParser extends AbstractGNUSourceCodeParser { name = namedTypeSpec.getName(); if (name.contains(typeId)) { idExpression = setRange(getNodeFactory().newIdExpression(name), name); + + // If the name was one of the completion names, add it to the completion + // node again now that it has a new parent. This ensures that completion + // proposals are offered for both contexts that the name appears in. + ASTCompletionNode completionNode = (ASTCompletionNode) getCompletionNode(); + if (completionNode != null && completionNode.containsName(name)) { + completionNode.addName(name); + } } } 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 ebe55719a8f..59cf69b2312 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 @@ -2032,4 +2032,12 @@ public class CompletionTests extends CompletionTestBase { public void testPartialSpecializationWithDeferredClassInstance_456224b() throws Exception { assertCompletionResults(new String[] { "Result" }); } + + // template + // struct A { + // using type_t = A + // }; + public void testNonTypeTemplateParameterCompletion_522010() throws Exception { + assertCompletionResults(new String[] { "TestParam" }); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java index 71f17585c8d..8016c3db7a7 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/DOMCompletionProposalComputer.java @@ -36,11 +36,13 @@ import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.swt.graphics.Image; import org.eclipse.cdt.core.dom.ILinkage; +import org.eclipse.cdt.core.dom.ast.ASTCompletionNode; import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTCompletionContext; import org.eclipse.cdt.core.dom.ast.IASTCompletionNode; +import org.eclipse.cdt.core.dom.ast.IASTCompletionNode.CompletionNameEntry; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionStyleMacroParameter; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; @@ -160,9 +162,10 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer } } else { boolean handleMacros= false; - IASTName[] names = completionNode.getNames(); + CompletionNameEntry[] entries = ((ASTCompletionNode) completionNode).getEntries(); - for (IASTName name : names) { + for (CompletionNameEntry entry : entries) { + IASTName name = entry.fName; if (name.getTranslationUnit() == null && !(name instanceof IASTInactiveCompletionName)) { // The node isn't properly hooked up, must have backtracked out of this node. // Inactive completion names are special in that they are not hooked up @@ -171,7 +174,7 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer continue; } - IASTCompletionContext astContext = name.getCompletionContext(); + IASTCompletionContext astContext = getCompletionContext(name, entry.fParent); if (astContext == null) { continue; } else if (astContext instanceof IASTIdExpression @@ -203,6 +206,13 @@ public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer return proposals; } + private static IASTCompletionContext getCompletionContext(IASTName name, IASTNode parent) { + if (parent instanceof IASTCompletionContext) { + return (IASTCompletionContext) parent; + } + return name.getCompletionContext(); + } + /** * Checks whether the invocation offset is inside or before the preprocessor directive keyword. * diff --git a/lrparser/org.eclipse.cdt.core.lrparser/src/org/eclipse/cdt/core/dom/lrparser/action/ASTCompletionNode.java b/lrparser/org.eclipse.cdt.core.lrparser/src/org/eclipse/cdt/core/dom/lrparser/action/ASTCompletionNode.java index 6ac50613317..b021de30609 100644 --- a/lrparser/org.eclipse.cdt.core.lrparser/src/org/eclipse/cdt/core/dom/lrparser/action/ASTCompletionNode.java +++ b/lrparser/org.eclipse.cdt.core.lrparser/src/org/eclipse/cdt/core/dom/lrparser/action/ASTCompletionNode.java @@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.cdt.core.dom.lrparser.action; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.core.dom.ast.IASTCompletionNode; @@ -38,7 +38,7 @@ import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; */ public class ASTCompletionNode implements IASTCompletionNode { - private final List names = new LinkedList(); + private final List entries = new ArrayList<>(); private final String prefix; private IASTTranslationUnit tu; @@ -62,7 +62,7 @@ public class ASTCompletionNode implements IASTCompletionNode { public void addName(IASTName name) { - names.add(name); + entries.add(new CompletionNameEntry(name, name.getParent())); } @@ -77,7 +77,11 @@ public class ASTCompletionNode implements IASTCompletionNode { @Override public IASTName[] getNames() { - return names.toArray(new IASTName[names.size()]); + IASTName[] names = new IASTName[entries.size()]; + for (int i = 0; i < entries.size(); ++i) { + names[i] = entries.get(i).fName; + } + return names; } @@ -100,6 +104,19 @@ public class ASTCompletionNode implements IASTCompletionNode { public IASTTranslationUnit getTranslationUnit() { return tu; } - + @Override + public boolean containsName(IASTName name) { + for (CompletionNameEntry entry : entries) { + if (entry.fName == name) { + return true; + } + } + return false; + } + + @Override + public CompletionNameEntry[] getEntries() { + return entries.toArray(new CompletionNameEntry[entries.size()]); + } }