| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/ast/ast.dart'; |
| import 'package:analyzer/src/dart/element/builder.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| |
| /// This class takes a [CompilationUnitElement] lazily resynthesized from a |
| /// fully resolved, but partial AST (contains only APIs), and full unresolved |
| /// AST - and splices them into a single AST with all declaration nodes |
| /// fully resolved, and function bodies and variable initializers unresolved. |
| /// |
| class DeclarationSplicer { |
| final CompilationUnitElementImpl _unitElement; |
| |
| _ElementWalker _walker; |
| |
| DeclarationSplicer(this._unitElement); |
| |
| void splice(CompilationUnit full) { |
| var partialNode = _unitElement.linkedContext.readUnitEagerly(); |
| _directives(full, partialNode); |
| _declarations(full, partialNode); |
| } |
| |
| FunctionBody _body(FunctionBody full) { |
| var holder = ElementHolder(); |
| |
| var elementBuilder = LocalElementBuilder(holder, null); |
| full.accept(elementBuilder); |
| |
| var element = _walker.element; |
| if (element is ExecutableElementImpl) { |
| element.encloseElements(holder.functions); |
| element.encloseElements(holder.labels); |
| element.encloseElements(holder.localVariables); |
| } |
| |
| return full; |
| } |
| |
| void _classDeclaration(ClassDeclaration full, ClassDeclaration partial) { |
| var element = _walker.getClass(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forClass(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| var fullList = full.members; |
| var partialList = partial.members; |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _classTypeAlias(ClassTypeAlias full, ClassTypeAlias partial) { |
| var element = _walker.getClass(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forClass(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _constructorDeclaration( |
| ConstructorDeclaration full, |
| ConstructorDeclaration partial, |
| ) { |
| var element = _walker.getConstructor(); |
| _match(partial.name, element); |
| (partial as ConstructorDeclarationImpl).declaredElement = element; |
| _walk(_ElementWalker.forExecutable(element), () { |
| _node(full.parameters, partial.parameters); |
| // TODO(scheglov) Not very nice. |
| if (full.initializers.isNotEmpty && partial.initializers.isEmpty) { |
| partial.initializers.addAll(full.initializers); |
| } |
| partial.body = _body(full.body); |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _declarations(CompilationUnit full, CompilationUnit partial) { |
| _walk(_ElementWalker.forCompilationUnit(_unitElement), () { |
| var fullList = full.declarations; |
| var partialList = partial.declarations; |
| for (var i = 0; i < fullList.length; ++i) { |
| var partialNode = _node(fullList[i], partialList[i]); |
| fullList[i] = partialNode; |
| } |
| }); |
| } |
| |
| void _directives(CompilationUnit full, CompilationUnit partial) { |
| var libraryElement = _unitElement.library; |
| var exportIndex = 0; |
| var importIndex = 0; |
| var partIndex = 0; |
| for (var directive in full.directives) { |
| if (directive is ExportDirective) { |
| var element = libraryElement.exports[exportIndex++]; |
| _metadata(directive.metadata, element); |
| } else if (directive is ImportDirective) { |
| var element = libraryElement.imports[importIndex++]; |
| _metadata(directive.metadata, element); |
| } else if (directive is LibraryDirective) { |
| var element = libraryElement; |
| _metadata(directive.metadata, element); |
| } else if (directive is PartDirective) { |
| var element = libraryElement.parts[partIndex++]; |
| _metadata(directive.metadata, element); |
| } |
| } |
| } |
| |
| void _enumConstantDeclaration( |
| EnumConstantDeclaration full, EnumConstantDeclaration partial) { |
| var element = _walker.getVariable(); |
| _match(partial.name, element); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _enumDeclaration(EnumDeclaration full, EnumDeclaration partial) { |
| var element = _walker.getEnum(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forClass(element), () { |
| var fullList = full.constants; |
| var partialList = partial.constants; |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _fieldDeclaration(FieldDeclaration full, FieldDeclaration partial) { |
| _node(full.fields, partial.fields); |
| |
| var first = partial.fields.variables[0]; |
| _metadata(partial.metadata, first.declaredElement); |
| } |
| |
| void _fieldFormalParameter( |
| FieldFormalParameter full, |
| FieldFormalParameter partial, |
| ) { |
| var element = _walker.getParameter(); |
| _match(partial.identifier, element); |
| _walk(_ElementWalker.forParameter(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.parameters, partial.parameters); |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _formalParameterList( |
| FormalParameterList full, |
| FormalParameterList partial, |
| ) { |
| var fullList = full.parameters; |
| var partialList = partial.parameters; |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| } |
| |
| void _functionDeclaration( |
| FunctionDeclaration full, |
| FunctionDeclaration partial, |
| ) { |
| var element = partial.propertyKeyword == null |
| ? _walker.getFunction() |
| : _walker.getAccessor(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forExecutable(element), () { |
| _node(full.functionExpression, partial.functionExpression); |
| }); |
| (partial.functionExpression as FunctionExpressionImpl).declaredElement = |
| element; |
| _metadata(partial.metadata, element); |
| _node(full.returnType, partial.returnType); |
| } |
| |
| void _functionExpression( |
| FunctionExpression full, |
| FunctionExpression partial, |
| ) { |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.parameters, partial.parameters); |
| partial.body = _body(full.body); |
| } |
| |
| void _functionTypeAlias(FunctionTypeAlias full, FunctionTypeAlias partial) { |
| var element = _walker.getTypedef(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forGenericTypeAlias(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.parameters, partial.parameters); |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _functionTypedFormalParameter( |
| FunctionTypedFormalParameter full, |
| FunctionTypedFormalParameter partial, |
| ) { |
| var element = _walker.getParameter(); |
| _match(partial.identifier, element); |
| _walk(_ElementWalker.forParameter(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.parameters, partial.parameters); |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _genericFunctionType( |
| GenericFunctionType full, |
| GenericFunctionType partial, |
| ) { |
| var element = (partial as GenericFunctionTypeImpl).declaredElement; |
| _walk(_ElementWalker.forGenericFunctionType(element), () { |
| _node(full.returnType, partial.returnType); |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.parameters, partial.parameters); |
| }); |
| } |
| |
| void _genericTypeAlias(GenericTypeAlias full, GenericTypeAlias partial) { |
| var element = _walker.getTypedef(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forGenericTypeAlias(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.functionType, partial.functionType); |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| /// Updates [node] to point to [element], after ensuring that the |
| /// element has the expected name. |
| E _match<E extends Element>(SimpleIdentifier node, E element) { |
| // TODO(scheglov) has troubles with getter/setter. |
| // if (element.name != node.name) { |
| // throw new StateError( |
| // 'Expected an element matching `${node.name}`, got `${element.name}`', |
| // ); |
| // } |
| if (node != null) { |
| node.staticElement = element; |
| } |
| return element; |
| } |
| |
| void _methodDeclaration(MethodDeclaration full, MethodDeclaration partial) { |
| var element = partial.propertyKeyword == null |
| ? _walker.getFunction() |
| : _walker.getAccessor(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forExecutable(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| _node(full.parameters, partial.parameters); |
| partial.body = _body(full.body); |
| }); |
| _metadata(partial.metadata, element); |
| _node(full.returnType, partial.returnType); |
| } |
| |
| void _mixinDeclaration(MixinDeclaration full, MixinDeclaration partial) { |
| var element = _walker.getMixin(); |
| _match(partial.name, element); |
| _walk(_ElementWalker.forClass(element), () { |
| _node(full.typeParameters, partial.typeParameters); |
| var fullList = full.members; |
| var partialList = partial.members; |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| }); |
| _metadata(partial.metadata, element); |
| } |
| |
| AstNode _node(AstNode full, AstNode partial) { |
| if (full == null && partial == null) { |
| return partial; |
| } else if (full is ClassDeclaration && partial is ClassDeclaration) { |
| _classDeclaration(full, partial); |
| return partial; |
| } else if (full is ClassTypeAlias && partial is ClassTypeAlias) { |
| _classTypeAlias(full, partial); |
| return partial; |
| } else if (full is ConstructorDeclaration && |
| partial is ConstructorDeclaration) { |
| _constructorDeclaration(full, partial); |
| return partial; |
| } else if (full is DefaultFormalParameter && |
| partial is DefaultFormalParameter) { |
| _node(full.parameter, partial.parameter); |
| return partial; |
| } else if (full is EnumConstantDeclaration && |
| partial is EnumConstantDeclaration) { |
| _enumConstantDeclaration(full, partial); |
| return partial; |
| } else if (full is EnumDeclaration && partial is EnumDeclaration) { |
| _enumDeclaration(full, partial); |
| return partial; |
| } else if (full is FieldDeclaration && partial is FieldDeclaration) { |
| _fieldDeclaration(full, partial); |
| return partial; |
| } else if (full is FieldFormalParameter && |
| partial is FieldFormalParameter) { |
| _fieldFormalParameter(full, partial); |
| return partial; |
| } else if (full is FormalParameterList && partial is FormalParameterList) { |
| _formalParameterList(full, partial); |
| return partial; |
| } else if (full is FunctionDeclaration && partial is FunctionDeclaration) { |
| _functionDeclaration(full, partial); |
| return partial; |
| } else if (full is FunctionExpression && partial is FunctionExpression) { |
| _functionExpression(full, partial); |
| return partial; |
| } else if (full is FunctionTypedFormalParameter && |
| partial is FunctionTypedFormalParameter) { |
| _functionTypedFormalParameter(full, partial); |
| return partial; |
| } else if (full is FunctionTypeAlias && partial is FunctionTypeAlias) { |
| _functionTypeAlias(full, partial); |
| return partial; |
| } else if (full is GenericFunctionType && partial is GenericFunctionType) { |
| _genericFunctionType(full, partial); |
| return partial; |
| } else if (full is GenericTypeAlias && partial is GenericTypeAlias) { |
| _genericTypeAlias(full, partial); |
| return partial; |
| } else if (full is MethodDeclaration && partial is MethodDeclaration) { |
| _methodDeclaration(full, partial); |
| return partial; |
| } else if (full is MixinDeclaration && partial is MixinDeclaration) { |
| _mixinDeclaration(full, partial); |
| return partial; |
| } else if (full is SimpleFormalParameter && |
| partial is SimpleFormalParameter) { |
| _simpleFormalParameter(full, partial); |
| return partial; |
| } else if (full is TopLevelVariableDeclaration && |
| partial is TopLevelVariableDeclaration) { |
| _topLevelVariableDeclaration(full, partial); |
| return partial; |
| } else if (full is TypeName && partial is TypeName) { |
| _typeName(full, partial); |
| return partial; |
| } else if (full is TypeParameter && partial is TypeParameter) { |
| _typeParameter(full, partial); |
| return partial; |
| } else if (full is TypeParameterList && partial is TypeParameterList) { |
| _typeParameterList(full, partial); |
| return partial; |
| } else if (full is VariableDeclaration && partial is VariableDeclaration) { |
| _variableDeclaration(full, partial); |
| return partial; |
| } else if (full is VariableDeclarationList && |
| partial is VariableDeclarationList) { |
| _variableDeclarationList(full, partial); |
| return partial; |
| } else { |
| throw UnimplementedError( |
| '${full.runtimeType} and ${partial.runtimeType}', |
| ); |
| } |
| } |
| |
| void _simpleFormalParameter( |
| SimpleFormalParameter full, |
| SimpleFormalParameter partial, |
| ) { |
| var element = _walker.getParameter(); |
| _match(partial.identifier, element); |
| (partial as SimpleFormalParameterImpl).declaredElement = element; |
| _metadata(partial.metadata, element); |
| _node(full.type, partial.type); |
| } |
| |
| void _topLevelVariableDeclaration( |
| TopLevelVariableDeclaration full, |
| TopLevelVariableDeclaration partial, |
| ) { |
| _node(full.variables, partial.variables); |
| |
| var first = partial.variables.variables[0]; |
| _metadata(partial.metadata, first.declaredElement); |
| } |
| |
| void _typeName(TypeName full, TypeName partial) { |
| var fullList = full.typeArguments?.arguments; |
| var partialList = partial.typeArguments?.arguments; |
| if (fullList != null && partialList != null) { |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| } |
| } |
| |
| void _typeParameter(TypeParameter full, TypeParameter partial) { |
| var element = _walker.getTypeParameter(); |
| _match(partial.name, element); |
| _node(full.bound, partial.bound); |
| _metadata(partial.metadata, element); |
| } |
| |
| void _typeParameterList(TypeParameterList full, TypeParameterList partial) { |
| var fullList = full.typeParameters; |
| var partialList = partial.typeParameters; |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| } |
| |
| void _variableDeclaration( |
| VariableDeclaration full, |
| VariableDeclaration partial, |
| ) { |
| var element = _walker.getVariable(); |
| _match(partial.name, element); |
| partial.initializer = full.initializer; |
| { |
| var elementBuilder = LocalElementBuilder(ElementHolder(), null); |
| partial.initializer?.accept(elementBuilder); |
| } |
| } |
| |
| void _variableDeclarationList( |
| VariableDeclarationList full, |
| VariableDeclarationList partial, |
| ) { |
| _node(full.type, partial.type); |
| |
| var fullList = full.variables; |
| var partialList = partial.variables; |
| for (var i = 0; i < fullList.length; ++i) { |
| _node(fullList[i], partialList[i]); |
| } |
| } |
| |
| void _walk(_ElementWalker walker, void f()) { |
| var outer = _walker; |
| _walker = walker; |
| f(); |
| _walker = outer; |
| } |
| |
| /// Associate [nodes] with the corresponding [ElementAnnotation]s. |
| static void _metadata(List<Annotation> nodes, Element element) { |
| var elements = element.metadata; |
| if (nodes.length != elements.length) { |
| throw StateError('Found ${nodes.length} annotation nodes and ' |
| '${elements.length} element annotations'); |
| } |
| for (var i = 0; i < nodes.length; i++) { |
| nodes[i].elementAnnotation = elements[i]; |
| } |
| } |
| } |
| |
| class _ElementWalker { |
| final Element element; |
| |
| List<PropertyAccessorElement> _accessors; |
| int _accessorIndex = 0; |
| |
| List<ClassElement> _classes; |
| int _classIndex = 0; |
| |
| List<ConstructorElement> _constructors; |
| int _constructorIndex = 0; |
| |
| List<ClassElement> _enums; |
| int _enumIndex = 0; |
| |
| List<ExecutableElement> _functions; |
| int _functionIndex = 0; |
| |
| List<ClassElement> _mixins; |
| int _mixinIndex = 0; |
| |
| List<ParameterElement> _parameters; |
| int _parameterIndex = 0; |
| |
| List<FunctionTypeAliasElement> _typedefs; |
| int _typedefIndex = 0; |
| |
| List<TypeParameterElement> _typeParameters; |
| int _typeParameterIndex = 0; |
| |
| List<VariableElement> _variables; |
| int _variableIndex = 0; |
| |
| _ElementWalker.forClass(ClassElement element) |
| : element = element, |
| _accessors = element.accessors.where(_isNotSynthetic).toList(), |
| _constructors = element.isMixinApplication |
| ? null |
| : element.constructors.where(_isNotSynthetic).toList(), |
| _functions = element.methods, |
| _typeParameters = element.typeParameters, |
| _variables = element.fields.where(_isNotSynthetic).toList(); |
| |
| _ElementWalker.forCompilationUnit(CompilationUnitElement element) |
| : element = element, |
| _accessors = element.accessors.where(_isNotSynthetic).toList(), |
| _classes = element.types, |
| _enums = element.enums, |
| _functions = element.functions, |
| _mixins = element.mixins, |
| _typedefs = element.functionTypeAliases, |
| _variables = element.topLevelVariables.where(_isNotSynthetic).toList(); |
| |
| _ElementWalker.forExecutable(ExecutableElement element) |
| : element = element, |
| _parameters = element.parameters, |
| _typeParameters = element.typeParameters; |
| |
| _ElementWalker.forGenericFunctionType(GenericFunctionTypeElement element) |
| : element = element, |
| _parameters = element.parameters, |
| _typeParameters = element.typeParameters; |
| |
| _ElementWalker.forGenericTypeAlias(FunctionTypeAliasElement element) |
| : element = element, |
| _parameters = element.parameters, |
| _typeParameters = element.typeParameters; |
| |
| _ElementWalker.forParameter(ParameterElement element) |
| : element = element, |
| _parameters = element.parameters, |
| _typeParameters = element.typeParameters; |
| |
| PropertyAccessorElement getAccessor() { |
| return _accessors[_accessorIndex++]; |
| } |
| |
| ClassElement getClass() { |
| return _classes[_classIndex++]; |
| } |
| |
| ConstructorElement getConstructor() { |
| return _constructors[_constructorIndex++]; |
| } |
| |
| ClassElement getEnum() { |
| return _enums[_enumIndex++]; |
| } |
| |
| ExecutableElement getFunction() { |
| return _functions[_functionIndex++]; |
| } |
| |
| ClassElement getMixin() { |
| return _mixins[_mixinIndex++]; |
| } |
| |
| ParameterElement getParameter() { |
| return _parameters[_parameterIndex++]; |
| } |
| |
| FunctionTypeAliasElement getTypedef() { |
| return _typedefs[_typedefIndex++]; |
| } |
| |
| TypeParameterElement getTypeParameter() { |
| return _typeParameters[_typeParameterIndex++]; |
| } |
| |
| VariableElement getVariable() { |
| return _variables[_variableIndex++]; |
| } |
| |
| static bool _isNotSynthetic(Element e) => !e.isSynthetic; |
| } |