Enable and fix a number of lints
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..0711aca
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,43 @@
+include: package:pedantic/analysis_options.yaml
+analyzer:
+  strong-mode:
+    implicit-casts: false
+linter:
+  rules:
+    - avoid_empty_else
+    - avoid_init_to_null
+    - avoid_null_checks_in_equality_operators
+    - avoid_unused_constructor_parameters
+    - await_only_futures
+    - camel_case_types
+    - cancel_subscriptions
+    - constant_identifier_names
+    - control_flow_in_finally
+    - directives_ordering
+    - empty_catches
+    - empty_constructor_bodies
+    - empty_statements
+    - hash_and_equals
+    - implementation_imports
+    - iterable_contains_unrelated_type
+    - library_names
+    - library_prefixes
+    - list_remove_unrelated_type
+    - non_constant_identifier_names
+    - overridden_fields
+    - package_api_docs
+    - package_names
+    - package_prefixed_library_names
+    - prefer_equal_for_default_values
+    - prefer_final_fields
+    - prefer_generic_function_type_aliases
+    - prefer_is_not_empty
+    - slash_for_doc_comments
+    - test_types_in_equals
+    - throw_in_finally
+    - type_init_formals
+    - unnecessary_brace_in_string_interps
+    - unnecessary_const
+    - unnecessary_new
+    - unrelated_type_equality_checks
+    - valid_regexps
diff --git a/benchmark/benchmark.dart b/benchmark/benchmark.dart
index c342816..4fe479a 100644
--- a/benchmark/benchmark.dart
+++ b/benchmark/benchmark.dart
@@ -49,7 +49,7 @@
       });
     }
 
