1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-18 22:45:23 +02:00

Bug 520913 - hover / open declaration for decltype and typeof keywords

Change-Id: I15db25080aa476e3e0e6f9c6ce063e9bd1e2b2b4
Signed-off-by: Michael Woski <woskimi@yahoo.de>
This commit is contained in:
Michael Woski 2017-08-12 23:19:51 +02:00
parent 11817c1c6a
commit f546a833d6
6 changed files with 175 additions and 91 deletions

View file

@ -110,6 +110,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConversionName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDecltypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTEnumerationSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTExpression;
@ -130,6 +131,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSwitchStatement;
@ -139,6 +141,7 @@ import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplatedTypeTemplateParameter;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUnaryExpression;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
@ -4449,4 +4452,53 @@ public class CPPSemantics {
}
return expressionType;
}
/**
* This method performs type deduction for auto, decltype or typeof
* declarations. This is used by {@code CSourceHover} and
* {@code OpenDeclarationsJob} after checking (see
* {@code SemanticUtil#isAutoOrDecltype(String)}) whether the selected text
* equals any of the mentioned keywords.
*
* @param node
* The decl-specifier or decltype-specifier in which the 'auto'
* or 'decltype' occurs.
* @return the deduced type or null
*/
public static IType resolveDecltypeOrAutoType(IASTNode node) {
IType type = null;
if (node instanceof ICPPASTDecltypeSpecifier) {
type = ((ICPPASTDecltypeSpecifier) node).getDecltypeExpression().getExpressionType();
}
if (node instanceof ICPPASTSimpleDeclSpecifier) {
int builtin = ((ICPPASTSimpleDeclSpecifier) node).getType();
if (builtin == ICPPASTSimpleDeclSpecifier.t_auto || builtin == ICPPASTSimpleDeclSpecifier.t_typeof
|| builtin == ICPPASTSimpleDeclSpecifier.t_decltype) {
IASTNode parent = node.getParent();
IASTDeclarator declarator = null;
if (parent instanceof IASTSimpleDeclaration) {
IASTDeclarator[] declarators = ((IASTSimpleDeclaration) parent).getDeclarators();
// It's invalid for different declarators to deduce
// different types with 'auto', so just get the type based on the
// first declarator.
if (declarators.length > 0)
declarator = declarators[0];
} else if (parent instanceof IASTParameterDeclaration
&& builtin != ICPPASTSimpleDeclSpecifier.t_auto) {
declarator = ((IASTParameterDeclaration) parent).getDeclarator();
} else if (parent instanceof ICPPASTTypeId && builtin != ICPPASTSimpleDeclSpecifier.t_auto) {
declarator = ((ICPPASTTypeId) parent).getAbstractDeclarator();
} else if (parent instanceof ICPPASTFunctionDefinition) {
declarator = ((ICPPASTFunctionDefinition) parent).getDeclarator();
}
if (declarator != null) {
type = CPPVisitor.createType(declarator);
if (type instanceof ICPPFunctionType) {
type = ((ICPPFunctionType) type).getReturnType();
}
}
}
}
return type;
}
}

View file

@ -739,6 +739,10 @@ public class SemanticUtil {
return false;
}
public static boolean isAutoOrDecltype(String code) {
return (code.equals(Keywords.AUTO) || code.equals(Keywords.TYPEOF) || code.equals(Keywords.DECLTYPE));
}
public static boolean isEmptyParameterList(IType[] parameters) {
if (parameters.length == 0) {
return true;

View file

@ -1311,4 +1311,31 @@ public class CPPSelectionTestsNoIndexer extends BaseSelectionTests {
IASTNode target = testF3(file, offset);
assertInstance(target, IASTName.class);
}
}
// struct Waldo {};
// template<typename T>
// struct Basket{};
// Waldo find();
// Waldo myFriend;
// int main(decltype(myFriend) p) {
// auto waldo = find();
// Basket<typeof(waldo)> basket;
// decltype(waldo) wuff;
// }
public void testDeclType_520913() throws Exception {
String code = getAboveComment();
IFile file = importFile("testBug520913.cpp", code);
int offset = code.indexOf("main") + 10;
IASTNode target = testF3(file, offset);
assertInstance(target, IASTName.class);
offset = code.indexOf("typeof");
target = testF3(file, offset);
assertInstance(target, IASTName.class);
assertEquals("Waldo", ((IASTName) target).toString());
offset = code.indexOf("wuff") - 10;
target = testF3(file, offset);
assertInstance(target, IASTName.class);
}}

