Index: pkg/analyzer/lib/src/lint/linter.dart |
diff --git a/pkg/analyzer/lib/src/lint/linter.dart b/pkg/analyzer/lib/src/lint/linter.dart |
deleted file mode 100644 |
index 5706f35ea8d26f8d5198b2c6bea474b33e6a6250..0000000000000000000000000000000000000000 |
--- a/pkg/analyzer/lib/src/lint/linter.dart |
+++ /dev/null |
@@ -1,429 +0,0 @@ |
-// Copyright (c) 2015, 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:io'; |
- |
-import 'package:analyzer/analyzer.dart'; |
-import 'package:analyzer/dart/ast/token.dart'; |
-import 'package:analyzer/src/generated/engine.dart' |
- show AnalysisErrorInfo, AnalysisErrorInfoImpl, Logger; |
-import 'package:analyzer/src/generated/java_engine.dart' show CaughtException; |
-import 'package:analyzer/src/generated/source.dart' show LineInfo; |
-import 'package:analyzer/src/generated/source_io.dart'; |
-import 'package:analyzer/src/lint/analysis.dart'; |
-import 'package:analyzer/src/lint/config.dart'; |
-import 'package:analyzer/src/lint/io.dart'; |
-import 'package:analyzer/src/lint/project.dart'; |
-import 'package:analyzer/src/lint/pub.dart'; |
-import 'package:analyzer/src/lint/registry.dart'; |
-import 'package:analyzer/src/services/lint.dart' show Linter; |
-import 'package:glob/glob.dart'; |
-import 'package:path/path.dart' as p; |
- |
-typedef Printer(String msg); |
- |
-/// Describes a String in valid camel case format. |
-class CamelCaseString { |
- static final _camelCaseMatcher = new RegExp(r'[A-Z][a-z]*'); |
- static final _camelCaseTester = new RegExp(r'^([_$]*)([A-Z?$]+[a-z0-9]*)+$'); |
- |
- final String value; |
- CamelCaseString(this.value) { |
- if (!isCamelCase(value)) { |
- throw new ArgumentError('$value is not CamelCase'); |
- } |
- } |
- |
- String get humanized => _humanize(value); |
- |
- @override |
- String toString() => value; |
- |
- static bool isCamelCase(String name) => _camelCaseTester.hasMatch(name); |
- |
- static String _humanize(String camelCase) => |
- _camelCaseMatcher.allMatches(camelCase).map((m) => m.group(0)).join(' '); |
-} |
- |
-/// Dart source linter. |
-class DartLinter implements AnalysisErrorListener { |
- final errors = <AnalysisError>[]; |
- |
- final LinterOptions options; |
- final Reporter reporter; |
- |
- /// The total number of sources that were analyzed. Only valid after |
- /// [lintFiles] has been called. |
- int numSourcesAnalyzed; |
- |
- /// Creates a new linter. |
- DartLinter(this.options, {this.reporter: const PrintingReporter()}); |
- |
- Iterable<AnalysisErrorInfo> lintFiles(List<File> files) { |
- List<AnalysisErrorInfo> errors = []; |
- var analysisDriver = new AnalysisDriver(options); |
- errors.addAll(analysisDriver.analyze(files.where((f) => isDartFile(f)))); |
- numSourcesAnalyzed = analysisDriver.numSourcesAnalyzed; |
- files.where((f) => isPubspecFile(f)).forEach((p) { |
- numSourcesAnalyzed++; |
- return errors.addAll(_lintPubspecFile(p)); |
- }); |
- return errors; |
- } |
- |
- Iterable<AnalysisErrorInfo> lintPubspecSource( |
- {String contents, String sourcePath}) { |
- var results = <AnalysisErrorInfo>[]; |
- |
- Uri sourceUrl = sourcePath == null ? null : p.toUri(sourcePath); |
- |
- var spec = new Pubspec.parse(contents, sourceUrl: sourceUrl); |
- |
- for (Linter lint in options.enabledLints) { |
- if (lint is LintRule) { |
- LintRule rule = lint; |
- var visitor = rule.getPubspecVisitor(); |
- if (visitor != null) { |
- // Analyzer sets reporters; if this file is not being analyzed, |
- // we need to set one ourselves. (Needless to say, when pubspec |
- // processing gets pushed down, this hack can go away.) |
- if (rule.reporter == null && sourceUrl != null) { |
- var source = createSource(sourceUrl); |
- rule.reporter = new ErrorReporter(this, source); |
- } |
- try { |
- spec.accept(visitor); |
- } on Exception catch (e) { |
- reporter.exception(new LinterException(e.toString())); |
- } |
- if (rule._locationInfo != null && rule._locationInfo.isNotEmpty) { |
- results.addAll(rule._locationInfo); |
- rule._locationInfo.clear(); |
- } |
- } |
- } |
- } |
- |
- return results; |
- } |
- |
- @override |
- onError(AnalysisError error) => errors.add(error); |
- |
- Iterable<AnalysisErrorInfo> _lintPubspecFile(File sourceFile) => |
- lintPubspecSource( |
- contents: sourceFile.readAsStringSync(), sourcePath: sourceFile.path); |
-} |
- |
-class FileGlobFilter extends LintFilter { |
- Iterable<Glob> includes; |
- Iterable<Glob> excludes; |
- |
- FileGlobFilter([Iterable<String> includeGlobs, Iterable<String> excludeGlobs]) |
- : includes = includeGlobs.map((glob) => new Glob(glob)), |
- excludes = excludeGlobs.map((glob) => new Glob(glob)); |
- |
- @override |
- bool filter(AnalysisError lint) { |
- // TODO specify order |
- return excludes.any((glob) => glob.matches(lint.source.fullName)) && |
- !includes.any((glob) => glob.matches(lint.source.fullName)); |
- } |
-} |
- |
-class Group implements Comparable<Group> { |
- /// Defined rule groups. |
- static const Group errors = |
- const Group._('errors', description: 'Possible coding errors.'); |
- static const Group pub = const Group._('pub', |
- description: 'Pub-related rules.', |
- link: const Hyperlink('See the <strong>Pubspec Format</strong>', |
- 'https://www.dartlang.org/tools/pub/pubspec.html')); |
- static const Group style = const Group._('style', |
- description: |
- 'Matters of style, largely derived from the official Dart Style Guide.', |
- link: const Hyperlink('See the <strong>Style Guide</strong>', |
- 'https://www.dartlang.org/articles/style-guide/')); |
- |
- /// List of builtin groups in presentation order. |
- static const Iterable<Group> builtin = const [errors, style, pub]; |
- |
- final String name; |
- final bool custom; |
- final String description; |
- final Hyperlink link; |
- |
- factory Group(String name, {String description: '', Hyperlink link}) { |
- var n = name.toLowerCase(); |
- return builtin.firstWhere((g) => g.name == n, |
- orElse: () => new Group._(name, |
- custom: true, description: description, link: link)); |
- } |
- |
- const Group._(this.name, {this.custom: false, this.description, this.link}); |
- |
- @override |
- int compareTo(Group other) => name.compareTo(other.name); |
-} |
- |
-class Hyperlink { |
- final String label; |
- final String href; |
- final bool bold; |
- const Hyperlink(this.label, this.href, {this.bold: false}); |
- String get html => '<a href="$href">${_emph(label)}</a>'; |
- String _emph(msg) => bold ? '<strong>$msg</strong>' : msg; |
-} |
- |
-/// Thrown when an error occurs in linting. |
-class LinterException implements Exception { |
- /// A message describing the error. |
- final String message; |
- |
- /// Creates a new LinterException with an optional error [message]. |
- const LinterException([this.message]); |
- |
- @override |
- String toString() => |
- message == null ? "LinterException" : "LinterException: $message"; |
-} |
- |
-/// Linter options. |
-class LinterOptions extends DriverOptions { |
- Iterable<LintRule> enabledLints; |
- LintFilter filter; |
- LinterOptions([this.enabledLints]) { |
- enabledLints ??= Registry.ruleRegistry; |
- } |
- void configure(LintConfig config) { |
- // TODO(pquitslund): revisit these default-to-on semantics. |
- enabledLints = Registry.ruleRegistry.where((LintRule rule) => |
- !config.ruleConfigs.any((rc) => rc.disables(rule.name))); |
- filter = new FileGlobFilter(config.fileIncludes, config.fileExcludes); |
- } |
-} |
- |
-/// Filtered lints are ommitted from linter output. |
-abstract class LintFilter { |
- bool filter(AnalysisError lint); |
-} |
- |
-/// Describes a lint rule. |
-abstract class LintRule extends Linter implements Comparable<LintRule> { |
- /// Description (in markdown format) suitable for display in a detailed lint |
- /// description. |
- final String details; |
- |
- /// Short description suitable for display in console output. |
- final String description; |
- |
- /// Lint group (for example, 'style'). |
- final Group group; |
- |
- /// Lint maturity (stable|experimental). |
- final Maturity maturity; |
- |
- /// Lint name. |
- @override |
- final String name; |
- |
- /// Until pubspec analysis is pushed into the analyzer proper, we need to |
- /// do some extra book-keeping to keep track of details that will help us |
- /// constitute AnalysisErrorInfos. |
- final List<AnalysisErrorInfo> _locationInfo = <AnalysisErrorInfo>[]; |
- |
- LintRule( |
- {this.name, |
- this.group, |
- this.description, |
- this.details, |
- this.maturity: Maturity.stable}); |
- |
- LintCode get lintCode => new _LintCode(name, description); |
- |
- @override |
- int compareTo(LintRule other) { |
- var g = group.compareTo(other.group); |
- if (g != 0) { |
- return g; |
- } |
- return name.compareTo(other.name); |
- } |
- |
- /// Return a visitor to be passed to provide access to Dart project context |
- /// and to perform project-level analyses. |
- ProjectVisitor getProjectVisitor() => null; |
- |
- /// Return a visitor to be passed to pubspecs to perform lint |
- /// analysis. |
- /// Lint errors are reported via this [Linter]'s error [reporter]. |
- PubspecVisitor getPubspecVisitor() => null; |
- |
- @override |
- AstVisitor getVisitor() => null; |
- |
- void reportLint(AstNode node, {bool ignoreSyntheticNodes: true}) { |
- if (node != null && (!node.isSynthetic || !ignoreSyntheticNodes)) { |
- reporter.reportErrorForNode(lintCode, node, []); |
- } |
- } |
- |
- void reportLintForToken(Token token, {bool ignoreSyntheticTokens: true}) { |
- if (token != null && (!token.isSynthetic || !ignoreSyntheticTokens)) { |
- reporter.reportErrorForToken(lintCode, token, []); |
- } |
- } |
- |
- void reportPubLint(PSNode node) { |
- Source source = createSource(node.span.sourceUrl); |
- |
- // Cache error and location info for creating AnalysisErrorInfos |
- // Note that error columns are 1-based |
- AnalysisError error = new AnalysisError( |
- source, node.span.start.column + 1, node.span.length, lintCode); |
- LineInfo lineInfo = new LineInfo.fromContent(source.contents.data); |
- |
- _locationInfo.add(new AnalysisErrorInfoImpl([error], lineInfo)); |
- |
- // Then do the reporting |
- reporter?.reportError(error); |
- } |
-} |
- |
-class Maturity implements Comparable<Maturity> { |
- static const Maturity stable = const Maturity._('stable', ordinal: 0); |
- static const Maturity experimental = const Maturity._('stable', ordinal: 1); |
- |
- final String name; |
- final int ordinal; |
- |
- factory Maturity(String name, {int ordinal}) { |
- switch (name.toLowerCase()) { |
- case 'stable': |
- return stable; |
- case 'experimental': |
- return experimental; |
- default: |
- return new Maturity._(name, ordinal: ordinal); |
- } |
- } |
- |
- const Maturity._(this.name, {this.ordinal}); |
- |
- @override |
- int compareTo(Maturity other) => this.ordinal - other.ordinal; |
-} |
- |
-class PrintingReporter implements Reporter, Logger { |
- final Printer _print; |
- |
- const PrintingReporter([this._print = print]); |
- |
- @override |
- void exception(LinterException exception) { |
- _print('EXCEPTION: $exception'); |
- } |
- |
- @override |
- void logError(String message, [CaughtException exception]) { |
- _print('ERROR: $message'); |
- } |
- |
- @override |
- void logInformation(String message, [CaughtException exception]) { |
- _print('INFO: $message'); |
- } |
- |
- @override |
- void warn(String message) { |
- _print('WARN: $message'); |
- } |
-} |
- |
-abstract class Reporter { |
- void exception(LinterException exception); |
- void warn(String message); |
-} |
- |
-/// Linter implementation. |
-class SourceLinter implements DartLinter, AnalysisErrorListener { |
- @override |
- final errors = <AnalysisError>[]; |
- @override |
- final LinterOptions options; |
- @override |
- final Reporter reporter; |
- |
- @override |
- int numSourcesAnalyzed; |
- |
- SourceLinter(this.options, {this.reporter: const PrintingReporter()}); |
- |
- @override |
- Iterable<AnalysisErrorInfo> lintFiles(List<File> files) { |
- List<AnalysisErrorInfo> errors = []; |
- var analysisDriver = new AnalysisDriver(options); |
- errors.addAll(analysisDriver.analyze(files.where((f) => isDartFile(f)))); |
- numSourcesAnalyzed = analysisDriver.numSourcesAnalyzed; |
- files.where((f) => isPubspecFile(f)).forEach((p) { |
- numSourcesAnalyzed++; |
- return errors.addAll(_lintPubspecFile(p)); |
- }); |
- return errors; |
- } |
- |
- @override |
- Iterable<AnalysisErrorInfo> lintPubspecSource( |
- {String contents, String sourcePath}) { |
- var results = <AnalysisErrorInfo>[]; |
- |
- Uri sourceUrl = sourcePath == null ? null : p.toUri(sourcePath); |
- |
- var spec = new Pubspec.parse(contents, sourceUrl: sourceUrl); |
- |
- for (Linter lint in options.enabledLints) { |
- if (lint is LintRule) { |
- LintRule rule = lint; |
- var visitor = rule.getPubspecVisitor(); |
- if (visitor != null) { |
- // Analyzer sets reporters; if this file is not being analyzed, |
- // we need to set one ourselves. (Needless to say, when pubspec |
- // processing gets pushed down, this hack can go away.) |
- if (rule.reporter == null && sourceUrl != null) { |
- var source = createSource(sourceUrl); |
- rule.reporter = new ErrorReporter(this, source); |
- } |
- try { |
- spec.accept(visitor); |
- } on Exception catch (e) { |
- reporter.exception(new LinterException(e.toString())); |
- } |
- if (rule._locationInfo != null && rule._locationInfo.isNotEmpty) { |
- results.addAll(rule._locationInfo); |
- rule._locationInfo.clear(); |
- } |
- } |
- } |
- } |
- |
- return results; |
- } |
- |
- @override |
- onError(AnalysisError error) => errors.add(error); |
- |
- @override |
- Iterable<AnalysisErrorInfo> _lintPubspecFile(File sourceFile) => |
- lintPubspecSource( |
- contents: sourceFile.readAsStringSync(), sourcePath: sourceFile.path); |
-} |
- |
-class _LintCode extends LintCode { |
- static final registry = <String, LintCode>{}; |
- |
- factory _LintCode(String name, String message) => registry.putIfAbsent( |
- name + message, () => new _LintCode._(name, message)); |
- |
- _LintCode._(String name, String message) : super(name, message); |
-} |