-    benchmarkPairs(String name, Function function) {
+    void benchmarkPairs(String name, void Function(String, String) function) {
       runBenchmark("${style.name}-$name", 1000, () {
         for (var file1 in files) {
           for (var file2 in files) {
@@ -72,7 +72,7 @@
     benchmark('relative', context.relative);
     benchmarkPairs('relative from', (String file, String from) {
       try {
-        return context.relative(file, from: from);
+        context.relative(file, from: from);
       } on p.PathException {
         // Do nothing.
       }
diff --git a/lib/src/characters.dart b/lib/src/characters.dart
index 7dddb2f..a083543 100644
--- a/lib/src/characters.dart
+++ b/lib/src/characters.dart
@@ -3,15 +3,15 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// This library contains character-code definitions.
-const PLUS = 0x2b;
-const MINUS = 0x2d;
-const PERIOD = 0x2e;
-const SLASH = 0x2f;
-const ZERO = 0x30;
-const NINE = 0x39;
-const COLON = 0x3a;
-const UPPER_A = 0x41;
-const UPPER_Z = 0x5a;
-const LOWER_A = 0x61;
-const LOWER_Z = 0x7a;
-const BACKSLASH = 0x5c;
+const plus = 0x2b;
+const minus = 0x2d;
+const period = 0x2e;
+const slash = 0x2f;
+const zero = 0x30;
+const nine = 0x39;
+const colon = 0x3a;
+const upperA = 0x41;
+const upperZ = 0x5a;
+const lowerA = 0x61;
+const lowerZ = 0x7a;
+const backslash = 0x5c;
diff --git a/lib/src/context.dart b/lib/src/context.dart
index dc507ff..30db74e 100644
--- a/lib/src/context.dart
+++ b/lib/src/context.dart
@@ -4,12 +4,12 @@
 
 import 'dart:math' as math;
 
+import '../path.dart' as p;
 import 'characters.dart' as chars;
 import 'internal_style.dart';
-import 'style.dart';
 import 'parsed_path.dart';
 import 'path_exception.dart';
-import '../path.dart' as p;
+import 'style.dart';
 
 Context createInternal() => Context._internal();
 
@@ -270,7 +270,7 @@
         buffer.clear();
         buffer.write(part);
       } else {
-        if (part.length > 0 && style.containsSeparator(part[0])) {
+        if (part.isNotEmpty && style.containsSeparator(part[0])) {
           // The part starts with a separator, so we don't need to add one.
         } else if (needsSeparator) {
           buffer.write(separator);
@@ -308,7 +308,7 @@
   List<String> split(String path) {
     var parsed = _parse(path);
     // Filter out empty parts that exist due to multiple separators in a row.
-    parsed.parts = parsed.parts.where((part) => !part.isEmpty).toList();
+    parsed.parts = parsed.parts.where((part) => part.isNotEmpty).toList();
     if (parsed.root != null) parsed.parts.insert(0, parsed.root);
     return parsed.parts;
   }
@@ -363,13 +363,13 @@
     var root = style.rootLength(path);
     if (root != 0) {
       start = root;
-      previous = chars.SLASH;
+      previous = chars.slash;
 
       // On Windows, the root still needs to be normalized if it contains a
       // forward slash.
       if (style == Style.windows) {
         for (var i = 0; i < root; i++) {
-          if (codeUnits[i] == chars.SLASH) return true;
+          if (codeUnits[i] == chars.slash) return true;
         }
       }
     }
@@ -378,7 +378,7 @@
       var codeUnit = codeUnits[i];
       if (style.isSeparator(codeUnit)) {
         // Forward slashes in Windows paths are normalized to backslashes.
-        if (style == Style.windows && codeUnit == chars.SLASH) return true;
+        if (style == Style.windows && codeUnit == chars.slash) return true;
 
         // Multiple separators are normalized to single separators.
         if (previous != null && style.isSeparator(previous)) return true;
@@ -387,9 +387,9 @@
         //
         // This can return false positives for ".../", but that's unlikely
         // enough that it's probably not going to cause performance issues.
-        if (previous == chars.PERIOD &&
+        if (previous == chars.period &&
             (previousPrevious == null ||
-                previousPrevious == chars.PERIOD ||
+                previousPrevious == chars.period ||
                 style.isSeparator(previousPrevious))) {
           return true;
         }
@@ -406,10 +406,10 @@
     if (style.isSeparator(previous)) return true;
 
     // Single dots and double dots are normalized to directory traversals.
-    if (previous == chars.PERIOD &&
+    if (previous == chars.period &&
         (previousPrevious == null ||
             style.isSeparator(previousPrevious) ||
-            previousPrevious == chars.PERIOD)) {
+            previousPrevious == chars.period)) {
       return true;
     }
 
@@ -474,7 +474,7 @@
     var fromParsed = _parse(from)..normalize();
     var pathParsed = _parse(path)..normalize();
 
-    if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') {
+    if (fromParsed.parts.isNotEmpty && fromParsed.parts[0] == '.') {
       return pathParsed.toString();
     }
 
@@ -489,8 +489,8 @@
     }
 
     // Strip off their common prefix.
-    while (fromParsed.parts.length > 0 &&
-        pathParsed.parts.length > 0 &&
+    while (fromParsed.parts.isNotEmpty &&
+        pathParsed.parts.isNotEmpty &&
         style.pathsEqual(fromParsed.parts[0], pathParsed.parts[0])) {
       fromParsed.parts.removeAt(0);
       fromParsed.separators.removeAt(1);
@@ -501,7 +501,7 @@
     // If there are any directories left in the from path, we need to walk up
     // out of them. If a directory left in the from path is '..', it cannot
     // be cancelled by adding a '..'.
-    if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') {
+    if (fromParsed.parts.isNotEmpty && fromParsed.parts[0] == '..') {
       throw PathException('Unable to find a path to "$path" from "$from".');
     }
     pathParsed.parts.insertAll(0, List.filled(fromParsed.parts.length, '..'));
@@ -510,7 +510,7 @@
         .insertAll(1, List.filled(fromParsed.parts.length, style.separator));
 
     // Corner case: the paths completely collapsed.
-    if (pathParsed.parts.length == 0) return '.';
+    if (pathParsed.parts.isEmpty) return '.';
 
     // Corner case: path was '.' and some '..' directories were added in front.
     // Don't add a final '/.' in that case.
@@ -628,7 +628,7 @@
     // Start by considering the last code unit as a separator, since
     // semantically we're starting at a new path component even if we're
     // comparing relative paths.
-    var lastCodeUnit = chars.SLASH;
+    var lastCodeUnit = chars.slash;
 
     /// The index of the last separator in [parent].
     int lastParentSeparator;
@@ -668,7 +668,7 @@
       //
       //     isWithin("foo/./bar", "foo/bar/baz") //=> true
       //     isWithin("foo/bar/../baz", "foo/bar/.foo") //=> false
-      if (parentCodeUnit == chars.PERIOD && style.isSeparator(lastCodeUnit)) {
+      if (parentCodeUnit == chars.period && style.isSeparator(lastCodeUnit)) {
         parentIndex++;
 
         // We've hit "/." at the end of the parent path, which we can ignore,
@@ -685,7 +685,7 @@
 
         // We've hit "/..", which may be a directory traversal operator that
         // we can't handle on the fast track.
-        if (parentCodeUnit == chars.PERIOD) {
+        if (parentCodeUnit == chars.period) {
           parentIndex++;
           if (parentIndex == parent.length ||
               style.isSeparator(parent.codeUnitAt(parentIndex))) {
@@ -699,7 +699,7 @@
 
       // This is the same logic as above, but for the child path instead of the
       // parent.
-      if (childCodeUnit == chars.PERIOD && style.isSeparator(lastCodeUnit)) {
+      if (childCodeUnit == chars.period && style.isSeparator(lastCodeUnit)) {
         childIndex++;
         if (childIndex == child.length) break;
         childCodeUnit = child.codeUnitAt(childIndex);
@@ -709,7 +709,7 @@
           continue;
         }
 
-        if (childCodeUnit == chars.PERIOD) {
+        if (childCodeUnit == chars.period) {
           childIndex++;
           if (childIndex == child.length ||
               style.isSeparator(child.codeUnitAt(childIndex))) {
@@ -826,11 +826,11 @@
       }
 
       // See if the path component is ".", "..", or a name.
-      if (i - start == 1 && path.codeUnitAt(start) == chars.PERIOD) {
+      if (i - start == 1 && path.codeUnitAt(start) == chars.period) {
         // Don't change the depth.
       } else if (i - start == 2 &&
-          path.codeUnitAt(start) == chars.PERIOD &&
-          path.codeUnitAt(start + 1) == chars.PERIOD) {
+          path.codeUnitAt(start) == chars.period &&
+          path.codeUnitAt(start + 1) == chars.period) {
         // ".." backs out a directory.
         depth--;
 
@@ -894,7 +894,7 @@
         continue;
       }
 
-      if (codeUnit == chars.PERIOD && wasSeparator) {
+      if (codeUnit == chars.period && wasSeparator) {
         // If a dot comes after a separator, it may be a directory traversal
         // operator. To check that, we need to know if it's followed by either
         // "/" or "./". Otherwise, it's just a normal character.
@@ -915,7 +915,7 @@
         // at the beginning of the path, since those may appear even in a
         // canonicalized path.
         if (!beginning &&
-            next == chars.PERIOD &&
+            next == chars.period &&
             (i + 2 == path.length ||
                 style.isSeparator(path.codeUnitAt(i + 2)))) {
           return null;
@@ -939,7 +939,7 @@
     var parsed = _parse(path);
 
     for (var i = parsed.parts.length - 1; i >= 0; i--) {
-      if (!parsed.parts[i].isEmpty) {
+      if (parsed.parts[i].isNotEmpty) {
         parsed.parts[i] = parsed.basenameWithoutExtension;
         break;
       }
diff --git a/lib/src/parsed_path.dart b/lib/src/parsed_path.dart
index 6984591..e4b7feb 100644
--- a/lib/src/parsed_path.dart
+++ b/lib/src/parsed_path.dart
@@ -87,14 +87,14 @@
   String get basenameWithoutExtension => _splitExtension()[0];
 
   bool get hasTrailingSeparator =>
-      !parts.isEmpty && (parts.last == '' || separators.last != '');
+      parts.isNotEmpty && (parts.last == '' || separators.last != '');
 
   void removeTrailingSeparators() {
-    while (!parts.isEmpty && parts.last == '') {
+    while (parts.isNotEmpty && parts.last == '') {
       parts.removeLast();
       separators.removeLast();
     }
-    if (separators.length > 0) separators[separators.length - 1] = '';
+    if (separators.isNotEmpty) separators[separators.length - 1] = '';
   }
 
   void normalize({bool canonicalize = false}) {
@@ -106,7 +106,7 @@
         // Do nothing. Ignore it.
       } else if (part == '..') {
         // Pop the last part off.
-        if (newParts.length > 0) {
+        if (newParts.isNotEmpty) {
           newParts.removeLast();
         } else {
           // Backed out past the beginning, so preserve the "..".
@@ -123,7 +123,7 @@
     }
 
     // If we collapsed down to nothing, do ".".
-    if (newParts.length == 0 && !isAbsolute) {
+    if (newParts.isEmpty && !isAbsolute) {
       newParts.add('.');
     }
 
@@ -133,7 +133,7 @@
         growable: true);
     newSeparators.insert(
         0,
-        isAbsolute && newParts.length > 0 && style.needsSeparator(root)
+        isAbsolute && newParts.isNotEmpty && style.needsSeparator(root)
             ? style.separator
             : '');
 
diff --git a/lib/src/style/posix.dart b/lib/src/style/posix.dart
index a2388b4..e673613 100644
--- a/lib/src/style/posix.dart
+++ b/lib/src/style/posix.dart
@@ -3,8 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import '../characters.dart' as chars;
-import '../parsed_path.dart';
 import '../internal_style.dart';
+import '../parsed_path.dart';
 
 /// The style for POSIX paths.
 class PosixStyle extends InternalStyle {
@@ -23,7 +23,7 @@
 
   bool containsSeparator(String path) => path.contains('/');
 
-  bool isSeparator(int codeUnit) => codeUnit == chars.SLASH;
+  bool isSeparator(int codeUnit) => codeUnit == chars.slash;
 
   bool needsSeparator(String path) =>
       path.isNotEmpty && !isSeparator(path.codeUnitAt(path.length - 1));
diff --git a/lib/src/style/url.dart b/lib/src/style/url.dart
index 3b12d0e..a189916 100644
--- a/lib/src/style/url.dart
+++ b/lib/src/style/url.dart
@@ -23,7 +23,7 @@
 
   bool containsSeparator(String path) => path.contains('/');
 
-  bool isSeparator(int codeUnit) => codeUnit == chars.SLASH;
+  bool isSeparator(int codeUnit) => codeUnit == chars.slash;
 
   bool needsSeparator(String path) {
     if (path.isEmpty) return false;
@@ -43,7 +43,7 @@
     for (var i = 0; i < path.length; i++) {
       var codeUnit = path.codeUnitAt(i);
       if (isSeparator(codeUnit)) return 0;
-      if (codeUnit == chars.COLON) {
+      if (codeUnit == chars.colon) {
         if (i == 0) return 0;
 
         // The root part is up until the next '/', or the full path. Skip ':'
diff --git a/lib/src/style/windows.dart b/lib/src/style/windows.dart
index 59039b8..37c501f 100644
--- a/lib/src/style/windows.dart
+++ b/lib/src/style/windows.dart
@@ -29,7 +29,7 @@
   bool containsSeparator(String path) => path.contains('/');
 
   bool isSeparator(int codeUnit) =>
-      codeUnit == chars.SLASH || codeUnit == chars.BACKSLASH;
+      codeUnit == chars.slash || codeUnit == chars.backslash;
 
   bool needsSeparator(String path) {
     if (path.isEmpty) return false;
@@ -38,9 +38,9 @@
 
   int rootLength(String path, {bool withDrive = false}) {
     if (path.isEmpty) return 0;
-    if (path.codeUnitAt(0) == chars.SLASH) return 1;
-    if (path.codeUnitAt(0) == chars.BACKSLASH) {
-      if (path.length < 2 || path.codeUnitAt(1) != chars.BACKSLASH) return 1;
+    if (path.codeUnitAt(0) == chars.slash) return 1;
+    if (path.codeUnitAt(0) == chars.backslash) {
+      if (path.length < 2 || path.codeUnitAt(1) != chars.backslash) return 1;
       // The path is a network share. Search for up to two '\'s, as they are
       // the server and share - and part of the root part.
       var index = path.indexOf('\\', 2);
@@ -56,7 +56,7 @@
     // Check for the letter.
     if (!isAlphabetic(path.codeUnitAt(0))) return 0;
     // Check for the ':'.
-    if (path.codeUnitAt(1) != chars.COLON) return 0;
+    if (path.codeUnitAt(1) != chars.colon) return 0;
     // Check for either '/' or '\'.
     if (!isSeparator(path.codeUnitAt(2))) return 0;
     return 3;
@@ -115,7 +115,7 @@
       // be empty. We add an empty component so the URL constructor produces
       // "file:///C:/", with a trailing slash. We also add an empty component if
       // the URL otherwise has a trailing slash.
-      if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) {
+      if (parsed.parts.isEmpty || parsed.hasTrailingSeparator) {
         parsed.parts.add("");
       }
 
@@ -132,8 +132,8 @@
     if (codeUnit1 == codeUnit2) return true;
 
     /// Forward slashes and backslashes are equivalent on Windows.
-    if (codeUnit1 == chars.SLASH) return codeUnit2 == chars.BACKSLASH;
-    if (codeUnit1 == chars.BACKSLASH) return codeUnit2 == chars.SLASH;
+    if (codeUnit1 == chars.slash) return codeUnit2 == chars.backslash;
+    if (codeUnit1 == chars.backslash) return codeUnit2 == chars.slash;
 
     // If this check fails, the code units are definitely different. If it
     // succeeds *and* either codeUnit is an ASCII letter, they're equivalent.
@@ -141,7 +141,7 @@
 
     // Now we just need to verify that one of the code units is an ASCII letter.
     var upperCase1 = codeUnit1 | _asciiCaseBit;
-    return upperCase1 >= chars.LOWER_A && upperCase1 <= chars.LOWER_Z;
+    return upperCase1 >= chars.lowerA && upperCase1 <= chars.lowerZ;
   }
 
   bool pathsEqual(String path1, String path2) {
@@ -156,9 +156,9 @@
   }
 
   int canonicalizeCodeUnit(int codeUnit) {
-    if (codeUnit == chars.SLASH) return chars.BACKSLASH;
-    if (codeUnit < chars.UPPER_A) return codeUnit;
-    if (codeUnit > chars.UPPER_Z) return codeUnit;
+    if (codeUnit == chars.slash) return chars.backslash;
+    if (codeUnit < chars.upperA) return codeUnit;
+    if (codeUnit > chars.upperZ) return codeUnit;
     return codeUnit | _asciiCaseBit;
   }
 
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 561ff63..2443ccd 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -7,18 +7,18 @@
 /// Returns whether [char] is the code for an ASCII letter (uppercase or
 /// lowercase).
 bool isAlphabetic(int char) =>
-    (char >= chars.UPPER_A && char <= chars.UPPER_Z) ||
-    (char >= chars.LOWER_A && char <= chars.LOWER_Z);
+    (char >= chars.upperA && char <= chars.upperZ) ||
+    (char >= chars.lowerA && char <= chars.lowerZ);
 
 /// Returns whether [char] is the code for an ASCII digit.
-bool isNumeric(int char) => char >= chars.ZERO && char <= chars.NINE;
+bool isNumeric(int char) => char >= chars.zero && char <= chars.nine;
 
 /// Returns whether [path] has a URL-formatted Windows drive letter beginning at
 /// [index].
 bool isDriveLetter(String path, int index) {
   if (path.length < index + 2) return false;
   if (!isAlphabetic(path.codeUnitAt(index))) return false;
-  if (path.codeUnitAt(index + 1) != chars.COLON) return false;
+  if (path.codeUnitAt(index + 1) != chars.colon) return false;
   if (path.length == index + 2) return true;
-  return path.codeUnitAt(index + 2) == chars.SLASH;
+  return path.codeUnitAt(index + 2) == chars.slash;
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index 5a33360..1b5426d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -12,4 +12,5 @@
  sdk: '>=2.0.0 <3.0.0'
 
 dev_dependencies:
+  pedantic: ^1.0.0
   test: '>=0.12.42 <2.0.0'
diff --git a/test/path_test.dart b/test/path_test.dart
index 8f5de03..1681159 100644
--- a/test/path_test.dart
+++ b/test/path_test.dart
@@ -13,9 +13,9 @@
     });
 
     test('separator', () {
-      // ignore: deprecated_member_use
+      // ignore: deprecated_member_use_from_same_package
       expect(path.Style.posix.separator, '/');
-      // ignore: deprecated_member_use
+      // ignore: deprecated_member_use_from_same_package
       expect(path.Style.windows.separator, '\\');
     });
 
diff --git a/test/relative_test.dart b/test/relative_test.dart
index ffc4454..8da7eac 100644
--- a/test/relative_test.dart
+++ b/test/relative_test.dart
@@ -28,7 +28,7 @@
 void relativeTest(path.Context context, String prefix) {
   var isRelative = (context.current == '.');
   // Cases where the arguments are absolute paths.
-  expectRelative(result, pathArg, fromArg) {
+  void expectRelative(String result, String pathArg, String fromArg) {
     expect(context.relative(pathArg, from: fromArg), context.normalize(result));
   }