/******************************************************************************* * Copyright (c) 2006, 2013 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 * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.indexer; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.core.dom.ast.ASTVisitor; import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; 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.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTInitializer; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTProblem; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IFunction; import org.eclipse.cdt.core.dom.ast.c.ICASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCapture; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDeclarator; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression; import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; 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.semantics.CPPVisitor; abstract public class IndexerASTVisitor extends ASTVisitor { /** * Represents a definition of a class or function. * IndexerASTVisitor builds a tree of these definitions, used for tracking enclosing * definitions of names. */ public static class Definition { Definition(IASTName name, IASTNode node) { fName= name; fNode= node; } IASTName fName; // The name of the entity being defined. IASTNode fNode; // The AST node for the entire definition. List<Definition> fChildren; // Definitions contained within this one. /** * Search the subtree of definitions rooted at this one for the nearest * definition that encloses the range defined by 'offset' and 'length'. * The name of the resulting definition is returned. * This function assumes that 'this.matches(offset, length)' is true. */ public IASTName search(int offset, int length) { if (fChildren != null) { for (Definition child : fChildren) { if (child.matches(offset, length)) { return child.search(offset, length); } } } return fName; } /** * Check whether this definition encloses the range defined by 'offset' and 'length'. */ boolean matches(int offset, int length) { if (!(fNode instanceof ASTNode)) { return false; } ASTNode node = (ASTNode) fNode; int nodeOffset = node.getOffset(); int nodeLength = node.getLength(); return nodeOffset <= offset && (nodeOffset + nodeLength) >= (offset + length); } } private IASTName fDefinitionName; private IASTNode fDefinitionNode; private ArrayList<Definition> fStack= new ArrayList<Definition>(); private ArrayList<IASTProblem> fProblems= new ArrayList<IASTProblem>(); public IndexerASTVisitor(boolean visitImplicitNames) { shouldVisitNames= true; shouldVisitImplicitNames = visitImplicitNames; shouldVisitDeclarations= true; shouldVisitInitializers= true; shouldVisitDeclSpecifiers= true; shouldVisitProblems= true; shouldVisitExpressions= true; // Root node representing the entire file fStack.add(new Definition(null, null)); } public List<IASTProblem> getProblems() { return fProblems; } abstract public void visit(IASTName name, IASTName definitionName); @Override final public int visit(IASTName name) { if (!(name instanceof ICPPASTQualifiedName)) { if (name != fDefinitionName) { visit(name, fDefinitionName); } } return PROCESS_CONTINUE; } private void push(IASTName name, IASTNode node) { assert !fStack.isEmpty(); Definition def = new Definition(name, node); Definition parent = fStack.get(fStack.size() - 1); if (parent.fChildren == null) { parent.fChildren = new ArrayList<>(); } parent.fChildren.add(def); fStack.add(def); name = getLastInQualified(name); fDefinitionName= name; fDefinitionNode= node; } private IASTName getLastInQualified(IASTName name) { return name.getLastName(); } private void pop(IASTNode node) { if (node == fDefinitionNode) { assert !fStack.isEmpty(); fStack.remove(fStack.size() - 1); if (fStack.isEmpty()) { fDefinitionName= null; fDefinitionNode= null; } else { Definition old= fStack.get(fStack.size()-1); fDefinitionName= old.fName; fDefinitionNode= old.fNode; } } } // functions and methods @Override public int visit(IASTDeclaration decl) { if (decl instanceof IASTFunctionDefinition) { IASTFunctionDefinition fdef= (IASTFunctionDefinition) decl; final IASTFunctionDeclarator declarator= fdef.getDeclarator(); IASTDeclarator nestedDeclarator= declarator; while (nestedDeclarator.getNestedDeclarator() != null) { nestedDeclarator= nestedDeclarator.getNestedDeclarator(); } IASTName name= getLastInQualified(nestedDeclarator.getName()); visit(name, fDefinitionName); push(name, decl); } else if (decl instanceof IASTSimpleDeclaration) { IASTSimpleDeclaration sdecl= (IASTSimpleDeclaration) decl; if (sdecl.getDeclSpecifier().getStorageClass() == IASTDeclSpecifier.sc_typedef) { IASTDeclarator[] declarators= sdecl.getDeclarators(); for (IASTDeclarator declarator : declarators) { if (declarator.getPointerOperators().length == 0 && declarator.getNestedDeclarator() == null) { IASTName name= getLastInQualified(declarator.getName()); visit(name, fDefinitionName); push(name, decl); } } } } return PROCESS_CONTINUE; } @Override public int leave(IASTDeclaration decl) { pop(decl); return PROCESS_CONTINUE; } // class definitions, typedefs @Override public int visit(IASTDeclSpecifier declspec) { if (declspec instanceof ICPPASTCompositeTypeSpecifier) { ICPPASTCompositeTypeSpecifier cts= (ICPPASTCompositeTypeSpecifier) declspec; IASTName name = getLastInQualified(cts.getName()); visit(name, fDefinitionName); push(name, declspec); } if (declspec instanceof ICASTCompositeTypeSpecifier) { ICASTCompositeTypeSpecifier cts= (ICASTCompositeTypeSpecifier) declspec; IASTName name = cts.getName(); visit(name, fDefinitionName); push(name, declspec); } return PROCESS_CONTINUE; } @Override public int leave(IASTDeclSpecifier declspec) { pop(declspec); return PROCESS_CONTINUE; } @Override public int visit(IASTProblem problem) { fProblems.add(problem); return PROCESS_SKIP; } // variable and field initializers @Override public int visit(IASTInitializer initializer) { if (!(fDefinitionNode instanceof IASTFunctionDefinition)) { IASTNode cand= initializer.getParent(); if (cand instanceof IASTDeclarator) { cand= ASTQueries.findInnermostDeclarator((IASTDeclarator) cand); push(((IASTDeclarator) cand).getName(), initializer); } } return PROCESS_CONTINUE; } @Override public int leave(IASTInitializer initializer) { pop(initializer); return PROCESS_CONTINUE; } // Lambda expressions @Override public int visit(IASTExpression expr) { if (expr instanceof ICPPASTLambdaExpression) { return visit((ICPPASTLambdaExpression) expr); } return PROCESS_CONTINUE; } public Definition getDefinitionTree() { assert !fStack.isEmpty(); return fStack.get(0); } private int visit(final ICPPASTLambdaExpression lambdaExpr) { // Captures for (ICPPASTCapture cap : lambdaExpr.getCaptures()) { if (!cap.accept(this)) return PROCESS_ABORT; } // Definition of closure type final IASTName closureName = lambdaExpr.getClosureTypeName(); visit(closureName, fDefinitionName); // Definition of call operator IASTName callOp= lambdaExpr.getFunctionCallOperatorName(); visit(callOp, closureName); IBinding owner = CPPVisitor.findDeclarationOwner(lambdaExpr, true); boolean localToFunction = owner instanceof IFunction; if (!localToFunction) push(callOp, lambdaExpr); // Local closures don't appear in the index, so don't refer to them. ICPPASTFunctionDeclarator dtor = lambdaExpr.getDeclarator(); if (dtor != null && !dtor.accept(this)) return PROCESS_ABORT; IASTCompoundStatement body = lambdaExpr.getBody(); if (body != null && !body.accept(this)) return PROCESS_ABORT; if (!localToFunction) pop(lambdaExpr); return PROCESS_SKIP; } }