blob: e7161ca1a6deb8f65517f9176e48fa7fdfea83dd [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 visitor = _AnalyzerVisitor();
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);
for (var astNode in unitResult.unit.declarations) {
childNodes.addAll(astNode.accept(visitor));
}
}
libraryNodes.add(ComparisonNode.sorted(importUri.toString(), childNodes));
}
}
return ComparisonNode.sorted('Component', libraryNodes);
}
/// Visitor for serializing the contents of an analyzer AST into
/// ComparisonNodes.
class _AnalyzerVisitor extends UnifyingAstVisitor<Iterable<ComparisonNode>> {
@override
List<ComparisonNode> visitClassDeclaration(ClassDeclaration node) {
return [ComparisonNode('Class ${node.name.name}')];
}
@override
List<ComparisonNode> visitEnumDeclaration(EnumDeclaration node) {
return [ComparisonNode('Enum ${node.name.name}')];
}
@override
List<ComparisonNode> 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';
}
return [ComparisonNode('$kind ${node.name.name}')];
}
@override
List<ComparisonNode> visitFunctionTypeAlias(FunctionTypeAlias node) {
return [ComparisonNode('Typedef ${node.name.name}')];
}
@override
Null visitNode(AstNode node) {
throw new UnimplementedError('AnalyzerVisitor: ${node.runtimeType}');
}
@override
Iterable<ComparisonNode> visitTopLevelVariableDeclaration(
TopLevelVariableDeclaration node) sync* {
for (var variableDeclaration in node.variables.variables) {
// Kernel calls top level variable declarations "fields".
yield ComparisonNode('Field ${variableDeclaration.name.name}');
}
}
}