| // Copyright 2014 The Flutter Authors. 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:io' show Directory; | |
| import 'package:analyzer/dart/analysis/analysis_context.dart'; | |
| import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; | |
| import 'package:analyzer/dart/analysis/results.dart'; | |
| import 'package:analyzer/dart/analysis/session.dart'; | |
| import 'package:path/path.dart' as path; | |
| import '../utils.dart'; | |
| /// Analyzes the dart source files in the given `flutterRootDirectory` with the | |
| /// given [AnalyzeRule]s. | |
| /// | |
| /// The `includePath` parameter takes a collection of paths relative to the given | |
| /// `flutterRootDirectory`. It specifies the files or directory this function | |
| /// should analyze. Defaults to null in which case this function analyzes the | |
| /// all dart source files in `flutterRootDirectory`. | |
| /// | |
| /// The `excludePath` parameter takes a collection of paths relative to the given | |
| /// `flutterRootDirectory` that this function should skip analyzing. | |
| /// | |
| /// If a compilation unit can not be resolved, this function ignores the | |
| /// corresponding dart source file and logs an error using [foundError]. | |
| Future<void> analyzeWithRules( | |
| String flutterRootDirectory, | |
| List<AnalyzeRule> rules, { | |
| Iterable<String>? includePaths, | |
| Iterable<String>? excludePaths, | |
| }) async { | |
| if (!Directory(flutterRootDirectory).existsSync()) { | |
| foundError(<String>['Analyzer error: the specified $flutterRootDirectory does not exist.']); | |
| } | |
| final Iterable<String> includes = | |
| includePaths?.map( | |
| (String relativePath) => path.canonicalize('$flutterRootDirectory/$relativePath'), | |
| ) ?? | |
| <String>[path.canonicalize(flutterRootDirectory)]; | |
| final collection = AnalysisContextCollection( | |
| includedPaths: includes.toList(), | |
| excludedPaths: excludePaths | |
| ?.map((String relativePath) => path.canonicalize('$flutterRootDirectory/$relativePath')) | |
| .toList(), | |
| ); | |
| final analyzerErrors = <String>[]; | |
| for (final AnalysisContext context in collection.contexts) { | |
| final Iterable<String> analyzedFilePaths = context.contextRoot.analyzedFiles(); | |
| final AnalysisSession session = context.currentSession; | |
| for (final filePath in analyzedFilePaths) { | |
| final SomeResolvedUnitResult unit = await session.getResolvedUnit(filePath); | |
| if (unit is ResolvedUnitResult) { | |
| for (final rule in rules) { | |
| rule.applyTo(unit); | |
| } | |
| } else { | |
| analyzerErrors.add( | |
| 'Analyzer error: file $unit could not be resolved. Expected "ResolvedUnitResult", got ${unit.runtimeType}.', | |
| ); | |
| } | |
| } | |
| } | |
| if (analyzerErrors.isNotEmpty) { | |
| foundError(analyzerErrors); | |
| } | |
| for (final verifier in rules) { | |
| verifier.reportViolations(flutterRootDirectory); | |
| } | |
| } | |
| /// An interface that defines a set of best practices, and collects information | |
| /// about code that violates the best practices in a [ResolvedUnitResult]. | |
| /// | |
| /// The [analyzeWithRules] function scans and analyzes the specified | |
| /// source directory using the dart analyzer package, and applies custom rules | |
| /// defined in the form of this interface on each resulting [ResolvedUnitResult]. | |
| /// The [reportViolations] method will be called at the end, once all | |
| /// [ResolvedUnitResult]s are parsed. | |
| /// | |
| /// Implementers can assume each [ResolvedUnitResult] is valid compilable dart | |
| /// code, as the caller only applies the custom rules once the code passes | |
| /// `flutter analyze`. | |
| abstract class AnalyzeRule { | |
| /// Applies this rule to the given [ResolvedUnitResult] (typically a file), and | |
| /// collects information about violations occurred in the compilation unit. | |
| void applyTo(ResolvedUnitResult unit); | |
| /// Reports all violations in the resolved compilation units [applyTo] was | |
| /// called on, if any. | |
| /// | |
| /// This method is called once all [ResolvedUnitResult] are parsed. | |
| /// | |
| /// The implementation typically calls [foundErrors] to report violations. | |
| void reportViolations(String workingDirectory); | |
| } |