Enable and fix lint prefer_final_locals (#131)

This package has a number of long methods as well as patterns where the
reassignment to local variables is a critical detail. Make it easier to
work with by marking locals that don't change with `final`.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 20eba0c..9f2325c 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -51,6 +51,7 @@
     - prefer_const_constructors
     - prefer_equal_for_default_values
     - prefer_final_fields
+    - prefer_final_locals
     - prefer_initializing_formals
     #- prefer_interpolation_to_compose_strings
     #- prefer_single_quotes
diff --git a/lib/dom.dart b/lib/dom.dart
index 85e02c4..fbd2c90 100644
--- a/lib/dom.dart
+++ b/lib/dom.dart
@@ -203,13 +203,13 @@
 
   // http://domparsing.spec.whatwg.org/#extensions-to-the-element-interface
   String get _outerHtml {
-    var str = StringBuffer();
+    final str = StringBuffer();
     _addOuterHtml(str);
     return str.toString();
   }
 
   String get _innerHtml {
-    var str = StringBuffer();
+    final str = StringBuffer();
     _addInnerHtml(str);
     return str.toString();
   }
@@ -284,16 +284,16 @@
 
     if (sourceSpan == null) return;
 
-    var tokenizer = HtmlTokenizer(sourceSpan.text,
+    final tokenizer = HtmlTokenizer(sourceSpan.text,
         generateSpans: true, attributeSpans: true);
 
     tokenizer.moveNext();
-    var token = tokenizer.current as StartTagToken;
+    final token = tokenizer.current as StartTagToken;
 
     if (token.attributeSpans == null) return; // no attributes
 
     for (var attr in token.attributeSpans) {
-      var offset = sourceSpan.start.offset;
+      final offset = sourceSpan.start.offset;
       _attributeSpans[attr.name] =
           sourceSpan.file.span(offset + attr.start, offset + attr.end);
       if (attr.startValue != null) {
@@ -400,8 +400,8 @@
     if (publicId != null || systemId != null) {
       // TODO(jmesserly): the html5 serialization spec does not add these. But
       // it seems useful, and the parser can handle it, so for now keeping it.
-      var pid = publicId ?? '';
-      var sid = systemId ?? '';
+      final pid = publicId ?? '';
+      final sid = systemId ?? '';
       return '<!DOCTYPE $name "$pid" "$sid">';
     } else {
       return '<!DOCTYPE $name>';
@@ -516,7 +516,7 @@
       }
     }
 
-    var fragment = parseFragment(html, container: parentTag);
+    final fragment = parseFragment(html, container: parentTag);
     Element element;
     if (fragment.children.length == 1) {
       element = fragment.children[0];
@@ -537,9 +537,9 @@
   // TODO(jmesserly): we can make this faster
   Element get previousElementSibling {
     if (parentNode == null) return null;
-    var siblings = parentNode.nodes;
+    final siblings = parentNode.nodes;
     for (var i = siblings.indexOf(this) - 1; i >= 0; i--) {
-      var s = siblings[i];
+      final s = siblings[i];
       if (s is Element) return s;
     }
     return null;
@@ -547,9 +547,9 @@
 
   Element get nextElementSibling {
     if (parentNode == null) return null;
-    var siblings = parentNode.nodes;
+    final siblings = parentNode.nodes;
     for (var i = siblings.indexOf(this) + 1; i < siblings.length; i++) {
-      var s = siblings[i];
+      final s = siblings[i];
       if (s is Element) return s;
     }
     return null;
@@ -557,7 +557,7 @@
 
   @override
   String toString() {
-    var prefix = Namespaces.getPrefix(namespaceUri);
+    final prefix = Namespaces.getPrefix(namespaceUri);
     return "<${prefix == null ? '' : '$prefix '}$localName>";
   }
 
@@ -632,7 +632,7 @@
         uri == Namespaces.svg) {
       return '';
     }
-    var prefix = Namespaces.getPrefix(uri);
+    final prefix = Namespaces.getPrefix(uri);
     // TODO(jmesserly): the spec doesn't define "qualified name".
     // I'm not sure if this is correct, but it should parse reasonably.
     return prefix == null ? '' : '$prefix:';
@@ -640,14 +640,14 @@
 
   @override
   Element clone(bool deep) {
-    var result = Element._(localName, namespaceUri)
+    final result = Element._(localName, namespaceUri)
       ..attributes = LinkedHashMap.from(attributes);
     return _clone(result, deep);
   }
 
   // http://dom.spec.whatwg.org/#dom-element-id
   String get id {
-    var result = attributes['id'];
+    final result = attributes['id'];
     return result ?? '';
   }
 
@@ -657,7 +657,7 @@
 
   // http://dom.spec.whatwg.org/#dom-element-classname
   String get className {
-    var result = attributes['class'];
+    final result = attributes['class'];
     return result ?? '';
   }
 
@@ -741,7 +741,7 @@
     //   2. we should update parent pointers in reverse order. That way they
     //      are removed from the original NodeList (if any) from the end, which
     //      is faster.
-    var list = _flattenDocFragments(collection);
+    final list = _flattenDocFragments(collection);
     for (var node in list.reversed) {
       _setParent(node);
     }
@@ -832,7 +832,7 @@
   @override
   void insertAll(int index, Iterable<Node> collection) {
     // Note: we need to be careful how we copy nodes. See note in addAll.
-    var list = _flattenDocFragments(collection);
+    final list = _flattenDocFragments(collection);
     for (var node in list.reversed) {
       _setParent(node);
     }
@@ -843,7 +843,7 @@
     // Note: this function serves two purposes:
     //  * it flattens document fragments
     //  * it creates a copy of [collections] when `collection is NodeList`.
-    var result = <Node>[];
+    final result = <Node>[];
     for (var node in collection) {
       if (node is DocumentFragment) {
         result.addAll(node.nodes);
@@ -994,7 +994,7 @@
   bool remove(Object element) {
     if (element is! Element) return false;
     for (var i = 0; i < length; i++) {
-      var indexElement = this[i];
+      final indexElement = this[i];
       if (identical(indexElement, element)) {
         indexElement.remove();
         return true;
diff --git a/lib/dom_parsing.dart b/lib/dom_parsing.dart
index 3c8774b..f108b59 100644
--- a/lib/dom_parsing.dart
+++ b/lib/dom_parsing.dart
@@ -111,7 +111,7 @@
 
   @override
   void visitComment(Comment node) {
-    var data = htmlSerializeEscape(node.data);
+    final data = htmlSerializeEscape(node.data);
     _str.write('<code class="markup comment">&lt;!--$data--></code>');
   }
 }
@@ -138,7 +138,7 @@
   // StringBuffer seems cleaner assuming Dart can unbox 1-char strings.
   StringBuffer result;
   for (var i = 0; i < text.length; i++) {
-    var ch = text[i];
+    final ch = text[i];
     String replace;
     switch (ch) {
       case '&':
@@ -201,7 +201,7 @@
   // Don't escape text for certain elements, notably <script>.
   final parent = node.parentNode;
   if (parent is Element) {
-    var tag = parent.localName;
+    final tag = parent.localName;
     if (rcdataElements.contains(tag) || tag == 'plaintext') {
       str.write(node.data);
       return;
diff --git a/lib/parser.dart b/lib/parser.dart
index 2a6c431..5cfd53d 100644
--- a/lib/parser.dart
+++ b/lib/parser.dart
@@ -38,7 +38,7 @@
 /// extracted from.
 Document parse(input,
     {String encoding, bool generateSpans = false, String sourceUrl}) {
-  var p = HtmlParser(input,
+  final p = HtmlParser(input,
       encoding: encoding, generateSpans: generateSpans, sourceUrl: sourceUrl);
   return p.parse();
 }
@@ -60,7 +60,7 @@
     String encoding,
     bool generateSpans = false,
     String sourceUrl}) {
-  var p = HtmlParser(input,
+  final p = HtmlParser(input,
       encoding: encoding, generateSpans: generateSpans, sourceUrl: sourceUrl);
   return p.parseFragment(container);
 }
@@ -277,7 +277,7 @@
   bool inForeignContent(Token token, int type) {
     if (tree.openElements.isEmpty) return false;
 
-    var node = tree.openElements.last;
+    final node = tree.openElements.last;
     if (node.namespaceUri == tree.defaultNamespace) return false;
 
     if (isMathMLTextIntegrationPoint(node)) {
@@ -310,7 +310,7 @@
 
   void mainLoop() {
     while (tokenizer.moveNext()) {
-      var token = tokenizer.current;
+      final token = tokenizer.current;
       var newToken = token;
       int type;
       while (newToken != null) {
@@ -362,7 +362,7 @@
 
     // When the loop finishes it's EOF
     var reprocess = true;
-    var reprocessPhases = [];
+    final reprocessPhases = [];
     while (reprocess) {
       reprocessPhases.add(phase);
       reprocess = phase.processEOF();
@@ -376,7 +376,7 @@
   /// better.
   SourceSpan get _lastSpan {
     if (tokenizer.stream.fileInfo == null) return null;
-    var pos = tokenizer.stream.position;
+    final pos = tokenizer.stream.position;
     return tokenizer.stream.fileInfo.location(pos).pointSpan();
   }
 
@@ -386,13 +386,13 @@
       span = _lastSpan;
     }
 
-    var err = ParseError(errorcode, span, datavars);
+    final err = ParseError(errorcode, span, datavars);
     errors.add(err);
     if (strict) throw err;
   }
 
   void adjustMathMLAttributes(StartTagToken token) {
-    var orig = token.data.remove('definitionurl');
+    final orig = token.data.remove('definitionurl');
     if (orig != null) {
       token.data['definitionURL'] = orig;
     }
@@ -464,7 +464,7 @@
       'zoomandpan': 'zoomAndPan'
     };
     for (var originalName in token.data.keys.toList()) {
-      var svgName = replacements[originalName];
+      final svgName = replacements[originalName];
       if (svgName != null) {
         token.data[svgName] = token.data.remove(originalName);
       }
@@ -490,7 +490,7 @@
     };
 
     for (var originalName in token.data.keys.toList()) {
-      var foreignName = replacements[originalName];
+      final foreignName = replacements[originalName];
       if (foreignName != null) {
         token.data[foreignName] = token.data.remove(originalName);
       }
@@ -502,7 +502,7 @@
     // specification.)
     for (var node in tree.openElements.reversed) {
       var nodeName = node.localName;
-      var last = node == tree.openElements[0];
+      final last = node == tree.openElements[0];
       if (last) {
         assert(innerHTMLMode);
         nodeName = innerHTML;
@@ -655,7 +655,7 @@
 
   /// Helper method for popping openElements.
   void popOpenElementsUntil(EndTagToken token) {
-    var name = token.name;
+    final name = token.name;
     var node = tree.openElements.removeLast();
     while (node.localName != name) {
       node = tree.openElements.removeLast();
@@ -682,10 +682,10 @@
 
   @override
   Token processDoctype(DoctypeToken token) {
-    var name = token.name;
+    final name = token.name;
     var publicId = token.publicId;
-    var systemId = token.systemId;
-    var correct = token.correct;
+    final systemId = token.systemId;
+    final correct = token.correct;
 
     if ((name != 'html' ||
         publicId != null ||
@@ -1046,15 +1046,15 @@
     tree.openElements.removeLast();
     token.selfClosingAcknowledged = true;
 
-    var attributes = token.data;
+    final attributes = token.data;
     if (!parser.tokenizer.stream.charEncodingCertain) {
-      var charset = attributes['charset'];
-      var content = attributes['content'];
+      final charset = attributes['charset'];
+      final content = attributes['content'];
       if (charset != null) {
         parser.tokenizer.stream.changeEncoding(charset);
       } else if (content != null) {
-        var data = EncodingBytes(content);
-        var codec = ContentAttrParser(data).parse();
+        final data = EncodingBytes(content);
+        final codec = ContentAttrParser(data).parse();
         parser.tokenizer.stream.changeEncoding(codec);
       }
     }
@@ -1082,7 +1082,7 @@
   }
 
   void endTagHead(EndTagToken token) {
-    var node = parser.tree.openElements.removeLast();
+    final node = parser.tree.openElements.removeLast();
     assert(node.localName == 'head');
     node.endSourceSpan = token.span;
     parser.phase = parser._afterHeadPhase;
@@ -1505,9 +1505,9 @@
   // helper
   void addFormattingElement(StartTagToken token) {
     tree.insertElement(token);
-    var element = tree.openElements.last;
+    final element = tree.openElements.last;
 
-    var matchingElements = [];
+    final matchingElements = [];
     for (Node node in tree.activeFormattingElements.reversed) {
       if (node == Marker) {
         break;
@@ -1555,7 +1555,7 @@
     var data = token.data;
     dropNewline = false;
     if (data.startsWith('\n')) {
-      var lastOpen = tree.openElements.last;
+      final lastOpen = tree.openElements.last;
       if (const ['pre', 'listing', 'textarea'].contains(lastOpen.localName) &&
           !lastOpen.hasContent()) {
         data = data.substring(1);
@@ -1662,7 +1662,7 @@
       'dt': ['dt', 'dd'],
       'dd': ['dt', 'dd']
     };
-    var stopNames = stopNamesMap[token.name];
+    final stopNames = stopNamesMap[token.name];
     for (var node in tree.openElements.reversed) {
       if (stopNames.contains(node.localName)) {
         parser.phase.processEndTag(EndTagToken(node.localName));
@@ -1702,7 +1702,7 @@
   }
 
   void startTagA(StartTagToken token) {
-    var afeAElement = tree.elementInActiveFormattingElements('a');
+    final afeAElement = tree.elementInActiveFormattingElements('a');
     if (afeAElement != null) {
       parser.parseError(token.span, 'unexpected-start-tag-implies-end-tag',
           {'startName': 'a', 'endName': 'a'});
@@ -1781,7 +1781,7 @@
   }
 
   void startTagInput(StartTagToken token) {
-    var savedFramesetOK = parser.framesetOK;
+    final savedFramesetOK = parser.framesetOK;
     startTagVoidFormatting(token);
     if (asciiUpper2Lower(token.data['type']) == 'hidden') {
       //input type=hidden doesn't change framesetOK
@@ -1818,8 +1818,8 @@
     if (tree.formPointer != null) {
       return;
     }
-    var formAttrs = LinkedHashMap<dynamic, String>();
-    var dataAction = token.data['action'];
+    final formAttrs = LinkedHashMap<dynamic, String>();
+    final dataAction = token.data['action'];
     if (dataAction != null) {
       formAttrs['action'] = dataAction;
     }
@@ -1832,7 +1832,7 @@
     var prompt = token.data['prompt'];
     prompt ??= 'This is a searchable index. Enter search keywords: ';
     processCharacters(CharactersToken(prompt));
-    var attributes = LinkedHashMap<dynamic, String>.from(token.data);
+    final attributes = LinkedHashMap<dynamic, String>.from(token.data);
     attributes.remove('action');
     attributes.remove('prompt');
     attributes['name'] = 'isindex';
@@ -1889,7 +1889,7 @@
   void startTagRpRt(StartTagToken token) {
     if (tree.elementInScope('ruby')) {
       tree.generateImpliedEndTags();
-      var last = tree.openElements.last;
+      final last = tree.openElements.last;
       if (last.localName != 'ruby') {
         parser.parseError(last.sourceSpan, 'undefined-error');
       }
@@ -2006,7 +2006,7 @@
     if (token.name == 'pre') {
       dropNewline = false;
     }
-    var inScope = tree.elementInScope(token.name);
+    final inScope = tree.elementInScope(token.name);
     if (inScope) {
       tree.generateImpliedEndTags();
     }
@@ -2019,7 +2019,7 @@
   }
 
   void endTagForm(EndTagToken token) {
-    var node = tree.formPointer;
+    final node = tree.formPointer;
     tree.formPointer = null;
     if (node == null || !tree.elementInScope(node)) {
       parser.parseError(token.span, 'unexpected-end-tag', {'name': 'form'});
@@ -2090,7 +2090,7 @@
       outerLoopCounter += 1;
 
       // Step 1 paragraph 1
-      var formattingElement =
+      final formattingElement =
           tree.elementInActiveFormattingElements(token.name);
       if (formattingElement == null ||
           (tree.openElements.contains(formattingElement) &&
@@ -2114,7 +2114,7 @@
 
       // Step 2
       // Start of the adoption agency algorithm proper
-      var afeIndex = tree.openElements.indexOf(formattingElement);
+      final afeIndex = tree.openElements.indexOf(formattingElement);
       Element furthestBlock;
       for (var element in slice(tree.openElements, afeIndex)) {
         if (specialElements.contains(getElementNameTuple(element))) {
@@ -2135,7 +2135,7 @@
         return;
       }
 
-      var commonAncestor = tree.openElements[afeIndex - 1];
+      final commonAncestor = tree.openElements[afeIndex - 1];
 
       // Step 5
       // The bookmark is supposed to help us identify where to reinsert
@@ -2170,7 +2170,7 @@
         }
         // Step 6.5
         //cite = node.parent
-        var clone = node.clone(false);
+        final clone = node.clone(false);
         // Replace node with clone
         tree.activeFormattingElements[
             tree.activeFormattingElements.indexOf(node)] = clone;
@@ -2198,14 +2198,14 @@
 
       if (const ['table', 'tbody', 'tfoot', 'thead', 'tr']
           .contains(commonAncestor.localName)) {
-        var nodePos = tree.getTableMisnestedNodePosition();
+        final nodePos = tree.getTableMisnestedNodePosition();
         nodePos[0].insertBefore(lastNode, nodePos[1]);
       } else {
         commonAncestor.nodes.add(lastNode);
       }
 
       // Step 8
-      var clone = formattingElement.clone(false);
+      final clone = formattingElement.clone(false);
 
       // Step 9
       furthestBlock.reparentChildren(clone);
@@ -2299,7 +2299,7 @@
 
   @override
   bool processEOF() {
-    var last = tree.openElements.last;
+    final last = tree.openElements.last;
     parser.parseError(last.sourceSpan, 'expected-named-closing-tag-but-got-eof',
         {'name': last.localName});
     tree.openElements.removeLast();
@@ -2308,7 +2308,7 @@
   }
 
   void endTagScript(EndTagToken token) {
-    var node = tree.openElements.removeLast();
+    final node = tree.openElements.removeLast();
     assert(node.localName == 'script');
     parser.phase = parser.originalPhase;
     //The rest of this method is all stuff that only happens if
@@ -2404,7 +2404,7 @@
   // processing methods
   @override
   bool processEOF() {
-    var last = tree.openElements.last;
+    final last = tree.openElements.last;
     if (last.localName != 'html') {
       parser.parseError(last.sourceSpan, 'eof-in-table');
     } else {
@@ -2416,7 +2416,7 @@
 
   @override
   Token processSpaceCharacters(SpaceCharactersToken token) {
-    var originalPhase = parser.phase;
+    final originalPhase = parser.phase;
     parser.phase = parser._inTableTextPhase;
     parser._inTableTextPhase.originalPhase = originalPhase;
     parser.phase.processSpaceCharacters(token);
@@ -2425,7 +2425,7 @@
 
   @override
   Token processCharacters(CharactersToken token) {
-    var originalPhase = parser.phase;
+    final originalPhase = parser.phase;
     parser.phase = parser._inTableTextPhase;
     parser._inTableTextPhase.originalPhase = originalPhase;
     parser.phase.processCharacters(token);
@@ -2517,7 +2517,7 @@
   void endTagTable(EndTagToken token) {
     if (tree.elementInScope('table', variant: 'table')) {
       tree.generateImpliedEndTags();
-      var last = tree.openElements.last;
+      final last = tree.openElements.last;
       if (last.localName != 'table') {
         parser.parseError(token.span, 'end-tag-too-early-named',
             {'gotName': 'table', 'expectedName': last.localName});
@@ -2525,7 +2525,7 @@
       while (tree.openElements.last.localName != 'table') {
         tree.openElements.removeLast();
       }
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
       parser.resetInsertionMode();
     } else {
@@ -2561,7 +2561,7 @@
     if (characterTokens.isEmpty) return;
 
     // TODO(sigmund,jmesserly): remove '' (dartbug.com/8480)
-    var data = characterTokens.map((t) => t.data).join('');
+    final data = characterTokens.map((t) => t.data).join('');
     FileSpan span;
 
     if (parser.generateSpans) {
@@ -2689,7 +2689,7 @@
   Token startTagTableElement(StartTagToken token) {
     parser.parseError(token.span, 'undefined-error');
     //XXX Have to duplicate logic here to find out if the tag is ignored
-    var ignoreEndTag = ignoreEndTagCaption();
+    final ignoreEndTag = ignoreEndTagCaption();
     parser.phase.processEndTag(EndTagToken('caption'));
     if (!ignoreEndTag) {
       return token;
@@ -2714,7 +2714,7 @@
       while (tree.openElements.last.localName != 'caption') {
         tree.openElements.removeLast();
       }
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
       tree.clearActiveFormattingElements();
       parser.phase = parser._inTablePhase;
@@ -2727,7 +2727,7 @@
 
   Token endTagTable(EndTagToken token) {
     parser.parseError(token.span, 'undefined-error');
-    var ignoreEndTag = ignoreEndTagCaption();
+    final ignoreEndTag = ignoreEndTagCaption();
     parser.phase.processEndTag(EndTagToken('caption'));
     if (!ignoreEndTag) {
       return token;
@@ -2781,7 +2781,7 @@
 
   @override
   bool processEOF() {
-    var ignoreEndTag = ignoreEndTagColgroup();
+    final ignoreEndTag = ignoreEndTagColgroup();
     if (ignoreEndTag) {
       assert(parser.innerHTMLMode);
       return false;
@@ -2793,7 +2793,7 @@
 
   @override
   Token processCharacters(CharactersToken token) {
-    var ignoreEndTag = ignoreEndTagColgroup();
+    final ignoreEndTag = ignoreEndTagColgroup();
     endTagColgroup(EndTagToken('colgroup'));
     return ignoreEndTag ? null : token;
   }
@@ -2804,7 +2804,7 @@
   }
 
   Token startTagOther(StartTagToken token) {
-    var ignoreEndTag = ignoreEndTagColgroup();
+    final ignoreEndTag = ignoreEndTagColgroup();
     endTagColgroup(EndTagToken('colgroup'));
     return ignoreEndTag ? null : token;
   }
@@ -2815,7 +2815,7 @@
       assert(parser.innerHTMLMode);
       parser.parseError(token.span, 'undefined-error');
     } else {
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
       parser.phase = parser._inTablePhase;
     }
@@ -2826,7 +2826,7 @@
   }
 
   Token endTagOther(EndTagToken token) {
-    var ignoreEndTag = ignoreEndTagColgroup();
+    final ignoreEndTag = ignoreEndTagColgroup();
     endTagColgroup(EndTagToken('colgroup'));
     return ignoreEndTag ? null : token;
   }
@@ -2886,7 +2886,7 @@
 
   // helper methods
   void clearStackToTableBodyContext() {
-    var tableTags = const ['tbody', 'tfoot', 'thead', 'html'];
+    final tableTags = const ['tbody', 'tfoot', 'thead', 'html'];
     while (!tableTags.contains(tree.openElements.last.localName)) {
       //XXX parser.parseError(token.span, "unexpected-implied-end-tag-in-table",
       //  {"name": tree.openElements.last.name})
@@ -2936,7 +2936,7 @@
   void endTagTableRowGroup(EndTagToken token) {
     if (tree.elementInScope(token.name, variant: 'table')) {
       clearStackToTableBodyContext();
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
       parser.phase = parser._inTablePhase;
     } else {
@@ -3026,7 +3026,7 @@
   // helper methods (XXX unify this with other table helper methods)
   void clearStackToTableRowContext() {
     while (true) {
-      var last = tree.openElements.last;
+      final last = tree.openElements.last;
       if (last.localName == 'tr' || last.localName == 'html') break;
 
       parser.parseError(
@@ -3066,7 +3066,7 @@
   }
 
   Token startTagTableOther(StartTagToken token) {
-    var ignoreEndTag = ignoreEndTagTr();
+    final ignoreEndTag = ignoreEndTagTr();
     endTagTr(EndTagToken('tr'));
     // XXX how are we sure it's always ignored in the innerHTML case?
     return ignoreEndTag ? null : token;
@@ -3079,7 +3079,7 @@
   void endTagTr(EndTagToken token) {
     if (!ignoreEndTagTr()) {
       clearStackToTableRowContext();
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
       parser.phase = parser._inTableBodyPhase;
     } else {
@@ -3090,7 +3090,7 @@
   }
 
   Token endTagTable(EndTagToken token) {
-    var ignoreEndTag = ignoreEndTagTr();
+    final ignoreEndTag = ignoreEndTagTr();
     endTagTr(EndTagToken('tr'));
     // Reprocess the current tag if the tr end tag was not ignored
     // XXX how are we sure it's always ignored in the innerHTML case?
@@ -3212,7 +3212,7 @@
             token.span, 'unexpected-cell-end-tag', {'name': token.name});
         popOpenElementsUntil(token);
       } else {
-        var node = tree.openElements.removeLast();
+        final node = tree.openElements.removeLast();
         node.endSourceSpan = token.span;
       }
       tree.clearActiveFormattingElements();
@@ -3291,7 +3291,7 @@
   // http://www.whatwg.org/specs/web-apps/current-work///in-select
   @override
   bool processEOF() {
-    var last = tree.openElements.last;
+    final last = tree.openElements.last;
     if (last.localName != 'html') {
       parser.parseError(last.sourceSpan, 'eof-in-select');
     } else {
@@ -3355,7 +3355,7 @@
 
   void endTagOption(EndTagToken token) {
     if (tree.openElements.last.localName == 'option') {
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
     } else {
       parser.parseError(
@@ -3372,7 +3372,7 @@
     }
     // It also closes </optgroup>
     if (tree.openElements.last.localName == 'optgroup') {
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
       // But nothing else
     } else {
@@ -3567,7 +3567,7 @@
       'textpath': 'textPath'
     };
 
-    var replace = replacements[token.name];
+    final replace = replacements[token.name];
     if (replace != null) {
       token.name = replace;
     }
@@ -3585,7 +3585,7 @@
 
   @override
   Token processStartTag(StartTagToken token) {
-    var currentNode = tree.openElements.last;
+    final currentNode = tree.openElements.last;
     if (breakoutElements.contains(token.name) ||
         (token.name == 'font' &&
             (token.data.containsKey('color') ||
@@ -3761,7 +3761,7 @@
 
   @override
   bool processEOF() {
-    var last = tree.openElements.last;
+    final last = tree.openElements.last;
     if (last.localName != 'html') {
       parser.parseError(last.sourceSpan, 'eof-in-frameset');
     } else {
@@ -3801,7 +3801,7 @@
       parser.parseError(
           token.span, 'unexpected-frameset-in-frameset-innerhtml');
     } else {
-      var node = tree.openElements.removeLast();
+      final node = tree.openElements.removeLast();
       node.endSourceSpan = token.span;
     }
     if (!parser.innerHTMLMode &&
@@ -4009,13 +4009,13 @@
 
   @override
   String toString({color}) {
-    var res = span.message(message, color: color);
+    final res = span.message(message, color: color);
     return span.sourceUrl == null ? 'ParserError on $res' : 'On $res';
   }
 }
 
 /// Convenience function to get the pair of namespace and localName.
 Pair<String, String> getElementNameTuple(Element e) {
-  var ns = e.namespaceUri ?? Namespaces.html;
+  final ns = e.namespaceUri ?? Namespaces.html;
   return Pair(ns, e.localName);
 }
diff --git a/lib/src/constants.dart b/lib/src/constants.dart
index 313b952..b09fc48 100644
--- a/lib/src/constants.dart
+++ b/lib/src/constants.dart
@@ -442,13 +442,13 @@
 // Note: this is intentially ASCII only
 bool isLetter(String char) {
   if (char == null) return false;
-  var cc = char.codeUnitAt(0);
+  final cc = char.codeUnitAt(0);
   return cc >= LOWER_A && cc <= LOWER_Z || cc >= UPPER_A && cc <= UPPER_Z;
 }
 
 bool isDigit(String char) {
   if (char == null) return false;
-  var cc = char.codeUnitAt(0);
+  final cc = char.codeUnitAt(0);
   return cc >= ZERO && cc < ZERO + 10;
 }
 
@@ -486,7 +486,7 @@
 // ASCII chars to.toLowerCase() case, unlike Dart's toLowerCase function.
 String asciiUpper2Lower(String text) {
   if (text == null) return null;
-  var result = List<int>(text.length);
+  final result = List<int>(text.length);
   for (var i = 0; i < text.length; i++) {
     var c = text.codeUnitAt(i);
     if (c >= UPPER_A && c <= UPPER_Z) {
diff --git a/lib/src/css_class_set.dart b/lib/src/css_class_set.dart
index 3fa49e0..3f98a33 100644
--- a/lib/src/css_class_set.dart
+++ b/lib/src/css_class_set.dart
@@ -16,11 +16,11 @@
 
   @override
   Set<String> readClasses() {
-    var s = LinkedHashSet<String>();
-    var classname = _element.className;
+    final s = LinkedHashSet<String>();
+    final classname = _element.className;
 
     for (var name in classname.split(' ')) {
-      var trimmed = name.trim();
+      final trimmed = name.trim();
       if (trimmed.isNotEmpty) {
         s.add(trimmed);
       }
@@ -112,7 +112,7 @@
   /// [shouldAdd] is false then we always remove [value] from the element.
   @override
   bool toggle(String value, [bool shouldAdd]) {
-    var s = readClasses();
+    final s = readClasses();
     var result = false;
     shouldAdd ??= !s.contains(value);
     if (shouldAdd) {
@@ -170,8 +170,8 @@
   @override
   bool remove(Object value) {
     if (value is! String) return false;
-    var s = readClasses();
-    var result = s.remove(value);
+    final s = readClasses();
+    final result = s.remove(value);
     writeClasses(s);
     return result;
   }
@@ -197,8 +197,8 @@
   ///   After f returns, the modified set is written to the
   ///       className property of this element.
   bool _modify(bool Function(Set<String>) f) {
-    var s = readClasses();
-    var ret = f(s);
+    final s = readClasses();
+    final ret = f(s);
     writeClasses(s);
     return ret;
   }
diff --git a/lib/src/encoding_parser.dart b/lib/src/encoding_parser.dart
index 51766a9..6fa96c6 100644
--- a/lib/src/encoding_parser.dart
+++ b/lib/src/encoding_parser.dart
@@ -15,7 +15,7 @@
   int get _length => _bytes.length;
 
   String _next() {
-    var p = __position = __position + 1;
+    final p = __position = __position + 1;
     if (p >= _length) {
       throw StateError('No more elements');
     } else if (p < 0) {
@@ -60,7 +60,7 @@
     skipChars ??= isWhitespace;
     var p = _position; // use property for the error-checking
     while (p < _length) {
-      var c = _bytes[p];
+      final c = _bytes[p];
       if (!skipChars(c)) {
         __position = p;
         return c;
@@ -74,7 +74,7 @@
   String _skipUntil(_CharPredicate untilChars) {
     var p = _position;
     while (p < _length) {
-      var c = _bytes[p];
+      final c = _bytes[p];
       if (untilChars(c)) {
         __position = p;
         return c;
@@ -88,11 +88,11 @@
   /// are found return true and advance the position to the byte after the
   /// match. Otherwise return false and leave the position alone.
   bool _matchBytes(String bytes) {
-    var p = _position;
+    final p = _position;
     if (_bytes.length < p + bytes.length) {
       return false;
     }
-    var data = _bytes.substring(p, p + bytes.length);
+    final data = _bytes.substring(p, p + bytes.length);
     if (data == bytes) {
       _position += bytes.length;
       return true;
@@ -103,7 +103,7 @@
   /// Look for the next sequence of bytes matching a given sequence. If
   /// a match is found advance the position to the last byte of the match
   bool _jumpTo(String bytes) {
-    var newPosition = _bytes.indexOf(bytes, _position);
+    final newPosition = _bytes.indexOf(bytes, _position);
     if (newPosition >= 0) {
       __position = newPosition + bytes.length - 1;
       return true;
@@ -152,7 +152,7 @@
       for (;;) {
         for (var dispatch in methodDispatch) {
           if (_data._matchBytes(dispatch.pattern)) {
-            var keepParsing = dispatch.handler();
+            final keepParsing = dispatch.handler();
             if (keepParsing) break;
 
             // We found an encoding. Stop.
@@ -179,20 +179,20 @@
     // We have a valid meta element we want to search for attributes
     while (true) {
       // Try to find the next attribute after the current position
-      var attr = _getAttribute();
+      final attr = _getAttribute();
       if (attr == null) return true;
 
       if (attr[0] == 'charset') {
-        var tentativeEncoding = attr[1];
-        var codec = codecName(tentativeEncoding);
+        final tentativeEncoding = attr[1];
+        final codec = codecName(tentativeEncoding);
         if (codec != null) {
           _encoding = codec;
           return false;
         }
       } else if (attr[0] == 'content') {
-        var contentParser = ContentAttrParser(EncodingBytes(attr[1]));
-        var tentativeEncoding = contentParser.parse();
-        var codec = codecName(tentativeEncoding);
+        final contentParser = ContentAttrParser(EncodingBytes(attr[1]));
+        final tentativeEncoding = contentParser.parse();
+        final codec = codecName(tentativeEncoding);
         if (codec != null) {
           _encoding = codec;
           return false;
@@ -220,7 +220,7 @@
       return true;
     }
 
-    var c = _data._skipUntil(_isSpaceOrAngleBracket);
+    final c = _data._skipUntil(_isSpaceOrAngleBracket);
     if (c == '<') {
       // return to the first step in the overall "two step" algorithm
       // reprocessing the < byte
@@ -247,8 +247,8 @@
       return null;
     }
     // Step 3
-    var attrName = [];
-    var attrValue = [];
+    final attrName = [];
+    final attrValue = [];
     // Step 4 attribute name
     while (true) {
       if (c == null) {
@@ -282,7 +282,7 @@
     // Step 10
     if (c == "'" || c == '"') {
       // 10.1
-      var quoteChar = c;
+      final quoteChar = c;
       while (true) {
         // 10.2
         c = _data._next();
@@ -343,9 +343,9 @@
       data._skipChars();
       // Look for an encoding between matching quote marks
       if (data._currentByte == '"' || data._currentByte == "'") {
-        var quoteMark = data._currentByte;
+        final quoteMark = data._currentByte;
         data._position += 1;
-        var oldPosition = data._position;
+        final oldPosition = data._position;
         if (data._jumpTo(quoteMark)) {
           return data._slice(oldPosition, data._position);
         } else {
@@ -353,7 +353,7 @@
         }
       } else {
         // Unquoted value
-        var oldPosition = data._position;
+        final oldPosition = data._position;
         try {
           data._skipUntil(isWhitespace);
           return data._slice(oldPosition, data._position);
diff --git a/lib/src/html_input_stream.dart b/lib/src/html_input_stream.dart
index 0b72da7..1a2384e 100644
--- a/lib/src/html_input_stream.dart
+++ b/lib/src/html_input_stream.dart
@@ -193,7 +193,7 @@
 
   /// Report the encoding declared by the meta element.
   String detectEncodingMeta() {
-    var parser = EncodingParser(slice(_rawBytes, 0, numBytesMeta));
+    final parser = EncodingParser(slice(_rawBytes, 0, numBytesMeta));
     var encoding = parser.getEncoding();
 
     if (const ['utf-16', 'utf-16-be', 'utf-16-le'].contains(encoding)) {
@@ -239,7 +239,7 @@
   /// Returns a string of characters from the stream up to but not
   /// including any character in 'characters' or EOF.
   String charsUntil(String characters, [bool opposite = false]) {
-    var start = _offset;
+    final start = _offset;
     String c;
     while ((c = peekChar()) != null && characters.contains(c) == opposite) {
       _offset += c.codeUnits.length;
@@ -314,7 +314,7 @@
       '[\u0009-\u000D\u0020-\u002F\u003A-\u0040\u005B-\u0060\u007B-\u007E]');
 
   if (encoding == null) return null;
-  var canonicalName = encoding.replaceAll(asciiPunctuation, '').toLowerCase();
+  final canonicalName = encoding.replaceAll(asciiPunctuation, '').toLowerCase();
   return encodings[canonicalName];
 }
 
@@ -322,7 +322,7 @@
 /// Since UTF-8 doesn't have byte order, it's somewhat of a misnomer, but it is
 /// used in HTML to detect the UTF-
 bool _hasUtf8Bom(List<int> bytes, [int offset = 0, int length]) {
-  var end = length != null ? offset + length : bytes.length;
+  final end = length != null ? offset + length : bytes.length;
   return (offset + 3) <= end &&
       bytes[offset] == 0xEF &&
       bytes[offset + 1] == 0xBB &&
diff --git a/lib/src/query_selector.dart b/lib/src/query_selector.dart
index 041c5d8..9d09857 100644
--- a/lib/src/query_selector.dart
+++ b/lib/src/query_selector.dart
@@ -14,7 +14,7 @@
     SelectorEvaluator().querySelector(node, _parseSelectorList(selector));
 
 List<Element> querySelectorAll(Node node, String selector) {
-  var results = <Element>[];
+  final results = <Element>[];
   SelectorEvaluator()
       .querySelectorAll(node, _parseSelectorList(selector), results);
   return results;
@@ -22,8 +22,8 @@
 
 // http://dev.w3.org/csswg/selectors-4/#grouping
 SelectorGroup _parseSelectorList(String selector) {
-  var errors = <Message>[];
-  var group = css.parseSelectorGroup(selector, errors: errors);
+  final errors = <Message>[];
+  final group = css.parseSelectorGroup(selector, errors: errors);
   if (group == null || errors.isNotEmpty) {
     throw FormatException("'$selector' is not a valid selector: $errors");
   }
@@ -42,7 +42,7 @@
   Element querySelector(Node root, SelectorGroup selector) {
     for (var element in root.nodes.whereType<Element>()) {
       if (matches(element, selector)) return element;
-      var result = querySelector(element, selector);
+      final result = querySelector(element, selector);
       if (result != null) return result;
     }
     return null;
@@ -62,7 +62,7 @@
 
   @override
   bool visitSelector(Selector selector) {
-    var old = _element;
+    final old = _element;
     var result = true;
 
     // Note: evaluate selectors right-to-left as it's more efficient.
@@ -212,10 +212,10 @@
       // http://dev.w3.org/csswg/selectors-4/#the-nth-child-pseudo
       case 'nth-child':
         // TODO(jmesserly): support An+B syntax too.
-        var exprs = selector.expression.expressions;
+        final exprs = selector.expression.expressions;
         if (exprs.length == 1 && exprs[0] is LiteralTerm) {
           final literal = exprs[0] as LiteralTerm;
-          var parent = _element.parentNode;
+          final parent = _element.parentNode;
           return parent != null &&
               (literal.value as num) > 0 &&
               parent.nodes.indexOf(_element) == literal.value;
@@ -226,8 +226,8 @@
       case 'lang':
         // TODO(jmesserly): shouldn't need to get the raw text here, but csslib
         // gets confused by the "-" in the expression, such as in "es-AR".
-        var toMatch = selector.expression.span.text;
-        var lang = _getInheritedLanguage(_element);
+        final toMatch = selector.expression.span.text;
+        final lang = _getInheritedLanguage(_element);
         // TODO(jmesserly): implement wildcards in level 4
         return lang != null && lang.startsWith(toMatch);
     }
@@ -236,7 +236,7 @@
 
   static String _getInheritedLanguage(Node node) {
     while (node != null) {
-      var lang = node.attributes['lang'];
+      final lang = node.attributes['lang'];
       if (lang != null) return lang;
       node = node.parent;
     }
@@ -276,12 +276,12 @@
   @override
   bool visitAttributeSelector(AttributeSelector selector) {
     // Match name first
-    var value = _element.attributes[selector.name.toLowerCase()];
+    final value = _element.attributes[selector.name.toLowerCase()];
     if (value == null) return false;
 
     if (selector.operatorKind == TokenKind.NO_MATCH) return true;
 
-    var select = '${selector.value}';
+    final select = '${selector.value}';
     switch (selector.operatorKind) {
       case TokenKind.EQUALS:
         return value == select;
diff --git a/lib/src/tokenizer.dart b/lib/src/tokenizer.dart
index 26b7115..7af9648 100644
--- a/lib/src/tokenizer.dart
+++ b/lib/src/tokenizer.dart
@@ -12,7 +12,7 @@
 // TODO(jmesserly): we could use a better data structure here like a trie, if
 // we had it implemented in Dart.
 Map<String, List<String>> entitiesByFirstChar = (() {
-  var result = <String, List<String>>{};
+  final result = <String, List<String>>{};
   for (var k in entities.keys) {
     result.putIfAbsent(k[0], () => []).add(k);
   }
@@ -110,7 +110,7 @@
     _attributeName.clear();
     _attributeName.write(name);
     _attributeValue.clear();
-    var attr = TagAttribute();
+    final attr = TagAttribute();
     _attributes.add(attr);
     if (attributeSpans) attr.start = stream.position - name.length;
   }
@@ -154,7 +154,7 @@
   /// Adds a token to the queue. Sets the span if needed.
   void _addToken(Token token) {
     if (generateSpans && token.span == null) {
-      var offset = stream.position;
+      final offset = stream.position;
       token.span = stream.fileInfo.span(_lastOffset, offset);
       if (token is! ParseErrorToken) {
         _lastOffset = offset;
@@ -174,7 +174,7 @@
       radix = 16;
     }
 
-    var charStack = [];
+    final charStack = [];
 
     // Consume all the characters that are in range while making sure we
     // don't hit an EOF.
@@ -185,7 +185,7 @@
     }
 
     // Convert the set of characters consumed to an int.
-    var charAsInt = int.parse(charStack.join(), radix: radix);
+    final charAsInt = int.parse(charStack.join(), radix: radix);
 
     // Certain characters get replaced with others
     var char = replacementCharacters[charAsInt];
@@ -259,7 +259,7 @@
     // Initialise to the default output for when no entity is matched
     var output = '&';
 
-    var charStack = [stream.char()];
+    final charStack = [stream.char()];
     if (isWhitespace(charStack[0]) ||
         charStack[0] == '<' ||
         charStack[0] == '&' ||
@@ -296,7 +296,7 @@
       var filteredEntityList = entitiesByFirstChar[charStack[0]] ?? const [];
 
       while (charStack.last != eof) {
-        var name = charStack.join();
+        final name = charStack.join();
         filteredEntityList =
             filteredEntityList.where((e) => e.startsWith(name)).toList();
 
@@ -315,7 +315,7 @@
 
       int entityLen;
       for (entityLen = charStack.length - 1; entityLen > 1; entityLen--) {
-        var possibleEntityName = charStack.sublist(0, entityLen).join();
+        final possibleEntityName = charStack.sublist(0, entityLen).join();
         if (entities.containsKey(possibleEntityName)) {
           entityName = possibleEntityName;
           break;
@@ -323,7 +323,7 @@
       }
 
       if (entityName != null) {
-        var lastChar = entityName[entityName.length - 1];
+        final lastChar = entityName[entityName.length - 1];
         if (lastChar != ';') {
           _addToken(ParseErrorToken('named-entity-without-semicolon'));
         }
@@ -366,7 +366,7 @@
   /// the state to "data" because that's what's needed after a token has been
   /// emitted.
   void emitCurrentToken() {
-    var token = currentToken;
+    final token = currentToken;
     // Add token to the queue to be yielded
     if (token is TagToken) {
       if (lowercaseElementName) {
@@ -400,7 +400,7 @@
   // Below are the various tokenizer states worked out.
 
   bool dataState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '&') {
       state = entityDataState;
     } else if (data == '<') {
@@ -421,7 +421,7 @@
       // have already been appended to lastFourChars and will have broken
       // any <!-- or --> sequences
     } else {
-      var chars = stream.charsUntil('&<\u0000');
+      final chars = stream.charsUntil('&<\u0000');
       _addToken(CharactersToken('$data$chars'));
     }
     return true;
@@ -434,7 +434,7 @@
   }
 
   bool rcdataState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '&') {
       state = characterReferenceInRcdata;
     } else if (data == '<') {
@@ -452,7 +452,7 @@
       _addToken(SpaceCharactersToken(
           '$data${stream.charsUntil(spaceCharacters, true)}'));
     } else {
-      var chars = stream.charsUntil('&<');
+      final chars = stream.charsUntil('&<');
       _addToken(CharactersToken('$data$chars'));
     }
     return true;
@@ -465,7 +465,7 @@
   }
 
   bool rawtextState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '<') {
       state = rawtextLessThanSignState;
     } else if (data == '\u0000') {
@@ -475,14 +475,14 @@
       // Tokenization ends.
       return false;
     } else {
-      var chars = stream.charsUntil('<\u0000');
+      final chars = stream.charsUntil('<\u0000');
       _addToken(CharactersToken('$data$chars'));
     }
     return true;
   }
 
   bool scriptDataState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '<') {
       state = scriptDataLessThanSignState;
     } else if (data == '\u0000') {
@@ -492,14 +492,14 @@
       // Tokenization ends.
       return false;
     } else {
-      var chars = stream.charsUntil('<\u0000');
+      final chars = stream.charsUntil('<\u0000');
       _addToken(CharactersToken('$data$chars'));
     }
     return true;
   }
 
   bool plaintextState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == eof) {
       // Tokenization ends.
       return false;
@@ -513,7 +513,7 @@
   }
 
   bool tagOpenState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '!') {
       state = markupDeclarationOpenState;
     } else if (data == '/') {
@@ -544,7 +544,7 @@
   }
 
   bool closeTagOpenState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isLetter(data)) {
       currentToken = EndTagToken(data);
       state = tagNameState;
@@ -566,7 +566,7 @@
   }
 
   bool tagNameState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       state = beforeAttributeNameState;
     } else if (data == '>') {
@@ -588,7 +588,7 @@
   }
 
   bool rcdataLessThanSignState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '/') {
       _buffer.clear();
       state = rcdataEndTagOpenState;
@@ -601,7 +601,7 @@
   }
 
   bool rcdataEndTagOpenState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isLetter(data)) {
       _buffer.write(data);
       state = rcdataEndTagNameState;
@@ -620,8 +620,8 @@
   }
 
   bool rcdataEndTagNameState() {
-    var appropriate = _tokenIsAppropriate();
-    var data = stream.char();
+    final appropriate = _tokenIsAppropriate();
+    final data = stream.char();
     if (isWhitespace(data) && appropriate) {
       currentToken = EndTagToken('$_buffer');
       state = beforeAttributeNameState;
@@ -643,7 +643,7 @@
   }
 
   bool rawtextLessThanSignState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '/') {
       _buffer.clear();
       state = rawtextEndTagOpenState;
@@ -656,7 +656,7 @@
   }
 
   bool rawtextEndTagOpenState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isLetter(data)) {
       _buffer.write(data);
       state = rawtextEndTagNameState;
@@ -669,8 +669,8 @@
   }
 
   bool rawtextEndTagNameState() {
-    var appropriate = _tokenIsAppropriate();
-    var data = stream.char();
+    final appropriate = _tokenIsAppropriate();
+    final data = stream.char();
     if (isWhitespace(data) && appropriate) {
       currentToken = EndTagToken('$_buffer');
       state = beforeAttributeNameState;
@@ -692,7 +692,7 @@
   }
 
   bool scriptDataLessThanSignState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '/') {
       _buffer.clear();
       state = scriptDataEndTagOpenState;
@@ -708,7 +708,7 @@
   }
 
   bool scriptDataEndTagOpenState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isLetter(data)) {
       _buffer.write(data);
       state = scriptDataEndTagNameState;
@@ -721,8 +721,8 @@
   }
 
   bool scriptDataEndTagNameState() {
-    var appropriate = _tokenIsAppropriate();
-    var data = stream.char();
+    final appropriate = _tokenIsAppropriate();
+    final data = stream.char();
     if (isWhitespace(data) && appropriate) {
       currentToken = EndTagToken('$_buffer');
       state = beforeAttributeNameState;
@@ -744,7 +744,7 @@
   }
 
   bool scriptDataEscapeStartState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
       state = scriptDataEscapeStartDashState;
@@ -756,7 +756,7 @@
   }
 
   bool scriptDataEscapeStartDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
       state = scriptDataEscapedDashDashState;
@@ -768,7 +768,7 @@
   }
 
   bool scriptDataEscapedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
       state = scriptDataEscapedDashState;
@@ -780,14 +780,14 @@
     } else if (data == eof) {
       state = dataState;
     } else {
-      var chars = stream.charsUntil('<-\u0000');
+      final chars = stream.charsUntil('<-\u0000');
       _addToken(CharactersToken('$data$chars'));
     }
     return true;
   }
 
   bool scriptDataEscapedDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
       state = scriptDataEscapedDashDashState;
@@ -807,7 +807,7 @@
   }
 
   bool scriptDataEscapedDashDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
     } else if (data == '<') {
@@ -829,7 +829,7 @@
   }
 
   bool scriptDataEscapedLessThanSignState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '/') {
       _buffer.clear();
       state = scriptDataEscapedEndTagOpenState;
@@ -847,7 +847,7 @@
   }
 
   bool scriptDataEscapedEndTagOpenState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isLetter(data)) {
       _buffer.clear();
       _buffer.write(data);
@@ -861,8 +861,8 @@
   }
 
   bool scriptDataEscapedEndTagNameState() {
-    var appropriate = _tokenIsAppropriate();
-    var data = stream.char();
+    final appropriate = _tokenIsAppropriate();
+    final data = stream.char();
     if (isWhitespace(data) && appropriate) {
       currentToken = EndTagToken('$_buffer');
       state = beforeAttributeNameState;
@@ -884,7 +884,7 @@
   }
 
   bool scriptDataDoubleEscapeStartState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data) || data == '/' || data == '>') {
       _addToken(CharactersToken(data));
       if ('$_buffer'.toLowerCase() == 'script') {
@@ -903,7 +903,7 @@
   }
 
   bool scriptDataDoubleEscapedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
       state = scriptDataDoubleEscapedDashState;
@@ -923,7 +923,7 @@
   }
 
   bool scriptDataDoubleEscapedDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
       state = scriptDataDoubleEscapedDashDashState;
@@ -947,7 +947,7 @@
   // TODO(jmesserly): report bug in original code
   // (was "Dash" instead of "DashDash")
   bool scriptDataDoubleEscapedDashDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       _addToken(CharactersToken('-'));
     } else if (data == '<') {
@@ -971,7 +971,7 @@
   }
 
   bool scriptDataDoubleEscapedLessThanSignState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '/') {
       _addToken(CharactersToken('/'));
       _buffer.clear();
@@ -984,7 +984,7 @@
   }
 
   bool scriptDataDoubleEscapeEndState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data) || data == '/' || data == '>') {
       _addToken(CharactersToken(data));
       if ('$_buffer'.toLowerCase() == 'script') {
@@ -1003,7 +1003,7 @@
   }
 
   bool beforeAttributeNameState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       stream.charsUntil(spaceCharacters, true);
     } else if (isLetter(data)) {
@@ -1032,7 +1032,7 @@
   }
 
   bool attributeNameState() {
-    var data = stream.char();
+    final data = stream.char();
     var leavingThisState = true;
     var emitToken = false;
     if (data == '=') {
@@ -1092,7 +1092,7 @@
   }
 
   bool afterAttributeNameState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       stream.charsUntil(spaceCharacters, true);
     } else if (data == '=') {
@@ -1123,7 +1123,7 @@
   }
 
   bool beforeAttributeValueState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       stream.charsUntil(spaceCharacters, true);
     } else if (data == '"') {
@@ -1162,7 +1162,7 @@
   }
 
   bool attributeValueDoubleQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '"') {
       _markAttributeValueEnd(-1);
       _markAttributeEnd(0);
@@ -1184,7 +1184,7 @@
   }
 
   bool attributeValueSingleQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == "'") {
       _markAttributeValueEnd(-1);
       _markAttributeEnd(0);
@@ -1206,7 +1206,7 @@
   }
 
   bool attributeValueUnQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       _markAttributeValueEnd(-1);
       state = beforeAttributeNameState;
@@ -1234,7 +1234,7 @@
   }
 
   bool afterAttributeValueState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       state = beforeAttributeNameState;
     } else if (data == '>') {
@@ -1254,7 +1254,7 @@
   }
 
   bool selfClosingStartTagState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '>') {
       currentTagToken.selfClosing = true;
       emitCurrentToken();
@@ -1286,7 +1286,7 @@
   }
 
   bool markupDeclarationOpenState() {
-    var charStack = [stream.char()];
+    final charStack = [stream.char()];
     if (charStack.last == '-') {
       charStack.add(stream.char());
       if (charStack.last == '-') {
@@ -1297,7 +1297,7 @@
     } else if (charStack.last == 'd' || charStack.last == 'D') {
       var matched = true;
       for (var expected in const ['oO', 'cC', 'tT', 'yY', 'pP', 'eE']) {
-        var char = stream.char();
+        final char = stream.char();
         charStack.add(char);
         if (char == eof || !expected.contains(char)) {
           matched = false;
@@ -1338,7 +1338,7 @@
   }
 
   bool commentStartState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       state = commentStartDashState;
     } else if (data == '\u0000') {
@@ -1360,7 +1360,7 @@
   }
 
   bool commentStartDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       state = commentEndState;
     } else if (data == '\u0000') {
@@ -1382,7 +1382,7 @@
   }
 
   bool commentState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       state = commentEndDashState;
     } else if (data == '\u0000') {
@@ -1399,7 +1399,7 @@
   }
 
   bool commentEndDashState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '-') {
       state = commentEndState;
     } else if (data == '\u0000') {
@@ -1418,7 +1418,7 @@
   }
 
   bool commentEndState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '>') {
       _addToken(currentToken);
       state = dataState;
@@ -1448,7 +1448,7 @@
   }
 
   bool commentEndBangState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '>') {
       _addToken(currentToken);
       state = dataState;
@@ -1471,7 +1471,7 @@
   }
 
   bool doctypeState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       state = beforeDoctypeNameState;
     } else if (data == eof) {
@@ -1488,7 +1488,7 @@
   }
 
   bool beforeDoctypeNameState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       return true;
     } else if (data == '>') {
@@ -1513,7 +1513,7 @@
   }
 
   bool doctypeNameState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       currentDoctypeToken.name = asciiUpper2Lower(currentDoctypeToken.name);
       state = afterDoctypeNameState;
@@ -1594,7 +1594,7 @@
   }
 
   bool afterDoctypePublicKeywordState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       state = beforeDoctypePublicIdentifierState;
     } else if (data == "'" || data == '"') {
@@ -1614,7 +1614,7 @@
   }
 
   bool beforeDoctypePublicIdentifierState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       return true;
     } else if (data == '"') {
@@ -1642,7 +1642,7 @@
   }
 
   bool doctypePublicIdentifierDoubleQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '"') {
       state = afterDoctypePublicIdentifierState;
     } else if (data == '\u0000') {
@@ -1665,7 +1665,7 @@
   }
 
   bool doctypePublicIdentifierSingleQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == "'") {
       state = afterDoctypePublicIdentifierState;
     } else if (data == '\u0000') {
@@ -1688,7 +1688,7 @@
   }
 
   bool afterDoctypePublicIdentifierState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       state = betweenDoctypePublicAndSystemIdentifiersState;
     } else if (data == '>') {
@@ -1716,7 +1716,7 @@
   }
 
   bool betweenDoctypePublicAndSystemIdentifiersState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       return true;
     } else if (data == '>') {
@@ -1742,7 +1742,7 @@
   }
 
   bool afterDoctypeSystemKeywordState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       state = beforeDoctypeSystemIdentifierState;
     } else if (data == "'" || data == '"') {
@@ -1762,7 +1762,7 @@
   }
 
   bool beforeDoctypeSystemIdentifierState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       return true;
     } else if (data == '"') {
@@ -1790,7 +1790,7 @@
   }
 
   bool doctypeSystemIdentifierDoubleQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '"') {
       state = afterDoctypeSystemIdentifierState;
     } else if (data == '\u0000') {
@@ -1813,7 +1813,7 @@
   }
 
   bool doctypeSystemIdentifierSingleQuotedState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == "'") {
       state = afterDoctypeSystemIdentifierState;
     } else if (data == '\u0000') {
@@ -1836,7 +1836,7 @@
   }
 
   bool afterDoctypeSystemIdentifierState() {
-    var data = stream.char();
+    final data = stream.char();
     if (isWhitespace(data)) {
       return true;
     } else if (data == '>') {
@@ -1855,7 +1855,7 @@
   }
 
   bool bogusDoctypeState() {
-    var data = stream.char();
+    final data = stream.char();
     if (data == '>') {
       _addToken(currentToken);
       state = dataState;
@@ -1869,7 +1869,7 @@
   }
 
   bool cdataSectionState() {
-    var data = <String>[];
+    final data = <String>[];
     var matchedEnd = 0;
     while (true) {
       var ch = stream.char();
diff --git a/lib/src/treebuilder.dart b/lib/src/treebuilder.dart
index b60d4a7..a1f0da1 100644
--- a/lib/src/treebuilder.dart
+++ b/lib/src/treebuilder.dart
@@ -48,7 +48,7 @@
   if (a.isEmpty) return true;
 
   for (var keyA in a.keys) {
-    var valB = b[keyA];
+    final valB = b[keyA];
     if (valB == null && !b.containsKey(keyA)) {
       return false;
     }
@@ -104,7 +104,7 @@
   bool elementInScope(target, {String variant}) {
     //If we pass a node in we match that. if we pass a string
     //match any node with that name
-    var exactNode = target is Node;
+    final exactNode = target is Node;
 
     var listElements1 = scopingElements;
     var listElements2 = const [];
@@ -189,13 +189,13 @@
       entry = activeFormattingElements[i];
 
       // TODO(jmesserly): optimize this. No need to create a token.
-      var cloneToken = StartTagToken(entry.localName,
+      final cloneToken = StartTagToken(entry.localName,
           namespace: entry.namespaceUri,
           data: LinkedHashMap.from(entry.attributes))
         ..span = entry.sourceSpan;
 
       // Step 9
-      var element = insertElement(cloneToken);
+      final element = insertElement(cloneToken);
 
       // Step 10
       activeFormattingElements[i] = element;
@@ -231,13 +231,13 @@
   }
 
   void insertRoot(StartTagToken token) {
-    var element = createElement(token);
+    final element = createElement(token);
     openElements.add(element);
     document.nodes.add(element);
   }
 
   void insertDoctype(DoctypeToken token) {
-    var doctype = DocumentType(token.name, token.publicId, token.systemId)
+    final doctype = DocumentType(token.name, token.publicId, token.systemId)
       ..sourceSpan = token.span;
     document.nodes.add(doctype);
   }
@@ -249,9 +249,9 @@
 
   /// Create an element but don't insert it anywhere
   Element createElement(StartTagToken token) {
-    var name = token.name;
-    var namespace = token.namespace ?? defaultNamespace;
-    var element = document.createElementNS(namespace, name)
+    final name = token.name;
+    final namespace = token.namespace ?? defaultNamespace;
+    final element = document.createElementNS(namespace, name)
       ..attributes = token.data
       ..sourceSpan = token.span;
     return element;
@@ -263,9 +263,9 @@
   }
 
   Element insertElementNormal(StartTagToken token) {
-    var name = token.name;
-    var namespace = token.namespace ?? defaultNamespace;
-    var element = document.createElementNS(namespace, name)
+    final name = token.name;
+    final namespace = token.namespace ?? defaultNamespace;
+    final element = document.createElementNS(namespace, name)
       ..attributes = token.data
       ..sourceSpan = token.span;
     openElements.last.nodes.add(element);
@@ -275,13 +275,13 @@
 
   Element insertElementTable(StartTagToken token) {
     /// Create an element and insert it into the tree
-    var element = createElement(token);
+    final element = createElement(token);
     if (!tableInsertModeElements.contains(openElements.last.localName)) {
       return insertElementNormal(token);
     } else {
       // We should be in the InTable mode. This means we want to do
       // special magic element rearranging
-      var nodePos = getTableMisnestedNodePosition();
+      final nodePos = getTableMisnestedNodePosition();
       if (nodePos[1] == null) {
         // TODO(jmesserly): I don't think this is reachable. If insertFromTable
         // is true, there will be a <table> element open, and it always has a
@@ -297,7 +297,7 @@
 
   /// Insert text data.
   void insertText(String data, FileSpan span) {
-    var parent = openElements.last;
+    final parent = openElements.last;
 
     if (!insertFromTable ||
         insertFromTable &&
@@ -306,7 +306,7 @@
     } else {
       // We should be in the InTable mode. This means we want to do
       // special magic element rearranging
-      var nodePos = getTableMisnestedNodePosition();
+      final nodePos = getTableMisnestedNodePosition();
       _insertText(nodePos[0], data, span, nodePos[1] as Element);
     }
   }
@@ -315,7 +315,7 @@
   /// start of node [refNode] or to the end of the node's text.
   static void _insertText(Node parent, String data, FileSpan span,
       [Element refNode]) {
-    var nodes = parent.nodes;
+    final nodes = parent.nodes;
     if (refNode == null) {
       if (nodes.isNotEmpty && nodes.last is Text) {
         final last = nodes.last as Text;
@@ -329,7 +329,7 @@
         nodes.add(Text(data)..sourceSpan = span);
       }
     } else {
-      var index = nodes.indexOf(refNode);
+      final index = nodes.indexOf(refNode);
       if (index > 0 && nodes[index - 1] is Text) {
         final last = nodes[index - 1] as Text;
         last.appendData(data);
@@ -370,7 +370,7 @@
   }
 
   void generateImpliedEndTags([String exclude]) {
-    var name = openElements.last.localName;
+    final name = openElements.last.localName;
     // XXX td, th and tr are not actually needed
     if (name != exclude &&
         const ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt']
@@ -388,7 +388,7 @@
   /// Return the final fragment.
   DocumentFragment getFragment() {
     //XXX assert innerHTML
-    var fragment = DocumentFragment();
+    final fragment = DocumentFragment();
     openElements[0].reparentChildren(fragment);
     return fragment;
   }
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 3eadc2e..559f6c3 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -36,7 +36,7 @@
 
 String padWithZeros(String str, int size) {
   if (str.length == size) return str;
-  var result = StringBuffer();
+  final result = StringBuffer();
   size -= str.length;
   for (var i = 0; i < size; i++) {
     result.write('0');
@@ -53,8 +53,8 @@
 String formatStr(String format, Map data) {
   if (data == null) return format;
   data.forEach((key, value) {
-    var result = StringBuffer();
-    var search = '%($key)';
+    final result = StringBuffer();
+    final search = '%($key)';
     int last = 0, match;
     while ((match = format.indexOf(search, last)) >= 0) {
       result.write(format.substring(last, match));
@@ -75,11 +75,11 @@
           result.write(value);
           break;
         case 'd':
-          var number = value.toString();
+          final number = value.toString();
           result.write(padWithZeros(number, numberSize));
           break;
         case 'x':
-          var number = (value as int).toRadixString(16);
+          final number = (value as int).toRadixString(16);
           result.write(padWithZeros(number, numberSize));
           break;
         default:
diff --git a/test/dom_test.dart b/test/dom_test.dart
index 078c3c7..f9a5cac 100644
--- a/test/dom_test.dart
+++ b/test/dom_test.dart
@@ -24,8 +24,8 @@
         '<div id=Foo>');
 
     test('getElementById', () {
-      var foo = doc.body.nodes[0];
-      var fooVar = foo.nodes[2];
+      final foo = doc.body.nodes[0];
+      final fooVar = foo.nodes[2];
       expect((foo as Element).id, 'foo');
       expect((fooVar as Element).id, 'Foo');
       expect(doc.getElementById('foo'), foo);
@@ -33,9 +33,9 @@
     });
 
     test('getElementsByClassName', () {
-      var foo = doc.body.nodes[0];
-      var barBaz = foo.nodes[0];
-      var quxBaz = foo.nodes[1];
+      final foo = doc.body.nodes[0];
+      final barBaz = foo.nodes[0];
+      final quxBaz = foo.nodes[1];
       expect((barBaz as Element).className, ' bar baz');
       expect((quxBaz as Element).className, 'qux  baz ');
       expect(doc.getElementsByClassName('baz'), [barBaz, quxBaz]);
@@ -45,17 +45,17 @@
     });
 
     test('getElementsByTagName', () {
-      var foo = doc.body.nodes[0];
-      var barBaz = foo.nodes[0];
-      var quxBaz = foo.nodes[1];
-      var fooVar = foo.nodes[2];
+      final foo = doc.body.nodes[0];
+      final barBaz = foo.nodes[0];
+      final quxBaz = foo.nodes[1];
+      final fooVar = foo.nodes[2];
       expect(doc.getElementsByTagName('div'), [foo, barBaz, quxBaz, fooVar]);
     });
   });
 
   group('fragments are flattened', () {
     test('add', () {
-      var doc = parse('<body>');
+      final doc = parse('<body>');
       doc.body.nodes.add(parseFragment('<x-foo>'));
       expect((doc.body.nodes[0] as Element).localName, 'x-foo');
       doc.body.nodes.add(parseFragment('<x-bar>'));
@@ -63,7 +63,7 @@
     });
 
     test('addLast', () {
-      var doc = parse('<body>');
+      final doc = parse('<body>');
       doc.body.nodes.addLast(parseFragment('<x-foo>'));
       expect((doc.body.nodes[0] as Element).localName, 'x-foo');
       doc.body.nodes.addLast(parseFragment('<x-bar>'));
@@ -71,7 +71,7 @@
     });
 
     test('addAll', () {
-      var doc = parse('<body><x-a></x-a>');
+      final doc = parse('<body><x-a></x-a>');
       doc.body.nodes.addAll([parseFragment('<x-b></x-b><x-c></x-c>')]);
       expect((doc.body.nodes[0] as Element).localName, 'x-a');
       expect((doc.body.nodes[1] as Element).localName, 'x-b');
@@ -177,8 +177,8 @@
     });
 
     test('replaceWith', () {
-      var fragment = parseFragment('<y-b></y-b><y-c></y-c>');
-      var doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
+      final fragment = parseFragment('<y-b></y-b><y-c></y-c>');
+      final doc = parse('<body><x-a></x-a><x-b></x-b><x-c></x-c>');
       doc.body.nodes[1].replaceWith(fragment);
       expect((doc.body.nodes[0] as Element).localName, 'x-a');
       expect((doc.body.nodes[1] as Element).localName, 'y-b');
diff --git a/test/parser_feature_test.dart b/test/parser_feature_test.dart
index 3ed0031..f59df1b 100644
--- a/test/parser_feature_test.dart
+++ b/test/parser_feature_test.dart
@@ -12,29 +12,29 @@
 void main() {
   _testElementSpans();
   test('doctype is cloneable', () {
-    var doc = parse('<!doctype HTML>');
+    final doc = parse('<!doctype HTML>');
     final doctype = doc.nodes[0] as DocumentType;
     expect(doctype.clone(false).toString(), '<!DOCTYPE html>');
   });
 
   test('line counter', () {
     // http://groups.google.com/group/html5lib-discuss/browse_frm/thread/f4f00e4a2f26d5c0
-    var doc = parse('<pre>\nx\n&gt;\n</pre>');
+    final doc = parse('<pre>\nx\n&gt;\n</pre>');
     expect(doc.body.innerHtml, '<pre>x\n&gt;\n</pre>');
   });
 
   test('namespace html elements on', () {
-    var doc = HtmlParser('', tree: TreeBuilder(true)).parse();
+    final doc = HtmlParser('', tree: TreeBuilder(true)).parse();
     expect((doc.nodes[0] as Element).namespaceUri, Namespaces.html);
   });
 
   test('namespace html elements off', () {
-    var doc = HtmlParser('', tree: TreeBuilder(false)).parse();
+    final doc = HtmlParser('', tree: TreeBuilder(false)).parse();
     expect((doc.nodes[0] as Element).namespaceUri, null);
   });
 
   test('parse error spans - full', () {
-    var parser = HtmlParser('''
+    final parser = HtmlParser('''
 <!DOCTYPE html>
 <html>
   <body>
@@ -42,10 +42,10 @@
   </body>
 </html>
 ''', generateSpans: true, sourceUrl: 'ParseError');
-    var doc = parser.parse();
+    final doc = parser.parse();
     expect(doc.body.outerHtml, '<body>\n  \n  \n\n</body>');
     expect(parser.errors.length, 1);
-    var error = parser.errors[0];
+    final error = parser.errors[0];
     expect(error.errorCode, 'unexpected-doctype');
 
     // Note: these values are 0-based, but the printed format is 1-based.
@@ -64,7 +64,7 @@
   });
 
   test('parse error spans - minimal', () {
-    var parser = HtmlParser('''
+    final parser = HtmlParser('''
 <!DOCTYPE html>
 <html>
   <body>
@@ -72,10 +72,10 @@
   </body>
 </html>
 ''');
-    var doc = parser.parse();
+    final doc = parser.parse();
     expect(doc.body.outerHtml, '<body>\n  \n  \n\n</body>');
     expect(parser.errors.length, 1);
-    var error = parser.errors[0];
+    final error = parser.errors[0];
     expect(error.errorCode, 'unexpected-doctype');
     expect(error.span.start.line, 3);
     // Note: error position is at the end, not the beginning
@@ -83,9 +83,9 @@
   });
 
   test('text spans should have the correct length', () {
-    var textContent = '\n  hello {{name}}';
-    var html = '<body><div>$textContent</div>';
-    var doc = parse(html, generateSpans: true);
+    final textContent = '\n  hello {{name}}';
+    final html = '<body><div>$textContent</div>';
+    final doc = parse(html, generateSpans: true);
     final text = doc.body.nodes[0].nodes[0] as Text;
     expect(text, const TypeMatcher<Text>());
     expect(text.data, textContent);
@@ -94,45 +94,45 @@
   });
 
   test('attribute spans', () {
-    var text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
-    var doc = parse(text, generateSpans: true);
-    var elem = doc.querySelector('element');
+    final text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
+    final doc = parse(text, generateSpans: true);
+    final elem = doc.querySelector('element');
     expect(elem.sourceSpan.start.offset, 0);
     expect(elem.sourceSpan.end.offset, text.length);
     expect(elem.sourceSpan.text, text);
 
     expect(elem.attributeSpans['quux'], null);
 
-    var span = elem.attributeSpans['extends'];
+    final span = elem.attributeSpans['extends'];
     expect(span.start.offset, text.indexOf('extends'));
     expect(span.text, 'extends="x-bar"');
   });
 
   test('attribute value spans', () {
-    var text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
-    var doc = parse(text, generateSpans: true);
-    var elem = doc.querySelector('element');
+    final text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
+    final doc = parse(text, generateSpans: true);
+    final elem = doc.querySelector('element');
 
     expect(elem.attributeValueSpans['quux'], null);
 
-    var span = elem.attributeValueSpans['extends'];
+    final span = elem.attributeValueSpans['extends'];
     expect(span.start.offset, text.indexOf('x-bar'));
     expect(span.text, 'x-bar');
   });
 
   test('attribute spans if no attributes', () {
-    var text = '<element>';
-    var doc = parse(text, generateSpans: true);
-    var elem = doc.querySelector('element');
+    final text = '<element>';
+    final doc = parse(text, generateSpans: true);
+    final elem = doc.querySelector('element');
 
     expect(elem.attributeSpans['quux'], null);
     expect(elem.attributeValueSpans['quux'], null);
   });
 
   test('attribute spans if no attribute value', () {
-    var text = '<foo template>';
-    var doc = parse(text, generateSpans: true);
-    var elem = doc.querySelector('foo');
+    final text = '<foo template>';
+    final doc = parse(text, generateSpans: true);
+    final elem = doc.querySelector('foo');
 
     expect(
         elem.attributeSpans['template'].start.offset, text.indexOf('template'));
@@ -140,9 +140,9 @@
   });
 
   test('attribute spans null if code parsed without spans', () {
-    var text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
-    var doc = parse(text);
-    var elem = doc.querySelector('element');
+    final text = '<element name="x-foo" extends="x-bar" constructor="Foo">';
+    final doc = parse(text);
+    final elem = doc.querySelector('element');
     expect(elem.sourceSpan, null);
     expect(elem.attributeSpans['quux'], null);
     expect(elem.attributeSpans['extends'], null);
@@ -160,8 +160,8 @@
   });
 
   test('empty document has html, body, and head', () {
-    var doc = parse('');
-    var html = '<html><head></head><body></body></html>';
+    final doc = parse('');
+    final html = '<html><head></head><body></body></html>';
     expect(doc.outerHtml, html);
     expect(doc.documentElement.outerHtml, html);
     expect(doc.head.outerHtml, '<head></head>');
@@ -169,7 +169,7 @@
   });
 
   test('strange table case', () {
-    var doc = parse('<table><tbody><foo>').body;
+    final doc = parse('<table><tbody><foo>').body;
     expect(doc.innerHtml, '<foo></foo><table><tbody></tbody></table>');
   });
 
@@ -177,7 +177,7 @@
     test('attribute order', () {
       // Note: the spec only requires a stable order.
       // However, we preserve the input order via LinkedHashMap
-      var body = parse('<foo d=1 a=2 c=3 b=4>').body;
+      final body = parse('<foo d=1 a=2 c=3 b=4>').body;
       expect(body.innerHtml, '<foo d="1" a="2" c="3" b="4"></foo>');
       expect(body.querySelector('foo').attributes.remove('a'), '2');
       expect(body.innerHtml, '<foo d="1" c="3" b="4"></foo>');
@@ -203,9 +203,9 @@
     });
 
     test('Escaping non-breaking space', () {
-      var text = '<span>foO\u00A0bar</span>';
+      final text = '<span>foO\u00A0bar</span>';
       expect(text.codeUnitAt(text.indexOf('O') + 1), 0xA0);
-      var e = parseFragment(text).firstChild as Element;
+      final e = parseFragment(text).firstChild as Element;
       expect(e.outerHtml, '<span>foO&nbsp;bar</span>');
     });
 
@@ -222,15 +222,15 @@
     test('xml namespaces', () {
       // Note: this is a nonsensical example, but it triggers the behavior
       // we're looking for with attribute names in foreign content.
-      var doc = parse('''
+      final doc = parse('''
         <body>
         <svg>
         <desc xlink:type="simple"
               xlink:href="http://example.com/logo.png"
               xlink:show="new"></desc>
       ''');
-      var n = doc.querySelector('desc');
-      var keys = n.attributes.keys.toList();
+      final n = doc.querySelector('desc');
+      final keys = n.attributes.keys.toList();
       expect(keys[0], const TypeMatcher<AttributeName>());
       expect(keys[0].prefix, 'xlink');
       expect(keys[0].namespace, 'http://www.w3.org/1999/xlink');
@@ -244,8 +244,8 @@
   });
 
   test('error printing without spans', () {
-    var parser = HtmlParser('foo');
-    var doc = parser.parse();
+    final parser = HtmlParser('foo');
+    final doc = parser.parse();
     expect(doc.body.innerHtml, 'foo');
     expect(parser.errors.length, 1);
     expect(parser.errors[0].errorCode, 'expected-doctype-but-got-chars');
@@ -262,9 +262,9 @@
   });
 
   test('Element.text', () {
-    var doc = parseFragment('<div>foo<div>bar</div>baz</div>');
-    var e = doc.firstChild;
-    var text = e.firstChild;
+    final doc = parseFragment('<div>foo<div>bar</div>baz</div>');
+    final e = doc.firstChild;
+    final text = e.firstChild;
     expect((text as Text).data, 'foo');
     expect(e.text, 'foobarbaz');
 
@@ -276,8 +276,8 @@
   });
 
   test('Text.text', () {
-    var doc = parseFragment('<div>foo<div>bar</div>baz</div>');
-    var e = doc.firstChild;
+    final doc = parseFragment('<div>foo<div>bar</div>baz</div>');
+    final e = doc.firstChild;
     final text = e.firstChild as Text;
     expect(text.data, 'foo');
     expect(text.text, 'foo');
@@ -289,9 +289,9 @@
   });
 
   test('Comment.text', () {
-    var doc = parseFragment('<div><!--foo-->bar</div>');
-    var e = doc.firstChild;
-    var c = e.firstChild;
+    final doc = parseFragment('<div><!--foo-->bar</div>');
+    final e = doc.firstChild;
+    final c = e.firstChild;
     expect((c as Comment).data, 'foo');
     expect(c.text, 'foo');
     expect(e.text, 'bar');
@@ -303,16 +303,16 @@
   });
 
   test('foreignObject end tag', () {
-    var p = HtmlParser('''
+    final p = HtmlParser('''
 <svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"
      version="1.1">
     <foreignObject width="320px" height="200px">
         <x-flow></x-flow>
     </foreignObject>
 </svg>''');
-    var doc = p.parseFragment();
+    final doc = p.parseFragment();
     expect(p.errors, isEmpty);
-    var svg = doc.querySelector('svg');
+    final svg = doc.querySelector('svg');
     expect(svg.children[0].children[0].localName, 'x-flow');
   });
 
@@ -359,129 +359,129 @@
 
   group('element spans', () {
     test('html and body', () {
-      var text = '<html><body>123</body></html>';
-      var doc = parse(text, generateSpans: true);
+      final text = '<html><body>123</body></html>';
+      final doc = parse(text, generateSpans: true);
       {
-        var elem = doc.querySelector('html');
+        final elem = doc.querySelector('html');
         assertSpan(elem.sourceSpan, 0, 6, '<html>');
         assertSpan(elem.endSourceSpan, 22, 29, '</html>');
       }
       {
-        var elem = doc.querySelector('body');
+        final elem = doc.querySelector('body');
         assertSpan(elem.sourceSpan, 6, 12, '<body>');
         assertSpan(elem.endSourceSpan, 15, 22, '</body>');
       }
     });
 
     test('normal', () {
-      var text = '<div><element><span></span></element></div>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('element');
+      final text = '<div><element><span></span></element></div>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('element');
       assertSpan(elem.sourceSpan, 5, 14, '<element>');
       assertSpan(elem.endSourceSpan, 27, 37, '</element>');
     });
 
     test('block', () {
-      var text = '<div>123</div>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('div');
+      final text = '<div>123</div>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('div');
       assertSpan(elem.sourceSpan, 0, 5, '<div>');
       assertSpan(elem.endSourceSpan, 8, 14, '</div>');
     });
 
     test('form', () {
-      var text = '<form>123</form>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('form');
+      final text = '<form>123</form>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('form');
       assertSpan(elem.sourceSpan, 0, 6, '<form>');
       assertSpan(elem.endSourceSpan, 9, 16, '</form>');
     });
 
     test('p explicit end', () {
-      var text = '<p>123</p>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('p');
+      final text = '<p>123</p>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('p');
       assertSpan(elem.sourceSpan, 0, 3, '<p>');
       assertSpan(elem.endSourceSpan, 6, 10, '</p>');
     });
 
     test('p implicit end', () {
-      var text = '<div><p>123<p>456</div>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('p');
+      final text = '<div><p>123<p>456</div>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('p');
       assertSpan(elem.sourceSpan, 5, 8, '<p>');
       expect(elem.endSourceSpan, isNull);
     });
 
     test('li', () {
-      var text = '<li>123</li>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('li');
+      final text = '<li>123</li>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('li');
       assertSpan(elem.sourceSpan, 0, 4, '<li>');
       assertSpan(elem.endSourceSpan, 7, 12, '</li>');
     });
 
     test('heading', () {
-      var text = '<h1>123</h1>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('h1');
+      final text = '<h1>123</h1>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('h1');
       assertSpan(elem.sourceSpan, 0, 4, '<h1>');
       assertSpan(elem.endSourceSpan, 7, 12, '</h1>');
     });
 
     test('formatting', () {
-      var text = '<b>123</b>';
-      var doc = parse(text, generateSpans: true);
-      var elem = doc.querySelector('b');
+      final text = '<b>123</b>';
+      final doc = parse(text, generateSpans: true);
+      final elem = doc.querySelector('b');
       assertSpan(elem.sourceSpan, 0, 3, '<b>');
       assertSpan(elem.endSourceSpan, 6, 10, '</b>');
     });
 
     test('table tbody', () {
-      var text = '<table><tbody>  </tbody></table>';
-      var doc = parse(text, generateSpans: true);
+      final text = '<table><tbody>  </tbody></table>';
+      final doc = parse(text, generateSpans: true);
       {
-        var elem = doc.querySelector('tbody');
+        final elem = doc.querySelector('tbody');
         assertSpan(elem.sourceSpan, 7, 14, '<tbody>');
         assertSpan(elem.endSourceSpan, 16, 24, '</tbody>');
       }
     });
 
     test('table tr td', () {
-      var text = '<table><tr><td>123</td></tr></table>';
-      var doc = parse(text, generateSpans: true);
+      final text = '<table><tr><td>123</td></tr></table>';
+      final doc = parse(text, generateSpans: true);
       {
-        var elem = doc.querySelector('table');
+        final elem = doc.querySelector('table');
         assertSpan(elem.sourceSpan, 0, 7, '<table>');
         assertSpan(elem.endSourceSpan, 28, 36, '</table>');
       }
       {
-        var elem = doc.querySelector('tr');
+        final elem = doc.querySelector('tr');
         assertSpan(elem.sourceSpan, 7, 11, '<tr>');
         assertSpan(elem.endSourceSpan, 23, 28, '</tr>');
       }
       {
-        var elem = doc.querySelector('td');
+        final elem = doc.querySelector('td');
         assertSpan(elem.sourceSpan, 11, 15, '<td>');
         assertSpan(elem.endSourceSpan, 18, 23, '</td>');
       }
     });
 
     test('select optgroup option', () {
-      var text = '<select><optgroup><option>123</option></optgroup></select>';
-      var doc = parse(text, generateSpans: true);
+      final text = '<select><optgroup><option>123</option></optgroup></select>';
+      final doc = parse(text, generateSpans: true);
       {
-        var elem = doc.querySelector('select');
+        final elem = doc.querySelector('select');
         assertSpan(elem.sourceSpan, 0, 8, '<select>');
         assertSpan(elem.endSourceSpan, 49, 58, '</select>');
       }
       {
-        var elem = doc.querySelector('optgroup');
+        final elem = doc.querySelector('optgroup');
         assertSpan(elem.sourceSpan, 8, 18, '<optgroup>');
         assertSpan(elem.endSourceSpan, 38, 49, '</optgroup>');
       }
       {
-        var elem = doc.querySelector('option');
+        final elem = doc.querySelector('option');
         assertSpan(elem.sourceSpan, 18, 26, '<option>');
         assertSpan(elem.endSourceSpan, 29, 38, '</option>');
       }
diff --git a/test/parser_test.dart b/test/parser_test.dart
index 232a50f..7173045 100644
--- a/test/parser_test.dart
+++ b/test/parser_test.dart
@@ -20,9 +20,9 @@
   // final namespaceExpected = new RegExp(@"^(\s*)<(\S+)>", multiLine: true);
   // return expected.replaceAll(namespaceExpected, @"$1<html $2>");
   final namespaceExpected = RegExp(r'^(\|\s*)<(\S+)>');
-  var lines = expected.split('\n');
+  final lines = expected.split('\n');
   for (var i = 0; i < lines.length; i++) {
-    var match = namespaceExpected.firstMatch(lines[i]);
+    final match = namespaceExpected.firstMatch(lines[i]);
     if (match != null) {
       lines[i] = '${match[1]}<html ${match[2]}>';
     }
@@ -40,8 +40,8 @@
     bool namespaceHTMLElements) {
   // XXX - move this out into the setup function
   // concatenate all consecutive character tokens into a single token
-  var builder = treeCtor(namespaceHTMLElements);
-  var parser = HtmlParser(input, tree: builder);
+  final builder = treeCtor(namespaceHTMLElements);
+  final parser = HtmlParser(input, tree: builder);
 
   Node document;
   if (innerHTML != null) {
@@ -50,7 +50,7 @@
     document = parser.parse();
   }
 
-  var output = testSerializer(document);
+  final output = testSerializer(document);
 
   if (namespaceHTMLElements) {
     expected = namespaceHtml(expected);
@@ -73,16 +73,16 @@
   for (var path in getDataFiles('tree-construction')) {
     if (!path.endsWith('.dat')) continue;
 
-    var tests = TestData(path, 'data');
-    var testName = pathos.basenameWithoutExtension(path);
+    final tests = TestData(path, 'data');
+    final testName = pathos.basenameWithoutExtension(path);
 
     group(testName, () {
       for (var testData in tests) {
-        var input = testData['data'];
+        final input = testData['data'];
         final errorString = testData['errors'];
         final errors = errorString?.split('\n');
-        var innerHTML = testData['document-fragment'];
-        var expected = testData['document'];
+        final innerHTML = testData['document-fragment'];
+        final expected = testData['document'];
 
         for (var treeCtor in treeTypes.values) {
           for (var namespaceHTMLElements in const [false, true]) {
@@ -100,7 +100,7 @@
 /// Extract the name for the test based on the test input data.
 dynamic _nameFor(String input) {
   // Using jsonDecode to unescape other unicode characters
-  var escapeQuote = input
+  final escapeQuote = input
       .replaceAll(RegExp('\\\\.'), '_')
       .replaceAll(RegExp('\u0000'), '_')
       .replaceAll('"', '\\"')
diff --git a/test/selectors/level1_baseline_test.dart b/test/selectors/level1_baseline_test.dart
index d5defb1..02d513b 100644
--- a/test/selectors/level1_baseline_test.dart
+++ b/test/selectors/level1_baseline_test.dart
@@ -18,7 +18,7 @@
 import 'selectors.dart';
 
 Document getTestContentDocument() {
-  var testPath = p.join(testDir, 'selectors', 'level1-content.html');
+  final testPath = p.join(testDir, 'selectors', 'level1-content.html');
   return parse(File(testPath).readAsStringSync());
 }
 
@@ -64,12 +64,12 @@
   //doc = frame.contentDocument;                 // Document Node tests
   doc = getTestContentDocument();
 
-  var element = doc.getElementById('root'); // In-document Element Node tests
+  final element = doc.getElementById('root'); // In-document Element Node tests
 
   //Setup the namespace tests
   setupSpecialElements(element);
 
-  var outOfScope = element
+  final outOfScope = element
       .clone(true); // Append this to the body before running the in-document
   // Element tests, but after running the Document tests. This
   // tests that no elements that are not descendants of element
@@ -81,9 +81,9 @@
         ''; // that none of these elements ever match.
   });
 
-  var detached = element.clone(true); // Detached Element Node tests
+  final detached = element.clone(true); // Detached Element Node tests
 
-  var fragment = doc.createDocumentFragment(); // Fragment Node tests
+  final fragment = doc.createDocumentFragment(); // Fragment Node tests
   fragment.append(element.clone(true));
 
   // Setup Tests
@@ -118,9 +118,7 @@
       doc.body.append(outOfScope); // Append before in-document Element tests.
       // None of these elements should match
     });
-    tearDown(() {
-      outOfScope.remove();
-    });
+    tearDown(outOfScope.remove);
     runValidSelectorTest(
         'In-document Element', element, validSelectors, testType, docType);
   });
diff --git a/test/selectors/level1_lib.dart b/test/selectors/level1_lib.dart
index 721eca6..388ae00 100644
--- a/test/selectors/level1_lib.dart
+++ b/test/selectors/level1_lib.dart
@@ -23,8 +23,8 @@
   parent.append(doc.createElement('undefined'));
 
   // Setup namespace tests
-  var anyNS = doc.createElement('div');
-  var noNS = doc.createElement('div');
+  final anyNS = doc.createElement('div');
+  final noNS = doc.createElement('div');
   anyNS.id = 'any-namespace';
   noNS.id = 'no-namespace';
 
@@ -71,17 +71,17 @@
  */
 void interfaceCheck(String type, obj) {
   runTest(() {
-    var q = obj.querySelector is Function;
+    final q = obj.querySelector is Function;
     assertTrue(q, type + ' supports querySelector.');
   }, type + ' supports querySelector');
 
   runTest(() {
-    var qa = obj.querySelectorAll is Function;
+    final qa = obj.querySelectorAll is Function;
     assertTrue(qa, type + ' supports querySelectorAll.');
   }, type + ' supports querySelectorAll');
 
   runTest(() {
-    var list = obj.querySelectorAll('div');
+    final list = obj.querySelectorAll('div');
     // TODO(jmesserly): testing List<Element> for now. It should return an
     // ElementList which has extra properties. Needed for dart:html compat.
     assertTrue(list is List<Element>,
@@ -100,7 +100,7 @@
     pre = root.querySelectorAll('div');
     preLength = pre.length;
 
-    var div = doc.createElement('div');
+    final div = doc.createElement('div');
     (root is Document ? root.body : root).append(div);
 
     assertEquals(
@@ -142,7 +142,7 @@
 
   runTest(() {
     // 4
-    var elm = root.querySelector('null');
+    final elm = root.querySelector('null');
     assertNotEquals(elm, null, 'This should find an element.');
     // TODO(jmesserly): change "localName" back to "tagName" once implemented.
     assertEquals(
@@ -151,7 +151,7 @@
 
   runTest(() {
     // 5
-    var elm = root.querySelector('undefined');
+    final elm = root.querySelector('undefined');
     assertNotEquals(elm, 'undefined', 'This should find an element.');
     // TODO(jmesserly): change "localName" back to "tagName" once implemented.
     assertEquals(elm.localName.toUpperCase(), 'UNDEFINED',
@@ -167,7 +167,7 @@
 
   runTest(() {
     // 7
-    var result = root.querySelectorAll('*');
+    final result = root.querySelectorAll('*');
     var i = 0;
     traverse(root as Node, (elem) {
       if (!identical(elem, root)) {
@@ -211,11 +211,11 @@
   }
 
   for (var i = 0; i < selectors.length; i++) {
-    var s = selectors[i];
-    var n = s['name'] as String;
-    var skip = _getSkip(n);
-    var q = s['selector'] as String;
-    var e = s['expect'] as List;
+    final s = selectors[i];
+    final n = s['name'] as String;
+    final skip = _getSkip(n);
+    final q = s['selector'] as String;
+    final e = s['expect'] as List;
 
     if ((s['exclude'] is! List ||
             (s['exclude'].indexOf(nodeType) == -1 &&
@@ -268,9 +268,9 @@
  */
 void runInvalidSelectorTest(String type, root, List selectors) {
   for (var i = 0; i < selectors.length; i++) {
-    var s = selectors[i];
-    var n = s['name'] as String;
-    var q = s['selector'] as String;
+    final s = selectors[i];
+    final n = s['name'] as String;
+    final q = s['selector'] as String;
 
     // Dart note: FormatException seems a reasonable mapping of SyntaxError
     runTest(() {
diff --git a/test/support.dart b/test/support.dart
index 0199535..34fedb2 100644
--- a/test/support.dart
+++ b/test/support.dart
@@ -23,7 +23,7 @@
 final testDataDir = p.join(testDir, 'data');
 
 Iterable<String> getDataFiles(String subdirectory) {
-  var dir = Directory(p.join(testDataDir, subdirectory));
+  final dir = Directory(p.join(testDataDir, subdirectory));
   return dir.listSync().whereType<File>().map((f) => f.path);
 }
 
@@ -45,14 +45,14 @@
   List<Map<String, String>> _getData() {
     var data = <String, String>{};
     String key;
-    var result = <Map<String, String>>[];
-    var lines = _text.split('\n');
+    final result = <Map<String, String>>[];
+    final lines = _text.split('\n');
     // Remove trailing newline to match Python
     if (lines.last == '') {
       lines.removeLast();
     }
     for (var line in lines) {
-      var heading = sectionHeading(line);
+      final heading = sectionHeading(line);
       if (heading != null) {
         if (data.isNotEmpty && heading == newTestHeading) {
           // Remove trailing newline
@@ -111,7 +111,7 @@
   set indent(int value) {
     if (_indent == value) return;
 
-    var arr = List<int>(value);
+    final arr = List<int>(value);
     for (var i = 0; i < value; i++) {
       arr[i] = 32;
     }
@@ -161,14 +161,14 @@
     _str.write(node);
     if (node.attributes.isNotEmpty) {
       indent += 2;
-      var keys = node.attributes.keys.toList();
+      final keys = node.attributes.keys.toList();
       keys.sort((x, y) {
         if (x is String) return x.compareTo(y as String);
         if (x is AttributeName) return x.compareTo(y as AttributeName);
         throw StateError('Cannot sort');
       });
       for (var key in keys) {
-        var v = node.attributes[key];
+        final v = node.attributes[key];
         if (key is AttributeName) {
           final attr = key as AttributeName;
           key = '${attr.prefix} ${attr.name}';
diff --git a/test/tokenizer_test.dart b/test/tokenizer_test.dart
index 41ae4d5..e5a05e9 100644
--- a/test/tokenizer_test.dart
+++ b/test/tokenizer_test.dart
@@ -27,14 +27,14 @@
 
   List parse(String str) {
     // Note: we need to pass bytes to the tokenizer if we want it to handle BOM.
-    var bytes = utf8.encode(str);
-    var tokenizer =
+    final bytes = utf8.encode(str);
+    final tokenizer =
         HtmlTokenizer(bytes, encoding: 'utf-8', generateSpans: _generateSpans);
     outputTokens = [];
 
     // Note: we can't get a closure of the state method. However, we can
     // create a new closure to invoke it via mirrors.
-    var mtok = reflect(tokenizer);
+    final mtok = reflect(tokenizer);
     tokenizer.state =
         () => mtok.invoke(Symbol(_state), const []).reflectee as bool;
 
@@ -43,7 +43,7 @@
     }
 
     while (tokenizer.moveNext()) {
-      var token = tokenizer.current;
+      final token = tokenizer.current;
       switch (token.kind) {
         case TokenKind.characters:
           processCharacters(token as CharactersToken);
@@ -117,7 +117,7 @@
 }
 
 List concatenateCharacterTokens(List tokens) {
-  var outputTokens = [];
+  final outputTokens = [];
   for (var token in tokens) {
     if (token.indexOf('ParseError') == -1 && token[0] == 'Character') {
       if (outputTokens.isNotEmpty &&
@@ -137,7 +137,7 @@
 List normalizeTokens(List tokens) {
   // TODO: convert tests to reflect arrays
   for (var i = 0; i < tokens.length; i++) {
-    var token = tokens[i];
+    final token = tokens[i];
     if (token[0] == 'ParseError') {
       tokens[i] = token[0];
     }
@@ -175,13 +175,15 @@
     expect(receivedTokens, equals(expectedTokens), reason: message);
   } else {
     // Sort the tokens into two groups; non-parse errors and parse errors
-    var expectedNonErrors = expectedTokens.where((t) => t != 'ParseError');
-    var receivedNonErrors = receivedTokens.where((t) => t != 'ParseError');
+    final expectedNonErrors = expectedTokens.where((t) => t != 'ParseError');
+    final receivedNonErrors = receivedTokens.where((t) => t != 'ParseError');
 
     expect(receivedNonErrors, equals(expectedNonErrors), reason: message);
     if (!ignoreErrors) {
-      var expectedParseErrors = expectedTokens.where((t) => t == 'ParseError');
-      var receivedParseErrors = receivedTokens.where((t) => t == 'ParseError');
+      final expectedParseErrors =
+          expectedTokens.where((t) => t == 'ParseError');
+      final receivedParseErrors =
+          receivedTokens.where((t) => t == 'ParseError');
       expect(receivedParseErrors, equals(expectedParseErrors), reason: message);
     }
   }
@@ -194,18 +196,18 @@
     testInfo = unescape(testInfo);
   }
 
-  var expected = concatenateCharacterTokens(testInfo['output'] as List);
+  final expected = concatenateCharacterTokens(testInfo['output'] as List);
   if (!testInfo.containsKey('lastStartTag')) {
     testInfo['lastStartTag'] = null;
   }
-  var parser = TokenizerTestParser(
+  final parser = TokenizerTestParser(
       testInfo['initialState'] as String,
       testInfo['lastStartTag'] as String,
       testInfo['generateSpans'] as bool /*?*/ ?? false);
   var tokens = parser.parse(testInfo['input'] as String);
   tokens = concatenateCharacterTokens(tokens);
-  var received = normalizeTokens(tokens);
-  var errorMsg = [
+  final received = normalizeTokens(tokens);
+  final errorMsg = [
     '\n\nInitial state:',
     testInfo['initialState'],
     '\nInput:',
@@ -215,7 +217,7 @@
     '\nreceived:',
     tokens
   ].map((s) => '$s').join('\n');
-  var ignoreErrorOrder = testInfo['ignoreErrorOrder'] as bool /*?*/ ?? false;
+  final ignoreErrorOrder = testInfo['ignoreErrorOrder'] as bool /*?*/ ?? false;
 
   expectTokensMatch(expected, received, ignoreErrorOrder, true, errorMsg);
 }
@@ -234,8 +236,8 @@
       token[1] = decode(token[1]);
       if ((token as List).length > 2) {
         for (var pair in token[2]) {
-          var key = pair[0];
-          var value = pair[1];
+          final key = pair[0];
+          final value = pair[1];
           token[2].remove(key);
           token[2][decode(key)] = decode(value);
         }
@@ -247,7 +249,7 @@
 
 String camelCase(String s) {
   s = s.toLowerCase();
-  var result = StringBuffer();
+  final result = StringBuffer();
   for (var match in RegExp(r'\W+(\w)(\w+)').allMatches(s)) {
     if (result.length == 0) result.write(s.substring(0, match.start));
     result.write(match.group(1).toUpperCase());
@@ -260,10 +262,10 @@
   for (var path in getDataFiles('tokenizer')) {
     if (!path.endsWith('.test')) continue;
 
-    var text = File(path).readAsStringSync();
-    var tests = jsonDecode(text);
-    var testName = pathos.basenameWithoutExtension(path);
-    var testList = tests['tests'] as List;
+    final text = File(path).readAsStringSync();
+    final tests = jsonDecode(text);
+    final testName = pathos.basenameWithoutExtension(path);
+    final testList = tests['tests'] as List;
     if (testList == null) continue;
 
     group(testName, () {