blob: b0aff013815cae184dd12993694ccf49de36194a [file] [log] [blame]
// Copyright (c) 2018, 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 'dart:async';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/generated/source.dart' show SourceKind;
import 'package:analyzer_fe_comparison/src/comparison_node.dart';
/// Analyzes the project located at [libPath] using the analyzer, and returns a
/// [ComparisonNode] representing it.
Future<ComparisonNode> driveAnalyzer(String libPath) async {
var contextCollection = AnalysisContextCollection(includedPaths: [libPath]);
var contexts = contextCollection.contexts;
if (contexts.length != 1) {
throw new StateError('Expected exactly one context');
}
var context = contexts[0];
var session = context.currentSession;
var uriConverter = session.uriConverter;
var contextRoot = context.contextRoot;
var libraryNodes = <ComparisonNode>[];
for (var filePath in contextRoot.analyzedFiles()) {
var kind = await session.getSourceKind(filePath);
if (kind == SourceKind.LIBRARY) {
var importUri = uriConverter.pathToUri(filePath);
var libraryElement = await session.getLibraryByUri(importUri.toString());
var childNodes = <ComparisonNode>[];
if (libraryElement.name.isNotEmpty) {
childNodes.add(ComparisonNode('name=${libraryElement.name}'));
}
for (var compilationUnit in libraryElement.units) {
var unitResult =
await session.getResolvedAst(compilationUnit.source.fullName);
_AnalyzerVisitor(childNodes)._visitList(unitResult.unit.declarations);
}
libraryNodes.add(ComparisonNode.sorted(importUri.toString(), childNodes));
}
}
return ComparisonNode.sorted('Component', libraryNodes);
}
/// Visitor for serializing the contents of an analyzer AST into
/// ComparisonNodes.
///
/// Results are accumulated into [_resultNodes].
class _AnalyzerVisitor extends UnifyingAstVisitor<void> {
final List<ComparisonNode> _resultNodes;
_AnalyzerVisitor(this._resultNodes);
@override
void visitClassDeclaration(ClassDeclaration node) {
var children = <ComparisonNode>[];
_AnalyzerVisitor(children)._visitList(node.members);
_resultNodes
.add(ComparisonNode.sorted('Class ${node.name.name}', children));
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
_resultNodes
.add(ComparisonNode('Constructor ${node.name?.name ?? '(unnamed)'}'));
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
var children = <ComparisonNode>[];
for (var enumValue in node.constants) {
children.add(ComparisonNode('EnumValue ${enumValue.name.name}'));
}
_resultNodes.add(ComparisonNode.sorted('Enum ${node.name.name}', children));
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
node.fields.accept(this);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
String kind;
if (node.isGetter) {
kind = 'Getter';
} else if (node.isSetter) {
kind = 'Setter';
} else {
// Kernel calls top level functions "methods".
kind = 'Method';
}
_resultNodes.add(ComparisonNode('$kind ${node.name.name}'));
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_resultNodes.add(ComparisonNode('Typedef ${node.name.name}'));
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
String kind;
if (node.isGetter) {
kind = 'Getter';
} else if (node.isSetter) {
kind = 'Setter';
} else if (node.isOperator) {
kind = 'Operator';
} else {
kind = 'Method';
}
_resultNodes.add(ComparisonNode('$kind ${node.name.name}'));
}
@override
Null visitNode(AstNode node) {
throw new UnimplementedError('AnalyzerVisitor: ${node.runtimeType}');
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
node.variables.accept(this);
}
@override
void visitVariableDeclarationList(VariableDeclarationList node) {
for (var variableDeclaration in node.variables) {
// Kernel calls both fields and top level variable declarations "fields".
_resultNodes
.add(ComparisonNode('Field ${variableDeclaration.name.name}'));
}
}
/// Visits all the nodes in [nodes].
void _visitList(List<AstNode> nodes) {
for (var astNode in nodes) {
astNode.accept(this);
}
}
}