/** * AST visitor to create compare structure. * * @since 5.0 */ class CStructureCreatorVisitor extends ASTVisitor { private static final String TRANSLATION_UNIT_NAME = CUIMessages.CStructureCreatorVisitor_translationUnitName; private static final String ANONYMOUS_NAME = CoreModelMessages.getString("CElementLabels.anonymous"); // $NON-NLS-1$ private Stack<DocumentRangeNode> fStack = new Stack<DocumentRangeNode>(); private IDocument fDocument; private String fTranslationUnitFileName; /** * Create visitor adding nodes to given root. * * @param root */ public CStructureCreatorVisitor(DocumentRangeNode root) { fDocument = root.getDocument(); fStack.clear(); fStack.push(root); // visitor options shouldVisitTranslationUnit = true; shouldVisitDeclarations = true; shouldVisitEnumerators = true; shouldVisitNamespaces = true; } /* * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit) */ @Override public int visit(IASTTranslationUnit tu) { fTranslationUnitFileName = tu.getFilePath(); push(ICElement.C_UNIT, TRANSLATION_UNIT_NAME, 0); // TODO fix ordering of includes and macros // includes final IASTPreprocessorIncludeStatement[] includeDirectives = tu.getIncludeDirectives(); for (int i = 0; i < includeDirectives.length; i++) { IASTPreprocessorIncludeStatement includeDirective = includeDirectives[i]; if (isLocalToFile(includeDirective)) { push( ICElement.C_INCLUDE, new String(includeDirective.getName().toCharArray()), getStartOffset(includeDirective)); pop(getEndOffset(includeDirective)); } } // macros final IASTPreprocessorMacroDefinition[] macroDefinitions = tu.getMacroDefinitions(); for (int i = 0; i < macroDefinitions.length; i++) { IASTPreprocessorMacroDefinition macroDefinition = macroDefinitions[i]; if (isLocalToFile(macroDefinition)) { push( ICElement.C_MACRO, new String(macroDefinition.getName().toCharArray()), getStartOffset(macroDefinition)); pop(getEndOffset(macroDefinition)); } } return super.visit(tu); } /** * Test whether given AST node is local to the source file and not part of an inclusion. * * @param node * @return <code>true</code> if the node is part of the source file. */ private boolean isLocalToFile(IASTNode node) { return fTranslationUnitFileName.equals(node.getContainingFilename()); } /** * Compute the start offset of given AST node. * * @param node * @return */ private int getStartOffset(IASTNode node) { IASTFileLocation fileLocation = getMinFileLocation(node.getNodeLocations()); if (fileLocation != null) { return fileLocation.getNodeOffset(); } DocumentRangeNode container = getCurrentContainer(); Object[] children = container.getChildren(); if (children != null && children.length > 0) { Position prevRange = ((DocumentRangeNode) children[children.length - 1]).getRange(); return prevRange.getOffset() + prevRange.getLength(); } // fallback: use container range start Position containerRange = container.getRange(); return containerRange.getOffset(); } /** * Compute the end offset of give AST node. * * @param node * @return */ private int getEndOffset(IASTNode node) { IASTFileLocation fileLocation = getMaxFileLocation(node.getNodeLocations()); if (fileLocation != null) { return fileLocation.getNodeOffset() + fileLocation.getNodeLength(); } // fallback: use container range end DocumentRangeNode container = getCurrentContainer(); Position containerRange = container.getRange(); return containerRange.getOffset() + containerRange.getLength(); } /* * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#leave(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit) */ @Override public int leave(IASTTranslationUnit tu) { super.leave(tu); assert getCurrentContainer().getTypeCode() == ICElement.C_UNIT; pop(fDocument.getLength()); return PROCESS_SKIP; } /* * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTDeclaration) */ @Override public int visit(IASTDeclaration node) { boolean isTemplateDecl = isTemplateDecl(node); final int startOffset = isTemplateDecl ? getStartOffset(node.getParent()) : getStartOffset(node); final int endOffset = getEndOffset(node); if (node instanceof IASTFunctionDefinition) { IASTFunctionDefinition functionDef = (IASTFunctionDefinition) node; final int nodeType; if (inClassBody()) { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_METHOD : ICElement.C_METHOD; } else { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_FUNCTION : ICElement.C_FUNCTION; } push(nodeType, getDeclaratorName(functionDef.getDeclarator()), startOffset); pop(endOffset); return PROCESS_SKIP; } else if (node instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) node; IASTDeclSpecifier declSpec = simpleDecl.getDeclSpecifier(); if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { ICPPASTCompositeTypeSpecifier compositeTypeSpec = (ICPPASTCompositeTypeSpecifier) declSpec; final String nodeName = getTypeName(compositeTypeSpec); final int nodeType; switch (compositeTypeSpec.getKey()) { case IASTCompositeTypeSpecifier.k_struct: nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_STRUCT : ICElement.C_STRUCT; break; case IASTCompositeTypeSpecifier.k_union: nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_UNION : ICElement.C_UNION; break; case ICPPASTCompositeTypeSpecifier.k_class: nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_CLASS : ICElement.C_CLASS; break; default: assert false : "Unexpected composite type specifier"; // $NON-NLS-1$ return PROCESS_CONTINUE; } push(nodeType, nodeName, startOffset); } else if (declSpec instanceof IASTEnumerationSpecifier) { IASTEnumerationSpecifier enumSpecifier = (IASTEnumerationSpecifier) declSpec; push(ICElement.C_ENUMERATION, getEnumerationName(enumSpecifier), startOffset); } else { IASTDeclarator[] declarators = simpleDecl.getDeclarators(); for (int i = 0; i < declarators.length; i++) { IASTDeclarator declarator = declarators[i]; int declStartOffset = declarators.length == 1 ? startOffset : getStartOffset(declarator); int declEndOffset = declarators.length == 1 ? endOffset : getEndOffset(declarator); final String nodeName = getDeclaratorName(declarator); if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_typedef) { push(ICElement.C_TYPEDEF, nodeName, declStartOffset); pop(declEndOffset); } else if (declarator instanceof IASTFunctionDeclarator && !hasNestedPointerOperators(declarator)) { final int nodeType; if (inClassBody()) { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_METHOD_DECLARATION : ICElement.C_METHOD_DECLARATION; } else { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_FUNCTION_DECLARATION : ICElement.C_FUNCTION_DECLARATION; } push(nodeType, nodeName, declStartOffset); pop(declEndOffset); } else if (declarator != null) { final int nodeType; if (inClassBody()) { nodeType = ICElement.C_FIELD; } else { if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_extern) { nodeType = ICElement.C_VARIABLE_DECLARATION; } else { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_VARIABLE : ICElement.C_VARIABLE; } } push(nodeType, nodeName, declStartOffset); pop(declEndOffset); } } } } else if (node instanceof IASTASMDeclaration) { // ignored } else if (node instanceof ICPPASTVisibilityLabel) { // ignored } else if (node instanceof ICPPASTNamespaceDefinition) { // handled below } else if (node instanceof ICPPASTNamespaceAlias) { // ignored } else if (node instanceof ICPPASTUsingDeclaration) { ICPPASTUsingDeclaration usingDecl = (ICPPASTUsingDeclaration) node; push(ICElement.C_USING, ASTStringUtil.getQualifiedName(usingDecl.getName()), startOffset); pop(endOffset); } else if (node instanceof ICPPASTUsingDirective) { ICPPASTUsingDirective usingDirective = (ICPPASTUsingDirective) node; push( ICElement.C_USING, ASTStringUtil.getQualifiedName(usingDirective.getQualifiedName()), startOffset); pop(endOffset); } else if (node instanceof ICPPASTLinkageSpecification) { // declarations get flattened } else if (node instanceof ICPPASTTemplateDeclaration) { // handled at child declaration level } else if (node instanceof ICPPASTTemplateSpecialization) { // ignored } else if (node instanceof ICPPASTExplicitTemplateInstantiation) { // ignored } else if (node instanceof IASTProblemDeclaration) { // ignored } return super.visit(node); } /* * @see org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor#visit(org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition) */ @Override public int visit(ICPPASTNamespaceDefinition namespace) { push( ICElement.C_NAMESPACE, ASTStringUtil.getQualifiedName(namespace.getName()), getStartOffset(namespace)); return super.visit(namespace); } /* * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator) */ @Override public int visit(IASTEnumerator enumerator) { push( ICElement.C_ENUMERATOR, ASTStringUtil.getQualifiedName(enumerator.getName()), getStartOffset(enumerator)); pop(getEndOffset(enumerator)); return super.visit(enumerator); } /* * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#leave(org.eclipse.cdt.core.dom.ast.IASTDeclaration) */ @Override public int leave(IASTDeclaration node) { super.leave(node); boolean isTemplateDecl = isTemplateDecl(node); final int endOffset = isTemplateDecl ? getEndOffset(node.getParent()) : getEndOffset(node); if (node instanceof IASTFunctionDefinition) { final int nodeType; if (inClassBody()) { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_METHOD : ICElement.C_METHOD; } else { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_FUNCTION : ICElement.C_FUNCTION; } assert getCurrentContainer().getTypeCode() == nodeType; pop(endOffset); } else if (node instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) node; IASTDeclSpecifier declSpec = simpleDecl.getDeclSpecifier(); boolean isCompositeType = false; if (declSpec instanceof ICPPASTCompositeTypeSpecifier) { isCompositeType = true; ICPPASTCompositeTypeSpecifier compositeTypeSpec = (ICPPASTCompositeTypeSpecifier) declSpec; final int nodeType; switch (compositeTypeSpec.getKey()) { case IASTCompositeTypeSpecifier.k_struct: nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_STRUCT : ICElement.C_STRUCT; break; case IASTCompositeTypeSpecifier.k_union: nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_UNION : ICElement.C_UNION; break; case ICPPASTCompositeTypeSpecifier.k_class: nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_CLASS : ICElement.C_CLASS; break; default: assert false : "Unexpected composite type specifier"; // $NON-NLS-1$ return PROCESS_CONTINUE; } assert getCurrentContainer().getTypeCode() == nodeType; pop(isTemplateDecl ? endOffset : getEndOffset(declSpec)); } else if (declSpec instanceof IASTEnumerationSpecifier) { isCompositeType = true; assert getCurrentContainer().getTypeCode() == ICElement.C_ENUMERATION; pop(getEndOffset(declSpec)); } if (isCompositeType) { IASTDeclarator[] declarators = simpleDecl.getDeclarators(); for (int i = 0; i < declarators.length; i++) { IASTDeclarator declarator = declarators[i]; final String nodeName = getDeclaratorName(declarator); final int declStartOffset = getStartOffset(declarator); final int declEndOffset = getEndOffset(declarator); if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_typedef) { push(ICElement.C_TYPEDEF, nodeName, declStartOffset); pop(declEndOffset); } else if (declarator instanceof IASTFunctionDeclarator && !hasNestedPointerOperators(declarator)) { final int nodeType; if (inClassBody()) { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_METHOD_DECLARATION : ICElement.C_METHOD_DECLARATION; } else { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_FUNCTION_DECLARATION : ICElement.C_FUNCTION_DECLARATION; } push(nodeType, nodeName, declStartOffset); pop(declEndOffset); } else if (declarator != null) { final int nodeType; if (inClassBody()) { nodeType = ICElement.C_FIELD; } else { if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_extern) { nodeType = ICElement.C_VARIABLE_DECLARATION; } else { nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_VARIABLE : ICElement.C_VARIABLE; } } push(nodeType, nodeName, declStartOffset); pop(declEndOffset); } } } } else if (node instanceof IASTASMDeclaration) { // ignored } else if (node instanceof ICPPASTVisibilityLabel) { // ignored } else if (node instanceof ICPPASTNamespaceDefinition) { // handled below } else if (node instanceof ICPPASTNamespaceAlias) { // ignored } else if (node instanceof ICPPASTUsingDeclaration) { // handled in visit } else if (node instanceof ICPPASTUsingDirective) { // handled in visit } else if (node instanceof ICPPASTLinkageSpecification) { // declarations get flattened } else if (node instanceof ICPPASTTemplateDeclaration) { // handled at child declaration level } else if (node instanceof ICPPASTTemplateSpecialization) { // ignored } else if (node instanceof ICPPASTExplicitTemplateInstantiation) { // ignored } else if (node instanceof IASTProblemDeclaration) { // ignored } return PROCESS_CONTINUE; } /* * @see org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor#leave(org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition) */ @Override public int leave(ICPPASTNamespaceDefinition namespace) { assert getCurrentContainer().getTypeCode() == ICElement.C_NAMESPACE; pop(getEndOffset(namespace)); return super.leave(namespace); } private DocumentRangeNode getCurrentContainer() { return fStack.peek(); } /** Adds a new node with the given type and name to the current container. */ private void push(int type, String name, int declarationStart) { if (name.length() == 0) { name = ANONYMOUS_NAME; } fStack.push(new CNode(getCurrentContainer(), type, name, declarationStart, 0)); } /** Closes the current node by setting its end position and pops it off the stack. */ private void pop(int declarationEnd) { DocumentRangeNode current = getCurrentContainer(); current.setAppendPosition(declarationEnd); current.setLength(declarationEnd - current.getRange().getOffset()); fStack.pop(); } /** @return <code>true</code> if the current container is class-like. */ private boolean inClassBody() { int typeCode = getCurrentContainer().getTypeCode(); return typeCode == ICElement.C_CLASS || typeCode == ICElement.C_TEMPLATE_CLASS || typeCode == ICElement.C_STRUCT || typeCode == ICElement.C_TEMPLATE_STRUCT || typeCode == ICElement.C_UNION || typeCode == ICElement.C_TEMPLATE_UNION; } /** * Test whether the given declaration is a templated declaration. * * @param node * @return <code>true</code> if the declaration is templated. */ private boolean isTemplateDecl(IASTDeclaration node) { return node.getParent() instanceof ICPPASTTemplateDeclaration; } private boolean hasNestedPointerOperators(IASTDeclarator declarator) { declarator = declarator.getNestedDeclarator(); while (declarator != null) { if (declarator.getPointerOperators().length > 0) { return true; } declarator = declarator.getNestedDeclarator(); } return false; } private String getEnumerationName(IASTEnumerationSpecifier enumSpecifier) { String nodeName = ASTStringUtil.getQualifiedName(enumSpecifier.getName()); if (nodeName.length() == 0) { nodeName = ANONYMOUS_NAME; } return nodeName; } private String getTypeName(IASTCompositeTypeSpecifier compositeTypeSpec) { String nodeName = ASTStringUtil.getQualifiedName(compositeTypeSpec.getName()); if (nodeName.length() == 0) { nodeName = ANONYMOUS_NAME; } return nodeName; } private String getDeclaratorName(IASTDeclarator node) { node = getInnermostDeclarator(node); IASTName name = node.getName(); String nodeName = ASTStringUtil.getQualifiedName(name); if (nodeName.length() == 0) { nodeName = ANONYMOUS_NAME; } return nodeName; } private IASTDeclarator getInnermostDeclarator(IASTDeclarator node) { IASTDeclarator nested = node.getNestedDeclarator(); while (nested != null) { node = nested; nested = node.getNestedDeclarator(); } return node; } private static IASTFileLocation getMaxFileLocation(IASTNodeLocation[] locations) { if (locations == null || locations.length == 0) { return null; } final IASTNodeLocation nodeLocation = locations[locations.length - 1]; return nodeLocation.asFileLocation(); } private static IASTFileLocation getMinFileLocation(IASTNodeLocation[] locations) { if (locations == null || locations.length == 0) { return null; } final IASTNodeLocation nodeLocation = locations[0]; return nodeLocation.asFileLocation(); } }
/** @see CreateElementInTUOperation#getMainTaskName */ @Override public String getMainTaskName() { return CoreModelMessages.getString("operation.createFieldProgress"); // $NON-NLS-1$ }