View file

@ -34,6 +34,7 @@ import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTNodeSelector;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IASTTypeId;
import org.eclipse.cdt.core.model.ICLanguageKeywords;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.IWorkingCopy;
@ -41,6 +42,8 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.ICModelBasedEditor;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
import org.eclipse.cdt.internal.formatter.scanner.Scanner;
import org.eclipse.cdt.internal.formatter.scanner.Token;
@ -76,33 +79,10 @@ public class CElementHyperlinkDetector extends AbstractHyperlinkDetector {
public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) {
if (ast == null)
return Status.CANCEL_STATUS;
final int offset= region.getOffset();
final int length= Math.max(1, region.getLength());
final IASTNodeSelector nodeSelector= ast.getNodeSelector(null);
IASTNode linkASTNode = null;
IASTNode linkASTNode = getLinkASTNode(document, ast, region);
IASTNodeLocation linkLocation = null;
IASTName selectedName= nodeSelector.findEnclosingName(offset, length);
if (selectedName != null) { // found a name
// Prefer include statement over the include name
if (selectedName.getParent() instanceof IASTPreprocessorIncludeStatement) {
linkASTNode = selectedName.getParent();
} else {
linkASTNode = selectedName;
}
} else {
final IASTNode implicit = nodeSelector.findEnclosingImplicitName(offset, length);
if (implicit != null) {
linkASTNode = implicit;
} else {
// Search for include statement
final IASTNode cand= nodeSelector.findEnclosingNode(offset, length);
if (cand instanceof IASTPreprocessorIncludeStatement) {
linkASTNode = cand;
}
}
}
if (linkASTNode != null) {
if (linkASTNode instanceof IASTName) {
IASTName astName = (IASTName) linkASTNode;
@ -167,6 +147,55 @@ public class CElementHyperlinkDetector extends AbstractHyperlinkDetector {
return new IHyperlink[] { new CElementHyperlink(hyperlinkRegion[0], openAction) };
}
private static IASTNode getLinkASTNode(IDocument document, IASTTranslationUnit ast, IRegion region) {
final int offset= region.getOffset();
final int length= Math.max(1, region.getLength());
final IASTNodeSelector nodeSelector= ast.getNodeSelector(null);
if (isOverAutoOrDecltype(document, offset)) {
IASTNode node = nodeSelector.findEnclosingNode(offset, length);
IASTTypeId enclosingTypeId = ASTQueries.findAncestorWithType(node, IASTTypeId.class);
if (enclosingTypeId != null) {
return enclosingTypeId;
}
}
IASTName selectedName= nodeSelector.findEnclosingName(offset, length);
if (selectedName != null) { // found a name
// Prefer include statement over the include name
if (selectedName.getParent() instanceof IASTPreprocessorIncludeStatement) {
return selectedName.getParent();
} else {
return selectedName;
}
} else {
final IASTNode implicit = nodeSelector.findEnclosingImplicitName(offset, length);
if (implicit != null) {
return implicit;
} else {
// Search for include statement
final IASTNode cand= nodeSelector.findEnclosingNode(offset, length);
if (cand instanceof IASTPreprocessorIncludeStatement) {
return cand;
}
}
}
return null;
}
private static boolean isOverAutoOrDecltype(IDocument document, int offset) {
try {
IRegion wordRegion = CWordFinder.findWord(document, offset);
if (wordRegion != null && wordRegion.getLength() > 0) {
String word = document.get(wordRegion.getOffset(), wordRegion.getLength());
return SemanticUtil.isAutoOrDecltype(word);
}
} catch (BadLocationException e) {
// Fall through and return false.
}
return false;
}
/**
* Returns the identifier at the given offset, or {@code null} if the there is no identifier
* at the offset.
@ -175,8 +204,10 @@ public class CElementHyperlinkDetector extends AbstractHyperlinkDetector {
IRegion wordRegion= CWordFinder.findWord(document, offset);
if (wordRegion != null && wordRegion.getLength() > 0) {
String word = document.get(wordRegion.getOffset(), wordRegion.getLength());
if (!Character.isDigit(word.charAt(0)) && !isLanguageKeyword(language, word)) {
return wordRegion;
if (!Character.isDigit(word.charAt(0))) {
if (SemanticUtil.isAutoOrDecltype(word) || !isLanguageKeyword(language, word)) {
return wordRegion;
}
}
}
return null;

View file

@ -15,8 +15,8 @@ package org.eclipse.cdt.internal.ui.search.actions;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.CVTYPE;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.PTR;
import static org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil.REF;
import java.util.ArrayList;
import java.util.Arrays;
@ -63,7 +63,6 @@ import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.IProblemBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTranslationUnit;
@ -101,6 +100,7 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.internal.core.dom.parser.ASTNode;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.HeuristicResolver;
@ -246,13 +246,11 @@ class OpenDeclarationsJob extends Job implements ASTRunnable {
final IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
IASTName sourceName= nodeSelector.findEnclosingName(selectionStart, selectionLength);
IASTName sourceName = nodeSelector.findEnclosingName(selectionStart, selectionLength);
IName[] implicitTargets = findImplicitTargets(ast, nodeSelector, selectionStart, selectionLength);
if (sourceName == null) {
if (implicitTargets.length > 0) {
if (navigateViaCElements(fTranslationUnit.getCProject(), fIndex, implicitTargets))
return Status.OK_STATUS;
}
if (sourceName == null || SemanticUtil.isAutoOrDecltype(fSelectedText)) {
if (navigateViaCElements(fTranslationUnit.getCProject(), fIndex, implicitTargets))
return Status.OK_STATUS;
} else {
boolean found= false;
final IASTNode parent = sourceName.getParent();
@ -479,28 +477,21 @@ class OpenDeclarationsJob extends Job implements ASTRunnable {
definitions = ArrayUtil.addAll(definitions, declNames);
}
}
} else {
} else if (SemanticUtil.isAutoOrDecltype(fSelectedText)) {
IASTNode enclosingNode = nodeSelector.findEnclosingNode(offset, length);
if (enclosingNode instanceof ICPPASTSimpleDeclSpecifier) {
if (((ICPPASTSimpleDeclSpecifier) enclosingNode).getType() == ICPPASTSimpleDeclSpecifier.t_auto) {
if (enclosingNode.getParent() instanceof IASTSimpleDeclaration) {
IASTDeclarator[] declarators = ((IASTSimpleDeclaration) enclosingNode.getParent()).getDeclarators();
if (declarators.length > 0) {
// It's invalid for different declarators to deduce different
// types with 'auto', so just get the type based on the first
// declarator.
IType type = CPPVisitor.createType(declarators[0]);
// Strip qualifiers, references, and pointers, but NOT
// typedefs, since for typedefs we want to navigate to the
// typedef declaration.
type = SemanticUtil.getNestedType(type, CVTYPE | REF | PTR);
if (type instanceof IBinding) {
IName[] declNames = findDeclNames(ast, NameKind.REFERENCE, (IBinding) type);
definitions = ArrayUtil.addAll(definitions, declNames);
}
}
}
}
IType type = CPPSemantics.resolveDecltypeOrAutoType(enclosingNode);
if (type instanceof ICPPUnknownType) {
IType hType = HeuristicResolver.resolveUnknownType((ICPPUnknownType) type, enclosingNode);
if (hType != null)
type = hType;
}
// Strip qualifiers, references, and pointers, but NOT
// typedefs, since for typedefs we want to refer to the
// typedef declaration.
type = SemanticUtil.getNestedType(type, CVTYPE | REF | PTR);
if (type instanceof IBinding) {
IName[] declNames = findDeclNames(ast, NameKind.REFERENCE, (IBinding) type);
definitions = ArrayUtil.addAll(definitions, declNames);
}
}
return ArrayUtil.trim(definitions);

View file

@ -53,7 +53,6 @@ import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTImplicitNameOwner;
@ -76,11 +75,9 @@ import org.eclipse.cdt.core.dom.ast.IProblemType;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeclarator;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTypeId;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplateInstance;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization;
@ -94,7 +91,6 @@ import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.parser.KeywordSetKey;
import org.eclipse.cdt.core.parser.Keywords;
import org.eclipse.cdt.core.parser.ParserFactory;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.ui.CUIPlugin;
@ -103,8 +99,12 @@ import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.internal.core.dom.parser.ASTQueries;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.HeuristicResolver;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.TypeOfDependentExpression;
import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
import org.eclipse.cdt.internal.corext.util.Strings;
@ -161,33 +161,12 @@ public class CSourceHover extends AbstractCEditorTextHover {
if (ast != null) {
try {
IASTNodeSelector nodeSelector = ast.getNodeSelector(null);
if (fSelection.equals(Keywords.AUTO)) {
IASTNode node = nodeSelector.findEnclosingNode(fTextRegion.getOffset(), fTextRegion.getLength());
if (node instanceof ICPPASTDeclSpecifier) {
ICPPASTDeclSpecifier declSpec = (ICPPASTDeclSpecifier) node;
IASTNode parent = declSpec.getParent();
IASTDeclarator[] declarators = IASTDeclarator.EMPTY_DECLARATOR_ARRAY;
if (parent instanceof IASTSimpleDeclaration) {
declarators = ((IASTSimpleDeclaration) parent).getDeclarators();
} else if (parent instanceof IASTParameterDeclaration) {
declarators = new IASTDeclarator[] { ((IASTParameterDeclaration) parent).getDeclarator() };
} else if (parent instanceof ICPPASTTypeId) {
declarators = new IASTDeclarator[] { ((ICPPASTTypeId) parent).getAbstractDeclarator() };
}
IType type = null;
for (IASTDeclarator declarator : declarators) {
IType t = CPPVisitor.createType(declarator);
if (type == null) {
type = t;
} else if (!type.isSameType(t)) {
// Type varies between declarators - don't display anything.
type = null;
break;
}
}
if (type != null && !(type instanceof IProblemType))
fSource = ASTTypeUtil.getType(type, false);
}
if (SemanticUtil.isAutoOrDecltype(fSelection)) {
IASTNode node = nodeSelector.findEnclosingNode(fTextRegion.getOffset(),
fTextRegion.getLength());
IType type = CPPSemantics.resolveDecltypeOrAutoType(node);
if (type != null && !(type instanceof IProblemType))
fSource = ASTTypeUtil.getType(type, false);
} else {
IASTName name= nodeSelector.findEnclosingName(fTextRegion.getOffset(), fTextRegion.getLength());
if (name != null) {
@ -742,8 +721,8 @@ public class CSourceHover extends AbstractCEditorTextHover {
return null;
// Before trying a search lets make sure that the user is not hovering
// over a keyword other than 'auto'.
if (selectionIsKeyword(expression) && !expression.equals(Keywords.AUTO))
// over a keyword other than 'auto', 'decltype' or 'typeof'.
if (selectionIsKeyword(expression) && !SemanticUtil.isAutoOrDecltype(expression))
return null;
// Try with the indexer.