/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.engine.internal.builder; import com.google.dart.engine.AnalysisEngine; import com.google.dart.engine.ast.AstNode; import com.google.dart.engine.ast.Block; import com.google.dart.engine.ast.CatchClause; import com.google.dart.engine.ast.ClassDeclaration; import com.google.dart.engine.ast.ClassMember; import com.google.dart.engine.ast.ClassTypeAlias; import com.google.dart.engine.ast.ConstructorDeclaration; import com.google.dart.engine.ast.DeclaredIdentifier; import com.google.dart.engine.ast.DefaultFormalParameter; import com.google.dart.engine.ast.EmptyFunctionBody; import com.google.dart.engine.ast.EnumDeclaration; import com.google.dart.engine.ast.Expression; import com.google.dart.engine.ast.FieldDeclaration; import com.google.dart.engine.ast.FieldFormalParameter; import com.google.dart.engine.ast.ForEachStatement; import com.google.dart.engine.ast.FormalParameter; import com.google.dart.engine.ast.FunctionBody; import com.google.dart.engine.ast.FunctionDeclaration; import com.google.dart.engine.ast.FunctionExpression; import com.google.dart.engine.ast.FunctionTypeAlias; import com.google.dart.engine.ast.FunctionTypedFormalParameter; import com.google.dart.engine.ast.Identifier; import com.google.dart.engine.ast.Label; import com.google.dart.engine.ast.LabeledStatement; import com.google.dart.engine.ast.MethodDeclaration; import com.google.dart.engine.ast.NormalFormalParameter; import com.google.dart.engine.ast.SimpleFormalParameter; import com.google.dart.engine.ast.SimpleIdentifier; import com.google.dart.engine.ast.SuperExpression; import com.google.dart.engine.ast.SwitchCase; import com.google.dart.engine.ast.SwitchDefault; import com.google.dart.engine.ast.SwitchStatement; import com.google.dart.engine.ast.TypeParameter; import com.google.dart.engine.ast.VariableDeclaration; import com.google.dart.engine.ast.VariableDeclarationList; import com.google.dart.engine.ast.visitor.RecursiveAstVisitor; import com.google.dart.engine.ast.visitor.UnifyingAstVisitor; import com.google.dart.engine.context.AnalysisException; import com.google.dart.engine.element.ConstructorElement; import com.google.dart.engine.element.FieldElement; import com.google.dart.engine.element.ParameterElement; import com.google.dart.engine.element.TypeParameterElement; import com.google.dart.engine.internal.element.ClassElementImpl; import com.google.dart.engine.internal.element.ConstFieldElementImpl; import com.google.dart.engine.internal.element.ConstLocalVariableElementImpl; import com.google.dart.engine.internal.element.ConstTopLevelVariableElementImpl; import com.google.dart.engine.internal.element.ConstructorElementImpl; import com.google.dart.engine.internal.element.DefaultFieldFormalParameterElementImpl; import com.google.dart.engine.internal.element.DefaultParameterElementImpl; import com.google.dart.engine.internal.element.FieldElementImpl; import com.google.dart.engine.internal.element.FieldFormalParameterElementImpl; import com.google.dart.engine.internal.element.FunctionElementImpl; import com.google.dart.engine.internal.element.FunctionTypeAliasElementImpl; import com.google.dart.engine.internal.element.LabelElementImpl; import com.google.dart.engine.internal.element.LocalVariableElementImpl; import com.google.dart.engine.internal.element.MethodElementImpl; import com.google.dart.engine.internal.element.ParameterElementImpl; import com.google.dart.engine.internal.element.PropertyAccessorElementImpl; import com.google.dart.engine.internal.element.PropertyInducingElementImpl; import com.google.dart.engine.internal.element.TopLevelVariableElementImpl; import com.google.dart.engine.internal.element.TypeParameterElementImpl; import com.google.dart.engine.internal.element.VariableElementImpl; import com.google.dart.engine.internal.type.FunctionTypeImpl; import com.google.dart.engine.internal.type.InterfaceTypeImpl; import com.google.dart.engine.internal.type.TypeParameterTypeImpl; import com.google.dart.engine.scanner.Keyword; import com.google.dart.engine.scanner.KeywordToken; import com.google.dart.engine.scanner.Token; import com.google.dart.engine.scanner.TokenType; import com.google.dart.engine.type.Type; import com.google.dart.engine.utilities.dart.ParameterKind; import java.util.ArrayList; import java.util.HashMap; /** * Instances of the class {@code ElementBuilder} traverse an AST structure and build the element * model representing the AST structure. * * @coverage dart.engine.resolver */ public class ElementBuilder extends RecursiveAstVisitor<Void> { /** * The element holder associated with the element that is currently being built. */ private ElementHolder currentHolder; /** * A flag indicating whether a variable declaration is in the context of a field declaration. */ private boolean inFieldContext = false; /** * A flag indicating whether a variable declaration is within the body of a method or function. */ private boolean inFunction = false; /** * A flag indicating whether the class currently being visited can be used as a mixin. */ private boolean isValidMixin = false; /** * A collection holding the function types defined in a class that need to have their type * arguments set to the types of the type parameters for the class, or {@code null} if we are not * currently processing nodes within a class. */ private ArrayList<FunctionTypeImpl> functionTypesToFix = null; /** * A table mapping field names to field elements for the fields defined in the current class, or * {@code null} if we are not in the scope of a class. */ private HashMap<String, FieldElement> fieldMap; /** * Initialize a newly created element builder to build the elements for a compilation unit. * * @param initialHolder the element holder associated with the compilation unit being built */ public ElementBuilder(ElementHolder initialHolder) { currentHolder = initialHolder; } @Override public Void visitBlock(Block node) { boolean wasInField = inFieldContext; inFieldContext = false; try { node.visitChildren(this); } finally { inFieldContext = wasInField; } return null; } @Override public Void visitCatchClause(CatchClause node) { SimpleIdentifier exceptionParameter = node.getExceptionParameter(); if (exceptionParameter != null) { LocalVariableElementImpl exception = new LocalVariableElementImpl(exceptionParameter); currentHolder.addLocalVariable(exception); exceptionParameter.setStaticElement(exception); SimpleIdentifier stackTraceParameter = node.getStackTraceParameter(); if (stackTraceParameter != null) { LocalVariableElementImpl stackTrace = new LocalVariableElementImpl(stackTraceParameter); currentHolder.addLocalVariable(stackTrace); stackTraceParameter.setStaticElement(stackTrace); } } return super.visitCatchClause(node); } @Override public Void visitClassDeclaration(ClassDeclaration node) { ElementHolder holder = new ElementHolder(); isValidMixin = true; functionTypesToFix = new ArrayList<FunctionTypeImpl>(); // // Process field declarations before constructors and methods so that field formal parameters // can be correctly resolved to their fields. // ElementHolder previousHolder = currentHolder; currentHolder = holder; try { final ArrayList<ClassMember> nonFields = new ArrayList<ClassMember>(); node.visitChildren(new UnifyingAstVisitor<Void>() { @Override public Void visitConstructorDeclaration(ConstructorDeclaration node) { nonFields.add(node); return null; } @Override public Void visitMethodDeclaration(MethodDeclaration node) { nonFields.add(node); return null; } @Override public Void visitNode(AstNode node) { return node.accept(ElementBuilder.this); } }); buildFieldMap(holder.getFieldsWithoutFlushing()); int count = nonFields.size(); for (int i = 0; i < count; i++) { nonFields.get(i).accept(this); } } finally { currentHolder = previousHolder; } SimpleIdentifier className = node.getName(); ClassElementImpl element = new ClassElementImpl(className); TypeParameterElement[] typeParameters = holder.getTypeParameters(); Type[] typeArguments = createTypeParameterTypes(typeParameters); InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); interfaceType.setTypeArguments(typeArguments); element.setType(interfaceType); ConstructorElement[] constructors = holder.getConstructors(); if (constructors.length == 0) { // // Create the default constructor. // constructors = createDefaultConstructors(interfaceType); } element.setAbstract(node.isAbstract()); element.setAccessors(holder.getAccessors()); element.setConstructors(constructors); element.setFields(holder.getFields()); element.setMethods(holder.getMethods()); element.setTypeParameters(typeParameters); element.setValidMixin(isValidMixin); int functionTypeCount = functionTypesToFix.size(); for (int i = 0; i < functionTypeCount; i++) { functionTypesToFix.get(i).setTypeArguments(typeArguments); } functionTypesToFix = null; currentHolder.addType(element); className.setStaticElement(element); fieldMap = null; holder.validate(); return null; } @Override public Void visitClassTypeAlias(ClassTypeAlias node) { ElementHolder holder = new ElementHolder(); functionTypesToFix = new ArrayList<FunctionTypeImpl>(); visitChildren(holder, node); SimpleIdentifier className = node.getName(); ClassElementImpl element = new ClassElementImpl(className); element.setAbstract(node.getAbstractKeyword() != null); element.setTypedef(true); TypeParameterElement[] typeParameters = holder.getTypeParameters(); element.setTypeParameters(typeParameters); Type[] typeArguments = createTypeParameterTypes(typeParameters); InterfaceTypeImpl interfaceType = new InterfaceTypeImpl(element); interfaceType.setTypeArguments(typeArguments); element.setType(interfaceType); // set default constructor element.setConstructors(createDefaultConstructors(interfaceType)); for (FunctionTypeImpl functionType : functionTypesToFix) { functionType.setTypeArguments(typeArguments); } functionTypesToFix = null; currentHolder.addType(element); className.setStaticElement(element); holder.validate(); return null; } @Override public Void visitConstructorDeclaration(ConstructorDeclaration node) { isValidMixin = false; ElementHolder holder = new ElementHolder(); boolean wasInFunction = inFunction; inFunction = true; try { visitChildren(holder, node); } finally { inFunction = wasInFunction; } FunctionBody body = node.getBody(); SimpleIdentifier constructorName = node.getName(); ConstructorElementImpl element = new ConstructorElementImpl(constructorName); if (node.getFactoryKeyword() != null) { element.setFactory(true); } element.setFunctions(holder.getFunctions()); element.setLabels(holder.getLabels()); element.setLocalVariables(holder.getLocalVariables()); element.setParameters(holder.getParameters()); element.setConst(node.getConstKeyword() != null); if (body.isAsynchronous()) { element.setAsynchronous(true); } if (body.isGenerator()) { element.setGenerator(true); } currentHolder.addConstructor(element); node.setElement(element); if (constructorName == null) { Identifier returnType = node.getReturnType(); if (returnType != null) { element.setNameOffset(returnType.getOffset()); } } else { constructorName.setStaticElement(element); } holder.validate(); return null; } @Override public Void visitDeclaredIdentifier(DeclaredIdentifier node) { SimpleIdentifier variableName = node.getIdentifier(); Token keyword = node.getKeyword(); LocalVariableElementImpl element = new LocalVariableElementImpl(variableName); ForEachStatement statement = (ForEachStatement) node.getParent(); int declarationEnd = node.getOffset() + node.getLength(); int statementEnd = statement.getOffset() + statement.getLength(); element.setVisibleRange(declarationEnd, statementEnd - declarationEnd - 1); element.setConst(matches(keyword, Keyword.CONST)); element.setFinal(matches(keyword, Keyword.FINAL)); currentHolder.addLocalVariable(element); variableName.setStaticElement(element); return super.visitDeclaredIdentifier(node); } @Override public Void visitDefaultFormalParameter(DefaultFormalParameter node) { ElementHolder holder = new ElementHolder(); NormalFormalParameter normalParameter = node.getParameter(); SimpleIdentifier parameterName = normalParameter.getIdentifier(); ParameterElementImpl parameter; if (normalParameter instanceof FieldFormalParameter) { parameter = new DefaultFieldFormalParameterElementImpl(parameterName); FieldElement field = fieldMap == null ? null : fieldMap.get(parameterName.getName()); if (field != null) { ((DefaultFieldFormalParameterElementImpl) parameter).setField(field); } } else { parameter = new DefaultParameterElementImpl(parameterName); } parameter.setConst(node.isConst()); parameter.setFinal(node.isFinal()); parameter.setParameterKind(node.getKind()); // set initializer, default value range Expression defaultValue = node.getDefaultValue(); if (defaultValue != null) { visit(holder, defaultValue); FunctionElementImpl initializer = new FunctionElementImpl( defaultValue.getBeginToken().getOffset()); initializer.setFunctions(holder.getFunctions()); initializer.setLabels(holder.getLabels()); initializer.setLocalVariables(holder.getLocalVariables()); initializer.setParameters(holder.getParameters()); initializer.setSynthetic(true); parameter.setInitializer(initializer); parameter.setDefaultValueCode(defaultValue.toSource()); } // visible range setParameterVisibleRange(node, parameter); currentHolder.addParameter(parameter); parameterName.setStaticElement(parameter); normalParameter.accept(this); holder.validate(); return null; } @Override public Void visitEnumDeclaration(EnumDeclaration node) { SimpleIdentifier enumName = node.getName(); ClassElementImpl enumElement = new ClassElementImpl(enumName); enumElement.setEnum(true); InterfaceTypeImpl enumType = new InterfaceTypeImpl(enumElement); enumElement.setType(enumType); currentHolder.addEnum(enumElement); enumName.setStaticElement(enumElement); return super.visitEnumDeclaration(node); } @Override public Void visitFieldDeclaration(FieldDeclaration node) { boolean wasInField = inFieldContext; inFieldContext = true; try { node.visitChildren(this); } finally { inFieldContext = wasInField; } return null; } @Override public Void visitFieldFormalParameter(FieldFormalParameter node) { if (!(node.getParent() instanceof DefaultFormalParameter)) { SimpleIdentifier parameterName = node.getIdentifier(); FieldElement field = fieldMap == null ? null : fieldMap.get(parameterName.getName()); FieldFormalParameterElementImpl parameter = new FieldFormalParameterElementImpl(parameterName); parameter.setConst(node.isConst()); parameter.setFinal(node.isFinal()); parameter.setParameterKind(node.getKind()); if (field != null) { parameter.setField(field); } currentHolder.addParameter(parameter); parameterName.setStaticElement(parameter); } // // The children of this parameter include any parameters defined on the type of this parameter. // ElementHolder holder = new ElementHolder(); visitChildren(holder, node); ((ParameterElementImpl) node.getElement()).setParameters(holder.getParameters()); holder.validate(); return null; } @Override public Void visitFunctionDeclaration(FunctionDeclaration node) { FunctionExpression expression = node.getFunctionExpression(); if (expression != null) { ElementHolder holder = new ElementHolder(); boolean wasInFunction = inFunction; inFunction = true; try { visitChildren(holder, expression); } finally { inFunction = wasInFunction; } FunctionBody body = expression.getBody(); Token property = node.getPropertyKeyword(); if (property == null || inFunction) { SimpleIdentifier functionName = node.getName(); FunctionElementImpl element = new FunctionElementImpl(functionName); element.setFunctions(holder.getFunctions()); element.setLabels(holder.getLabels()); element.setLocalVariables(holder.getLocalVariables()); element.setParameters(holder.getParameters()); if (body.isAsynchronous()) { element.setAsynchronous(true); } if (body.isGenerator()) { element.setGenerator(true); } if (inFunction) { Block enclosingBlock = node.getAncestor(Block.class); if (enclosingBlock != null) { int functionEnd = node.getOffset() + node.getLength(); int blockEnd = enclosingBlock.getOffset() + enclosingBlock.getLength(); element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); } } currentHolder.addFunction(element); expression.setElement(element); functionName.setStaticElement(element); } else { SimpleIdentifier propertyNameNode = node.getName(); if (propertyNameNode == null) { // TODO(brianwilkerson) Report this internal error. return null; } String propertyName = propertyNameNode.getName(); TopLevelVariableElementImpl variable = (TopLevelVariableElementImpl) currentHolder.getTopLevelVariable(propertyName); if (variable == null) { variable = new TopLevelVariableElementImpl(node.getName().getName(), -1); variable.setFinal(true); variable.setSynthetic(true); currentHolder.addTopLevelVariable(variable); } if (matches(property, Keyword.GET)) { PropertyAccessorElementImpl getter = new PropertyAccessorElementImpl(propertyNameNode); getter.setFunctions(holder.getFunctions()); getter.setLabels(holder.getLabels()); getter.setLocalVariables(holder.getLocalVariables()); if (body.isAsynchronous()) { getter.setAsynchronous(true); } if (body.isGenerator()) { getter.setGenerator(true); } getter.setVariable(variable); getter.setGetter(true); getter.setStatic(true); variable.setGetter(getter); currentHolder.addAccessor(getter); expression.setElement(getter); propertyNameNode.setStaticElement(getter); } else { PropertyAccessorElementImpl setter = new PropertyAccessorElementImpl(propertyNameNode); setter.setFunctions(holder.getFunctions()); setter.setLabels(holder.getLabels()); setter.setLocalVariables(holder.getLocalVariables()); setter.setParameters(holder.getParameters()); if (body.isAsynchronous()) { setter.setAsynchronous(true); } if (body.isGenerator()) { setter.setGenerator(true); } setter.setVariable(variable); setter.setSetter(true); setter.setStatic(true); variable.setSetter(setter); variable.setFinal(false); currentHolder.addAccessor(setter); expression.setElement(setter); propertyNameNode.setStaticElement(setter); } } holder.validate(); } return null; } @Override public Void visitFunctionExpression(FunctionExpression node) { ElementHolder holder = new ElementHolder(); boolean wasInFunction = inFunction; inFunction = true; try { visitChildren(holder, node); } finally { inFunction = wasInFunction; } FunctionBody body = node.getBody(); FunctionElementImpl element = new FunctionElementImpl(node.getBeginToken().getOffset()); element.setFunctions(holder.getFunctions()); element.setLabels(holder.getLabels()); element.setLocalVariables(holder.getLocalVariables()); element.setParameters(holder.getParameters()); if (body.isAsynchronous()) { element.setAsynchronous(true); } if (body.isGenerator()) { element.setGenerator(true); } if (inFunction) { Block enclosingBlock = node.getAncestor(Block.class); if (enclosingBlock != null) { int functionEnd = node.getOffset() + node.getLength(); int blockEnd = enclosingBlock.getOffset() + enclosingBlock.getLength(); element.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); } } FunctionTypeImpl type = new FunctionTypeImpl(element); if (functionTypesToFix != null) { functionTypesToFix.add(type); } element.setType(type); currentHolder.addFunction(element); node.setElement(element); holder.validate(); return null; } @Override public Void visitFunctionTypeAlias(FunctionTypeAlias node) { ElementHolder holder = new ElementHolder(); visitChildren(holder, node); SimpleIdentifier aliasName = node.getName(); ParameterElement[] parameters = holder.getParameters(); TypeParameterElement[] typeParameters = holder.getTypeParameters(); FunctionTypeAliasElementImpl element = new FunctionTypeAliasElementImpl(aliasName); element.setParameters(parameters); element.setTypeParameters(typeParameters); FunctionTypeImpl type = new FunctionTypeImpl(element); type.setTypeArguments(createTypeParameterTypes(typeParameters)); element.setType(type); currentHolder.addTypeAlias(element); aliasName.setStaticElement(element); holder.validate(); return null; } @Override public Void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { if (!(node.getParent() instanceof DefaultFormalParameter)) { SimpleIdentifier parameterName = node.getIdentifier(); ParameterElementImpl parameter = new ParameterElementImpl(parameterName); parameter.setParameterKind(node.getKind()); setParameterVisibleRange(node, parameter); currentHolder.addParameter(parameter); parameterName.setStaticElement(parameter); } // // The children of this parameter include any parameters defined on the type of this parameter. // ElementHolder holder = new ElementHolder(); visitChildren(holder, node); ((ParameterElementImpl) node.getElement()).setParameters(holder.getParameters()); holder.validate(); return null; } @Override public Void visitLabeledStatement(LabeledStatement node) { boolean onSwitchStatement = node.getStatement() instanceof SwitchStatement; for (Label label : node.getLabels()) { SimpleIdentifier labelName = label.getLabel(); LabelElementImpl element = new LabelElementImpl(labelName, onSwitchStatement, false); currentHolder.addLabel(element); labelName.setStaticElement(element); } return super.visitLabeledStatement(node); } @Override public Void visitMethodDeclaration(MethodDeclaration node) { try { ElementHolder holder = new ElementHolder(); boolean wasInFunction = inFunction; inFunction = true; try { visitChildren(holder, node); } finally { inFunction = wasInFunction; } boolean isStatic = node.isStatic(); Token property = node.getPropertyKeyword(); FunctionBody body = node.getBody(); if (property == null) { SimpleIdentifier methodName = node.getName(); String nameOfMethod = methodName.getName(); if (nameOfMethod.equals(TokenType.MINUS.getLexeme()) && node.getParameters().getParameters().size() == 0) { nameOfMethod = "unary-"; } MethodElementImpl element = new MethodElementImpl(nameOfMethod, methodName.getOffset()); element.setAbstract(node.isAbstract()); element.setFunctions(holder.getFunctions()); element.setLabels(holder.getLabels()); element.setLocalVariables(holder.getLocalVariables()); element.setParameters(holder.getParameters()); element.setStatic(isStatic); if (body.isAsynchronous()) { element.setAsynchronous(true); } if (body.isGenerator()) { element.setGenerator(true); } currentHolder.addMethod(element); methodName.setStaticElement(element); } else { SimpleIdentifier propertyNameNode = node.getName(); String propertyName = propertyNameNode.getName(); FieldElementImpl field = (FieldElementImpl) currentHolder.getField(propertyName); if (field == null) { field = new FieldElementImpl(node.getName().getName(), -1); field.setFinal(true); field.setStatic(isStatic); field.setSynthetic(true); currentHolder.addField(field); } if (matches(property, Keyword.GET)) { PropertyAccessorElementImpl getter = new PropertyAccessorElementImpl(propertyNameNode); getter.setFunctions(holder.getFunctions()); getter.setLabels(holder.getLabels()); getter.setLocalVariables(holder.getLocalVariables()); if (body.isAsynchronous()) { getter.setAsynchronous(true); } if (body.isGenerator()) { getter.setGenerator(true); } getter.setVariable(field); getter.setAbstract(body instanceof EmptyFunctionBody && node.getExternalKeyword() == null); getter.setGetter(true); getter.setStatic(isStatic); field.setGetter(getter); currentHolder.addAccessor(getter); propertyNameNode.setStaticElement(getter); } else { PropertyAccessorElementImpl setter = new PropertyAccessorElementImpl(propertyNameNode); setter.setFunctions(holder.getFunctions()); setter.setLabels(holder.getLabels()); setter.setLocalVariables(holder.getLocalVariables()); setter.setParameters(holder.getParameters()); if (body.isAsynchronous()) { setter.setAsynchronous(true); } if (body.isGenerator()) { setter.setGenerator(true); } setter.setVariable(field); setter.setAbstract(body instanceof EmptyFunctionBody && !matches(node.getExternalKeyword(), Keyword.EXTERNAL)); setter.setSetter(true); setter.setStatic(isStatic); field.setSetter(setter); field.setFinal(false); currentHolder.addAccessor(setter); propertyNameNode.setStaticElement(setter); } } holder.validate(); } catch (Exception ex) { if (node.getName().getStaticElement() == null) { ClassDeclaration classNode = node.getAncestor(ClassDeclaration.class); StringBuilder builder = new StringBuilder(); builder.append("The element for the method "); builder.append(node.getName()); builder.append(" in "); builder.append(classNode.getName()); builder.append(" was not set while trying to build the element model."); AnalysisEngine.getInstance().getLogger().logError( builder.toString(), new AnalysisException(builder.toString(), ex)); } else { String message = "Exception caught in ElementBuilder.visitMethodDeclaration()"; AnalysisEngine.getInstance().getLogger().logError( message, new AnalysisException(message, ex)); } } finally { if (node.getName().getStaticElement() == null) { ClassDeclaration classNode = node.getAncestor(ClassDeclaration.class); StringBuilder builder = new StringBuilder(); builder.append("The element for the method "); builder.append(node.getName()); builder.append(" in "); builder.append(classNode.getName()); builder.append(" was not set while trying to resolve types."); AnalysisEngine.getInstance().getLogger().logError( builder.toString(), new AnalysisException(builder.toString())); } } return null; } @Override public Void visitSimpleFormalParameter(SimpleFormalParameter node) { if (!(node.getParent() instanceof DefaultFormalParameter)) { SimpleIdentifier parameterName = node.getIdentifier(); ParameterElementImpl parameter = new ParameterElementImpl(parameterName); parameter.setConst(node.isConst()); parameter.setFinal(node.isFinal()); parameter.setParameterKind(node.getKind()); setParameterVisibleRange(node, parameter); currentHolder.addParameter(parameter); parameterName.setStaticElement(parameter); } return super.visitSimpleFormalParameter(node); } @Override public Void visitSuperExpression(SuperExpression node) { isValidMixin = false; return super.visitSuperExpression(node); } @Override public Void visitSwitchCase(SwitchCase node) { for (Label label : node.getLabels()) { SimpleIdentifier labelName = label.getLabel(); LabelElementImpl element = new LabelElementImpl(labelName, false, true); currentHolder.addLabel(element); labelName.setStaticElement(element); } return super.visitSwitchCase(node); } @Override public Void visitSwitchDefault(SwitchDefault node) { for (Label label : node.getLabels()) { SimpleIdentifier labelName = label.getLabel(); LabelElementImpl element = new LabelElementImpl(labelName, false, true); currentHolder.addLabel(element); labelName.setStaticElement(element); } return super.visitSwitchDefault(node); } @Override public Void visitTypeParameter(TypeParameter node) { SimpleIdentifier parameterName = node.getName(); TypeParameterElementImpl typeParameter = new TypeParameterElementImpl(parameterName); TypeParameterTypeImpl typeParameterType = new TypeParameterTypeImpl(typeParameter); typeParameter.setType(typeParameterType); currentHolder.addTypeParameter(typeParameter); parameterName.setStaticElement(typeParameter); return super.visitTypeParameter(node); } @Override public Void visitVariableDeclaration(VariableDeclaration node) { Token keyword = ((VariableDeclarationList) node.getParent()).getKeyword(); boolean isConst = matches(keyword, Keyword.CONST); boolean isFinal = matches(keyword, Keyword.FINAL); boolean hasInitializer = node.getInitializer() != null; VariableElementImpl element; if (inFieldContext) { SimpleIdentifier fieldName = node.getName(); FieldElementImpl field; if (isConst && hasInitializer) { field = new ConstFieldElementImpl(fieldName); } else { field = new FieldElementImpl(fieldName); } element = field; currentHolder.addField(field); fieldName.setStaticElement(field); } else if (inFunction) { SimpleIdentifier variableName = node.getName(); LocalVariableElementImpl variable; if (isConst && hasInitializer) { variable = new ConstLocalVariableElementImpl(variableName); } else { variable = new LocalVariableElementImpl(variableName); } element = variable; Block enclosingBlock = node.getAncestor(Block.class); int functionEnd = node.getOffset() + node.getLength(); int blockEnd = enclosingBlock.getOffset() + enclosingBlock.getLength(); // TODO(brianwilkerson) This isn't right for variables declared in a for loop. variable.setVisibleRange(functionEnd, blockEnd - functionEnd - 1); currentHolder.addLocalVariable(variable); variableName.setStaticElement(element); } else { SimpleIdentifier variableName = node.getName(); TopLevelVariableElementImpl variable; if (isConst && hasInitializer) { variable = new ConstTopLevelVariableElementImpl(variableName); } else { variable = new TopLevelVariableElementImpl(variableName); } element = variable; currentHolder.addTopLevelVariable(variable); variableName.setStaticElement(element); } element.setConst(isConst); element.setFinal(isFinal); if (hasInitializer) { ElementHolder holder = new ElementHolder(); boolean wasInFieldContext = inFieldContext; inFieldContext = false; try { visit(holder, node.getInitializer()); } finally { inFieldContext = wasInFieldContext; } FunctionElementImpl initializer = new FunctionElementImpl( node.getInitializer().getBeginToken().getOffset()); initializer.setFunctions(holder.getFunctions()); initializer.setLabels(holder.getLabels()); initializer.setLocalVariables(holder.getLocalVariables()); initializer.setSynthetic(true); element.setInitializer(initializer); holder.validate(); } if (element instanceof PropertyInducingElementImpl) { PropertyInducingElementImpl variable = (PropertyInducingElementImpl) element; if (inFieldContext) { ((FieldElementImpl) variable).setStatic(matches( ((FieldDeclaration) node.getParent().getParent()).getStaticKeyword(), Keyword.STATIC)); } PropertyAccessorElementImpl getter = new PropertyAccessorElementImpl(variable); getter.setGetter(true); currentHolder.addAccessor(getter); variable.setGetter(getter); if (!isFinal) { PropertyAccessorElementImpl setter = new PropertyAccessorElementImpl(variable); setter.setSetter(true); ParameterElementImpl parameter = new ParameterElementImpl( "_" + variable.getName(), variable.getNameOffset()); parameter.setSynthetic(true); parameter.setParameterKind(ParameterKind.REQUIRED); setter.setParameters(new ParameterElement[] {parameter}); currentHolder.addAccessor(setter); variable.setSetter(setter); } } return null; } /** * Build the table mapping field names to field elements for the fields defined in the current * class. * * @param fields the field elements defined in the current class */ private void buildFieldMap(FieldElement[] fields) { fieldMap = new HashMap<String, FieldElement>(); int count = fields.length; for (int i = 0; i < count; i++) { FieldElement field = fields[i]; fieldMap.put(field.getName(), field); } } /** * Creates the {@link ConstructorElement}s array with the single default constructor element. * * @param interfaceType the interface type for which to create a default constructor * @return the {@link ConstructorElement}s array with the single default constructor element */ private ConstructorElement[] createDefaultConstructors(InterfaceTypeImpl interfaceType) { ConstructorElementImpl constructor = new ConstructorElementImpl(null); constructor.setSynthetic(true); constructor.setReturnType(interfaceType); FunctionTypeImpl type = new FunctionTypeImpl(constructor); functionTypesToFix.add(type); constructor.setType(type); return new ConstructorElement[] {constructor}; } /** * Create the types associated with the given type parameters, setting the type of each type * parameter, and return an array of types corresponding to the given parameters. * * @param typeParameters the type parameters for which types are to be created * @return an array of types corresponding to the given parameters */ private Type[] createTypeParameterTypes(TypeParameterElement[] typeParameters) { int typeParameterCount = typeParameters.length; Type[] typeArguments = new Type[typeParameterCount]; for (int i = 0; i < typeParameterCount; i++) { TypeParameterElementImpl typeParameter = (TypeParameterElementImpl) typeParameters[i]; TypeParameterTypeImpl typeParameterType = new TypeParameterTypeImpl(typeParameter); typeParameter.setType(typeParameterType); typeArguments[i] = typeParameterType; } return typeArguments; } /** * Return the body of the function that contains the given parameter, or {@code null} if no * function body could be found. * * @param node the parameter contained in the function whose body is to be returned * @return the body of the function that contains the given parameter */ private FunctionBody getFunctionBody(FormalParameter node) { AstNode parent = node.getParent(); while (parent != null) { if (parent instanceof ConstructorDeclaration) { return ((ConstructorDeclaration) parent).getBody(); } else if (parent instanceof FunctionExpression) { return ((FunctionExpression) parent).getBody(); } else if (parent instanceof MethodDeclaration) { return ((MethodDeclaration) parent).getBody(); } parent = parent.getParent(); } return null; } /** * Return {@code true} if the given token is a token for the given keyword. * * @param token the token being tested * @param keyword the keyword being tested for * @return {@code true} if the given token is a token for the given keyword */ private boolean matches(Token token, Keyword keyword) { return token != null && token.getType() == TokenType.KEYWORD && ((KeywordToken) token).getKeyword() == keyword; } /** * Sets the visible source range for formal parameter. */ private void setParameterVisibleRange(FormalParameter node, ParameterElementImpl element) { FunctionBody body = getFunctionBody(node); if (body != null) { element.setVisibleRange(body.getOffset(), body.getLength()); } } /** * Make the given holder be the current holder while visiting the given node. * * @param holder the holder that will gather elements that are built while visiting the children * @param node the node to be visited */ private void visit(ElementHolder holder, AstNode node) { if (node != null) { ElementHolder previousHolder = currentHolder; currentHolder = holder; try { node.accept(this); } finally { currentHolder = previousHolder; } } } /** * Make the given holder be the current holder while visiting the children of the given node. * * @param holder the holder that will gather elements that are built while visiting the children * @param node the node whose children are to be visited */ private void visitChildren(ElementHolder holder, AstNode node) { if (node != null) { ElementHolder previousHolder = currentHolder; currentHolder = holder; try { node.visitChildren(this); } finally { currentHolder = previousHolder; } } } }