Index: lib/strong_mode.dart |
diff --git a/lib/strong_mode.dart b/lib/strong_mode.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8b96969ed0e18e5cc705660e07e05304709e486d |
--- /dev/null |
+++ b/lib/strong_mode.dart |
@@ -0,0 +1,211 @@ |
+// 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. |
+ |
+/// Types needed to implement "strong" checking in the Dart analyzer. |
+/// This is intended to be used by analyzer_cli and analysis_server packages. |
+library dev_compiler.strong_mode; |
+ |
+import 'package:analyzer/src/generated/engine.dart' |
+ show AnalysisContextImpl, AnalysisErrorInfo, AnalysisErrorInfoImpl; |
+import 'package:analyzer/src/generated/error.dart' |
+ show |
+ AnalysisError, |
+ ErrorCode, |
+ CompileTimeErrorCode, |
+ StaticTypeWarningCode, |
+ HintCode; |
+import 'package:analyzer/src/generated/source.dart' show Source; |
+import 'package:args/args.dart'; |
+import 'package:logging/logging.dart' show Level; |
+ |
+import 'src/checker/checker.dart' show CodeChecker; |
+import 'src/checker/resolver.dart' show LibraryResolverWithInference; |
+import 'src/checker/rules.dart' show RestrictedRules; |
+import 'src/report.dart' show CheckerReporter, Message; |
+ |
+/// A type checker for Dart code that operates under stronger rules, and has |
+/// the ability to do local type inference in some situations. |
+class StrongChecker { |
+ final AnalysisContextImpl _context; |
+ final CodeChecker _checker; |
+ final _ErrorReporter _reporter; |
+ final StrongModeOptions _options; |
+ |
+ StrongChecker._(this._context, this._options, this._checker, this._reporter); |
+ |
+ factory StrongChecker( |
+ AnalysisContextImpl context, StrongModeOptions options) { |
+ // TODO(jmesserly): is there a cleaner way to plug this in? |
+ if (context.libraryResolverFactory != null) { |
+ throw new ArgumentError.value(context, 'context', |
+ 'Analysis context must not have libraryResolverFactory already set.'); |
+ } |
+ context.libraryResolverFactory = |
+ (c) => new LibraryResolverWithInference(c, options); |
+ |
+ var rules = new RestrictedRules(context.typeProvider, options: options); |
+ var reporter = new _ErrorReporter(); |
+ var checker = new CodeChecker(rules, reporter, options); |
+ return new StrongChecker._(context, options, checker, reporter); |
+ } |
+ |
+ /// Computes and returns DDC errors for the [source]. |
+ AnalysisErrorInfo computeErrors(Source source) { |
+ var errors = new List<AnalysisError>(); |
+ |
+ // TODO(jmesserly): change DDC to emit ErrorCodes directly. |
+ _reporter._log = (Message msg) { |
+ // Skip hints unless requested. |
+ if (msg.level < Level.WARNING && !_options.hints) return; |
+ |
+ var errorCodeFactory = _levelToErrorCode[msg.level]; |
+ var category = '${msg.runtimeType}'; |
+ var errorCode = errorCodeFactory(category, msg.message); |
+ var len = msg.end - msg.begin; |
+ errors.add(new AnalysisError.con2(source, msg.begin, len, errorCode)); |
+ }; |
+ |
+ for (Source librarySource in _context.getLibrariesContaining(source)) { |
+ var resolved = _context.resolveCompilationUnit2(source, librarySource); |
+ _checker.visitCompilationUnit(resolved); |
+ } |
+ _reporter._log = null; |
+ return new AnalysisErrorInfoImpl(errors, _context.getLineInfo(source)); |
+ } |
+} |
+ |
+/// Maps a DDC log level to an analyzer ErrorCode subclass. |
+final _levelToErrorCode = <Level, _ErrorCodeFactory>{ |
+ Level.SEVERE: (n, m) => new CompileTimeErrorCode(n, m), |
+ Level.WARNING: (n, m) => new StaticTypeWarningCode(n, m), |
+ Level.INFO: (n, m) => new HintCode(n, m) |
+}; |
+ |
+class _ErrorReporter implements CheckerReporter { |
+ _CheckerReporterLog _log; |
+ void log(Message message) => _log(message); |
+} |
+ |
+class StrongModeOptions { |
+ |
+ /// Whether to infer return types and field types from overriden members. |
vsm
2015/06/10 23:31:17
overriden -> overridden
Jennifer Messerly
2015/06/11 14:46:48
Good catch. Done. Also fixed this typo in a few mo
|
+ final bool inferFromOverrides; |
+ static const inferFromOverridesDefault = true; |
+ |
+ /// Whether to infer types for consts and fields by looking at initializers on |
+ /// the RHS. For example, in a constant declaration like: |
+ /// |
+ /// const A = B; |
+ /// |
+ /// We can infer the type of `A` based on the type of `B`. |
+ /// |
+ /// The inference algorithm determines what variables depend on others, and |
+ /// computes types by visiting the variable dependency graph in topological |
+ /// order. This ensures that the inferred type is deterministic when applying |
+ /// inference on library cycles. |
+ /// |
+ /// When this feature is turned off, we don't use the type of `B` to infer the |
+ /// type of `A`, even if `B` has a declared type. |
+ final bool inferTransitively; |
+ static const inferTransitivelyDefault = true; |
+ |
+ /// Restrict inference of fields and top-levels to those that are final and |
+ /// const. |
+ final bool onlyInferConstsAndFinalFields; |
+ static const onlyInferConstAndFinalFieldsDefault = false; |
+ |
+ /// Whether to infer types downwards from local context |
+ final bool inferDownwards; |
+ static const inferDownwardsDefault = true; |
+ |
+ /// Whether to inject casts between Dart assignable types. |
+ final bool relaxedCasts; |
+ |
+ /// A list of non-nullable type names (e.g., 'int') |
+ final List<String> nonnullableTypes; |
+ static const List<String> NONNULLABLE_TYPES = const <String>[]; |
+ |
+ /// Whether to include hints about dynamic invokes and runtime checks. |
+ // TODO(jmesserly): this option is not used yet by DDC server mode or batch |
+ // compile to JS. |
+ final bool hints; |
+ |
+ const StrongModeOptions({this.hints: false, |
+ this.inferFromOverrides: inferFromOverridesDefault, |
+ this.inferTransitively: inferTransitivelyDefault, |
+ this.onlyInferConstsAndFinalFields: onlyInferConstAndFinalFieldsDefault, |
+ this.inferDownwards: inferDownwardsDefault, this.relaxedCasts: true, |
+ this.nonnullableTypes: StrongModeOptions.NONNULLABLE_TYPES}); |
+ |
+ StrongModeOptions.fromArguments(ArgResults args, {String prefix: ''}) |
+ : relaxedCasts = args[prefix + 'relaxed-casts'], |
+ inferDownwards = args[prefix + 'infer-downwards'], |
+ inferFromOverrides = args[prefix + 'infer-from-overrides'], |
+ inferTransitively = args[prefix + 'infer-transitively'], |
+ onlyInferConstsAndFinalFields = args[prefix + 'infer-only-finals'], |
+ nonnullableTypes = _optionsToList(args[prefix + 'nonnullable'], |
+ defaultValue: StrongModeOptions.NONNULLABLE_TYPES), |
+ hints = args[prefix + 'hints']; |
+ |
+ static ArgParser addArguments(ArgParser parser, |
+ {String prefix: '', bool hide: false}) { |
+ return parser |
+ ..addFlag(prefix + 'hints', |
+ help: 'Display hints about dynamic casts and dispatch operations', |
+ defaultsTo: false, |
+ hide: hide) |
+ ..addFlag(prefix + 'relaxed-casts', |
+ help: 'Cast between Dart assignable types', |
+ defaultsTo: true, |
+ hide: hide) |
+ ..addOption(prefix + 'nonnullable', |
+ abbr: prefix == '' ? 'n' : null, |
+ help: 'Comma separated string of non-nullable types', |
+ defaultsTo: null, |
+ hide: hide) |
+ ..addFlag(prefix + 'infer-downwards', |
+ help: 'Infer types downwards from local context', |
+ defaultsTo: inferDownwardsDefault, |
+ hide: hide) |
+ ..addFlag(prefix + 'infer-from-overrides', |
+ help: 'Infer unspecified types of fields and return types from\n' |
+ 'definitions in supertypes', |
+ defaultsTo: inferFromOverridesDefault, |
+ hide: hide) |
+ ..addFlag(prefix + 'infer-transitively', |
+ help: 'Infer consts/fields from definitions in other libraries', |
+ defaultsTo: inferTransitivelyDefault, |
+ hide: hide) |
+ ..addFlag(prefix + 'infer-only-finals', |
+ help: 'Do not infer non-const or non-final fields', |
+ defaultsTo: onlyInferConstAndFinalFieldsDefault, |
+ hide: hide); |
+ } |
+ |
+ bool operator ==(Object other) { |
+ if (other is! StrongModeOptions) return false; |
+ StrongModeOptions s = other; |
+ return inferFromOverrides == s.inferFromOverrides && |
+ inferTransitively == s.inferTransitively && |
+ onlyInferConstsAndFinalFields == s.onlyInferConstsAndFinalFields && |
+ inferDownwards == s.inferDownwards && |
+ relaxedCasts == s.relaxedCasts && |
+ nonnullableTypes.length == s.nonnullableTypes.length && |
+ new Set.from(nonnullableTypes).containsAll(s.nonnullableTypes); |
+ } |
+} |
+ |
+typedef void _CheckerReporterLog(Message message); |
+typedef ErrorCode _ErrorCodeFactory(String name, String message); |
+ |
+List<String> _optionsToList(String option, |
+ {List<String> defaultValue: const <String>[]}) { |
+ if (option == null) { |
+ return defaultValue; |
+ } else if (option.isEmpty) { |
+ return <String>[]; |
+ } else { |
+ return option.split(','); |
+ } |
+} |