Enable and fix a number of lints (#45)

- Disable implicit casts and fix errors.
- Enable lints that are used in other well maintained dart-lang repos.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 108d105..f28d5a1 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1 +1,92 @@
 include: package:pedantic/analysis_options.yaml
+analyzer:
+  strong-mode:
+    implicit-casts: false
+linter:
+  rules:
+  - always_declare_return_types
+  - annotate_overrides
+  - avoid_bool_literals_in_conditional_expressions
+  - avoid_classes_with_only_static_members
+  - avoid_empty_else
+  - avoid_function_literals_in_foreach_calls
+  - avoid_init_to_null
+  - avoid_null_checks_in_equality_operators
+  - avoid_relative_lib_imports
+  - avoid_renaming_method_parameters
+  - avoid_return_types_on_setters
+  - avoid_returning_null
+  - avoid_returning_null_for_future
+  - avoid_returning_null_for_void
+  - avoid_returning_this
+  - avoid_shadowing_type_parameters
+  - avoid_single_cascade_in_expression_statements
+  - avoid_types_as_parameter_names
+  - avoid_unused_constructor_parameters
+  - await_only_futures
+  - camel_case_types
+  - cancel_subscriptions
+  - cascade_invocations
+  # Enable once https://github.com/dart-lang/sdk/issues/31761 is fixed
+  #- comment_references
+  - constant_identifier_names
+  - control_flow_in_finally
+  - directives_ordering
+  - empty_catches
+  - empty_constructor_bodies
+  - empty_statements
+  - file_names
+  - hash_and_equals
+  - implementation_imports
+  - invariant_booleans
+  - iterable_contains_unrelated_type
+  - library_names
+  - library_prefixes
+  - list_remove_unrelated_type
+  - no_adjacent_strings_in_list
+  - no_duplicate_case_values
+  - non_constant_identifier_names
+  - null_closures
+  - omit_local_variable_types
+  - only_throw_errors
+  - overridden_fields
+  - package_api_docs
+  - package_names
+  - package_prefixed_library_names
+  - prefer_adjacent_string_concatenation
+  - prefer_collection_literals
+  - prefer_conditional_assignment
+  - prefer_const_constructors
+  - prefer_contains
+  - prefer_equal_for_default_values
+  - prefer_final_fields
+  - prefer_final_locals
+  - prefer_generic_function_type_aliases
+  - prefer_initializing_formals
+  - prefer_interpolation_to_compose_strings
+  - prefer_is_empty
+  - prefer_is_not_empty
+  - prefer_null_aware_operators
+  #- prefer_single_quotes
+  - prefer_typing_uninitialized_variables
+  - recursive_getters
+  - slash_for_doc_comments
+  - test_types_in_equals
+  - throw_in_finally
+  - type_init_formals
+  - unawaited_futures
+  - unnecessary_await_in_return
+  - unnecessary_brace_in_string_interps
+  - unnecessary_const
+  - unnecessary_getters_setters
+  - unnecessary_lambdas
+  - unnecessary_new
+  - unnecessary_null_aware_assignments
+  - unnecessary_parenthesis
+  - unnecessary_statements
+  - unnecessary_this
+  - unrelated_type_equality_checks
+  - use_function_type_syntax_for_parameters
+  - use_rethrow_when_possible
+  - valid_regexps
+  - void_checks
diff --git a/lib/src/colors.dart b/lib/src/colors.dart
index 2931eea..b48d468 100644
--- a/lib/src/colors.dart
+++ b/lib/src/colors.dart
@@ -3,10 +3,10 @@
 // BSD-style license that can be found in the LICENSE file.
 
 // Color constants used for generating messages.
-const String RED = '\u001b[31m';
+const String red = '\u001b[31m';
 
-const String YELLOW = '\u001b[33m';
+const String yellow = '\u001b[33m';
 
-const String BLUE = '\u001b[34m';
+const String blue = '\u001b[34m';
 
-const String NONE = '\u001b[0m';
+const String none = '\u001b[0m';
diff --git a/lib/src/file.dart b/lib/src/file.dart
index 44760c7..97a2f65 100644
--- a/lib/src/file.dart
+++ b/lib/src/file.dart
@@ -12,8 +12,8 @@
 import 'span_with_context.dart';
 
 // Constants to determine end-of-lines.
-const int _LF = 10;
-const int _CR = 13;
+const int _lf = 10;
+const int _cr = 13;
 
 /// A class representing a source file.
 ///
@@ -71,28 +71,28 @@
   /// forwards-compatibility, callers should only pass in characters less than
   /// or equal to `0xFFFF`.
   SourceFile.decoded(Iterable<int> decodedChars, {url})
-      : url = url is String ? Uri.parse(url) : url,
+      : url = url is String ? Uri.parse(url) : url as Uri,
         _decodedChars = Uint32List.fromList(decodedChars.toList()) {
     for (var i = 0; i < _decodedChars.length; i++) {
       var c = _decodedChars[i];
-      if (c == _CR) {
+      if (c == _cr) {
         // Return not followed by newline is treated as a newline
-        var j = i + 1;
-        if (j >= _decodedChars.length || _decodedChars[j] != _LF) c = _LF;
+        final j = i + 1;
+        if (j >= _decodedChars.length || _decodedChars[j] != _lf) c = _lf;
       }
-      if (c == _LF) _lineStarts.add(i + 1);
+      if (c == _lf) _lineStarts.add(i + 1);
     }
   }
 
-  /// Returns a span in [this] from [start] to [end] (exclusive).
+  /// Returns a span from [start] to [end] (exclusive).
   ///
   /// If [end] isn't passed, it defaults to the end of the file.
   FileSpan span(int start, [int end]) {
-    if (end == null) end = length;
+    end ??= length;
     return _FileSpan(this, start, end);
   }
 
-  /// Returns a location in [this] at [offset].
+  /// Returns a location at [offset].
   FileLocation location(int offset) => FileLocation._(this, offset);
 
   /// Gets the 0-based line corresponding to [offset].
@@ -143,10 +143,10 @@
   ///
   /// Returns the index of the line in [_lineStarts].
   int _binarySearch(int offset) {
-    int min = 0;
-    int max = _lineStarts.length - 1;
+    var min = 0;
+    var max = _lineStarts.length - 1;
     while (min < max) {
-      var half = min + ((max - min) ~/ 2);
+      final half = min + ((max - min) ~/ 2);
       if (_lineStarts[half] > offset) {
         max = half;
       } else {
@@ -178,7 +178,7 @@
           "lines in the file, $lines.");
     }
 
-    var lineStart = _lineStarts[line];
+    final lineStart = _lineStarts[line];
     if (lineStart > offset) {
       throw RangeError("Line $line comes after offset $offset.");
     }
@@ -190,7 +190,7 @@
   ///
   /// [column] defaults to 0.
   int getOffset(int line, [int column]) {
-    if (column == null) column = 0;
+    column ??= 0;
 
     if (line < 0) {
       throw RangeError("Line may not be negative, was $line.");
@@ -201,7 +201,7 @@
       throw RangeError("Column may not be negative, was $column.");
     }
 
-    var result = _lineStarts[line] + column;
+    final result = _lineStarts[line] + column;
     if (result > length ||
         (line + 1 < lines && result >= _lineStarts[line + 1])) {
       throw RangeError("Line $line doesn't have $column columns.");
@@ -224,12 +224,19 @@
 ///
 /// A [FileLocation] can be created using [SourceFile.location].
 class FileLocation extends SourceLocationMixin implements SourceLocation {
-  /// The [file] that [this] belongs to.
+  /// The [file] that `this` belongs to.
   final SourceFile file;
 
+  @override
   final int offset;
+
+  @override
   Uri get sourceUrl => file.url;
+
+  @override
   int get line => file.getLine(offset);
+
+  @override
   int get column => file.getColumn(offset);
 
   FileLocation._(this.file, this.offset) {
@@ -241,27 +248,31 @@
     }
   }
 
+  @override
   FileSpan pointSpan() => _FileSpan(file, offset, offset);
 }
 
 /// A [SourceSpan] within a [SourceFile].
 ///
 /// Unlike the base [SourceSpan], [FileSpan] lazily computes its line and column
-/// values based on its offset and the contents of [file]. [FileSpan.message] is
-/// also able to provide more context then [SourceSpan.message], and
-/// [FileSpan.union] will return a [FileSpan] if possible.
+/// values based on its offset and the contents of [file]. [SourceSpan.message]
+/// is also able to provide more context then [SourceSpan.message], and
+/// [SourceSpan.union] will return a [FileSpan] if possible.
 ///
 /// A [FileSpan] can be created using [SourceFile.span].
 abstract class FileSpan implements SourceSpanWithContext {
-  /// The [file] that [this] belongs to.
+  /// The [file] that `this` belongs to.
   SourceFile get file;
 
+  @override
   FileLocation get start;
+
+  @override
   FileLocation get end;
 
-  /// Returns a new span that covers both [this] and [other].
+  /// Returns a new span that covers both `this` and [other].
   ///
-  /// Unlike [union], [other] may be disjoint from [this]. If it is, the text
+  /// Unlike [union], [other] may be disjoint from `this`. If it is, the text
   /// between the two will be covered by the returned span.
   FileSpan expand(FileSpan other);
 }
@@ -272,6 +283,7 @@
 /// to make certain operations more efficient. If we used `is FileSpan`, that
 /// would break if external classes implemented the interface.
 class _FileSpan extends SourceSpanMixin implements FileSpan {
+  @override
   final SourceFile file;
 
   /// The offset of the beginning of the span.
@@ -286,15 +298,25 @@
   /// objects.
   final int _end;
 
+  @override
   Uri get sourceUrl => file.url;
+
+  @override
   int get length => _end - _start;
+
+  @override
   FileLocation get start => FileLocation._(file, _start);
+
+  @override
   FileLocation get end => FileLocation._(file, _end);
+
+  @override
   String get text => file.getText(_start, _end);
 
+  @override
   String get context {
-    var endLine = file.getLine(_end);
-    var endColumn = file.getColumn(_end);
+    final endLine = file.getLine(_end);
+    final endColumn = file.getColumn(_end);
 
     int endOffset;
     if (endColumn == 0 && endLine != 0) {
@@ -336,25 +358,27 @@
     }
   }
 
+  @override
   int compareTo(SourceSpan other) {
     if (other is! _FileSpan) return super.compareTo(other);
 
-    _FileSpan otherFile = other;
-    var result = _start.compareTo(otherFile._start);
+    final otherFile = other as _FileSpan;
+    final result = _start.compareTo(otherFile._start);
     return result == 0 ? _end.compareTo(otherFile._end) : result;
   }
 
+  @override
   SourceSpan union(SourceSpan other) {
     if (other is! FileSpan) return super.union(other);
 
-    _FileSpan span = expand(other);
+    final span = expand(other as _FileSpan);
 
     if (other is _FileSpan) {
-      if (this._start > other._end || other._start > this._end) {
+      if (_start > other._end || other._start > _end) {
         throw ArgumentError("Spans $this and $other are disjoint.");
       }
     } else {
-      if (this._start > other.end.offset || other.start.offset > this._end) {
+      if (_start > other.end.offset || other.start.offset > _end) {
         throw ArgumentError("Spans $this and $other are disjoint.");
       }
     }
@@ -362,6 +386,7 @@
     return span;
   }
 
+  @override
   bool operator ==(other) {
     if (other is! FileSpan) return super == other;
     if (other is! _FileSpan) {
@@ -374,25 +399,27 @@
   }
 
   // Eliminates dart2js warning about overriding `==`, but not `hashCode`
+  @override
   int get hashCode => super.hashCode;
 
-  /// Returns a new span that covers both [this] and [other].
+  /// Returns a new span that covers both `this` and [other].
   ///
-  /// Unlike [union], [other] may be disjoint from [this]. If it is, the text
+  /// Unlike [union], [other] may be disjoint from `this`. If it is, the text
   /// between the two will be covered by the returned span.
+  @override
   FileSpan expand(FileSpan other) {
     if (sourceUrl != other.sourceUrl) {
-      throw ArgumentError("Source URLs \"${sourceUrl}\" and "
+      throw ArgumentError("Source URLs \"$sourceUrl\" and "
           " \"${other.sourceUrl}\" don't match.");
     }
 
     if (other is _FileSpan) {
-      var start = math.min(this._start, other._start);
-      var end = math.max(this._end, other._end);
+      final start = math.min(_start, other._start);
+      final end = math.max(_end, other._end);
       return _FileSpan(file, start, end);
     } else {
-      var start = math.min(this._start, other.start.offset);
-      var end = math.max(this._end, other.end.offset);
+      final start = math.min(_start, other.start.offset);
+      final end = math.max(_end, other.end.offset);
       return _FileSpan(file, start, end);
     }
   }
diff --git a/lib/src/highlighter.dart b/lib/src/highlighter.dart
index f68e52a..94bc857 100644
--- a/lib/src/highlighter.dart
+++ b/lib/src/highlighter.dart
@@ -44,8 +44,8 @@
   /// alignment.
   static const _spacesPerTab = 4;
 
-  /// Creats a [Highlighter] that will return a message associated with [span]
-  /// when [write] is called.
+  /// Creates a [Highlighter] that will return a message associated with [span]
+  /// when [highlight] is called.
   ///
   /// [color] may either be a [String], a [bool], or `null`. If it's a string,
   /// it indicates an [ANSI terminal color
@@ -55,7 +55,7 @@
   /// highlighted using the default color. If it's `false` or `null`, it
   /// indicates that the text shouldn't be highlighted.
   factory Highlighter(SourceSpan span, {color}) {
-    if (color == true) color = colors.RED;
+    if (color == true) color = colors.red;
     if (color == false) color = null;
 
     var newSpan = _normalizeContext(span);
@@ -63,7 +63,7 @@
     newSpan = _normalizeTrailingNewline(newSpan);
     newSpan = _normalizeEndOfLine(newSpan);
 
-    return Highlighter._(newSpan, color);
+    return Highlighter._(newSpan, color as String);
   }
 
   /// Normalizes [span] to ensure that it's a [SourceSpanWithContext] whose
@@ -89,7 +89,7 @@
   /// Normalizes [span] to replace Windows-style newlines with Unix-style
   /// newlines.
   static SourceSpanWithContext _normalizeNewlines(SourceSpanWithContext span) {
-    var text = span.text;
+    final text = span.text;
     if (!text.contains("\r\n")) return span;
 
     var endOffset = span.end.offset;
@@ -121,7 +121,7 @@
     // significant, so we shouldn't trim it.
     if (span.text.endsWith("\n\n")) return span;
 
-    var context = span.context.substring(0, span.context.length - 1);
+    final context = span.context.substring(0, span.context.length - 1);
     var text = span.text;
     var start = span.start;
     var end = span.end;
@@ -142,7 +142,7 @@
     if (span.end.column != 0) return span;
     if (span.end.line == span.start.line) return span;
 
-    var text = span.text.substring(0, span.text.length - 1);
+    final text = span.text.substring(0, span.text.length - 1);
 
     return SourceSpanWithContext(
         span.start,
@@ -192,7 +192,7 @@
 
     // If [_span.context] contains lines prior to the one [_span.text] appears
     // on, write those first.
-    var lineStart =
+    final lineStart =
         findLineStart(_span.context, _span.text, _span.start.column);
     assert(lineStart != null); // enforced by [_normalizeContext]
 
@@ -202,7 +202,7 @@
       // [findLineStart] is guaranteed to return a position immediately after a
       // newline. Including that newline would add an extra empty line to the
       // end of [lines].
-      var lines = context.substring(0, lineStart - 1).split("\n");
+      final lines = context.substring(0, lineStart - 1).split("\n");
       var lineNumber = _span.start.line - lines.length;
       for (var line in lines) {
         _writeSidebar(line: lineNumber);
@@ -214,9 +214,9 @@
       context = context.substring(lineStart);
     }
 
-    var lines = context.split("\n");
+    final lines = context.split("\n");
 
-    var lastLineIndex = _span.end.line - _span.start.line;
+    final lastLineIndex = _span.end.line - _span.start.line;
     if (lines.last.isEmpty && lines.length > lastLineIndex + 1) {
       // Trim a trailing newline so we don't add an empty line to the end of the
       // highlight.
@@ -242,15 +242,14 @@
     var startColumn = math.min(_span.start.column, line.length);
     var endColumn = math.min(
         startColumn + _span.end.offset - _span.start.offset, line.length);
-    var textBefore = line.substring(0, startColumn);
+    final textBefore = line.substring(0, startColumn);
 
     // If the span covers the entire first line other than initial whitespace,
     // don't bother pointing out exactly where it begins.
     if (_multiline && _isOnlyWhitespace(textBefore)) {
       _buffer.write(" ");
       _colorize(() {
-        _buffer.write(glyph.glyphOrAscii("┌", "/"));
-        _buffer.write(" ");
+        _buffer..write(glyph.glyphOrAscii("┌", "/"))..write(" ");
         _writeText(line);
       });
       _buffer.writeln();
@@ -259,15 +258,15 @@
 
     _buffer.write(" " * _paddingAfterSidebar);
     _writeText(textBefore);
-    var textInside = line.substring(startColumn, endColumn);
+    final textInside = line.substring(startColumn, endColumn);
     _colorize(() => _writeText(textInside));
     _writeText(line.substring(endColumn));
     _buffer.writeln();
 
     // Adjust the start and end column to account for any tabs that were
     // converted to spaces.
-    var tabsBefore = _countTabs(textBefore);
-    var tabsInside = _countTabs(textInside);
+    final tabsBefore = _countTabs(textBefore);
+    final tabsInside = _countTabs(textInside);
     startColumn = startColumn + tabsBefore * (_spacesPerTab - 1);
     endColumn = endColumn + (tabsBefore + tabsInside) * (_spacesPerTab - 1);
 
@@ -277,9 +276,10 @@
     if (_multiline) {
       _buffer.write(" ");
       _colorize(() {
-        _buffer.write(glyph.topLeftCorner);
-        _buffer.write(glyph.horizontalLine * (startColumn + 1));
-        _buffer.write("^");
+        _buffer
+          ..write(glyph.topLeftCorner)
+          ..write(glyph.horizontalLine * (startColumn + 1))
+          ..write("^");
       });
     } else {
       _buffer.write(" " * (startColumn + 1));
@@ -300,8 +300,7 @@
 
       _buffer.write(" ");
       _colorize(() {
-        _buffer.write(glyph.verticalLine);
-        _buffer.write(" ");
+        _buffer..write(glyph.verticalLine)..write(" ");
         _writeText(line);
       });
       _buffer.writeln();
@@ -323,8 +322,7 @@
     if (_multiline && endColumn == line.length) {
       _buffer.write(" ");
       _colorize(() {
-        _buffer.write(glyph.glyphOrAscii("└", "\\"));
-        _buffer.write(" ");
+        _buffer..write(glyph.glyphOrAscii("└", "\\"))..write(" ");
         _writeText(line);
       });
       _buffer.writeln();
@@ -332,10 +330,9 @@
     }
 
     _buffer.write(" ");
-    var textInside = line.substring(0, endColumn);
+    final textInside = line.substring(0, endColumn);
     _colorize(() {
-      _buffer.write(glyph.verticalLine);
-      _buffer.write(" ");
+      _buffer..write(glyph.verticalLine)..write(" ");
       _writeText(textInside);
     });
     _writeText(line.substring(endColumn));
@@ -343,7 +340,7 @@
 
     // Adjust the end column to account for any tabs that were converted to
     // spaces.
-    var tabsInside = _countTabs(textInside);
+    final tabsInside = _countTabs(textInside);
     endColumn = endColumn + tabsInside * (_spacesPerTab - 1);
 
     // Write the highlight for the final line, which is an arrow pointing to the
@@ -351,9 +348,10 @@
     _writeSidebar();
     _buffer.write(" ");
     _colorize(() {
-      _buffer.write(glyph.bottomLeftCorner);
-      _buffer.write(glyph.horizontalLine * endColumn);
-      _buffer.write("^");
+      _buffer
+        ..write(glyph.bottomLeftCorner)
+        ..write(glyph.horizontalLine * endColumn)
+        ..write("^");
     });
     _buffer.writeln();
   }
@@ -395,7 +393,7 @@
         _buffer.write(" " * _paddingBeforeSidebar);
       }
       _buffer.write(end ?? glyph.verticalLine);
-    }, color: colors.BLUE);
+    }, color: colors.blue);
   }
 
   /// Returns the number of hard tabs in [text].
@@ -419,9 +417,9 @@
   /// enabled.
   ///
   /// If [color] is passed, it's used as the color; otherwise, [_color] is used.
-  void _colorize(void callback(), {String color}) {
+  void _colorize(void Function() callback, {String color}) {
     if (_color != null) _buffer.write(color ?? _color);
     callback();
-    if (_color != null) _buffer.write(colors.NONE);
+    if (_color != null) _buffer.write(colors.none);
   }
 }
diff --git a/lib/src/location.dart b/lib/src/location.dart
index a271880..c03c98c 100644
--- a/lib/src/location.dart
+++ b/lib/src/location.dart
@@ -32,7 +32,7 @@
   ///
   /// This prints 1-based lines and columns.
   String get toolString {
-    var source = sourceUrl == null ? 'unknown source' : sourceUrl;
+    final source = sourceUrl == null ? 'unknown source' : sourceUrl;
     return '$source:${line + 1}:${column + 1}';
   }
 
@@ -42,9 +42,9 @@
   /// means that [line] defaults to 0 and [column] defaults to [offset].
   ///
   /// [sourceUrl] may be either a [String], a [Uri], or `null`.
-  SourceLocation(int offset, {sourceUrl, int line, int column})
-      : sourceUrl = sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl,
-        offset = offset,
+  SourceLocation(this.offset, {sourceUrl, int line, int column})
+      : sourceUrl =
+            sourceUrl is String ? Uri.parse(sourceUrl) : sourceUrl as Uri,
         line = line == null ? 0 : line,
         column = column == null ? offset : column {
     if (offset < 0) {
@@ -56,12 +56,12 @@
     }
   }
 
-  /// Returns the distance in characters between [this] and [other].
+  /// Returns the distance in characters between `this` and [other].
   ///
   /// This always returns a non-negative value.
   int distance(SourceLocation other) {
     if (sourceUrl != other.sourceUrl) {
-      throw ArgumentError("Source URLs \"${sourceUrl}\" and "
+      throw ArgumentError("Source URLs \"$sourceUrl\" and "
           "\"${other.sourceUrl}\" don't match.");
     }
     return (offset - other.offset).abs();
@@ -72,22 +72,26 @@
 
   /// Compares two locations.
   ///
-  /// [other] must have the same source URL as [this].
+  /// [other] must have the same source URL as `this`.
+  @override
   int compareTo(SourceLocation other) {
     if (sourceUrl != other.sourceUrl) {
-      throw ArgumentError("Source URLs \"${sourceUrl}\" and "
+      throw ArgumentError("Source URLs \"$sourceUrl\" and "
           "\"${other.sourceUrl}\" don't match.");
     }
     return offset - other.offset;
   }
 
+  @override
   bool operator ==(other) =>
       other is SourceLocation &&
       sourceUrl == other.sourceUrl &&
       offset == other.offset;
 
+  @override
   int get hashCode => sourceUrl.hashCode + offset;
 
+  @override
   String toString() => '<$runtimeType: $offset $toolString>';
 }
 
diff --git a/lib/src/location_mixin.dart b/lib/src/location_mixin.dart
index 7d34311..bd773ae 100644
--- a/lib/src/location_mixin.dart
+++ b/lib/src/location_mixin.dart
@@ -14,35 +14,42 @@
 
 /// A mixin for easily implementing [SourceLocation].
 abstract class SourceLocationMixin implements SourceLocation {
+  @override
   String get toolString {
-    var source = sourceUrl == null ? 'unknown source' : sourceUrl;
+    final source = sourceUrl == null ? 'unknown source' : sourceUrl;
     return '$source:${line + 1}:${column + 1}';
   }
 
+  @override
   int distance(SourceLocation other) {
     if (sourceUrl != other.sourceUrl) {
-      throw ArgumentError("Source URLs \"${sourceUrl}\" and "
+      throw ArgumentError("Source URLs \"$sourceUrl\" and "
           "\"${other.sourceUrl}\" don't match.");
     }
     return (offset - other.offset).abs();
   }
 
+  @override
   SourceSpan pointSpan() => SourceSpan(this, this, "");
 
+  @override
   int compareTo(SourceLocation other) {
     if (sourceUrl != other.sourceUrl) {
-      throw ArgumentError("Source URLs \"${sourceUrl}\" and "
+      throw ArgumentError("Source URLs \"$sourceUrl\" and "
           "\"${other.sourceUrl}\" don't match.");
     }
     return offset - other.offset;
   }
 
+  @override
   bool operator ==(other) =>
       other is SourceLocation &&
       sourceUrl == other.sourceUrl &&
       offset == other.offset;
 
+  @override
   int get hashCode => sourceUrl.hashCode + offset;
 
+  @override
   String toString() => '<$runtimeType: $offset $toolString>';
 }
diff --git a/lib/src/span.dart b/lib/src/span.dart
index b32e1ff..87ce846 100644
--- a/lib/src/span.dart
+++ b/lib/src/span.dart
@@ -6,6 +6,7 @@
 
 import 'location.dart';
 import 'span_mixin.dart';
+import 'span_with_context.dart';
 
 /// A class that describes a segment of source text.
 abstract class SourceSpan implements Comparable<SourceSpan> {
@@ -35,16 +36,17 @@
   factory SourceSpan(SourceLocation start, SourceLocation end, String text) =>
       SourceSpanBase(start, end, text);
 
-  /// Creates a new span that's the union of [this] and [other].
+  /// Creates a new span that's the union of `this` and [other].
   ///
   /// The two spans must have the same source URL and may not be disjoint.
-  /// [text] is computed by combining [this.text] and [other.text].
+  /// [text] is computed by combining `this.text` and `other.text`.
   SourceSpan union(SourceSpan other);
 
   /// Compares two spans.
   ///
-  /// [other] must have the same source URL as [this]. This orders spans by
+  /// [other] must have the same source URL as `this`. This orders spans by
   /// [start] then [length].
+  @override
   int compareTo(SourceSpan other);
 
   /// Formats [message] in a human-friendly way associated with this span.
@@ -85,8 +87,11 @@
 /// A base class for source spans with [start], [end], and [text] known at
 /// construction time.
 class SourceSpanBase extends SourceSpanMixin {
+  @override
   final SourceLocation start;
+  @override
   final SourceLocation end;
+  @override
   final String text;
 
   SourceSpanBase(this.start, this.end, this.text) {
diff --git a/lib/src/span_exception.dart b/lib/src/span_exception.dart
index 6d3448b..2ce0f1a 100644
--- a/lib/src/span_exception.dart
+++ b/lib/src/span_exception.dart
@@ -20,16 +20,17 @@
 
   SourceSpanException(this._message, this._span);
 
-  /// Returns a string representation of [this].
+  /// Returns a string representation of `this`.
   ///
   /// [color] may either be a [String], a [bool], or `null`. If it's a string,
-  /// it indicates an ANSII terminal color escape that should be used to
+  /// it indicates an ANSI terminal color escape that should be used to
   /// highlight the span's text. If it's `true`, it indicates that the text
   /// should be highlighted using the default color. If it's `false` or `null`,
   /// it indicates that the text shouldn't be highlighted.
+  @override
   String toString({color}) {
     if (span == null) return message;
-    return "Error on " + span.message(message, color: color);
+    return "Error on ${span.message(message, color: color)}";
   }
 }
 
@@ -37,11 +38,12 @@
 class SourceSpanFormatException extends SourceSpanException
     implements FormatException {
   // This is a getter so that subclasses can override it.
-  dynamic get source => _source;
-  final _source;
+  @override
+  final dynamic source;
 
-  int get offset => span == null ? null : span.start.offset;
+  @override
+  int get offset => span?.start?.offset;
 
-  SourceSpanFormatException(String message, SourceSpan span, [this._source])
+  SourceSpanFormatException(String message, SourceSpan span, [this.source])
       : super(message, span);
 }
diff --git a/lib/src/span_mixin.dart b/lib/src/span_mixin.dart
index 75ecb3d..8960611 100644
--- a/lib/src/span_mixin.dart
+++ b/lib/src/span_mixin.dart
@@ -16,58 +16,69 @@
 /// [start] comes before [end], and that [text] has a number of characters equal
 /// to the distance between [start] and [end].
 abstract class SourceSpanMixin implements SourceSpan {
+  @override
   Uri get sourceUrl => start.sourceUrl;
+
+  @override
   int get length => end.offset - start.offset;
 
+  @override
   int compareTo(SourceSpan other) {
-    var result = start.compareTo(other.start);
+    final result = start.compareTo(other.start);
     return result == 0 ? end.compareTo(other.end) : result;
   }
 
+  @override
   SourceSpan union(SourceSpan other) {
     if (sourceUrl != other.sourceUrl) {
-      throw ArgumentError("Source URLs \"${sourceUrl}\" and "
+      throw ArgumentError("Source URLs \"$sourceUrl\" and "
           " \"${other.sourceUrl}\" don't match.");
     }
 
-    var start = min(this.start, other.start);
-    var end = max(this.end, other.end);
-    var beginSpan = start == this.start ? this : other;
-    var endSpan = end == this.end ? this : other;
+    final start = min(this.start, other.start);
+    final end = max(this.end, other.end);
+    final beginSpan = start == this.start ? this : other;
+    final endSpan = end == this.end ? this : other;
 
     if (beginSpan.end.compareTo(endSpan.start) < 0) {
       throw ArgumentError("Spans $this and $other are disjoint.");
     }
 
-    var text = beginSpan.text +
+    final text = beginSpan.text +
         endSpan.text.substring(beginSpan.end.distance(endSpan.start));
     return SourceSpan(start, end, text);
   }
 
+  @override
   String message(String message, {color}) {
-    var buffer = StringBuffer();
-    buffer.write('line ${start.line + 1}, column ${start.column + 1}');
+    final buffer = StringBuffer()
+      ..write('line ${start.line + 1}, column ${start.column + 1}');
     if (sourceUrl != null) buffer.write(' of ${p.prettyUri(sourceUrl)}');
     buffer.write(': $message');
 
-    var highlight = this.highlight(color: color);
+    final highlight = this.highlight(color: color);
     if (highlight.isNotEmpty) {
-      buffer.writeln();
-      buffer.write(highlight);
+      buffer
+        ..writeln()
+        ..write(highlight);
     }
 
     return buffer.toString();
   }
 
+  @override
   String highlight({color}) {
-    if (this is! SourceSpanWithContext && this.length == 0) return "";
+    if (this is! SourceSpanWithContext && length == 0) return "";
     return Highlighter(this, color: color).highlight();
   }
 
+  @override
   bool operator ==(other) =>
       other is SourceSpan && start == other.start && end == other.end;
 
+  @override
   int get hashCode => start.hashCode + (31 * end.hashCode);
 
+  @override
   String toString() => '<$runtimeType: from $start to $end "$text">';
 }
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 228b240..33791de 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -4,12 +4,12 @@
 
 /// Returns the minimum of [obj1] and [obj2] according to
 /// [Comparable.compareTo].
-Comparable min(Comparable obj1, Comparable obj2) =>
+T min<T extends Comparable>(T obj1, T obj2) =>
     obj1.compareTo(obj2) > 0 ? obj2 : obj1;
 
 /// Returns the maximum of [obj1] and [obj2] according to
 /// [Comparable.compareTo].
-Comparable max(Comparable obj1, Comparable obj2) =>
+T max<T extends Comparable>(T obj1, T obj2) =>
     obj1.compareTo(obj2) > 0 ? obj1 : obj2;
 
 /// Returns the number of instances of [codeUnit] in [string].
@@ -31,7 +31,7 @@
   if (text.isEmpty) {
     var beginningOfLine = 0;
     while (true) {
-      var index = context.indexOf("\n", beginningOfLine);
+      final index = context.indexOf("\n", beginningOfLine);
       if (index == -1) {
         return context.length - beginningOfLine >= column
             ? beginningOfLine
@@ -46,10 +46,11 @@
   var index = context.indexOf(text);
   while (index != -1) {
     // Start looking before [index] in case [text] starts with a newline.
-    var lineStart = index == 0 ? 0 : context.lastIndexOf('\n', index - 1) + 1;
-    var textColumn = index - lineStart;
+    final lineStart = index == 0 ? 0 : context.lastIndexOf('\n', index - 1) + 1;
+    final textColumn = index - lineStart;
     if (column == textColumn) return lineStart;
     index = context.indexOf(text, index + 1);
   }
+  // ignore: avoid_returning_null
   return null;
 }
diff --git a/test/file_test.dart b/test/file_test.dart
index 6759ca2..2cfe2d4 100644
--- a/test/file_test.dart
+++ b/test/file_test.dart
@@ -5,8 +5,8 @@
 import 'package:test/test.dart';
 import 'package:source_span/source_span.dart';
 
-main() {
-  var file;
+void main() {
+  SourceFile file;
   setUp(() {
     file = SourceFile.fromString("""
 foo bar baz
@@ -109,7 +109,7 @@
 
     group("for span().union()", () {
       test("source URLs must match", () {
-        var other = SourceSpan(SourceLocation(10), SourceLocation(11), "_");
+        final other = SourceSpan(SourceLocation(10), SourceLocation(11), "_");
 
         expect(() => file.span(9, 10).union(other), throwsArgumentError);
       });
@@ -121,7 +121,7 @@
     });
 
     test("for span().expand() source URLs must match", () {
-      var other = SourceFile.fromString("""
+      final other = SourceFile.fromString("""
 foo bar baz
 whiz bang boom
 zip zap zop""", url: "bar.dart").span(10, 11);
@@ -148,13 +148,13 @@
 
   group("span()", () {
     test("returns a span between the given offsets", () {
-      var span = file.span(5, 10);
+      final span = file.span(5, 10);
       expect(span.start, equals(file.location(5)));
       expect(span.end, equals(file.location(10)));
     });
 
     test("end defaults to the end of the file", () {
-      var span = file.span(5);
+      final span = file.span(5);
       expect(span.start, equals(file.location(5)));
       expect(span.end, equals(file.location(file.length)));
     });
@@ -238,8 +238,8 @@
     });
 
     test("pointSpan() returns a FileSpan", () {
-      var location = file.location(15);
-      var span = location.pointSpan();
+      final location = file.location(15);
+      final span = location.pointSpan();
       expect(span, isA<FileSpan>());
       expect(span.start, equals(location));
       expect(span.end, equals(location));
@@ -258,26 +258,26 @@
 
     group("context", () {
       test("contains the span's text", () {
-        var span = file.span(8, 15);
+        final span = file.span(8, 15);
         expect(span.context.contains(span.text), isTrue);
         expect(span.context, equals('foo bar baz\nwhiz bang boom\n'));
       });
 
       test("contains the previous line for a point span at the end of a line",
           () {
-        var span = file.span(25, 25);
+        final span = file.span(25, 25);
         expect(span.context, equals('whiz bang boom\n'));
       });
 
       test("contains the next line for a point span at the beginning of a line",
           () {
-        var span = file.span(12, 12);
+        final span = file.span(12, 12);
         expect(span.context, equals('whiz bang boom\n'));
       });
 
       group("for a point span at the end of a file", () {
         test("without a newline, contains the last line", () {
-          var span = file.span(file.length, file.length);
+          final span = file.span(file.length, file.length);
           expect(span.context, equals('zip zap zop'));
         });
 
@@ -288,57 +288,57 @@
 zip zap zop
 """, url: "foo.dart");
 
-          var span = file.span(file.length, file.length);
+          final span = file.span(file.length, file.length);
           expect(span.context, isEmpty);
         });
       });
     });
 
     group("union()", () {
-      var span;
+      FileSpan span;
       setUp(() {
         span = file.span(5, 12);
       });
 
       test("works with a preceding adjacent span", () {
-        var other = file.span(0, 5);
-        var result = span.union(other);
+        final other = file.span(0, 5);
+        final result = span.union(other);
         expect(result.start, equals(other.start));
         expect(result.end, equals(span.end));
         expect(result.text, equals("foo bar baz\n"));
       });
 
       test("works with a preceding overlapping span", () {
-        var other = file.span(0, 8);
-        var result = span.union(other);
+        final other = file.span(0, 8);
+        final result = span.union(other);
         expect(result.start, equals(other.start));
         expect(result.end, equals(span.end));
         expect(result.text, equals("foo bar baz\n"));
       });
 
       test("works with a following adjacent span", () {
-        var other = file.span(12, 16);
-        var result = span.union(other);
+        final other = file.span(12, 16);
+        final result = span.union(other);
         expect(result.start, equals(span.start));
         expect(result.end, equals(other.end));
         expect(result.text, equals("ar baz\nwhiz"));
       });
 
       test("works with a following overlapping span", () {
-        var other = file.span(9, 16);
-        var result = span.union(other);
+        final other = file.span(9, 16);
+        final result = span.union(other);
         expect(result.start, equals(span.start));
         expect(result.end, equals(other.end));
         expect(result.text, equals("ar baz\nwhiz"));
       });
 
       test("works with an internal overlapping span", () {
-        var other = file.span(7, 10);
+        final other = file.span(7, 10);
         expect(span.union(other), equals(span));
       });
 
       test("works with an external overlapping span", () {
-        var other = file.span(0, 16);
+        final other = file.span(0, 16);
         expect(span.union(other), equals(other));
       });
 
@@ -347,9 +347,9 @@
       });
 
       test("returns a base SourceSpan for a SourceSpan input", () {
-        var other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
+        final other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
             SourceLocation(5, sourceUrl: "foo.dart"), "hey, ");
-        var result = span.union(other);
+        final result = span.union(other);
         expect(result, isNot(isA<FileSpan>()));
         expect(result.start, equals(other.start));
         expect(result.end, equals(span.end));
@@ -358,50 +358,50 @@
     });
 
     group("expand()", () {
-      var span;
+      FileSpan span;
       setUp(() {
         span = file.span(5, 12);
       });
 
       test("works with a preceding nonadjacent span", () {
-        var other = file.span(0, 3);
-        var result = span.expand(other);
+        final other = file.span(0, 3);
+        final result = span.expand(other);
         expect(result.start, equals(other.start));
         expect(result.end, equals(span.end));
         expect(result.text, equals("foo bar baz\n"));
       });
 
       test("works with a preceding overlapping span", () {
-        var other = file.span(0, 8);
-        var result = span.expand(other);
+        final other = file.span(0, 8);
+        final result = span.expand(other);
         expect(result.start, equals(other.start));
         expect(result.end, equals(span.end));
         expect(result.text, equals("foo bar baz\n"));
       });
 
       test("works with a following nonadjacent span", () {
-        var other = file.span(14, 16);
-        var result = span.expand(other);
+        final other = file.span(14, 16);
+        final result = span.expand(other);
         expect(result.start, equals(span.start));
         expect(result.end, equals(other.end));
         expect(result.text, equals("ar baz\nwhiz"));
       });
 
       test("works with a following overlapping span", () {
-        var other = file.span(9, 16);
-        var result = span.expand(other);
+        final other = file.span(9, 16);
+        final result = span.expand(other);
         expect(result.start, equals(span.start));
         expect(result.end, equals(other.end));
         expect(result.text, equals("ar baz\nwhiz"));
       });
 
       test("works with an internal overlapping span", () {
-        var other = file.span(7, 10);
+        final other = file.span(7, 10);
         expect(span.expand(other), equals(span));
       });
 
       test("works with an external overlapping span", () {
-        var other = file.span(0, 16);
+        final other = file.span(0, 16);
         expect(span.expand(other), equals(other));
       });
     });
diff --git a/test/highlight_test.dart b/test/highlight_test.dart
index c215680..33719bc 100644
--- a/test/highlight_test.dart
+++ b/test/highlight_test.dart
@@ -2,13 +2,14 @@
 // 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 'package:term_glyph/term_glyph.dart' as glyph;
-import 'package:test/test.dart';
+// ignore_for_file: prefer_interpolation_to_compose_strings
 
 import 'package:source_span/source_span.dart';
 import 'package:source_span/src/colors.dart' as colors;
+import 'package:term_glyph/term_glyph.dart' as glyph;
+import 'package:test/test.dart';
 
-main() {
+void main() {
   bool oldAscii;
   setUpAll(() {
     oldAscii = glyph.ascii;
@@ -19,7 +20,7 @@
     glyph.ascii = oldAscii;
   });
 
-  var file;
+  SourceFile file;
   setUp(() {
     file = SourceFile.fromString("""
 foo bar baz
@@ -37,7 +38,7 @@
   });
 
   test("gracefully handles a missing source URL", () {
-    var span = SourceFile.fromString("foo bar baz").span(4, 7);
+    final span = SourceFile.fromString("foo bar baz").span(4, 7);
     expect(span.highlight(), equals("""
   ,
 1 | foo bar baz
@@ -122,7 +123,7 @@
     });
 
     test("on an empty line", () {
-      var file = SourceFile.fromString("foo\n\nbar");
+      final file = SourceFile.fromString("foo\n\nbar");
       expect(file.location(4).pointSpan().highlight(), equals("""
   ,
 2 | 
@@ -193,7 +194,7 @@
     });
 
     test("highlights the full first line even if it's indented", () {
-      var file = SourceFile.fromString("""
+      final file = SourceFile.fromString("""
   foo bar baz
   whiz bang boom
   zip zap zop
@@ -209,7 +210,7 @@
     });
 
     test("highlights the full first line if it's empty", () {
-      var file = SourceFile.fromString("""
+      final file = SourceFile.fromString("""
 foo
 
 bar
@@ -241,7 +242,7 @@
     });
 
     test("highlights the full last line with a trailing Windows newline", () {
-      var file = SourceFile.fromString("""
+      final file = SourceFile.fromString("""
 foo bar baz\r
 whiz bang boom\r
 zip zap zop\r
@@ -268,7 +269,7 @@
     test(
         "highlights the full last line at the end of the file with no trailing "
         "newline", () {
-      var file = SourceFile.fromString("""
+      final file = SourceFile.fromString("""
 foo bar baz
 whiz bang boom
 zip zap zop""");
@@ -283,7 +284,7 @@
     });
 
     test("highlights the full last line if it's empty", () {
-      var file = SourceFile.fromString("""
+      final file = SourceFile.fromString("""
 foo
 
 bar
@@ -297,7 +298,7 @@
     });
 
     test("highlights multiple empty lines", () {
-      var file = SourceFile.fromString("foo\n\n\n\nbar");
+      final file = SourceFile.fromString("foo\n\n\n\nbar");
       expect(file.span(4, 7).highlight(), equals("""
   ,
 2 | / 
@@ -308,7 +309,7 @@
 
     // Regression test for #32
     test("highlights the end of a line and an empty line", () {
-      var file = SourceFile.fromString("foo\n\n");
+      final file = SourceFile.fromString("foo\n\n");
       expect(file.span(3, 5).highlight(), equals("""
   ,
 1 |   foo
@@ -321,7 +322,7 @@
   group("prints tabs as spaces", () {
     group("in a single-line span", () {
       test("before the highlighted section", () {
-        var span = SourceFile.fromString("foo\tbar baz").span(4, 7);
+        final span = SourceFile.fromString("foo\tbar baz").span(4, 7);
 
         expect(span.highlight(), equals("""
   ,
@@ -331,7 +332,7 @@
       });
 
       test("within the highlighted section", () {
-        var span = SourceFile.fromString("foo bar\tbaz bang").span(4, 11);
+        final span = SourceFile.fromString("foo bar\tbaz bang").span(4, 11);
 
         expect(span.highlight(), equals("""
   ,
@@ -341,7 +342,7 @@
       });
 
       test("after the highlighted section", () {
-        var span = SourceFile.fromString("foo bar\tbaz").span(4, 7);
+        final span = SourceFile.fromString("foo bar\tbaz").span(4, 7);
 
         expect(span.highlight(), equals("""
   ,
@@ -353,7 +354,7 @@
 
     group("in a multi-line span", () {
       test("before the highlighted section", () {
-        var span = SourceFile.fromString("""
+        final span = SourceFile.fromString("""
 foo\tbar baz
 whiz bang boom
 """).span(4, 21);
@@ -368,7 +369,7 @@
       });
 
       test("within the first highlighted line", () {
-        var span = SourceFile.fromString("""
+        final span = SourceFile.fromString("""
 foo bar\tbaz
 whiz bang boom
 """).span(4, 21);
@@ -383,7 +384,7 @@
       });
 
       test("within a middle highlighted line", () {
-        var span = SourceFile.fromString("""
+        final span = SourceFile.fromString("""
 foo bar baz
 whiz\tbang boom
 zip zap zop
@@ -400,7 +401,7 @@
       });
 
       test("within the last highlighted line", () {
-        var span = SourceFile.fromString("""
+        final span = SourceFile.fromString("""
 foo bar baz
 whiz\tbang boom
 """).span(4, 21);
@@ -415,7 +416,7 @@
       });
 
       test("after the highlighted section", () {
-        var span = SourceFile.fromString("""
+        final span = SourceFile.fromString("""
 foo bar baz
 whiz bang\tboom
 """).span(4, 21);
@@ -433,7 +434,7 @@
 
   group("supports lines of preceding and following context for a span", () {
     test("within a single line", () {
-      var span = SourceSpanWithContext(
+      final span = SourceSpanWithContext(
           SourceLocation(20, line: 2, column: 5, sourceUrl: "foo.dart"),
           SourceLocation(27, line: 2, column: 12, sourceUrl: "foo.dart"),
           "foo bar",
@@ -450,7 +451,7 @@
     });
 
     test("covering a full line", () {
-      var span = SourceSpanWithContext(
+      final span = SourceSpanWithContext(
           SourceLocation(15, line: 2, column: 0, sourceUrl: "foo.dart"),
           SourceLocation(33, line: 3, column: 0, sourceUrl: "foo.dart"),
           "-----foo bar-----\n",
@@ -467,7 +468,7 @@
     });
 
     test("covering multiple full lines", () {
-      var span = SourceSpanWithContext(
+      final span = SourceSpanWithContext(
           SourceLocation(15, line: 2, column: 0, sourceUrl: "foo.dart"),
           SourceLocation(23, line: 4, column: 0, sourceUrl: "foo.dart"),
           "foo\nbar\n",
@@ -495,38 +496,38 @@
 
     test("colorizes if color is true", () {
       expect(file.span(4, 7).highlight(color: true), equals("""
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE} foo ${colors.RED}bar${colors.NONE} baz
-${colors.BLUE}  |${colors.NONE}     ${colors.RED}^^^${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none} foo ${colors.red}bar${colors.none} baz
+${colors.blue}  |${colors.none}     ${colors.red}^^^${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
 
     test("uses the given color if it's passed", () {
-      expect(file.span(4, 7).highlight(color: colors.YELLOW), equals("""
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE} foo ${colors.YELLOW}bar${colors.NONE} baz
-${colors.BLUE}  |${colors.NONE}     ${colors.YELLOW}^^^${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+      expect(file.span(4, 7).highlight(color: colors.yellow), equals("""
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none} foo ${colors.yellow}bar${colors.none} baz
+${colors.blue}  |${colors.none}     ${colors.yellow}^^^${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
 
     test("colorizes a multiline span", () {
       expect(file.span(4, 34).highlight(color: true), equals("""
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE}   foo ${colors.RED}bar baz${colors.NONE}
-${colors.BLUE}  |${colors.NONE} ${colors.RED},-----^${colors.NONE}
-${colors.BLUE}2 |${colors.NONE} ${colors.RED}| whiz bang boom${colors.NONE}
-${colors.BLUE}3 |${colors.NONE} ${colors.RED}| zip zap${colors.NONE} zop
-${colors.BLUE}  |${colors.NONE} ${colors.RED}'-------^${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none}   foo ${colors.red}bar baz${colors.none}
+${colors.blue}  |${colors.none} ${colors.red},-----^${colors.none}
+${colors.blue}2 |${colors.none} ${colors.red}| whiz bang boom${colors.none}
+${colors.blue}3 |${colors.none} ${colors.red}| zip zap${colors.none} zop
+${colors.blue}  |${colors.none} ${colors.red}'-------^${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
 
     test("colorizes a multiline span that highlights full lines", () {
       expect(file.span(0, 39).highlight(color: true), equals("""
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE} ${colors.RED}/ foo bar baz${colors.NONE}
-${colors.BLUE}2 |${colors.NONE} ${colors.RED}| whiz bang boom${colors.NONE}
-${colors.BLUE}3 |${colors.NONE} ${colors.RED}\\ zip zap zop${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none} ${colors.red}/ foo bar baz${colors.none}
+${colors.blue}2 |${colors.none} ${colors.red}| whiz bang boom${colors.none}
+${colors.blue}3 |${colors.none} ${colors.red}\\ zip zap zop${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
   });
 
diff --git a/test/location_test.dart b/test/location_test.dart
index 9032936..c0e9c07 100644
--- a/test/location_test.dart
+++ b/test/location_test.dart
@@ -2,11 +2,11 @@
 // 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 'package:test/test.dart';
 import 'package:source_span/source_span.dart';
+import 'package:test/test.dart';
 
-main() {
-  var location;
+void main() {
+  SourceLocation location;
   setUp(() {
     location = SourceLocation(15, line: 2, column: 6, sourceUrl: "foo.dart");
   });
@@ -48,19 +48,19 @@
     });
 
     test('gracefully handles a missing source URL', () {
-      var location = SourceLocation(15, line: 2, column: 6);
+      final location = SourceLocation(15, line: 2, column: 6);
       expect(location.toolString, equals('unknown source:3:7'));
     });
   });
 
   test("distance returns the absolute distance between locations", () {
-    var other = SourceLocation(10, sourceUrl: "foo.dart");
+    final other = SourceLocation(10, sourceUrl: "foo.dart");
     expect(location.distance(other), equals(5));
     expect(other.distance(location), equals(5));
   });
 
   test("pointSpan returns an empty span at location", () {
-    var span = location.pointSpan();
+    final span = location.pointSpan();
     expect(span.start, equals(location));
     expect(span.end, equals(location));
     expect(span.text, isEmpty);
@@ -68,7 +68,7 @@
 
   group("compareTo()", () {
     test("sorts by offset", () {
-      var other = SourceLocation(20, sourceUrl: "foo.dart");
+      final other = SourceLocation(20, sourceUrl: "foo.dart");
       expect(location.compareTo(other), lessThan(0));
       expect(other.compareTo(location), greaterThan(0));
     });
@@ -80,17 +80,17 @@
 
   group("equality", () {
     test("two locations with the same offset and source are equal", () {
-      var other = SourceLocation(15, sourceUrl: "foo.dart");
+      final other = SourceLocation(15, sourceUrl: "foo.dart");
       expect(location, equals(other));
     });
 
     test("a different offset isn't equal", () {
-      var other = SourceLocation(10, sourceUrl: "foo.dart");
+      final other = SourceLocation(10, sourceUrl: "foo.dart");
       expect(location, isNot(equals(other)));
     });
 
     test("a different source isn't equal", () {
-      var other = SourceLocation(15, sourceUrl: "bar.dart");
+      final other = SourceLocation(15, sourceUrl: "bar.dart");
       expect(location, isNot(equals(other)));
     });
   });
diff --git a/test/span_test.dart b/test/span_test.dart
index 6a18b42..d53e1e0 100644
--- a/test/span_test.dart
+++ b/test/span_test.dart
@@ -8,7 +8,7 @@
 import 'package:source_span/source_span.dart';
 import 'package:source_span/src/colors.dart' as colors;
 
-main() {
+void main() {
   bool oldAscii;
   setUpAll(() {
     oldAscii = glyph.ascii;
@@ -19,7 +19,7 @@
     glyph.ascii = oldAscii;
   });
 
-  var span;
+  SourceSpan span;
   setUp(() {
     span = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
         SourceLocation(12, sourceUrl: "foo.dart"), "foo bar");
@@ -28,42 +28,42 @@
   group('errors', () {
     group('for new SourceSpan()', () {
       test('source URLs must match', () {
-        var start = SourceLocation(0, sourceUrl: "foo.dart");
-        var end = SourceLocation(1, sourceUrl: "bar.dart");
+        final start = SourceLocation(0, sourceUrl: "foo.dart");
+        final end = SourceLocation(1, sourceUrl: "bar.dart");
         expect(() => SourceSpan(start, end, "_"), throwsArgumentError);
       });
 
       test('end must come after start', () {
-        var start = SourceLocation(1);
-        var end = SourceLocation(0);
+        final start = SourceLocation(1);
+        final end = SourceLocation(0);
         expect(() => SourceSpan(start, end, "_"), throwsArgumentError);
       });
 
       test('text must be the right length', () {
-        var start = SourceLocation(0);
-        var end = SourceLocation(1);
+        final start = SourceLocation(0);
+        final end = SourceLocation(1);
         expect(() => SourceSpan(start, end, "abc"), throwsArgumentError);
       });
     });
 
     group('for new SourceSpanWithContext()', () {
       test('context must contain text', () {
-        var start = SourceLocation(2);
-        var end = SourceLocation(5);
+        final start = SourceLocation(2);
+        final end = SourceLocation(5);
         expect(() => SourceSpanWithContext(start, end, "abc", "--axc--"),
             throwsArgumentError);
       });
 
       test('text starts at start.column in context', () {
-        var start = SourceLocation(3);
-        var end = SourceLocation(5);
+        final start = SourceLocation(3);
+        final end = SourceLocation(5);
         expect(() => SourceSpanWithContext(start, end, "abc", "--abc--"),
             throwsArgumentError);
       });
 
       test('text starts at start.column of line in multi-line context', () {
-        var start = SourceLocation(4, line: 55, column: 3);
-        var end = SourceLocation(7, line: 55, column: 6);
+        final start = SourceLocation(4, line: 55, column: 3);
+        final end = SourceLocation(7, line: 55, column: 6);
         expect(() => SourceSpanWithContext(start, end, "abc", "\n--abc--"),
             throwsArgumentError);
         expect(() => SourceSpanWithContext(start, end, "abc", "\n----abc--"),
@@ -77,10 +77,10 @@
       });
 
       test('text can occur multiple times in context', () {
-        var start1 = SourceLocation(4, line: 55, column: 2);
-        var end1 = SourceLocation(7, line: 55, column: 5);
-        var start2 = SourceLocation(4, line: 55, column: 8);
-        var end2 = SourceLocation(7, line: 55, column: 11);
+        final start1 = SourceLocation(4, line: 55, column: 2);
+        final end1 = SourceLocation(7, line: 55, column: 5);
+        final start2 = SourceLocation(4, line: 55, column: 8);
+        final end2 = SourceLocation(7, line: 55, column: 11);
         SourceSpanWithContext(start1, end1, "abc", "--abc---abc--\n");
         SourceSpanWithContext(start1, end1, "abc", "--abc--abc--\n");
         SourceSpanWithContext(start2, end2, "abc", "--abc---abc--\n");
@@ -96,14 +96,14 @@
 
     group('for union()', () {
       test('source URLs must match', () {
-        var other = SourceSpan(SourceLocation(12, sourceUrl: "bar.dart"),
+        final other = SourceSpan(SourceLocation(12, sourceUrl: "bar.dart"),
             SourceLocation(13, sourceUrl: "bar.dart"), "_");
 
         expect(() => span.union(other), throwsArgumentError);
       });
 
       test('spans may not be disjoint', () {
-        var other = SourceSpan(SourceLocation(13, sourceUrl: 'foo.dart'),
+        final other = SourceSpan(SourceLocation(13, sourceUrl: 'foo.dart'),
             SourceLocation(14, sourceUrl: 'foo.dart'), "_");
 
         expect(() => span.union(other), throwsArgumentError);
@@ -111,7 +111,7 @@
     });
 
     test('for compareTo() source URLs must match', () {
-      var other = SourceSpan(SourceLocation(12, sourceUrl: "bar.dart"),
+      final other = SourceSpan(SourceLocation(12, sourceUrl: "bar.dart"),
           SourceLocation(13, sourceUrl: "bar.dart"), "_");
 
       expect(() => span.compareTo(other), throwsArgumentError);
@@ -127,54 +127,54 @@
 
   group("union()", () {
     test("works with a preceding adjacent span", () {
-      var other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
           SourceLocation(5, sourceUrl: "foo.dart"), "hey, ");
 
-      var result = span.union(other);
+      final result = span.union(other);
       expect(result.start, equals(other.start));
       expect(result.end, equals(span.end));
       expect(result.text, equals("hey, foo bar"));
     });
 
     test("works with a preceding overlapping span", () {
-      var other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
           SourceLocation(8, sourceUrl: "foo.dart"), "hey, foo");
 
-      var result = span.union(other);
+      final result = span.union(other);
       expect(result.start, equals(other.start));
       expect(result.end, equals(span.end));
       expect(result.text, equals("hey, foo bar"));
     });
 
     test("works with a following adjacent span", () {
-      var other = SourceSpan(SourceLocation(12, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(12, sourceUrl: "foo.dart"),
           SourceLocation(16, sourceUrl: "foo.dart"), " baz");
 
-      var result = span.union(other);
+      final result = span.union(other);
       expect(result.start, equals(span.start));
       expect(result.end, equals(other.end));
       expect(result.text, equals("foo bar baz"));
     });
 
     test("works with a following overlapping span", () {
-      var other = SourceSpan(SourceLocation(9, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(9, sourceUrl: "foo.dart"),
           SourceLocation(16, sourceUrl: "foo.dart"), "bar baz");
 
-      var result = span.union(other);
+      final result = span.union(other);
       expect(result.start, equals(span.start));
       expect(result.end, equals(other.end));
       expect(result.text, equals("foo bar baz"));
     });
 
     test("works with an internal overlapping span", () {
-      var other = SourceSpan(SourceLocation(7, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(7, sourceUrl: "foo.dart"),
           SourceLocation(10, sourceUrl: "foo.dart"), "o b");
 
       expect(span.union(other), equals(span));
     });
 
     test("works with an external overlapping span", () {
-      var other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
           SourceLocation(16, sourceUrl: "foo.dart"), "hey, foo bar baz");
 
       expect(span.union(other), equals(other));
@@ -192,7 +192,7 @@
     });
 
     test("gracefully handles a missing source URL", () {
-      var span = SourceSpan(SourceLocation(5), SourceLocation(12), "foo bar");
+      final span = SourceSpan(SourceLocation(5), SourceLocation(12), "foo bar");
 
       expect(span.message("oh no"), equalsIgnoringWhitespace("""
 line 1, column 6: oh no
@@ -203,7 +203,7 @@
     });
 
     test("gracefully handles empty text", () {
-      var span = SourceSpan(SourceLocation(5), SourceLocation(5), "");
+      final span = SourceSpan(SourceLocation(5), SourceLocation(5), "");
 
       expect(span.message("oh no"), equals("line 1, column 6: oh no"));
     });
@@ -220,40 +220,40 @@
     test("colorizes if color is true", () {
       expect(span.message("oh no", color: true), equals("""
 line 1, column 6 of foo.dart: oh no
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE} ${colors.RED}foo bar${colors.NONE}
-${colors.BLUE}  |${colors.NONE} ${colors.RED}^^^^^^^${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none} ${colors.red}foo bar${colors.none}
+${colors.blue}  |${colors.none} ${colors.red}^^^^^^^${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
 
     test("uses the given color if it's passed", () {
-      expect(span.message("oh no", color: colors.YELLOW), equals("""
+      expect(span.message("oh no", color: colors.yellow), equals("""
 line 1, column 6 of foo.dart: oh no
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE} ${colors.YELLOW}foo bar${colors.NONE}
-${colors.BLUE}  |${colors.NONE} ${colors.YELLOW}^^^^^^^${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none} ${colors.yellow}foo bar${colors.none}
+${colors.blue}  |${colors.none} ${colors.yellow}^^^^^^^${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
 
     test("with context, underlines the right column", () {
-      var spanWithContext = SourceSpanWithContext(
+      final spanWithContext = SourceSpanWithContext(
           SourceLocation(5, sourceUrl: "foo.dart"),
           SourceLocation(12, sourceUrl: "foo.dart"),
           "foo bar",
           "-----foo bar-----");
 
-      expect(spanWithContext.message("oh no", color: colors.YELLOW), equals("""
+      expect(spanWithContext.message("oh no", color: colors.yellow), equals("""
 line 1, column 6 of foo.dart: oh no
-${colors.BLUE}  ,${colors.NONE}
-${colors.BLUE}1 |${colors.NONE} -----${colors.YELLOW}foo bar${colors.NONE}-----
-${colors.BLUE}  |${colors.NONE}      ${colors.YELLOW}^^^^^^^${colors.NONE}
-${colors.BLUE}  '${colors.NONE}"""));
+${colors.blue}  ,${colors.none}
+${colors.blue}1 |${colors.none} -----${colors.yellow}foo bar${colors.none}-----
+${colors.blue}  |${colors.none}      ${colors.yellow}^^^^^^^${colors.none}
+${colors.blue}  '${colors.none}"""));
     });
   });
 
   group("compareTo()", () {
     test("sorts by start location first", () {
-      var other = SourceSpan(SourceLocation(6, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(6, sourceUrl: "foo.dart"),
           SourceLocation(14, sourceUrl: "foo.dart"), "oo bar b");
 
       expect(span.compareTo(other), lessThan(0));
@@ -261,7 +261,7 @@
     });
 
     test("sorts by length second", () {
-      var other = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
           SourceLocation(14, sourceUrl: "foo.dart"), "foo bar b");
 
       expect(span.compareTo(other), lessThan(0));
@@ -275,28 +275,28 @@
 
   group("equality", () {
     test("two spans with the same locations are equal", () {
-      var other = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
           SourceLocation(12, sourceUrl: "foo.dart"), "foo bar");
 
       expect(span, equals(other));
     });
 
     test("a different start isn't equal", () {
-      var other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(0, sourceUrl: "foo.dart"),
           SourceLocation(12, sourceUrl: "foo.dart"), "hey, foo bar");
 
       expect(span, isNot(equals(other)));
     });
 
     test("a different end isn't equal", () {
-      var other = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
+      final other = SourceSpan(SourceLocation(5, sourceUrl: "foo.dart"),
           SourceLocation(16, sourceUrl: "foo.dart"), "foo bar baz");
 
       expect(span, isNot(equals(other)));
     });
 
     test("a different source URL isn't equal", () {
-      var other = SourceSpan(SourceLocation(5, sourceUrl: "bar.dart"),
+      final other = SourceSpan(SourceLocation(5, sourceUrl: "bar.dart"),
           SourceLocation(12, sourceUrl: "bar.dart"), "foo bar");
 
       expect(span, isNot(equals(other)));
diff --git a/test/utils_test.dart b/test/utils_test.dart
index a8146e3..a4a372e 100644
--- a/test/utils_test.dart
+++ b/test/utils_test.dart
@@ -5,18 +5,18 @@
 import 'package:test/test.dart';
 import 'package:source_span/src/utils.dart';
 
-main() {
+void main() {
   group('find line start', () {
     test('skip entries in wrong column', () {
-      var context = '0_bb\n1_bbb\n2b____\n3bbb\n';
-      var index = findLineStart(context, 'b', 1);
+      final context = '0_bb\n1_bbb\n2b____\n3bbb\n';
+      final index = findLineStart(context, 'b', 1);
       expect(index, 11);
       expect(context.substring(index - 1, index + 3), '\n2b_');
     });
 
     test('end of line column for empty text', () {
-      var context = '0123\n56789\nabcdefgh\n';
-      var index = findLineStart(context, '', 5);
+      final context = '0123\n56789\nabcdefgh\n';
+      final index = findLineStart(context, '', 5);
       expect(index, 5);
       expect(context[index], '5');
     });
@@ -33,25 +33,25 @@
     });
 
     test('empty text in empty context', () {
-      var index = findLineStart('', '', 0);
+      final index = findLineStart('', '', 0);
       expect(index, 0);
     });
 
     test('found on the first line', () {
-      var context = '0\n2\n45\n';
-      var index = findLineStart(context, '0', 0);
+      final context = '0\n2\n45\n';
+      final index = findLineStart(context, '0', 0);
       expect(index, 0);
     });
 
     test('finds text that starts with a newline', () {
-      var context = '0\n2\n45\n';
-      var index = findLineStart(context, '\n2', 1);
+      final context = '0\n2\n45\n';
+      final index = findLineStart(context, '\n2', 1);
       expect(index, 0);
     });
 
     test('not found', () {
-      var context = '0\n2\n45\n';
-      var index = findLineStart(context, '0', 1);
+      final context = '0\n2\n45\n';
+      final index = findLineStart(context, '0', 1);
       expect(index, isNull);
     });
   });