diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 430a85e..3f9e337 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -7,5 +7,4 @@
 - package-ecosystem: "github-actions"
   directory: "/"
   schedule:
-    # Check for updates to GitHub Actions every weekday
-    interval: "daily"
+    interval: "monthly"
diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml
index 62bd4b7..579ab68 100644
--- a/.github/workflows/dart.yml
+++ b/.github/workflows/dart.yml
@@ -1,4 +1,4 @@
-# Created with package:mono_repo v5.0.1
+# Created with package:mono_repo v6.2.0
 name: Dart CI
 on:
   push:
@@ -19,20 +19,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:stable"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:stable"
           restore-keys: |
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: stable
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - name: mono_repo self validate
-        run: dart pub global activate mono_repo 5.0.1
+        run: dart pub global activate mono_repo 6.2.0
       - name: mono_repo self validate
         run: dart pub global run mono_repo generate --validate
   job_002:
@@ -40,20 +40,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:2.12.0;packages:protobuf;commands:analyze_1-analyze_2"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:protobuf;commands:analyze_1-analyze_2"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:2.12.0;packages:protobuf
-            os:ubuntu-latest;pub-cache-hosted;dart:2.12.0
+            os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:protobuf
+            os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: "2.12.0"
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -72,20 +72,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:api_benchmark-query_benchmark;commands:format-command_0-command_1-analyze_0"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:api_benchmark-query_benchmark;commands:format-command_0-command_1-analyze_0"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:api_benchmark-query_benchmark
-            os:ubuntu-latest;pub-cache-hosted;dart:dev
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:api_benchmark-query_benchmark
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: api_benchmark_pub_upgrade
         name: api_benchmark; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -133,20 +133,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protobuf;commands:format-analyze_0"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protobuf;commands:format-analyze_0"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protobuf
-            os:ubuntu-latest;pub-cache-hosted;dart:dev
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protobuf
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -165,20 +165,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protoc_plugin;commands:format-command_0-command_2-analyze_0"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protoc_plugin;commands:format-command_0-command_2-analyze_0"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protoc_plugin
-            os:ubuntu-latest;pub-cache-hosted;dart:dev
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protoc_plugin
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protoc_plugin_pub_upgrade
         name: protoc_plugin; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -205,20 +205,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:2.12.0;packages:protobuf;commands:test"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:protobuf;commands:test"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:2.12.0;packages:protobuf
-            os:ubuntu-latest;pub-cache-hosted;dart:2.12.0
+            os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:protobuf
+            os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: "2.12.0"
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -239,20 +239,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:2.12.0;packages:protoc_plugin;commands:command_0-command_2-test"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:protoc_plugin;commands:command_0-command_2-test"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:2.12.0;packages:protoc_plugin
-            os:ubuntu-latest;pub-cache-hosted;dart:2.12.0
+            os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0;packages:protoc_plugin
+            os:ubuntu-latest;pub-cache-hosted;sdk:2.12.0
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: "2.12.0"
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protoc_plugin_pub_upgrade
         name: protoc_plugin; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -281,20 +281,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protobuf;commands:test"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protobuf;commands:test"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protobuf
-            os:ubuntu-latest;pub-cache-hosted;dart:dev
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protobuf
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -315,20 +315,20 @@
     runs-on: ubuntu-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protoc_plugin;commands:command_0-command_2-test"
+          key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protoc_plugin;commands:command_0-command_2-test"
           restore-keys: |
-            os:ubuntu-latest;pub-cache-hosted;dart:dev;packages:protoc_plugin
-            os:ubuntu-latest;pub-cache-hosted;dart:dev
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:protoc_plugin
+            os:ubuntu-latest;pub-cache-hosted;sdk:dev
             os:ubuntu-latest;pub-cache-hosted
             os:ubuntu-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protoc_plugin_pub_upgrade
         name: protoc_plugin; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -357,20 +357,20 @@
     runs-on: macos-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:macos-latest;pub-cache-hosted;dart:2.12.0;packages:protobuf;commands:test"
+          key: "os:macos-latest;pub-cache-hosted;sdk:2.12.0;packages:protobuf;commands:test"
           restore-keys: |
-            os:macos-latest;pub-cache-hosted;dart:2.12.0;packages:protobuf
-            os:macos-latest;pub-cache-hosted;dart:2.12.0
+            os:macos-latest;pub-cache-hosted;sdk:2.12.0;packages:protobuf
+            os:macos-latest;pub-cache-hosted;sdk:2.12.0
             os:macos-latest;pub-cache-hosted
             os:macos-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: "2.12.0"
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -391,20 +391,20 @@
     runs-on: macos-latest
     steps:
       - name: Cache Pub hosted dependencies
-        uses: actions/cache@v2.1.6
+        uses: actions/cache@v3
         with:
           path: "~/.pub-cache/hosted"
-          key: "os:macos-latest;pub-cache-hosted;dart:dev;packages:protobuf;commands:test"
+          key: "os:macos-latest;pub-cache-hosted;sdk:dev;packages:protobuf;commands:test"
           restore-keys: |
-            os:macos-latest;pub-cache-hosted;dart:dev;packages:protobuf
-            os:macos-latest;pub-cache-hosted;dart:dev
+            os:macos-latest;pub-cache-hosted;sdk:dev;packages:protobuf
+            os:macos-latest;pub-cache-hosted;sdk:dev
             os:macos-latest;pub-cache-hosted
             os:macos-latest
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -424,11 +424,11 @@
     name: "run_tests; windows; Dart 2.12.0; PKG: protobuf; `dart test`"
     runs-on: windows-latest
     steps:
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: "2.12.0"
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
@@ -448,11 +448,11 @@
     name: "run_tests; windows; Dart dev; PKG: protobuf; `dart test`"
     runs-on: windows-latest
     steps:
-      - uses: dart-lang/setup-dart@v1.1
+      - uses: dart-lang/setup-dart@v1.3
         with:
           sdk: dev
       - id: checkout
-        uses: actions/checkout@v2.3.4
+        uses: actions/checkout@v3.0.0
       - id: protobuf_pub_upgrade
         name: protobuf; dart pub upgrade
         if: "always() && steps.checkout.conclusion == 'success'"
diff --git a/analysis_options.yaml b/analysis_options.yaml
deleted file mode 100644
index 6e1ff09..0000000
--- a/analysis_options.yaml
+++ /dev/null
@@ -1,79 +0,0 @@
-linter:
-  rules:
-  - always_require_non_null_named_parameters
-  - annotate_overrides
-  - avoid_empty_else
-  - avoid_function_literals_in_foreach_calls
-  - avoid_init_to_null
-  - avoid_null_checks_in_equality_operators
-  - avoid_relative_lib_imports
-  - avoid_renaming_method_parameters
-  - avoid_return_types_on_setters
-  - avoid_returning_null_for_void
-  - avoid_shadowing_type_parameters
-  - avoid_single_cascade_in_expression_statements
-  - avoid_types_as_parameter_names
-  - await_only_futures
-  - camel_case_extensions
-  - camel_case_types
-  - constant_identifier_names
-  - control_flow_in_finally
-  - curly_braces_in_flow_control_structures
-  - directives_ordering
-  - empty_catches
-  - empty_constructor_bodies
-  - empty_statements
-  - exhaustive_cases
-  - file_names
-  - hash_and_equals
-  - implementation_imports
-  - iterable_contains_unrelated_type
-  - library_names
-  - library_prefixes
-  - list_remove_unrelated_type
-  - no_duplicate_case_values
-  - non_constant_identifier_names
-  - null_closures
-  - overridden_fields
-  - package_names
-  - package_prefixed_library_names
-  - prefer_adjacent_string_concatenation
-  - prefer_collection_literals
-  - prefer_conditional_assignment
-  - prefer_contains
-  - prefer_equal_for_default_values
-  - prefer_final_fields
-  - prefer_for_elements_to_map_fromIterable
-  - prefer_function_declarations_over_variables
-  - prefer_generic_function_type_aliases
-  - prefer_if_null_operators
-  - prefer_initializing_formals
-  - prefer_inlined_adds
-  - prefer_is_empty
-  - prefer_is_not_empty
-  - prefer_is_not_operator
-  - prefer_iterable_whereType
-  - prefer_null_aware_operators
-  - prefer_relative_imports
-  - prefer_single_quotes
-  - prefer_spread_collections
-  - prefer_typing_uninitialized_variables
-  - prefer_void_to_null
-  - provide_deprecation_message
-  - recursive_getters
-  - slash_for_doc_comments
-  - type_init_formals
-  - unnecessary_brace_in_string_interps
-  - unnecessary_const
-  - unnecessary_getters_setters
-  - unnecessary_new
-  - unnecessary_null_in_if_null_operators
-  - unnecessary_overrides
-  - unnecessary_string_escapes
-  - unnecessary_string_interpolations
-  - unnecessary_this
-  - unrelated_type_equality_checks
-  - use_function_type_syntax_for_parameters
-  - use_rethrow_when_possible
-  - valid_regexps
-  - void_checks
diff --git a/api_benchmark/analysis_options.yaml b/api_benchmark/analysis_options.yaml
new file mode 100644
index 0000000..e841247
--- /dev/null
+++ b/api_benchmark/analysis_options.yaml
@@ -0,0 +1,9 @@
+include: package:lints/recommended.yaml
+
+linter:
+  rules:
+    - comment_references
+    - directives_ordering
+    - prefer_relative_imports
+    - prefer_single_quotes
+    - prefer_spread_collections
diff --git a/api_benchmark/mono_pkg.yaml b/api_benchmark/mono_pkg.yaml
index e822136..b23af8b 100644
--- a/api_benchmark/mono_pkg.yaml
+++ b/api_benchmark/mono_pkg.yaml
@@ -7,4 +7,4 @@
       - command: ./../tool/setup.sh
       - command: ./compile_protos.sh
       - analyze: --fatal-infos
-      dart: dev
+      sdk: dev
diff --git a/api_benchmark/pubspec.yaml b/api_benchmark/pubspec.yaml
index 4c59173..e5e6601 100644
--- a/api_benchmark/pubspec.yaml
+++ b/api_benchmark/pubspec.yaml
@@ -5,17 +5,21 @@
 name: api_benchmark
 description: Benchmarking a number of different api calls.
 
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
+
 environment:
   sdk: '>=2.11.99 <3.0.0'
 
 dependencies:
-  protobuf:
+  protobuf: any
 
 dev_dependencies:
   build: ^2.0.0
   build_runner: ^2.0.6
   build_web_compilers: ^3.0.0
   glob: ^2.0.0
+  lints: ^1.0.0
   yaml: ^3.0.0
   protoc_plugin:
     path: "../protoc_plugin"
diff --git a/protobuf/CHANGELOG.md b/protobuf/CHANGELOG.md
index 77c583c..9debaf7 100644
--- a/protobuf/CHANGELOG.md
+++ b/protobuf/CHANGELOG.md
@@ -1,5 +1,6 @@
-## 2.0.1-dev
+## 2.0.1
 
+* Fix bug of parsing map-values with default values.
 * Merge fixes from version `1.1.2` - `1.1.4` into v2.
 
 ## 2.0.0
@@ -293,7 +294,7 @@
 
 ## 0.9.0+1
 
-*   Dart SDK upper constraint raised to declare compatability with Dart 2.0
+*   Dart SDK upper constraint raised to declare compatibility with Dart 2.0
     stable.
 
 ## 0.9.0
diff --git a/protobuf/LICENSE b/protobuf/LICENSE
index ab3bfa0..7efe25f 100644
--- a/protobuf/LICENSE
+++ b/protobuf/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2013, the Dart project authors. 
+Copyright 2013, the Dart project authors.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
diff --git a/protobuf/README.md b/protobuf/README.md
index 25c67cd..0bba861 100644
--- a/protobuf/README.md
+++ b/protobuf/README.md
@@ -1,17 +1,17 @@
 [![pub package](https://img.shields.io/pub/v/protobuf.svg)](https://pub.dev/packages/protobuf)
 
-Provides runtime support for a Dart implementation of protobufs.
+Provides runtime support for a Dart implementation of [protobufs][1].
 
-Typically one do not need to import this library–only libraries
-generated by the protoc plugin import this library directly.
+Typically one does not need to import this library. The [protoc plugin][2]
+compiles proto definitions to Dart files that import this library. If your
+library or application uses generated protobuf libraries, add this library as a
+dependency.
 
-If your library or application uses generated protobuf libraries, add this
-library as a dependency.
+[1]: https://developers.google.com/protocol-buffers
+[2]: https://pub.dev/packages/protoc_plugin
 
 ### References
 
-* [Dart tutorial](https://developers.google.com/protocol-buffers/docs/darttutorial)
-* [Dart generated code guide](https://developers.google.com/protocol-buffers/docs/reference/dart-generated)
-* [Dart API](https://pub.dev/documentation/protobuf/latest/)
-* [Protobuf repository](https://github.com/google/protobuf)
-* [Protoc plugin project](https://github.com/google/protobuf.dart/tree/master/protoc_plugin)
+* [Protobuf Dart tutorial](https://developers.google.com/protocol-buffers/docs/darttutorial)
+* [Guide to generated Dart code](https://developers.google.com/protocol-buffers/docs/reference/dart-generated)
+* [Dart API](https://pub.dev/documentation/protobuf/latest/protobuf/protobuf-library.html)
diff --git a/protobuf/analysis_options.yaml b/protobuf/analysis_options.yaml
index 56e6618..e841247 100644
--- a/protobuf/analysis_options.yaml
+++ b/protobuf/analysis_options.yaml
@@ -2,7 +2,8 @@
 
 linter:
   rules:
-    directives_ordering: true
-    prefer_relative_imports: true
-    prefer_single_quotes: true
-    prefer_spread_collections: false
+    - comment_references
+    - directives_ordering
+    - prefer_relative_imports
+    - prefer_single_quotes
+    - prefer_spread_collections
diff --git a/protobuf/benchmarks/README.md b/protobuf/benchmarks/README.md
index df5d8ab..6a0824e 100644
--- a/protobuf/benchmarks/README.md
+++ b/protobuf/benchmarks/README.md
@@ -3,8 +3,8 @@
 This directory contains protobuf benchmarks adapted from [original protobuf](
 https://github.com/google/protobuf/tree/master/benchmarks) library.
 
-These benchmarks cover parsing and serializing protobufs both to
-binary and JSON formats.
+These benchmarks cover parsing and serializing protobufs both to binary and
+JSON formats, and hash code generation for messages.
 
 ## Prerequisites
 
diff --git a/protobuf/benchmarks/benchmark_js.dart b/protobuf/benchmarks/benchmark_js.dart
index 0d32987..63c08c5 100644
--- a/protobuf/benchmarks/benchmark_js.dart
+++ b/protobuf/benchmarks/benchmark_js.dart
@@ -26,4 +26,5 @@
   ToBinaryBenchmark(datasets).report();
   ToJsonBenchmark(datasets).report();
   FromJsonBenchmark(datasets).report();
+  HashCodeBenchmark(datasets).report();
 }
diff --git a/protobuf/benchmarks/benchmark_vm.dart b/protobuf/benchmarks/benchmark_vm.dart
index 2b28bc4..77306f8 100644
--- a/protobuf/benchmarks/benchmark_vm.dart
+++ b/protobuf/benchmarks/benchmark_vm.dart
@@ -24,4 +24,5 @@
   ToBinaryBenchmark(datasets).report();
   ToJsonBenchmark(datasets).report();
   FromJsonBenchmark(datasets).report();
+  HashCodeBenchmark(datasets).report();
 }
diff --git a/protobuf/benchmarks/common.dart b/protobuf/benchmarks/common.dart
index dbdfe1e..dbccf9d 100644
--- a/protobuf/benchmarks/common.dart
+++ b/protobuf/benchmarks/common.dart
@@ -42,7 +42,7 @@
   /// Messages deserialized from [packed]. Used in serialization benchmarks.
   final List<GeneratedMessage> unpacked = <GeneratedMessage>[];
 
-  /// Create [Dataset] from a [BenchmarkDataset] proto.
+  /// Create [Dataset] from a `BenchmarkDataset` proto.
   factory Dataset.fromBinary(List<int> binary) {
     final dataSet = BenchmarkDataset.fromBuffer(binary);
 
@@ -73,7 +73,7 @@
   static Factories forMessage(String name) =>
       _factories[name] ?? (throw 'Unsupported message: $name');
 
-  /// Mapping between [BenchmarkProto.messageName] and corresponding
+  /// Mapping between `BenchmarkDataset.messageName` and corresponding
   /// deserialization factories.
   static final _factories = {
     'benchmarks.proto2.GoogleMessage1': Factories._(
@@ -164,3 +164,17 @@
     }
   }
 }
+
+/// HashCode computation benchmark.
+class HashCodeBenchmark extends _ProtobufBenchmark {
+  HashCodeBenchmark(datasets) : super(datasets, 'HashCode');
+
+  @override
+  void run() {
+    for (final dataset in datasets) {
+      for (final unpacked in dataset.unpacked) {
+        unpacked.hashCode;
+      }
+    }
+  }
+}
diff --git a/protobuf/benchmarks/compile-js.sh b/protobuf/benchmarks/compile-js.sh
index 0a93550..a232416 100755
--- a/protobuf/benchmarks/compile-js.sh
+++ b/protobuf/benchmarks/compile-js.sh
@@ -10,7 +10,7 @@
 
 mkdir -p "${OUTPUT_DIR}"
 
-dart2js --trust-type-annotations        \
+dart2js --omit-implicit-checks          \
         --trust-primitives              \
         -o "${OUTPUT_DIR}"/benchmark.js \
         "${SCRIPT_DIR}"/benchmark_js.dart
diff --git a/protobuf/benchmarks/compile-protos.sh b/protobuf/benchmarks/compile-protos.sh
index b01cb47..46dafd6 100755
--- a/protobuf/benchmarks/compile-protos.sh
+++ b/protobuf/benchmarks/compile-protos.sh
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 # Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
@@ -30,8 +30,6 @@
 
 mkdir -p ${OUTPUT_DIR}
 
-for proto in ${PROTOS[@]}
-do
-  echo "PROTOC ${proto}"
-  protoc -I"${SCRIPT_DIR}" --dart_out="${OUTPUT_DIR}" "${SCRIPT_DIR}/$proto"
-done
+set -x
+
+protoc -I"${SCRIPT_DIR}" --dart_out="${OUTPUT_DIR}" "${PROTOS[@]/#/$SCRIPT_DIR/}"
diff --git a/protobuf/benchmarks/d8.dart b/protobuf/benchmarks/d8.dart
index d709383..f55ef70 100644
--- a/protobuf/benchmarks/d8.dart
+++ b/protobuf/benchmarks/d8.dart
@@ -11,11 +11,11 @@
 import 'package:js/js.dart';
 
 /// Read the file at the given [path] and return its contents in
-/// an [ArrayBuffer].
+/// an [ByteBuffer].
 @JS()
 external ByteBuffer readbuffer(String path);
 
-/// Read the file at the given [path] and return its contents in
+/// Read the [file] and return its contents in
 /// a Uint8List.
 Uint8List readAsBytesSync(String file) {
   return Uint8List.view(readbuffer(file));
diff --git a/protobuf/lib/meta.dart b/protobuf/lib/meta.dart
index 6601c02..446853b 100644
--- a/protobuf/lib/meta.dart
+++ b/protobuf/lib/meta.dart
@@ -26,8 +26,6 @@
   'eventPlugin',
   'extensionsAreInitialized',
   'freeze',
-  'fromBuffer',
-  'fromJson',
   'getDefaultForField',
   'getExtension',
   'getField',
@@ -60,7 +58,6 @@
   'writeToCodedBufferWriter',
   'writeToJson',
   'writeToJsonMap',
-  r'$_defaultFor',
   r'$_ensure',
   r'$_get',
   r'$_getI64',
@@ -83,6 +80,12 @@
   r'$_setString',
   r'$_setUnsignedInt32',
   r'$_whichOneof',
+
+  // Names below are no longer reserved and should be removed in the next major
+  // release
+  'fromBuffer',
+  'fromJson',
+  r'$_defaultFor',
 ];
 
 // List of names which cannot be used in a subclass of ProtobufEnum.
@@ -91,8 +94,11 @@
   'Object',
   'ProtobufEnum',
   'hashCode',
-  'initByValue',
   'noSuchMethod',
   'runtimeType',
-  'toString'
+  'toString',
+
+  // Names below are no longer reserved and should be removed in the next major
+  // release
+  'initByValue',
 ];
diff --git a/protobuf/lib/protobuf.dart b/protobuf/lib/protobuf.dart
index e2d5cdc..97e9044 100644
--- a/protobuf/lib/protobuf.dart
+++ b/protobuf/lib/protobuf.dart
@@ -11,6 +11,7 @@
 import 'dart:typed_data' show TypedData, Uint8List, ByteData, Endian;
 
 import 'package:fixnum/fixnum.dart' show Int64;
+import 'package:meta/meta.dart' show UseResult;
 
 import 'src/protobuf/json_parsing_context.dart';
 import 'src/protobuf/permissive_compare.dart';
@@ -20,6 +21,7 @@
 part 'src/protobuf/coded_buffer.dart';
 part 'src/protobuf/coded_buffer_reader.dart';
 part 'src/protobuf/coded_buffer_writer.dart';
+part 'src/protobuf/consts.dart';
 part 'src/protobuf/builder_info.dart';
 part 'src/protobuf/event_plugin.dart';
 part 'src/protobuf/exceptions.dart';
diff --git a/protobuf/lib/src/protobuf/builder_info.dart b/protobuf/lib/src/protobuf/builder_info.dart
index 28f8b64..890daaf 100644
--- a/protobuf/lib/src/protobuf/builder_info.dart
+++ b/protobuf/lib/src/protobuf/builder_info.dart
@@ -8,12 +8,22 @@
 class BuilderInfo {
   /// The fully qualified name of this message.
   final String qualifiedMessageName;
+
+  /// Maps [FieldInfo.index]s to [FieldInfo]s.
   final List<FieldInfo> byIndex = <FieldInfo>[];
+
+  /// Maps [FieldInfo.tagNumber]s to [FieldInfo]s.
   final Map<int, FieldInfo> fieldInfo = <int, FieldInfo>{};
+
   final Map<String, FieldInfo> byTagAsString = <String, FieldInfo>{};
+
+  /// Maps [FieldInfo.name]s to [FieldInfo]s.
   final Map<String, FieldInfo> byName = <String, FieldInfo>{};
-  // Maps a tag number to the corresponding oneof index (if any).
+
+  /// Maps a tag number ([FieldInfo.tagNumber]) to the corresponding `oneof`
+  /// index (if any).
   final Map<int, int> oneofs = <int, int>{};
+
   bool hasExtensions = false;
   bool hasRequiredFields = true;
   List<FieldInfo>? _sortedByTag;
@@ -56,8 +66,8 @@
   void addMapField<K, V>(
       int tagNumber,
       String name,
-      int? keyFieldType,
-      int? valueFieldType,
+      int keyFieldType,
+      int valueFieldType,
       BuilderInfo mapEntryBuilderInfo,
       CreateBuilderFunc? valueCreator,
       {ProtobufEnum? defaultEnumValue,
@@ -211,8 +221,8 @@
   // Map field.
   void m<K, V>(int tagNumber, String name,
       {String? entryClassName,
-      int? keyFieldType,
-      int? valueFieldType,
+      required int keyFieldType,
+      required int valueFieldType,
       CreateBuilderFunc? valueCreator,
       ValueOfFunc? valueOf,
       List<ProtobufEnum>? enumValues,
@@ -267,7 +277,7 @@
     return i?.valueOf;
   }
 
-  /// The FieldInfo for each field in tag number order.
+  /// The [FieldInfo] for each field in tag number order.
   List<FieldInfo> get sortedByTag => _sortedByTag ??= _computeSortedByTag();
 
   /// The message name. Also see [qualifiedMessageName].
diff --git a/protobuf/lib/src/protobuf/coded_buffer.dart b/protobuf/lib/src/protobuf/coded_buffer.dart
index 45c3f4e..be316c4 100644
--- a/protobuf/lib/src/protobuf/coded_buffer.dart
+++ b/protobuf/lib/src/protobuf/coded_buffer.dart
@@ -53,7 +53,7 @@
         fs._setFieldUnchecked(meta, fi, input.readBool());
         break;
       case PbFieldType._OPTIONAL_BYTES:
-        fs._setFieldUnchecked(meta, fi, input.readBytes());
+        fs._setFieldUnchecked(meta, fi, Uint8List.fromList(input.readBytes()));
         break;
       case PbFieldType._OPTIONAL_STRING:
         fs._setFieldUnchecked(meta, fi, input.readString());
@@ -126,7 +126,9 @@
         _readPackable(meta, fs, input, wireType, fi, input.readBool);
         break;
       case PbFieldType._REPEATED_BYTES:
-        fs._ensureRepeatedField(meta, fi).add(input.readBytes());
+        fs
+            ._ensureRepeatedField(meta, fi)
+            .add(Uint8List.fromList(input.readBytes()));
         break;
       case PbFieldType._REPEATED_STRING:
         fs._ensureRepeatedField(meta, fi).add(input.readString());
diff --git a/protobuf/lib/src/protobuf/coded_buffer_writer.dart b/protobuf/lib/src/protobuf/coded_buffer_writer.dart
index a39c620..2c8f37f 100644
--- a/protobuf/lib/src/protobuf/coded_buffer_writer.dart
+++ b/protobuf/lib/src/protobuf/coded_buffer_writer.dart
@@ -14,7 +14,7 @@
 /// length-delimited representation, which means that they are represented as
 /// a varint encoded length followed by specified number of bytes of data.
 ///
-/// Due to this [CodedBufferWritter] maintains two output buffers:
+/// Due to this [CodedBufferWriter] maintains two output buffers:
 /// [_outputChunks] which contains all continuously written bytes and
 /// [_splices] which describes additional bytes to splice in-between
 /// [_outputChunks] bytes.
@@ -65,7 +65,7 @@
   }
 
   void writeField(int fieldNumber, int fieldType, fieldValue) {
-    final valueType = fieldType & ~0x07;
+    final valueType = PbFieldType._baseType(fieldType);
 
     if ((fieldType & PbFieldType._PACKED_BIT) != 0) {
       if (!fieldValue.isEmpty) {
@@ -79,8 +79,6 @@
       return;
     }
 
-    final wireFormat = _wireTypes[_valueTypeIndex(valueType)];
-
     if ((fieldType & PbFieldType._MAP_BIT) != 0) {
       final keyWireFormat =
           _wireTypes[_valueTypeIndex(fieldValue.keyFieldType)];
@@ -99,6 +97,8 @@
       return;
     }
 
+    final wireFormat = _wireTypes[_valueTypeIndex(valueType)];
+
     if ((fieldType & PbFieldType._REPEATED_BIT) != 0) {
       for (var i = 0; i < fieldValue.length; i++) {
         _writeValue(fieldNumber, valueType, fieldValue[i], wireFormat);
@@ -176,8 +176,8 @@
 
   /// Move the current [_outputChunk] into [_outputChunks].
   ///
-  /// If [allocateNew] is [true] then allocate a new chunk, otherwise
-  /// set [_outputChunk] to null.
+  /// If [allocateNew] is `true` then allocate a new chunk, otherwise
+  /// set [_outputChunk] to `null`.
   void _commitChunk(bool allocateNew) {
     if (_bytesInChunk != 0) {
       _outputChunks.add(_outputChunk);
@@ -445,8 +445,10 @@
   /// where multiplication becomes a floating point multiplication.
   ///
   /// [1] http://supertech.csail.mit.edu/papers/debruijn.pdf
-  static int _valueTypeIndex(int powerOf2) =>
-      ((0x077CB531 * powerOf2) >> 27) & 31;
+  int _valueTypeIndex(int powerOf2) {
+    assert(powerOf2 & (powerOf2 - 1) == 0, '$powerOf2 is not a power of 2');
+    return ((0x077CB531 * powerOf2) >> 27) & 31;
+  }
 
   /// Precomputed indices for all FbFieldType._XYZ_BIT values:
   ///
diff --git a/protobuf/lib/src/protobuf/consts.dart b/protobuf/lib/src/protobuf/consts.dart
new file mode 100644
index 0000000..a4fcd16
--- /dev/null
+++ b/protobuf/lib/src/protobuf/consts.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of protobuf;
+
+/// Constant string value of double.infinity.toString() and the infinity value
+/// recognized by double.parse(..).
+const infinity = 'Infinity';
+
+/// Constant string value of double.negativeInfinity.toString() and the negative
+/// infinity value recognized by double.parse(..).
+const negativeInfinity = '-Infinity';
+
+/// Constant string value of double.nan.toString() and the NaN (not a number)
+/// value recognized by double.parse(..).
+const nan = 'NaN';
diff --git a/protobuf/lib/src/protobuf/extension_field_set.dart b/protobuf/lib/src/protobuf/extension_field_set.dart
index 2497e03..c3f9e3a 100644
--- a/protobuf/lib/src/protobuf/extension_field_set.dart
+++ b/protobuf/lib/src/protobuf/extension_field_set.dart
@@ -132,6 +132,9 @@
     if (_parent._hasObservers) {
       _parent._eventPlugin!.beforeSetField(fi, value);
     }
+    // If there was already an unknown field with the same tag number,
+    // overwrite it.
+    _parent._unknownFields?.clearField(fi.tagNumber);
     _values[fi.tagNumber] = value;
   }
 
diff --git a/protobuf/lib/src/protobuf/extension_registry.dart b/protobuf/lib/src/protobuf/extension_registry.dart
index 93aa9fc..196b1fd 100644
--- a/protobuf/lib/src/protobuf/extension_registry.dart
+++ b/protobuf/lib/src/protobuf/extension_registry.dart
@@ -35,7 +35,7 @@
     return null;
   }
 
-  /// Returns a shallow copy of [message], with all extensions in [this] parsed
+  /// Returns a shallow copy of [message], with all extensions in `this` parsed
   /// from the unknown fields of [message] and of every nested submessage.
   ///
   /// Extensions already present in [message] will be preserved.
@@ -49,7 +49,7 @@
   ///
   /// Using this method to retrieve extensions is more expensive overall than
   /// using an [ExtensionRegistry] with all the needed extensions when doing
-  /// [GeneratedMessage.fromBuffer].
+  /// [GeneratedMessage.mergeFromBuffer].
   ///
   /// Example:
   ///
@@ -148,7 +148,7 @@
     } else if (field is MapFieldInfo) {
       final messageMap = message._fieldSet._values[field.index!];
       if (messageMap == null) continue;
-      if (_isGroupOrMessage(field.valueFieldType!)) {
+      if (_isGroupOrMessage(field.valueFieldType)) {
         for (var key in messageMap.keys) {
           final GeneratedMessage value = messageMap[key];
           final reparsedValue = _reparseMessage(value, extensionRegistry);
diff --git a/protobuf/lib/src/protobuf/field_info.dart b/protobuf/lib/src/protobuf/field_info.dart
index 9d9deb3..637ed96 100644
--- a/protobuf/lib/src/protobuf/field_info.dart
+++ b/protobuf/lib/src/protobuf/field_info.dart
@@ -8,44 +8,71 @@
 class FieldInfo<T> {
   FrozenPbList<T>? _emptyList;
 
-  /// Name of this field as the `json_name` reported by protoc.
-  ///
-  /// This will typically be in camel case.
+  /// Name of this field as the `json_name` reported by protoc. Example:
+  /// ```proto
+  /// message Msg {
+  ///   int32 foo_name = 1 [json_name = "barName"];
+  /// }
+  /// ```
+  /// Here `name` of the field is `barName`. When `json_name` is not specified
+  /// in the proto definition, this is the camelCase version of the field name.
+  /// In the example above, without the `json_name` field option, `name` would
+  /// be `"fooName"`.
   final String name;
 
-  /// The name of this field as written in the proto-definition.
-  ///
+  /// Name of this field as written in the proto definition. Example:
+  /// ```proto
+  /// message SearchRequest {
+  ///   ...
+  ///   int32 result_per_page = 3;
+  /// }
+  /// ```
+  /// `protoName` for the `result_per_page` field above is `"result_per_page"`.
   /// This will typically consist of words separated with underscores.
-  final String protoName;
+  String get protoName {
+    return _protoName ??= _unCamelCase(name);
+  }
 
+  String? _protoName;
+
+  /// Field number as specified in the proto definition. Example:
+  /// ```proto
+  /// message SearchRequest {
+  ///   ...
+  ///   int32 result_per_page = 3;
+  /// }
+  /// ```
+  /// `tagNumber` of `result_per_page` field is 3.
   final int tagNumber;
-  final int? index; // index of the field's value. Null for extensions.
+
+  /// Index of the field in [_FieldSet._values] list of this field's message.
+  /// `null` for extensions.
+  final int? index;
+
+  /// Type of this field. See `field_type.dart`.
   final int type;
 
-  // Constructs the default value of a field.
-  // (Only used for repeated fields where check is null.)
+  /// Constructs the default value of a field. For repeated fields, only used
+  /// when the `check` property is null.
   final MakeDefaultFunc? makeDefault;
 
-  // Creates an empty message or group when decoding a message.
-  // Not used for other types.
-  // see GeneratedMessage._getEmptyMessage
+  /// Only available in fields with message type. Creates an empty message or
+  /// group when decoding a message.
   final CreateBuilderFunc? subBuilder;
 
-  // List of all enum enumValues.
-  // (Not used for other types.)
+  /// Only available in enum fields. List of all enum values.
   final List<ProtobufEnum>? enumValues;
 
-  // Default enum value, if type is a PbList<ProtobufEnum> or a
-  // PbMap<[anything], ProtobufEnum>.
+  /// Only available in enum fields. Default enum value or a
+  /// `PbMap<[anything], ProtobufEnum>`.
   final ProtobufEnum? defaultEnumValue;
 
-  // Looks up the enum value given its integer code.
-  // (Not used for other types.)
-  // see GeneratedMessage._getValueOfFunc
+  /// Only available in enum fields. Looks up the enum value given its integer
+  /// code.
   final ValueOfFunc? valueOf;
 
-  // Verifies an item being added to a repeated field
-  // (Not used for non-repeated fields.)
+  /// Only available in repeated fields. Verifies an item being added to a
+  /// repeated field.
   final CheckFunc<T>? check;
 
   FieldInfo(this.name, this.tagNumber, this.index, this.type,
@@ -57,7 +84,7 @@
       String? protoName})
       : makeDefault = findMakeDefault(type, defaultOrMaker),
         check = null,
-        protoName = protoName ?? _unCamelCase(name),
+        _protoName = protoName,
         assert(type != 0),
         assert(!_isGroupOrMessage(type) ||
             subBuilder != null ||
@@ -67,7 +94,7 @@
   // Represents a field that has been removed by a program transformation.
   FieldInfo.dummy(this.index)
       : name = '<removed field>',
-        protoName = '<removed field>',
+        _protoName = '<removed field>',
         tagNumber = 0,
         type = 0,
         makeDefault = null,
@@ -81,7 +108,7 @@
       this.check, this.subBuilder,
       {this.valueOf, this.enumValues, this.defaultEnumValue, String? protoName})
       : makeDefault = (() => PbList<T>(check: check!)),
-        protoName = protoName ?? _unCamelCase(name) {
+        _protoName = protoName {
     ArgumentError.checkNotNull(name, 'name');
     ArgumentError.checkNotNull(tagNumber, 'tagNumber');
     assert(_isRepeated(type));
@@ -105,8 +132,8 @@
   bool get isEnum => _isEnum(type);
   bool get isMapField => _isMapField(type);
 
-  /// Returns a read-only default value for a field.
-  /// (Unlike getField, doesn't create a repeated field.)
+  /// Returns a read-only default value for a field. Unlike
+  /// [GeneratedMessage.getField], doesn't create a repeated field.
   dynamic get readonlyDefault {
     if (isRepeated) {
       return _emptyList ??= FrozenPbList._([]);
@@ -198,8 +225,17 @@
 }
 
 class MapFieldInfo<K, V> extends FieldInfo<PbMap<K, V>?> {
-  final int? keyFieldType;
-  final int? valueFieldType;
+  /// Key type of the map. Per proto2 and proto3 specs, this needs to be an
+  /// integer type or `string`, and the type cannot be `repeated`.
+  ///
+  /// The `int` value is interpreted the same way as [FieldInfo.type].
+  final int keyFieldType;
+
+  /// Value type of the map. Per proto2 and proto3 specs, this can be any type
+  /// other than `map`, and the type cannot be `repeated`.
+  ///
+  /// The `int` value is interpreted the same way as [FieldInfo.type].
+  final int valueFieldType;
 
   /// Creates a new empty instance of the value type.
   ///
diff --git a/protobuf/lib/src/protobuf/field_set.dart b/protobuf/lib/src/protobuf/field_set.dart
index f2e6faa..ff36282 100644
--- a/protobuf/lib/src/protobuf/field_set.dart
+++ b/protobuf/lib/src/protobuf/field_set.dart
@@ -244,7 +244,7 @@
 
   List<T> _getDefaultList<T>(FieldInfo<T> fi) {
     assert(fi.isRepeated);
-    if (_isReadOnly) return FrozenPbList._(const []);
+    if (_isReadOnly) return fi.readonlyDefault;
 
     // TODO(skybrian) we could avoid this by generating another
     // method for repeated fields:
@@ -387,7 +387,7 @@
     assert(fi.index != null); // Map fields are not allowed to be extensions.
 
     var value = _getFieldOrNull(fi);
-    if (value != null) return (value as Map<K, V>) as PbMap<K, V>;
+    if (value != null) return value as PbMap<K, V>;
 
     var newValue = fi._createMapField(_message!);
     _setNonExtensionFieldUnchecked(meta, fi, newValue);
@@ -465,8 +465,7 @@
       if (defaultValue != null) return defaultValue;
       value = _getDefault(_nonExtensionInfoByIndex(index));
     }
-    bool result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated getter for `bool` fields that default to
@@ -474,8 +473,7 @@
   bool _$getBF(int index) {
     var value = _values[index];
     if (value == null) return false;
-    bool result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated getter for int fields.
@@ -485,8 +483,7 @@
       if (defaultValue != null) return defaultValue;
       value = _getDefault(_nonExtensionInfoByIndex(index));
     }
-    int result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated getter for `int` fields (int32, uint32,
@@ -494,8 +491,7 @@
   int _$getIZ(int index) {
     var value = _values[index];
     if (value == null) return 0;
-    int result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated getter for String fields.
@@ -505,8 +501,7 @@
       if (defaultValue != null) return defaultValue;
       value = _getDefault(_nonExtensionInfoByIndex(index));
     }
-    String result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated getter for String fields that default to
@@ -514,16 +509,14 @@
   String _$getSZ(int index) {
     var value = _values[index];
     if (value == null) return '';
-    String result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated getter for Int64 fields.
   Int64 _$getI64(int index) {
     var value = _values[index];
     value ??= _getDefault(_nonExtensionInfoByIndex(index));
-    Int64 result = value;
-    return result;
+    return value;
   }
 
   /// The implementation of a generated 'has' method.
@@ -636,6 +629,11 @@
     // We don't want reading a field to change equality comparisons.
     if (val is List && val.isEmpty) return true;
 
+    // An empty map field is the same as uninitialized.
+    // This is because accessing a map field automatically creates it.
+    // We don't want reading a field to change equality comparisons.
+    if (val is Map && val.isEmpty) return true;
+
     // For now, initialized and uninitialized fields are different.
     // TODO(skybrian) consider other cases; should we compare with the
     // default value or not?
@@ -654,60 +652,65 @@
     if (_hashCodesCanBeMemoized && _memoizedHashCode != null) {
       return _memoizedHashCode!;
     }
-    // Hashes the value of one field (recursively).
-    int hashField(int hash, FieldInfo fi, value) {
-      if (value is List && value.isEmpty) {
-        return hash; // It's either repeated or an empty byte array.
-      }
-
-      hash = _HashUtils._combine(hash, fi.tagNumber);
-      if (_isBytes(fi.type)) {
-        // Bytes are represented as a List<int> (Usually with byte-data).
-        // We special case that to match our equality semantics.
-        hash = _HashUtils._combine(hash, _HashUtils._hashObjects(value));
-      } else if (!_isEnum(fi.type)) {
-        hash = _HashUtils._combine(hash, value.hashCode);
-      } else if (fi.isRepeated) {
-        hash = _HashUtils._hashObjects(value.map((enm) => enm.value));
-      } else {
-        ProtobufEnum enm = value;
-        hash = _HashUtils._combine(hash, enm.value);
-      }
-
-      return hash;
-    }
-
-    int hashEachField(int hash) {
-      //non-extension fields
-      hash = _infosSortedByTag.where((fi) => _values[fi.index!] != null).fold(
-          hash, (int h, FieldInfo fi) => hashField(h, fi, _values[fi.index!]));
-
-      if (!_hasExtensions) return hash;
-
-      hash =
-          _sorted(_extensions!._tagNumbers).fold(hash, (int h, int tagNumber) {
-        var fi = _extensions!._getInfoOrNull(tagNumber)!;
-        return hashField(h, fi, _extensions!._getFieldOrNull(fi));
-      });
-
-      return hash;
-    }
 
     // Hash with descriptor.
     var hash = _HashUtils._combine(0, _meta.hashCode);
-    // Hash with fields.
-    hash = hashEachField(hash);
-    // Hash with unknown fields.
-    if (_hasUnknownFields) {
-      hash = _HashUtils._combine(hash, _unknownFields.hashCode);
+
+    // Hash with non-extension fields.
+    final values = _values;
+    for (final fi in _infosSortedByTag) {
+      final value = values[fi.index!];
+      if (value == null) continue;
+      hash = _hashField(hash, fi, value);
     }
 
+    // Hash with extension fields.
+    if (_hasExtensions) {
+      final extensions = _extensions!;
+      final sortedByTagNumbers = _sorted(extensions._tagNumbers);
+      for (final tagNumber in sortedByTagNumbers) {
+        final fi = extensions._getInfoOrNull(tagNumber)!;
+        hash = _hashField(hash, fi, extensions._getFieldOrNull(fi));
+      }
+    }
+
+    // Hash with unknown fields.
+    hash = _HashUtils._combine(hash, _unknownFields?.hashCode ?? 0);
+
     if (_isReadOnly && _hashCodesCanBeMemoized) {
       _frozenState = hash;
     }
     return hash;
   }
 
+  // Hashes the value of one field (recursively).
+  static int _hashField(int hash, FieldInfo fi, value) {
+    if (value is List && value.isEmpty) {
+      return hash; // It's either repeated or an empty byte array.
+    }
+
+    if (value is Map && value.isEmpty) {
+      return hash;
+    }
+
+    hash = _HashUtils._combine(hash, fi.tagNumber);
+    if (_isBytes(fi.type)) {
+      // Bytes are represented as a List<int> (Usually with byte-data).
+      // We special case that to match our equality semantics.
+      hash = _HashUtils._combine(hash, _HashUtils._hashObjects(value));
+    } else if (!_isEnum(fi.type)) {
+      hash = _HashUtils._combine(hash, value.hashCode);
+    } else if (fi.isRepeated) {
+      hash = _HashUtils._combine(
+          hash, _HashUtils._hashObjects(value.map((enm) => enm.value)));
+    } else {
+      ProtobufEnum enm = value;
+      hash = _HashUtils._combine(hash, enm.value);
+    }
+
+    return hash;
+  }
+
   void writeString(StringBuffer out, String indent) {
     void renderValue(key, value) {
       if (value is GeneratedMessage) {
@@ -741,7 +744,8 @@
     }
 
     for (var fi in _infosSortedByTag) {
-      writeFieldValue(_values[fi.index!], fi.name);
+      writeFieldValue(_values[fi.index!],
+          fi.name == '' ? fi.tagNumber.toString() : fi.name);
     }
 
     if (_hasExtensions) {
@@ -765,7 +769,7 @@
   /// recursively merged.
   void _mergeFromMessage(_FieldSet other) {
     // TODO(https://github.com/google/protobuf.dart/issues/60): Recognize
-    // when [this] and [other] are the same protobuf (e.g. from cloning). In
+    // when `this` and [other] are the same protobuf (e.g. from cloning). In
     // this case, we can merge the non-extension fields without field lookups or
     // validation checks.
 
@@ -803,7 +807,7 @@
 
     if (fi!.isMapField) {
       var f = fi as MapFieldInfo<dynamic, dynamic>;
-      mustClone = _isGroupOrMessage(f.valueFieldType!);
+      mustClone = _isGroupOrMessage(f.valueFieldType);
       var map = f._ensureMapField(meta, this) as PbMap<dynamic, dynamic>;
       if (mustClone) {
         for (MapEntry entry in fieldValue.entries) {
diff --git a/protobuf/lib/src/protobuf/generated_message.dart b/protobuf/lib/src/protobuf/generated_message.dart
index 64d1395..6fa71d7 100644
--- a/protobuf/lib/src/protobuf/generated_message.dart
+++ b/protobuf/lib/src/protobuf/generated_message.dart
@@ -29,19 +29,6 @@
     if (eventPlugin != null) eventPlugin!.attach(this);
   }
 
-  GeneratedMessage.fromBuffer(
-      List<int> input, ExtensionRegistry extensionRegistry) {
-    __fieldSet = _FieldSet(this, info_, eventPlugin);
-    if (eventPlugin != null) eventPlugin!.attach(this);
-    mergeFromBuffer(input, extensionRegistry);
-  }
-
-  GeneratedMessage.fromJson(String input, ExtensionRegistry extensionRegistry) {
-    __fieldSet = _FieldSet(this, info_, eventPlugin);
-    if (eventPlugin != null) eventPlugin!.attach(this);
-    mergeFromJson(input, extensionRegistry);
-  }
-
   // Overridden by subclasses.
   BuilderInfo get info_;
 
@@ -78,11 +65,11 @@
 
   /// Returns a writable, shallow copy of this message.
   ///
-  /// Sub messages will be shared with [this] and will still be frozen if [this]
+  /// Sub messages will be shared with `this` and will still be frozen if `this`
   /// is frozen.
   ///
   /// The lists representing repeated fields are copied. But their elements will
-  /// be shared with the corresponding list in [this].
+  /// be shared with the corresponding list in `this`.
   ///
   /// Similarly for map fields, the maps will be copied, but share the elements.
   // TODO(nichite, sigurdm): Consider returning an actual builder object that
@@ -95,7 +82,7 @@
 
   /// Apply [updates] to a copy of this message.
   ///
-  /// Makes a writable shawwol copy of this message, applies the [updates] to
+  /// Makes a writable shallow copy of this message, applies the [updates] to
   /// it, and marks the copy read-only before returning it.
   @Deprecated('Using this can add significant size overhead to your binary. '
       'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
@@ -216,10 +203,10 @@
   /// actual runtime value) are represented as strings. Enumerated values are
   /// represented as their integer value.
   ///
-  /// For the proto3 JSON format use: [toProto3JSON].
+  /// For the proto3 JSON format use: [toProto3Json].
   String writeToJson() => jsonEncode(writeToJsonMap());
 
-  /// Returns an Object representing Proto3 JSON serialization of [this].
+  /// Returns an Object representing Proto3 JSON serialization of `this`.
   ///
   /// The key for each field is be the camel-cased name of the field.
   ///
@@ -273,7 +260,7 @@
   /// Merges field values from [data], a JSON object, encoded as described by
   /// [GeneratedMessage.writeToJson].
   ///
-  /// For the proto3 JSON format use: [mergeFromProto3JSON].
+  /// For the proto3 JSON format use: [mergeFromProto3Json].
   void mergeFromJson(String data,
       [ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]) {
     /// Disable lazy creation of Dart objects for a dart2js speedup.
@@ -506,27 +493,42 @@
 
   // Support for generating a read-only default singleton instance.
 
-  static final Map<Function?, Function> _defaultMakers = {};
+  static final Map<Function?, _SingletonMaker<GeneratedMessage>>
+      _defaultMakers = {};
 
   static T Function() _defaultMakerFor<T extends GeneratedMessage>(
-      T Function()? createFn) {
-    return (_defaultMakers[createFn] ??= _createDefaultMakerFor<T>(createFn!))
-        as T Function();
-  }
-
-  static T Function() _createDefaultMakerFor<T extends GeneratedMessage>(
-      T Function() createFn) {
-    T? defaultValue;
-    T defaultMaker() {
-      return defaultValue ??= createFn()..freeze();
-    }
-
-    return defaultMaker;
-  }
+          T Function()? createFn) =>
+      _getSingletonMaker(createFn!)._frozenSingletonCreator;
 
   /// For generated code only.
   static T $_defaultFor<T extends GeneratedMessage>(T Function() createFn) =>
-      _defaultMakerFor<T>(createFn)();
+      _getSingletonMaker(createFn)._frozenSingleton;
+
+  static _SingletonMaker<T> _getSingletonMaker<T extends GeneratedMessage>(
+      T Function() fun) {
+    final oldMaker = _defaultMakers[fun];
+    if (oldMaker != null) {
+      // The CFE will insert an implicit downcast to `_SingletonMaker<T>`. We
+      // avoid making that explicit because implicit downcasts are avoided by
+      // dart2js in production code.
+      return oldMaker as dynamic;
+    }
+    return _defaultMakers[fun] = _SingletonMaker<T>(fun);
+  }
+}
+
+// We use a class that creates singletones instead of a closure function. We do
+// so because the result of the lookup in [_defaultMakers] has to be downcasted.
+// A downcast to a generic interface type is much easier to perform at runtime
+// than a downcast to a generic function type.
+class _SingletonMaker<T extends GeneratedMessage> {
+  final T Function() _creator;
+
+  _SingletonMaker(this._creator);
+
+  late final T _frozenSingleton = _creator()..freeze();
+  // ignore: prefer_function_declarations_over_variables
+  late final T Function() _frozenSingletonCreator = () => _frozenSingleton;
 }
 
 /// The package name of a protobuf message.
@@ -543,6 +545,8 @@
   ///
   /// Makes a writable shallow copy of this message, applies the [updates] to
   /// it, and marks the copy read-only before returning it.
+  @UseResult('[GeneratedMessageGenericExtensions.rebuild] '
+      'does not update the message, returns a new message')
   T rebuild(void Function(T) updates) {
     if (!isFrozen) {
       throw ArgumentError('Rebuilding only works on frozen messages.');
diff --git a/protobuf/lib/src/protobuf/json.dart b/protobuf/lib/src/protobuf/json.dart
index 0a5c827..011323f 100644
--- a/protobuf/lib/src/protobuf/json.dart
+++ b/protobuf/lib/src/protobuf/json.dart
@@ -15,14 +15,25 @@
     switch (baseType) {
       case PbFieldType._BOOL_BIT:
       case PbFieldType._STRING_BIT:
-      case PbFieldType._FLOAT_BIT:
-      case PbFieldType._DOUBLE_BIT:
       case PbFieldType._INT32_BIT:
       case PbFieldType._SINT32_BIT:
       case PbFieldType._UINT32_BIT:
       case PbFieldType._FIXED32_BIT:
       case PbFieldType._SFIXED32_BIT:
         return fieldValue;
+      case PbFieldType._FLOAT_BIT:
+      case PbFieldType._DOUBLE_BIT:
+        final value = fieldValue as double;
+        if (value.isNaN) {
+          return nan;
+        }
+        if (value.isInfinite) {
+          return value.isNegative ? negativeInfinity : infinity;
+        }
+        if (fieldValue.toInt() == fieldValue) {
+          return fieldValue.toInt();
+        }
+        return value;
       case PbFieldType._BYTES_BIT:
         // Encode 'bytes' as a base64-encoded string.
         return base64Encode(fieldValue as List<int>);
@@ -45,9 +56,9 @@
 
   List _writeMap(dynamic fieldValue, MapFieldInfo fi) =>
       List.from(fieldValue.entries.map((MapEntry e) => {
-            '${PbMap._keyFieldNumber}': convertToMap(e.key, fi.keyFieldType!),
+            '${PbMap._keyFieldNumber}': convertToMap(e.key, fi.keyFieldType),
             '${PbMap._valueFieldNumber}':
-                convertToMap(e.value, fi.valueFieldType!)
+                convertToMap(e.value, fi.valueFieldType)
           }));
 
   var result = <String, dynamic>{};
@@ -131,14 +142,14 @@
         entryFieldSet,
         jsonEntry['${PbMap._keyFieldNumber}'],
         PbMap._keyFieldNumber,
-        fi.keyFieldType!,
+        fi.keyFieldType,
         registry);
     var convertedValue = _convertJsonValue(
         entryMeta,
         entryFieldSet,
         jsonEntry['${PbMap._valueFieldNumber}'],
         PbMap._valueFieldNumber,
-        fi.valueFieldType!,
+        fi.valueFieldType,
         registry);
     // In the case of an unknown enum value, the converted value may return
     // null. The default enum value should be used in these cases, which is
@@ -166,7 +177,7 @@
 /// Converts [value] from the Json format to the Dart data type
 /// suitable for inserting into the corresponding [GeneratedMessage] field.
 ///
-/// Returns the converted value.  This function returns [null] if it is an
+/// Returns the converted value.  This function returns `null` if it is an
 /// unknown enum value, in which case the caller should figure out the default
 /// enum value to return instead.
 /// This function throws [ArgumentError] if it cannot convert the value.
diff --git a/protobuf/lib/src/protobuf/json_parsing_context.dart b/protobuf/lib/src/protobuf/json_parsing_context.dart
index 7d74fbf..5d11ced 100644
--- a/protobuf/lib/src/protobuf/json_parsing_context.dart
+++ b/protobuf/lib/src/protobuf/json_parsing_context.dart
@@ -24,7 +24,7 @@
     _path.removeLast();
   }
 
-  /// Returns a FormatException indicating the indices to the current [path].
+  /// Returns a FormatException indicating the indices to the current path.
   Exception parseException(String message, Object? source) {
     var formattedPath = _path.map((s) => '["$s"]').join();
     return FormatException(
diff --git a/protobuf/lib/src/protobuf/mixins/well_known.dart b/protobuf/lib/src/protobuf/mixins/well_known.dart
index 308832b..66854c9 100644
--- a/protobuf/lib/src/protobuf/mixins/well_known.dart
+++ b/protobuf/lib/src/protobuf/mixins/well_known.dart
@@ -8,7 +8,6 @@
 
 import '../../../protobuf.dart';
 import '../json_parsing_context.dart';
-import '../type_registry.dart';
 
 abstract class AnyMixin implements GeneratedMessage {
   String get typeUrl;
@@ -86,7 +85,7 @@
           'The type of the Any message (${any.typeUrl}) is not in the given typeRegistry.');
     }
     var unpacked = info.createEmptyInstance!()..mergeFromBuffer(any.value);
-    var proto3Json = unpacked.toProto3Json();
+    var proto3Json = unpacked.toProto3Json(typeRegistry: typeRegistry);
     if (info.toProto3Json == null) {
       var map = proto3Json as Map<String, dynamic>;
       map['@type'] = any.typeUrl;
@@ -152,16 +151,19 @@
   ///
   /// The result is in UTC time zone and has microsecond precision, as
   /// [DateTime] does not support nanosecond precision.
-  DateTime toDateTime() => DateTime.fromMicrosecondsSinceEpoch(
-      seconds.toInt() * Duration.microsecondsPerSecond + nanos ~/ 1000,
-      isUtc: true);
+  ///
+  /// Use [toLocal] to convert to local time zone, instead of the default UTC.
+  DateTime toDateTime({bool toLocal = false}) =>
+      DateTime.fromMicrosecondsSinceEpoch(
+          seconds.toInt() * Duration.microsecondsPerSecond + nanos ~/ 1000,
+          isUtc: !toLocal);
 
-  /// Updates [target] to be the time at [datetime].
+  /// Updates [target] to be the time at [dateTime].
   ///
   /// Time zone information will not be preserved.
   static void setFromDateTime(TimestampMixin target, DateTime dateTime) {
     var micros = dateTime.microsecondsSinceEpoch;
-    target.seconds = Int64(micros ~/ Duration.microsecondsPerSecond);
+    target.seconds = Int64((micros / Duration.microsecondsPerSecond).floor());
     target.nanos = (micros % Duration.microsecondsPerSecond).toInt() * 1000;
   }
 
diff --git a/protobuf/lib/src/protobuf/pb_list.dart b/protobuf/lib/src/protobuf/pb_list.dart
index fac87cd..6b53d1f 100644
--- a/protobuf/lib/src/protobuf/pb_list.dart
+++ b/protobuf/lib/src/protobuf/pb_list.dart
@@ -75,118 +75,86 @@
   /// Freezes the list by converting to [FrozenPbList].
   FrozenPbList<E> toFrozenPbList() => FrozenPbList<E>.from(this);
 
-  /// Adds [value] at the end of the list, extending the length by one.
-  /// Throws an [UnsupportedError] if the list is not extendable.
   @override
-  void add(E value) {
-    check(value);
-    _wrappedList.add(value);
+  void add(E element) {
+    check(element);
+    _wrappedList.add(element);
   }
 
-  /// Appends all elements of the [collection] to the end of list.
-  /// Extends the length of the list by the length of [collection].
-  /// Throws an [UnsupportedError] if the list is not extendable.
   @override
-  void addAll(Iterable<E> collection) {
-    collection.forEach(check);
-    _wrappedList.addAll(collection);
+  void addAll(Iterable<E> iterable) {
+    iterable.forEach(check);
+    _wrappedList.addAll(iterable);
   }
 
-  /// Returns an [Iterable] of the objects in this list in reverse order.
   @override
   Iterable<E> get reversed => _wrappedList.reversed;
 
-  /// Sorts this list according to the order specified by the [compare]
-  /// function.
   @override
   void sort([int Function(E a, E b)? compare]) => _wrappedList.sort(compare);
 
-  /// Shuffles the elements of this list randomly.
   @override
   void shuffle([math.Random? random]) => _wrappedList.shuffle(random);
 
-  /// Removes all objects from this list; the length of the list becomes zero.
   @override
   void clear() => _wrappedList.clear();
 
-  /// Inserts a new element in the list.
-  /// The element must be valid (and not nullable) for the PbList type.
   @override
   void insert(int index, E element) {
     check(element);
     _wrappedList.insert(index, element);
   }
 
-  /// Inserts all elements of [iterable] at position [index] in the list.
-  ///
-  /// Elements in [iterable] must be valid and not nullable for the PbList type.
   @override
   void insertAll(int index, Iterable<E> iterable) {
     iterable.forEach(check);
     _wrappedList.insertAll(index, iterable);
   }
 
-  /// Overwrites elements of `this` with elements of [iterable] starting at
-  /// position [index] in the list.
-  ///
-  /// Elements in [iterable] must be valid and not nullable for the PbList type.
   @override
   void setAll(int index, Iterable<E> iterable) {
     iterable.forEach(check);
     _wrappedList.setAll(index, iterable);
   }
 
-  /// Removes the first occurrence of [value] from this list.
   @override
-  bool remove(Object? value) => _wrappedList.remove(value);
+  bool remove(Object? element) => _wrappedList.remove(element);
 
-  /// Removes the object at position [index] from this list.
   @override
   E removeAt(int index) => _wrappedList.removeAt(index);
 
-  /// Pops and returns the last object in this list.
   @override
   E removeLast() => _wrappedList.removeLast();
 
-  /// Removes all objects from this list that satisfy [test].
   @override
   void removeWhere(bool Function(E element) test) =>
       _wrappedList.removeWhere(test);
 
-  /// Removes all objects from this list that fail to satisfy [test].
   @override
   void retainWhere(bool Function(E element) test) =>
       _wrappedList.retainWhere(test);
 
-  /// Copies [:end - start:] elements of the [from] array, starting from
-  /// [skipCount], into [:this:], starting at [start].
-  /// Throws an [UnsupportedError] if the list is not extendable.
   @override
-  void setRange(int start, int end, Iterable<E> from, [int skipCount = 0]) {
+  void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
     // NOTE: In case `take()` returns less than `end - start` elements, the
     // _wrappedList will fail with a `StateError`.
-    from.skip(skipCount).take(end - start).forEach(check);
-    _wrappedList.setRange(start, end, from, skipCount);
+    iterable.skip(skipCount).take(end - start).forEach(check);
+    _wrappedList.setRange(start, end, iterable, skipCount);
   }
 
-  /// Removes the objects in the range [start] inclusive to [end] exclusive.
   @override
   void removeRange(int start, int end) => _wrappedList.removeRange(start, end);
 
-  /// Sets the objects in the range [start] inclusive to [end] exclusive to the
-  /// given [fillValue].
   @override
-  void fillRange(int start, int end, [E? fillValue]) {
-    check(fillValue);
-    _wrappedList.fillRange(start, end, fillValue);
+  void fillRange(int start, int end, [E? fill]) {
+    check(fill);
+    _wrappedList.fillRange(start, end, fill);
   }
 
-  /// Removes the objects in the range [start] inclusive to [end] exclusive and
-  /// inserts the contents of [replacement] in its place.
   @override
-  void replaceRange(int start, int end, Iterable<E> replacement) {
-    final values = replacement.toList();
-    replacement.forEach(check);
+  void replaceRange(int start, int end, Iterable<E> newContents) {
+    final values = newContents.toList();
+    newContents.forEach(check);
     _wrappedList.replaceRange(start, end, values);
   }
 }
@@ -213,167 +181,119 @@
   @override
   int get hashCode => _HashUtils._hashObjects(_wrappedList);
 
-  /// Returns an [Iterator] for the list.
   @override
   Iterator<E> get iterator => _wrappedList.iterator;
 
-  /// Returns a new lazy [Iterable] with elements that are created by calling
-  /// `f` on each element of this `PbListBase` in iteration order.
   @override
   Iterable<T> map<T>(T Function(E e) f) => _wrappedList.map<T>(f);
 
-  /// Returns a new lazy [Iterable] with all elements that satisfy the predicate
-  /// [test].
   @override
   Iterable<E> where(bool Function(E element) test) => _wrappedList.where(test);
 
-  /// Expands each element of this [Iterable] into zero or more elements.
   @override
   Iterable<T> expand<T>(Iterable<T> Function(E element) f) =>
       _wrappedList.expand(f);
 
-  /// Returns true if the collection contains an element equal to [element].
   @override
   bool contains(Object? element) => _wrappedList.contains(element);
 
-  /// Applies the function [f] to each element of this list in iteration order.
   @override
-  void forEach(void Function(E element) f) {
-    _wrappedList.forEach(f);
+  void forEach(void Function(E element) action) {
+    _wrappedList.forEach(action);
   }
 
-  /// Reduces a collection to a single value by iteratively combining elements
-  /// of the collection using the provided function.
   @override
   E reduce(E Function(E value, E element) combine) =>
       _wrappedList.reduce(combine);
 
-  /// Reduces a collection to a single value by iteratively combining each
-  /// element of the collection with an existing value.
   @override
   T fold<T>(T initialValue, T Function(T previousValue, E element) combine) =>
       _wrappedList.fold(initialValue, combine);
 
-  /// Checks whether every element of this iterable satisfies [test].
   @override
   bool every(bool Function(E element) test) => _wrappedList.every(test);
 
-  /// Converts each element to a [String] and concatenates the strings.
   @override
   String join([String separator = '']) => _wrappedList.join(separator);
 
-  /// Checks whether any element of this iterable satisfies [test].
   @override
   bool any(bool Function(E element) test) => _wrappedList.any(test);
 
-  /// Creates a [List] containing the elements of this [Iterable].
   @override
   List<E> toList({bool growable = true}) =>
       _wrappedList.toList(growable: growable);
 
-  /// Creates a [Set] containing the same elements as this iterable.
   @override
   Set<E> toSet() => _wrappedList.toSet();
 
-  /// Returns `true` if there are no elements in this collection.
   @override
   bool get isEmpty => _wrappedList.isEmpty;
 
-  /// Returns `true` if there is at least one element in this collection.
   @override
   bool get isNotEmpty => _wrappedList.isNotEmpty;
 
-  /// Returns a lazy iterable of the [count] first elements of this iterable.
   @override
   Iterable<E> take(int count) => _wrappedList.take(count);
 
-  /// Returns a lazy iterable of the leading elements satisfying [test].
   @override
   Iterable<E> takeWhile(bool Function(E value) test) =>
       _wrappedList.takeWhile(test);
 
-  /// Returns an [Iterable] that provides all but the first [count] elements.
   @override
   Iterable<E> skip(int count) => _wrappedList.skip(count);
 
-  /// Returns an `Iterable` that skips leading elements while [test] is
-  /// satisfied.
   @override
   Iterable<E> skipWhile(bool Function(E value) test) =>
       _wrappedList.skipWhile(test);
 
-  /// Returns the first element.
   @override
   E get first => _wrappedList.first;
 
-  /// Returns the last element.
   @override
   E get last => _wrappedList.last;
 
-  /// Checks that this iterable has only one element, and returns that element.
   @override
   E get single => _wrappedList.single;
 
-  /// Returns the first element that satisfies the given predicate [test].
   @override
   E firstWhere(bool Function(E element) test, {E Function()? orElse}) =>
       _wrappedList.firstWhere(test, orElse: orElse);
 
-  /// Returns the last element that satisfies the given predicate [test].
   @override
   E lastWhere(bool Function(E element) test, {E Function()? orElse}) =>
       _wrappedList.lastWhere(test, orElse: orElse);
 
-  /// Returns the single element that satisfies [test].
-  // TODO(jakobr): Implement once Dart 2 corelib changes have landed.
-  //E singleWhere(bool test(E element), {E orElse()}) =>
-  //    _wrappedList.singleWhere(test, orElse: orElse);
-
-  /// Returns the [index]th element.
   @override
   E elementAt(int index) => _wrappedList.elementAt(index);
 
-  /// Returns a string representation of (some of) the elements of `this`.
   @override
   String toString() => _wrappedList.toString();
 
-  /// Returns the element at the given [index] in the list or throws an
-  /// [IndexOutOfRangeException] if [index] is out of bounds.
   @override
   E operator [](int index) => _wrappedList[index];
 
-  /// Returns the number of elements in this collection.
   @override
   int get length => _wrappedList.length;
 
   // TODO(jakobr): E instead of Object once dart-lang/sdk#31311 is fixed.
-  /// Returns the first index of [element] in this list.
   @override
   int indexOf(Object? element, [int start = 0]) =>
       _wrappedList.indexOf(element as E, start);
 
   // TODO(jakobr): E instead of Object once dart-lang/sdk#31311 is fixed.
-  /// Returns the last index of [element] in this list.
   @override
   int lastIndexOf(Object? element, [int? start]) =>
       _wrappedList.lastIndexOf(element as E, start);
 
-  /// Returns a new list containing the objects from [start] inclusive to [end]
-  /// exclusive.
   @override
   List<E> sublist(int start, [int? end]) => _wrappedList.sublist(start, end);
 
-  /// Returns an [Iterable] that iterates over the objects in the range [start]
-  /// inclusive to [end] exclusive.
   @override
   Iterable<E> getRange(int start, int end) => _wrappedList.getRange(start, end);
 
-  /// Returns an unmodifiable [Map] view of `this`.
   @override
   Map<int, E> asMap() => _wrappedList.asMap();
 
-  /// Sets the entry at the given [index] in the list to [value].
-  /// Throws an [IndexOutOfRangeException] if [index] is out of bounds.
   @override
   void operator []=(int index, E value) {
     check(value);
diff --git a/protobuf/lib/src/protobuf/pb_map.dart b/protobuf/lib/src/protobuf/pb_map.dart
index cb0b5cd..712f812 100644
--- a/protobuf/lib/src/protobuf/pb_map.dart
+++ b/protobuf/lib/src/protobuf/pb_map.dart
@@ -5,8 +5,17 @@
 part of protobuf;
 
 class PbMap<K, V> extends MapBase<K, V> {
-  final int? keyFieldType;
-  final int? valueFieldType;
+  /// Key type of the map. Per proto2 and proto3 specs, this needs to be an
+  /// integer type or `string`, and the type cannot be `repeated`.
+  ///
+  /// The `int` value is interpreted the same way as [FieldInfo.type].
+  final int keyFieldType;
+
+  /// Value type of the map. Per proto2 and proto3 specs, this can be any type
+  /// other than `map`, and the type cannot be `repeated`.
+  ///
+  /// The `int` value is interpreted the same way as [FieldInfo.type].
+  final int valueFieldType;
 
   static const int _keyFieldNumber = 1;
   static const int _valueFieldNumber = 2;
@@ -52,11 +61,6 @@
       return false;
     }
     for (final key in keys) {
-      if (!other.containsKey(key)) {
-        return false;
-      }
-    }
-    for (final key in keys) {
       if (other[key] != this[key]) {
         return false;
       }
@@ -100,8 +104,10 @@
     _mergeFromCodedBufferReader(mapEntryMeta, entryFieldSet, input, registry!);
     input.checkLastTagWas(0);
     input._currentLimit = oldLimit;
-    var key = entryFieldSet._$get<K>(0, null);
-    var value = entryFieldSet._$get<V>(1, null);
+    var key =
+        entryFieldSet._values[0] ?? mapEntryMeta.byIndex[0].makeDefault!();
+    var value =
+        entryFieldSet._values[1] ?? mapEntryMeta.byIndex[1].makeDefault!();
     _wrappedMap[key] = value;
   }
 
@@ -113,7 +119,7 @@
 
   PbMap freeze() {
     _isReadonly = true;
-    if (_isGroupOrMessage(valueFieldType!)) {
+    if (_isGroupOrMessage(valueFieldType)) {
       for (var subMessage in values as Iterable<GeneratedMessage>) {
         subMessage.freeze();
       }
diff --git a/protobuf/lib/src/protobuf/proto3_json.dart b/protobuf/lib/src/protobuf/proto3_json.dart
index 6d35db9..4b69b9c 100644
--- a/protobuf/lib/src/protobuf/proto3_json.dart
+++ b/protobuf/lib/src/protobuf/proto3_json.dart
@@ -61,13 +61,11 @@
         case PbFieldType._FLOAT_BIT:
         case PbFieldType._DOUBLE_BIT:
           double value = fieldValue;
-          if (value.isNaN) return 'NaN';
+          if (value.isNaN) {
+            return nan;
+          }
           if (value.isInfinite) {
-            if (value.isNegative) {
-              return '-Infinity';
-            } else {
-              return 'Infinity';
-            }
+            return value.isNegative ? negativeInfinity : infinity;
           }
           return value;
         case PbFieldType._UINT64_BIT:
@@ -96,7 +94,7 @@
     if (fieldInfo.isMapField) {
       jsonValue = (value as PbMap).map((key, entryValue) {
         var mapEntryInfo = fieldInfo as MapFieldInfo;
-        return MapEntry(convertToMapKey(key, mapEntryInfo.keyFieldType!),
+        return MapEntry(convertToMapKey(key, mapEntryInfo.keyFieldType),
             valueToProto3Json(entryValue, mapEntryInfo.valueFieldType));
       });
     } else if (fieldInfo.isRepeated) {
@@ -223,6 +221,7 @@
           throw context.parseException(
               'Expected enum as a string or integer', value);
         case PbFieldType._UINT32_BIT:
+        case PbFieldType._FIXED32_BIT:
           int result;
           if (value is int) {
             result = value;
@@ -235,7 +234,6 @@
           return check32BitUnsigned(result);
         case PbFieldType._INT32_BIT:
         case PbFieldType._SINT32_BIT:
-        case PbFieldType._FIXED32_BIT:
         case PbFieldType._SFIXED32_BIT:
           int result;
           if (value is int) {
@@ -366,7 +364,7 @@
                   throw context.parseException('Expected a String key', subKey);
                 }
                 context.addMapIndex(subKey);
-                fieldValues[decodeMapKey(subKey, mapFieldInfo.keyFieldType!)] =
+                fieldValues[decodeMapKey(subKey, mapFieldInfo.keyFieldType)] =
                     convertProto3JsonValue(
                         subValue, mapFieldInfo.valueFieldInfo);
                 context.popIndex();
diff --git a/protobuf/lib/src/protobuf/protobuf_enum.dart b/protobuf/lib/src/protobuf/protobuf_enum.dart
index 32253db..2fb989c 100644
--- a/protobuf/lib/src/protobuf/protobuf_enum.dart
+++ b/protobuf/lib/src/protobuf/protobuf_enum.dart
@@ -54,7 +54,7 @@
   @override
   int get hashCode => value;
 
-  /// Returns this enum's [name].
+  /// Returns this enum's [name] or the [value] if names are not represented.
   @override
-  String toString() => name;
+  String toString() => name == '' ? value.toString() : name;
 }
diff --git a/protobuf/lib/src/protobuf/unknown_field_set.dart b/protobuf/lib/src/protobuf/unknown_field_set.dart
index a0baca6..8c1159c 100644
--- a/protobuf/lib/src/protobuf/unknown_field_set.dart
+++ b/protobuf/lib/src/protobuf/unknown_field_set.dart
@@ -28,6 +28,11 @@
     _fields.clear();
   }
 
+  void clearField(int tagNumber) {
+    _ensureWritable('clearField');
+    _fields.remove(tagNumber);
+  }
+
   UnknownFieldSetField? getField(int tagNumber) => _fields[tagNumber];
 
   bool hasField(int tagNumber) => _fields.containsKey(tagNumber);
@@ -267,12 +272,13 @@
     return hash;
   }
 
-  List get values => []
-    ..addAll(lengthDelimited)
-    ..addAll(varints)
-    ..addAll(fixed32s)
-    ..addAll(fixed64s)
-    ..addAll(groups);
+  List get values => [
+        ...lengthDelimited,
+        ...varints,
+        ...fixed32s,
+        ...fixed64s,
+        ...groups,
+      ];
 
   void writeTo(int fieldNumber, CodedBufferWriter output) {
     void write(type, value) {
diff --git a/protobuf/mono_pkg.yaml b/protobuf/mono_pkg.yaml
index 5f8f170..1136bf1 100644
--- a/protobuf/mono_pkg.yaml
+++ b/protobuf/mono_pkg.yaml
@@ -5,12 +5,12 @@
     - group:
       - format
       - analyze: --fatal-infos
-      dart: [dev]
+      sdk: [dev]
     - group:
       - analyze: lib
       - analyze: test
-      dart: [2.12.0]
+      sdk: [2.12.0]
   - run_tests:
     - group: [test]
-      dart: [2.12.0, dev]
+      sdk: [2.12.0, dev]
       os: [linux, osx, windows]
diff --git a/protobuf/pubspec.yaml b/protobuf/pubspec.yaml
index 0bec734..259c8c6 100644
--- a/protobuf/pubspec.yaml
+++ b/protobuf/pubspec.yaml
@@ -1,19 +1,21 @@
 name: protobuf
-version: 2.0.1-dev
+version: 2.0.1
 description: >-
   Runtime library for protocol buffers support.
   Use https://pub.dev/packages/protoc_plugin to generate dart code for your '.proto' files.
-homepage: https://github.com/google/protobuf.dart
+repository: https://github.com/google/protobuf.dart/tree/master/protobuf
 
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
   fixnum: ^1.0.0
   collection: ^1.15.0
+  meta: ^1.7.0
 
 dev_dependencies:
   test: ^1.16.0
   benchmark_harness: ^2.0.0
   js: ^0.6.3
   lints: ^1.0.0
+  matcher: ^0.12.11
diff --git a/protobuf/test/event_test.dart b/protobuf/test/event_test.dart
index 77c4886..9d3f1d1 100644
--- a/protobuf/test/event_test.dart
+++ b/protobuf/test/event_test.dart
@@ -5,8 +5,6 @@
 /// Tests event delivery using PbEventMixin.
 library event_test;
 
-import 'package:protobuf/protobuf.dart'
-    show GeneratedMessage, Extension, ExtensionRegistry, PbFieldType;
 import 'package:protobuf/protobuf.dart';
 import 'package:protobuf/src/protobuf/mixins/event_mixin.dart'
     show PbEventMixin, PbFieldChange;
diff --git a/protobuf/test/json_test.dart b/protobuf/test/json_test.dart
index 6291ad8..bf72b2b 100644
--- a/protobuf/test/json_test.dart
+++ b/protobuf/test/json_test.dart
@@ -105,7 +105,7 @@
     expect(decoded.int64, value);
   });
 
-  test('tesFrozentInt64JsonEncoding', () {
+  test('testFrozentInt64JsonEncoding', () {
     final value = Int64.parseInt('1234567890123456789');
     final frozen = T()
       ..int64 = value
diff --git a/protoc_plugin/Makefile b/protoc_plugin/Makefile
index 02718d8..3a7ada9 100644
--- a/protoc_plugin/Makefile
+++ b/protoc_plugin/Makefile
@@ -21,10 +21,12 @@
 	google/protobuf/timestamp \
 	google/protobuf/type \
 	google/protobuf/unittest_import \
+	google/protobuf/unittest_nested_any \
 	google/protobuf/unittest_optimize_for \
 	google/protobuf/unittest_well_known_types \
 	google/protobuf/unittest \
 	google/protobuf/wrappers \
+	bytes \
 	custom_option \
 	dart_name \
         default_value_escape \
@@ -54,7 +56,9 @@
 	package1 \
 	package2 \
 	package3 \
+	proto2_repeated \
 	proto3_optional \
+	proto3_repeated \
 	service \
 	service2 \
 	service3 \
@@ -96,7 +100,7 @@
 protos: $(PLUGIN_PATH) $(TEST_PROTO_LIBS)
 
 run-tests: protos
-	pub run test
+	dart run test
 
 clean:
 	rm -rf benchmark/lib/generated
diff --git a/protoc_plugin/README.md b/protoc_plugin/README.md
index 6c06b0f..20506d3 100644
--- a/protoc_plugin/README.md
+++ b/protoc_plugin/README.md
@@ -2,64 +2,96 @@
 
 [![pub package](https://img.shields.io/pub/v/protoc_plugin.svg)](https://pub.dev/packages/protoc_plugin)
 
-This repository provides a plugin for the [protoc
-compiler](https://developers.google.com/protocol-buffers/docs/cpptutorial#compiling-your-protocol-buffers).
-It generates Dart files for working with data in protocol buffers format.
+This repository provides a plugin for the [protoc compiler][1]. It generates
+Dart files for working with data in protocol buffers format.
 
-Requirements
-------------
+[1]: https://developers.google.com/protocol-buffers/docs/cpptutorial#compiling-your-protocol-buffers
 
-To compile a .proto file, you must use the 'protoc' command which is [installed
-separately](https://developers.google.com/protocol-buffers/docs/downloads).
-Protobuf 3.0.0 or above is required.
+## Requirements
 
-The generated files are pure Dart code that run in either in the Dart VM or in a
-browser (using dart2js). They depend the [protobuf Dart
-package](https://pub.dev/packages/protobuf). A Dart project that
-includes generated files should add "protobuf" to its pubspec.yaml file.
+To compile a .proto file, you must use the `protoc` command which is [installed
+separately][1]. protoc 3.0.0 or above is required.
 
+The generated files are pure Dart code that run either in the Dart VM or in a
+browser (using [dart2js][2]). They depend on the [protobuf Dart package][3]. A
+Dart project that includes generated files should add `protobuf` as a
+dependency in the `pubspec.yaml` file.
 
-How to build and use
---------------------
+[1]: https://developers.google.com/protocol-buffers/docs/downloads
+[2]: https://dart.dev/tools/dart2js
+[3]: https://pub.dev/packages/protobuf
 
-*Note:* currently the workflow is POSIX-oriented.
+## How to build
 
-To build standalone `protoc` plugin:
-- run `pub install` to install all dependencies
-- Now you can use the plugin either by adding the `bin` directory to your `PATH`,
-  or passing it directly with `protoc`'s `--plugin` option.
+Make sure you have `dart` executable in your `PATH`. See the [Dart installation
+instructions][1] for details.
 
-Please, remember that the plugin is pure Dart script and requires the presence
-of `dart` executable in your `PATH`.
+If you encounter any issues while following the instructions below, please make
+sure you have the latest version of Dart installed.
 
-When both the `dart` executable and `bin/protoc-gen-dart` are in the
-`PATH` the protocol buffer compiler can be invoked to generate like this:
+[1]: https://dart.dev/get-dart
+
+**The recommended way** is to activate the latest published version of the
+plugin:
+
+    $ dart pub global activate protoc_plugin
+
+Make sure you have `~/.pub-cache/bin` in your `PATH`.
+
+This method installs a Dart script and requires presence of `dart` executable
+in your `PATH`.
+
+**To build from the source:** 
+
+- Run `dart pub get` in `protoc_plugin` directory
+- Add `protoc_plugin/bin` to your `PATH`, or pass the path to
+  `protoc_plugin/bin/protoc-gen-dart` to `protoc`'s `--plugin` option.
+
+The `protoc-gen-dart` executable is a Dart script and requires presence of
+`dart` executable in your `PATH`.
+
+**To build a standalone executable from the source:**
+
+- Run `dart pub get` in `protoc_plugin`
+- Build standalone executable with `dart compile exe bin/protoc_plugin.dart` in
+  `protoc_plugin`
+
+The generated executable does not require a `dart` executable to run. You
+should copy the generated executable `protoc_plugin/bin/protoc_plugin.exe` to
+your `PATH` with name `protoc-gen-dart`, or pass the path to it to `protoc`'s
+`--plugin` option when invoking `protoc`.
+
+## How to use
+
+Once you have `protoc-gen-dart` in the `PATH` the protocol buffer compiler can
+be invoked like this to generate Dart for the proto file `test.proto`:
 
     $ protoc --dart_out=. test.proto
 
-### Optionally using `pub global`
+If you don't want to add `protoc-gen-dart` to `PATH`, you can specify the path
+to it like this:
 
-    $ pub global activate protoc_plugin
-
-And then add `.pub-cache/bin` in your home dir to your `PATH` if you haven't already.
-
-This will activate the latest published version of the plugin. If you wish to use a
-local working copy, use
-
-    $ pub global activate -s path <path/to/your/dart-protoc-plugin>
-
+    $ protoc --dart_out=. test.proto --plugin=<path to plugin executable>
 
 ### Options to control the generated Dart code
 
 The protocol buffer compiler accepts options for each plugin. For the
 Dart plugin, these options are passed together with the `--dart_out`
-option. The individial options are separated using comma, and the
+option. The individual options are separated using comma, and the
 final output directive is separated from the options using colon. Pass
 options `<option 1>` and `<option 2>` like this:
 
     --dart_out="<option 1>,<option 2>:."
+    
+### Generating grpc Headers
 
- ### Generating Code Info
+To generate code for [grpc], you will need to pass in the `grpc` option:
+
+    --dart_out="grpc:."
+
+[grpc]: https://pub.dev/packages/grpc
+
+### Generating Code Info
 
 The plugin includes the `generate_kythe_info` option, which, if passed at run
 time, will make the plugin generate metadata files alongside the `.dart` files
@@ -68,61 +100,69 @@
 
     --dart_out="generate_kythe_info,<other options>:."
 
-Using protocol buffer libraries to build new libraries
-------------------------------------------------------
+## Using protocol buffer libraries to build new libraries
 
 The protocol buffer compiler produces several files for each `.proto` file
 it compiles. In some cases this is not exactly what is needed, e.g one
 would like to create new libraries which exposes the objects in these
-libraries or create new librares combining object definitions from
+libraries or create new libraries combining object definitions from
 several `.proto` libraries into one.
 
-The best way to aproach this is to create the new libraries needed and
+The best way to approach this is to create the new libraries needed and
 re-export the relevant protocol buffer classes.
 
 Say we have the file `m1.proto` with the following content
 
-    message M1 {
-      optional string a;
-    }
+```proto
+syntax = "proto3";
+
+message M1 {
+  string a = 1;
+}
+```
 
 and `m2.proto` containing
 
-    message M2 {
-      optional string b;
-    }
+```proto
+syntax = "proto3";
+
+message M2 {
+  string b = 1;
+}
+```
 
 Compiling these to Dart will produce two libraries in `m1.pb.dart` and
-`m2.pb.dart`. The following code shows a library M which combines
+`m2.pb.dart`. The following code shows a library `M` which combines
 these two protocol buffer libraries, exposes the classes `M1` and `M2` and
-adds som additional methods.
+adds some additional methods.
 
-    library M;
+```dart
+library M;
 
-    import "m1.pb.dart";
-    import "m2.pb.dart";
+import "m1.pb.dart";
+import "m2.pb.dart";
 
-    export "m1.pb.dart" show M1;
-    export "m2.pb.dart" show M2;
+export "m1.pb.dart" show M1;
+export "m2.pb.dart" show M2;
 
-    M1 createM1() => new M1();
-    M2 createM2() => new M2();
+M1 createM1() => new M1();
+M2 createM2() => new M2();
+```
 
-Hacking
--------
+## Hacking
 
 Here are some ways to get protoc:
 
 * Linux: `apt-get install protobuf-compiler`
-* Mac [homebrew](http://brew.sh/): `brew install protobuf`
+* Mac [homebrew](https://brew.sh/): `brew install protobuf`
 
 If the version installed this way doesn't work, an alternative is to
 [compile protoc from source](https://developers.google.com/protocol-buffers/docs/downloads).
 
-Remember to run the tests. That is as easy as `make run-tests`.
+Remember to run the tests. That is as easy as `dart pub get` and then `make
+run-tests`.
 
-Useful references
------------------
+## Useful references
 
 * [Main Dart site](https://dart.dev)
 * [Main protobuf site](https://github.com/protocolbuffers/protobuf)
diff --git a/protoc_plugin/analysis_options.yaml b/protoc_plugin/analysis_options.yaml
index dc7ec89..5cb030e 100644
--- a/protoc_plugin/analysis_options.yaml
+++ b/protoc_plugin/analysis_options.yaml
@@ -6,9 +6,10 @@
 
 linter:
   rules:
-    camel_case_types: true
-    control_flow_in_finally: true
-    directives_ordering: true
-    prefer_relative_imports: true
-    prefer_single_quotes: true
-    throw_in_finally: true
+    - camel_case_types
+    - comment_references
+    - control_flow_in_finally
+    - directives_ordering
+    - prefer_relative_imports
+    - prefer_single_quotes
+    - throw_in_finally
diff --git a/protoc_plugin/bin/protoc_plugin.dart b/protoc_plugin/bin/protoc_plugin.dart
index 8e45871..54da34c 100755
--- a/protoc_plugin/bin/protoc_plugin.dart
+++ b/protoc_plugin/bin/protoc_plugin.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'dart:io';
 
 import 'package:protoc_plugin/protoc.dart';
diff --git a/protoc_plugin/bin/protoc_plugin_bazel.dart b/protoc_plugin/bin/protoc_plugin_bazel.dart
index 843239e..ce74938 100755
--- a/protoc_plugin/bin/protoc_plugin_bazel.dart
+++ b/protoc_plugin/bin/protoc_plugin_bazel.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'dart:io';
 
 import 'package:protoc_plugin/bazel.dart';
diff --git a/protoc_plugin/lib/names.dart b/protoc_plugin/lib/names.dart
index 1694f0b..f96d861 100644
--- a/protoc_plugin/lib/names.dart
+++ b/protoc_plugin/lib/names.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'dart:math' as math;
 
 import 'package:protobuf/meta.dart';
@@ -24,11 +22,15 @@
 
   /// The index of this field in MessageGenerator.fieldList.
   /// The same index will be stored in FieldInfo.index.
-  final int index;
+  ///
+  /// `null` for extensions.
+  final int? index;
 
   /// The position of this field as it appeared in the original DescriptorProto.
   /// Used to construct metadata.
-  final int sourcePosition;
+  ///
+  /// `null` for extensions.
+  final int? sourcePosition;
 
   /// Identifier for generated getters/setters.
   final String fieldName;
@@ -36,17 +38,17 @@
   /// Identifier for the generated hasX() method, without braces.
   ///
   /// `null` for repeated fields.
-  final String hasMethodName;
+  final String? hasMethodName;
 
   /// Identifier for the generated clearX() method, without braces.
   ///
   /// `null` for repeated fields.
-  final String clearMethodName;
+  final String? clearMethodName;
 
   // Identifier for the generated ensureX() method, without braces.
   //
-  //'null' for scalar, repeated, and map fields.
-  final String ensureMethodName;
+  // 'null' for scalar, repeated, and map fields.
+  final String? ensureMethodName;
 
   FieldNames(this.descriptor, this.index, this.sourcePosition, this.fieldName,
       {this.hasMethodName, this.clearMethodName, this.ensureMethodName});
@@ -152,13 +154,13 @@
 ///
 /// The chosen name is added to [usedNames].
 ///
-/// If [variants] is given, all the variants of a name must be available before
-/// that name is chosen, and all the chosen variants will be added to
+/// If [generateVariants] is given, all the variants of a name must be available
+/// before that name is chosen, and all the chosen variants will be added to
 /// [usedNames].
 /// The returned name is that, which will generate the accepted variants.
 String disambiguateName(
     String name, Set<String> usedNames, Iterable<String> suffixes,
-    {List<String> Function(String candidate) generateVariants}) {
+    {List<String> Function(String candidate)? generateVariants}) {
   generateVariants ??= (String name) => <String>[name];
 
   bool allVariantsAvailable(List<String> variants) {
@@ -259,21 +261,19 @@
     indexes[field.name] = index;
   }
 
-  var existingNames = <String>{}
-    ..addAll(reservedMemberNames)
-    ..addAll(reserved);
+  var existingNames = <String>{...reservedMemberNames, ...reserved};
 
-  var fieldNames = List<FieldNames>.filled(indexes.length, null);
+  var fieldNames = List<FieldNames?>.filled(indexes.length, null);
 
   void takeFieldNames(FieldNames chosen) {
-    fieldNames[chosen.index] = chosen;
+    fieldNames[chosen.index!] = chosen;
 
     existingNames.add(chosen.fieldName);
     if (chosen.hasMethodName != null) {
-      existingNames.add(chosen.hasMethodName);
+      existingNames.add(chosen.hasMethodName!);
     }
     if (chosen.clearMethodName != null) {
-      existingNames.add(chosen.clearMethodName);
+      existingNames.add(chosen.clearMethodName!);
     }
   }
 
@@ -281,17 +281,17 @@
   // They have higher priority than automatically chosen names.
   // Explicitly setting a name that's already taken is a build error.
   for (var field in sorted) {
-    if (_nameOption(field).isNotEmpty) {
+    if (_nameOption(field)!.isNotEmpty) {
       takeFieldNames(_memberNamesFromOption(descriptor, field,
-          indexes[field.name], sourcePositions[field.name], existingNames));
+          indexes[field.name]!, sourcePositions[field.name]!, existingNames));
     }
   }
 
   // Then do other fields.
   // They are automatically renamed until we find something unused.
   for (var field in sorted) {
-    if (_nameOption(field).isEmpty) {
-      var index = indexes[field.name];
+    if (_nameOption(field)!.isEmpty) {
+      var index = indexes[field.name]!;
       var sourcePosition = sourcePositions[field.name];
       takeFieldNames(
           _unusedMemberNames(field, index, sourcePosition, existingNames));
@@ -302,16 +302,9 @@
 
   void takeOneofNames(OneofNames chosen) {
     oneofNames.add(chosen);
-
-    if (chosen.whichOneofMethodName != null) {
-      existingNames.add(chosen.whichOneofMethodName);
-    }
-    if (chosen.clearMethodName != null) {
-      existingNames.add(chosen.clearMethodName);
-    }
-    if (chosen.byTagMapName != null) {
-      existingNames.add(chosen.byTagMapName);
-    }
+    existingNames.add(chosen.whichOneofMethodName);
+    existingNames.add(chosen.clearMethodName);
+    existingNames.add(chosen.byTagMapName);
   }
 
   List<String> oneofNameVariants(String name) {
@@ -336,7 +329,7 @@
         _defaultWhichMethodName(oneofName), oneofEnumName, enumMapName));
   }
 
-  return MemberNames(fieldNames, oneofNames);
+  return MemberNames(fieldNames.cast<FieldNames>(), oneofNames);
 }
 
 /// Chooses the member names for a field that has the 'dart_name' option.
@@ -359,7 +352,7 @@
     }
   }
 
-  var name = _nameOption(field);
+  var name = _nameOption(field)!;
   if (name.isEmpty) {
     throw ArgumentError("field doesn't have dart_name option");
   }
@@ -379,7 +372,7 @@
   var clearMethod = 'clear${_capitalize(name)}';
   checkAvailable(clearMethod);
 
-  String ensureMethod;
+  String? ensureMethod;
 
   if (_isGroupOrMessage(field)) {
     ensureMethod = 'ensure${_capitalize(name)}';
@@ -399,8 +392,8 @@
   }
 }
 
-FieldNames _unusedMemberNames(FieldDescriptorProto field, int index,
-    int sourcePosition, Set<String> existingNames) {
+FieldNames _unusedMemberNames(FieldDescriptorProto field, int? index,
+    int? sourcePosition, Set<String> existingNames) {
   if (_isRepeated(field)) {
     return FieldNames(
         field,
@@ -427,7 +420,8 @@
       _memberNamesSuffix(field.number),
       generateVariants: generateNameVariants);
 
-  return FieldNames(field, index, sourcePosition, _defaultFieldName(name),
+  return FieldNames(field, index, sourcePosition,
+      avoidInitialUnderscore(_defaultFieldName(name)),
       hasMethodName: _defaultHasMethodName(name),
       clearMethodName: _defaultClearMethodName(name),
       ensureMethodName:
@@ -454,7 +448,7 @@
 /// The suffix to use for this field in Dart method names.
 /// (It should be camelcase and begin with an uppercase letter.)
 String _fieldMethodSuffix(FieldDescriptorProto field) {
-  var name = _nameOption(field);
+  var name = _nameOption(field)!;
   if (name.isNotEmpty) return _capitalize(name);
 
   if (field.type != FieldDescriptorProto_Type.TYPE_GROUP) {
@@ -483,8 +477,8 @@
     field.type == FieldDescriptorProto_Type.TYPE_MESSAGE ||
     field.type == FieldDescriptorProto_Type.TYPE_GROUP;
 
-String _nameOption(FieldDescriptorProto field) =>
-    field.options.getExtension(Dart_options.dartName) as String;
+String? _nameOption(FieldDescriptorProto field) =>
+    field.options.getExtension(Dart_options.dartName) as String?;
 
 bool _isDartFieldName(String name) => name.startsWith(_dartFieldNameExpr);
 
diff --git a/protoc_plugin/lib/protoc.dart b/protoc_plugin/lib/protoc.dart
index cba09b7..f6001b3 100644
--- a/protoc_plugin/lib/protoc.dart
+++ b/protoc_plugin/lib/protoc.dart
@@ -1,5 +1,3 @@
-// @dart=2.11
-
 import 'dart:convert';
 
 import 'package:dart_style/dart_style.dart';
diff --git a/protoc_plugin/lib/src/base_type.dart b/protoc_plugin/lib/src/base_type.dart
index 0003f1b..4721566 100644
--- a/protoc_plugin/lib/src/base_type.dart
+++ b/protoc_plugin/lib/src/base_type.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 /// Represents the base type of a particular field in a proto definition.
@@ -19,11 +17,11 @@
   final String typeConstantSuffix;
 
   // Method name of the setter method for this type.
-  final String setter;
+  final String? setter;
 
   // The generator corresponding to this type.
   // (Null for primitive types.)
-  final ProtobufContainer generator;
+  final ProtobufContainer? generator;
 
   const BaseType._raw(this.descriptor, this.typeConstantSuffix, this.unprefixed,
       this.setter, this.generator);
@@ -37,18 +35,19 @@
 
   /// The package where this type is declared.
   /// (Always the empty string for primitive types.)
-  String get package => generator == null ? '' : generator.package;
+  String get package => generator == null ? '' : generator!.package;
 
   /// The Dart expression to use for this type when in a different file.
   String get prefixed => generator == null
       ? unprefixed
-      : generator.fileImportPrefix + '.' + unprefixed;
+      : generator!.fileImportPrefix + '.' + unprefixed;
 
   /// Returns the name to use in generated code for this Dart type.
   ///
   /// Doesn't include the List type for repeated fields.
-  /// [protoFileUri] represents the current proto file where we are generating
-  /// code. The Dart class might be imported from a different proto file.
+  /// [FileGenerator.protoFileUri] represents the current proto file where we
+  /// are generating code.
+  /// The Dart class might be imported from a different proto file.
   String getDartType(FileGenerator fileGen) =>
       (fileGen.protoFileUri == generator?.fileGen?.protoFileUri)
           ? unprefixed
@@ -134,6 +133,6 @@
     }
 
     return BaseType._raw(
-        field.type, constSuffix, generator.classname, null, generator);
+        field.type, constSuffix, generator.classname!, null, generator);
   }
 }
diff --git a/protoc_plugin/lib/src/client_generator.dart b/protoc_plugin/lib/src/client_generator.dart
index 500c0c9..8fa0374 100644
--- a/protoc_plugin/lib/src/client_generator.dart
+++ b/protoc_plugin/lib/src/client_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 class ClientApiGenerator {
diff --git a/protoc_plugin/lib/src/code_generator.dart b/protoc_plugin/lib/src/code_generator.dart
index 38e7596..1d93559 100644
--- a/protoc_plugin/lib/src/code_generator.dart
+++ b/protoc_plugin/lib/src/code_generator.dart
@@ -2,10 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'dart:io';
-import 'dart:typed_data';
 
 import 'package:fixnum/fixnum.dart';
 import 'package:protobuf/protobuf.dart';
@@ -24,7 +21,7 @@
   static int _idx = 0;
 
   String get package;
-  String get classname;
+  String? get classname;
   String get fullName;
 
   /// The field path contains the field IDs and indices (for repeated fields)
@@ -33,7 +30,7 @@
   /// the message in question.
   /// For more information see
   /// https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto#L728
-  List<int> get fieldPath;
+  List<int>? get fieldPath;
 
   /// The fully qualified name with a leading '.'.
   ///
@@ -43,12 +40,12 @@
   String get fileImportPrefix => _getFileImportPrefix();
 
   String get binaryDescriptorName =>
-      '${lowerCaseFirstLetter(classname)}Descriptor';
+      '${lowerCaseFirstLetter(classname!)}Descriptor';
 
   String _getFileImportPrefix() {
-    var path = fileGen.protoFileUri.toString();
+    var path = fileGen!.protoFileUri.toString();
     if (_importPrefixes.containsKey(path)) {
-      return _importPrefixes[path];
+      return _importPrefixes[path]!;
     }
     final alias = '\$$_idx';
     _importPrefixes[path] = alias;
@@ -59,21 +56,21 @@
   /// The generator of the .pb.dart file defining this entity.
   ///
   /// (Represents the .pb.dart file that we need to import in order to use it.)
-  FileGenerator get fileGen;
+  FileGenerator? get fileGen;
 
   // The generator containing this entity.
-  ProtobufContainer get parent;
+  ProtobufContainer? get parent;
 
   /// The top-level parent of this entity. If this entity is a top-level entity,
   /// returns this.
-  ProtobufContainer get toplevelParent {
+  ProtobufContainer? get toplevelParent {
     if (parent == null) {
       return null;
     }
     if (parent is FileGenerator) {
       return this;
     }
-    return parent.toplevelParent;
+    return parent?.toplevelParent;
   }
 }
 
@@ -85,14 +82,12 @@
 
   /// Runs the code generator. The optional [optionParsers] can be used to
   /// change how command line options are parsed (see [parseGenerationOptions]
-  /// for details), and [outputConfiguration] can be used to override where
+  /// for details), and [config] can be used to override where
   /// generated files are created and how imports between generated files are
   /// constructed (see [OutputConfiguration] for details).
   void generate(
-      {Map<String, SingleOptionParser> optionParsers,
-      OutputConfiguration config}) {
-    config ??= DefaultOutputConfiguration();
-
+      {Map<String, SingleOptionParser>? optionParsers,
+      OutputConfiguration config = const DefaultOutputConfiguration()}) {
     var extensions = ExtensionRegistry();
     Dart_options.registerAllExtensions(extensions);
 
@@ -137,14 +132,19 @@
 
   @override
   String get package => '';
+
   @override
-  String get classname => null;
+  String? get classname => null;
+
   @override
   String get fullName => '';
+
   @override
-  FileGenerator get fileGen => null;
+  FileGenerator? get fileGen => null;
+
   @override
-  ProtobufContainer get parent => null;
+  ProtobufContainer? get parent => null;
+
   @override
-  List<int> get fieldPath => [];
+  List<int>? get fieldPath => [];
 }
diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart
index 2b3da04..b1159ce 100644
--- a/protoc_plugin/lib/src/enum_generator.dart
+++ b/protoc_plugin/lib/src/enum_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 class EnumAlias {
@@ -14,11 +12,14 @@
 
 class EnumGenerator extends ProtobufContainer {
   @override
-  final ProtobufContainer parent;
+  final ProtobufContainer? parent;
+
   @override
-  final String classname;
+  final String? classname;
+
   @override
   final String fullName;
+
   final EnumDescriptorProto _descriptor;
   final List<EnumValueDescriptorProto> _canonicalValues =
       <EnumValueDescriptorProto>[];
@@ -28,20 +29,19 @@
   /// Maps the name of an enum value to the Dart name we will use for it.
   final Map<String, String> dartNames = <String, String>{};
   final List<int> _originalAliasIndices = <int>[];
-  List<int> _fieldPath;
+  List<int>? _fieldPath;
   final List<int> _fieldPathSegment;
 
   /// See [[ProtobufContainer]
   @override
-  List<int> get fieldPath =>
-      _fieldPath ??= List.from(parent.fieldPath)..addAll(_fieldPathSegment);
+  List<int>? get fieldPath =>
+      _fieldPath ??= List.from(parent!.fieldPath!)..addAll(_fieldPathSegment);
 
   EnumGenerator._(EnumDescriptorProto descriptor, this.parent,
       Set<String> usedClassNames, int repeatedFieldIndex, int fieldIdTag)
-      : assert(parent != null),
-        _fieldPathSegment = [fieldIdTag, repeatedFieldIndex],
+      : _fieldPathSegment = [fieldIdTag, repeatedFieldIndex],
         classname = messageOrEnumClassName(descriptor.name, usedClassNames,
-            parent: parent?.classname ?? ''),
+            parent: parent!.classname ?? ''),
         fullName = parent.fullName == ''
             ? descriptor.name
             : '${parent.fullName}.${descriptor.name}',
@@ -80,9 +80,10 @@
             _nestedFieldTag);
 
   @override
-  String get package => parent.package;
+  String get package => parent!.package;
+
   @override
-  FileGenerator get fileGen => parent.fileGen;
+  FileGenerator? get fileGen => parent!.fileGen;
 
   /// Make this enum available as a field type.
   void register(GenerationContext ctx) {
@@ -93,7 +94,7 @@
   /// [usage] represents the .pb.dart file where the expression will be used.
   String getJsonConstant(FileGenerator usage) {
     var name = '$classname\$json';
-    if (usage.protoFileUri == fileGen.protoFileUri) {
+    if (usage.protoFileUri == fileGen!.protoFileUri) {
       return name;
     }
     return '$fileImportPrefix.$name';
@@ -106,13 +107,15 @@
         'class $classname extends $protobufImportPrefix.ProtobufEnum {',
         '}\n', [
       NamedLocation(
-          name: classname, fieldPathSegment: fieldPath, start: 'class '.length)
+          name: classname!,
+          fieldPathSegment: fieldPath!,
+          start: 'class '.length)
     ], () {
       // -----------------------------------------------------------------
       // Define enum types.
       for (var i = 0; i < _canonicalValues.length; i++) {
         var val = _canonicalValues[i];
-        final name = dartNames[val.name];
+        final name = dartNames[val.name]!;
         final conditionalValName = configurationDependent(
             'protobuf.omit_enum_names', quoted(val.name));
         out.printlnAnnotated(
@@ -121,7 +124,7 @@
             [
               NamedLocation(
                   name: name,
-                  fieldPathSegment: List.from(fieldPath)
+                  fieldPathSegment: List.from(fieldPath!)
                     ..addAll([_enumValueTag, _originalCanonicalIndices[i]]),
                   start: 'static const $classname '.length)
             ]);
@@ -130,14 +133,14 @@
         out.println();
         for (var i = 0; i < _aliases.length; i++) {
           var alias = _aliases[i];
-          final name = dartNames[alias.value.name];
+          final name = dartNames[alias.value.name]!;
           out.printlnAnnotated(
               'static const $classname $name ='
               ' ${dartNames[alias.canonicalValue.name]};',
               [
                 NamedLocation(
                     name: name,
-                    fieldPathSegment: List.from(fieldPath)
+                    fieldPathSegment: List.from(fieldPath!)
                       ..addAll([_enumValueTag, _originalAliasIndices[i]]),
                     start: 'static const $classname '.length)
               ]);
@@ -169,11 +172,11 @@
 
   /// Writes a Dart constant containing the JSON for the EnumProtoDescriptor.
   void generateConstants(IndentingWriter out) {
-    var name = getJsonConstant(fileGen);
+    var name = getJsonConstant(fileGen!);
     var json = _descriptor.writeToJsonMap();
 
     out.println('@$coreImportPrefix.Deprecated'
-        '(\'Use ${toplevelParent.binaryDescriptorName} instead\')');
+        '(\'Use ${toplevelParent!.binaryDescriptorName} instead\')');
     out.print('const $name = ');
     writeJsonConst(out, json);
     out.println(';');
diff --git a/protoc_plugin/lib/src/extension_generator.dart b/protoc_plugin/lib/src/extension_generator.dart
index 9e8f088..1351038 100644
--- a/protoc_plugin/lib/src/extension_generator.dart
+++ b/protoc_plugin/lib/src/extension_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 class ExtensionGenerator {
@@ -11,15 +9,15 @@
   final ProtobufContainer _parent;
 
   // populated by resolve()
-  ProtobufField _field;
+  late ProtobufField _field;
+  bool _resolved = false;
   final String _extensionName;
   String _extendedFullName = '';
-  List<int> _fieldPath;
   final List<int> _fieldPathSegment;
 
-  /// See [[ProtobufContainer]
-  List<int> get fieldPath =>
-      _fieldPath ??= List.from(_parent.fieldPath)..addAll(_fieldPathSegment);
+  /// See [ProtobufContainer]
+  late final List<int> fieldPath = List.from(_parent.fieldPath!)
+    ..addAll(_fieldPathSegment);
 
   ExtensionGenerator._(this._descriptor, this._parent, Set<String> usedNames,
       int repeatedFieldIndex, int fieldIdTag)
@@ -40,6 +38,7 @@
 
   void resolve(GenerationContext ctx) {
     _field = ProtobufField.extension(_descriptor, _parent, ctx);
+    _resolved = true;
 
     var extendedType = ctx.getFieldType(_descriptor.extendee);
     // TODO(skybrian) When would this be null?
@@ -51,16 +50,16 @@
   String get package => _parent.package;
 
   /// The generator of the .pb.dart file where this extension will be defined.
-  FileGenerator get fileGen => _parent.fileGen;
+  FileGenerator? get fileGen => _parent.fileGen;
 
   String get name {
-    if (_field == null) throw StateError('resolve not called');
+    if (!_resolved) throw StateError('resolve not called');
     var name = _extensionName;
     return _parent is MessageGenerator ? '${_parent.classname}.$name' : name;
   }
 
   bool get needsFixnumImport {
-    if (_field == null) throw StateError('resolve not called');
+    if (!_resolved) throw StateError('resolve not called');
     return _field.needsFixnumImport;
   }
 
@@ -70,37 +69,35 @@
   /// add its generator.
   void addImportsTo(
       Set<FileGenerator> imports, Set<FileGenerator> enumImports) {
-    if (_field == null) throw StateError('resolve not called');
+    if (!_resolved) throw StateError('resolve not called');
     var typeGen = _field.baseType.generator;
     if (typeGen != null) {
       // The type of this extension is defined in a different file,
       // so we need to import it.
       if (typeGen is EnumGenerator) {
         // Enums are always in a different file.
-        enumImports.add(typeGen.fileGen);
+        enumImports.add(typeGen.fileGen!);
       } else if (typeGen.fileGen != fileGen) {
-        imports.add(typeGen.fileGen);
+        imports.add(typeGen.fileGen!);
       }
     }
   }
 
-  /// Adds dependencies of [generateConstants] to [imports].
-  ///
   /// For each .pb.dart file that the generated code needs to import,
   /// add its generator.
   void addConstantImportsTo(Set<FileGenerator> imports) {
-    if (_field == null) throw StateError('resolve not called');
+    if (!_resolved) throw StateError('resolve not called');
     // No dependencies - nothing to do.
   }
 
   void generate(IndentingWriter out) {
-    if (_field == null) throw StateError('resolve not called');
+    if (!_resolved) throw StateError('resolve not called');
 
     var name = _extensionName;
     final conditionalName = configurationDependent(
         'protobuf.omit_field_names', quoted(_extensionName));
     var type = _field.baseType;
-    var dartType = type.getDartType(fileGen);
+    var dartType = type.getDartType(fileGen!);
     final conditionalExtendedName = configurationDependent(
         'protobuf.omit_message_names', quoted(_extendedFullName));
 
@@ -111,7 +108,7 @@
     positionals.add('${_field.number}');
     positionals.add(_field.typeConstant);
 
-    var named = <String, String>{};
+    var named = <String, String?>{};
     named['protoName'] = _field.quotedProtoName;
     if (_field.isRepeated) {
       invocation = '$protobufImportPrefix.Extension<$dartType>.repeated';
@@ -125,16 +122,15 @@
       }
     } else {
       invocation = '$protobufImportPrefix.Extension<$dartType>';
-      named['defaultOrMaker'] = _field.generateDefaultFunction(fileGen);
+      named['defaultOrMaker'] = _field.generateDefaultFunction();
       if (type.isMessage || type.isGroup) {
         named['subBuilder'] = '$dartType.create';
       } else if (type.isEnum) {
-        var dartEnum = type.getDartType(fileGen);
+        var dartEnum = type.getDartType(fileGen!);
         named['valueOf'] = '$dartEnum.valueOf';
         named['enumValues'] = '$dartEnum.values';
       }
     }
-    assert(invocation != null);
     var fieldDefinition = 'static final ';
     out.printAnnotated(
         '$fieldDefinition$name = '
diff --git a/protoc_plugin/lib/src/file_generator.dart b/protoc_plugin/lib/src/file_generator.dart
index afd013c..b2600b6 100644
--- a/protoc_plugin/lib/src/file_generator.dart
+++ b/protoc_plugin/lib/src/file_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 final _dartIdentifier = RegExp(r'^\w+$');
@@ -32,6 +30,11 @@
       ': $value';
 }
 
+enum ProtoSyntax {
+  proto2,
+  proto3,
+}
+
 /// Generates the Dart output files for one .proto input file.
 ///
 /// Outputs include .pb.dart, pbenum.dart, and .pbjson.dart.
@@ -91,16 +94,16 @@
           throw mixinError('Cycle in parent chain: $cycle');
         }
         parentChain.add(parentName);
-        currentMixin = dartMixins[parentName];
+        currentMixin = dartMixins[parentName]!;
       }
     }
 
     // Turn DartMixins into PbMixins.
     final pbMixins = <String, PbMixin>{};
-    PbMixin resolveMixin(String name) {
+    PbMixin? resolveMixin(String name) {
       if (pbMixins.containsKey(name)) return pbMixins[name];
       if (dartMixins.containsKey(name)) {
-        var dartMixin = dartMixins[name];
+        var dartMixin = dartMixins[name]!;
         var pbMixin = PbMixin(dartMixin.name,
             importFrom: dartMixin.importFrom,
             parent: resolveMixin(dartMixin.parent));
@@ -145,8 +148,13 @@
   /// True if cross-references have been resolved.
   bool _linked = false;
 
+  final ProtoSyntax syntax;
+
   FileGenerator(this.descriptor, this.options)
-      : protoFileUri = Uri.file(descriptor.name) {
+      : protoFileUri = Uri.file(descriptor.name),
+        syntax = descriptor.syntax == 'proto3'
+            ? ProtoSyntax.proto3
+            : ProtoSyntax.proto2 {
     if (protoFileUri.isAbsolute) {
       // protoc should never generate an import with an absolute path.
       throw 'FAILURE: Import with absolute path is not supported';
@@ -154,7 +162,7 @@
 
     var declaredMixins = _getDeclaredMixins(descriptor);
     var defaultMixinName =
-        descriptor.options?.getExtension(Dart_options.defaultMixin) as String ??
+        descriptor.options.getExtension(Dart_options.defaultMixin) as String? ??
             '';
     var defaultMixin =
         declaredMixins[defaultMixinName] ?? findMixin(defaultMixinName);
@@ -206,14 +214,19 @@
 
   @override
   String get package => descriptor.package;
+
   @override
   String get classname => '';
+
   @override
   String get fullName => descriptor.package;
+
   @override
   FileGenerator get fileGen => this;
+
   @override
-  ProtobufContainer get parent => null;
+  ProtobufContainer? get parent => null;
+
   @override
   List<int> get fieldPath => [];
 
@@ -638,6 +651,7 @@
   'prefer_final_fields',
   'return_of_invalid_type',
   'unnecessary_const',
+  'unnecessary_import',
   'unnecessary_this',
   'unused_import',
   'unused_shown_name',
diff --git a/protoc_plugin/lib/src/grpc_generator.dart b/protoc_plugin/lib/src/grpc_generator.dart
index 73fe67e..dd936aa 100644
--- a/protoc_plugin/lib/src/grpc_generator.dart
+++ b/protoc_plugin/lib/src/grpc_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 class GrpcServiceGenerator {
@@ -24,13 +22,13 @@
   final _undefinedDeps = <String, String>{};
 
   /// Fully-qualified gRPC service name.
-  String _fullServiceName;
+  late final String _fullServiceName;
 
   /// Dart class name for client stub.
-  String _clientClassname;
+  late final String _clientClassname;
 
   /// Dart class name for server stub.
-  String _serviceClassname;
+  late final String _serviceClassname;
 
   /// List of gRPC methods.
   final _methods = <_GrpcMethod>[];
@@ -39,7 +37,7 @@
     final name = _descriptor.name;
     final package = fileGen.package;
 
-    if (package != null && package.isNotEmpty) {
+    if (package.isNotEmpty) {
       _fullServiceName = '$package.$name';
     } else {
       _fullServiceName = name;
@@ -69,7 +67,7 @@
   void _addDependency(GenerationContext ctx, String fqname, String location) {
     if (_deps.containsKey(fqname)) return; // Already added.
 
-    final mg = ctx.getFieldType(fqname) as MessageGenerator;
+    final mg = ctx.getFieldType(fqname) as MessageGenerator?;
     if (mg == null) {
       _undefinedDeps[fqname] = location;
       return;
diff --git a/protoc_plugin/lib/src/linker.dart b/protoc_plugin/lib/src/linker.dart
index 740e719..ef99f9f 100644
--- a/protoc_plugin/lib/src/linker.dart
+++ b/protoc_plugin/lib/src/linker.dart
@@ -2,14 +2,12 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import '../protoc.dart' show FileGenerator;
 import 'code_generator.dart';
 import 'options.dart';
 
 /// Resolves all cross-references in a set of proto files.
-void link(GenerationOptions options, Iterable<FileGenerator> files) {
+void link(GenerationOptions? options, Iterable<FileGenerator> files) {
   var ctx = GenerationContext(options);
 
   // Register the targets of cross-references.
@@ -41,7 +39,7 @@
 }
 
 class GenerationContext {
-  final GenerationOptions options;
+  final GenerationOptions? options;
 
   /// The files available for import.
   final Map<String, FileGenerator> _files = <String, FileGenerator>{};
@@ -67,9 +65,9 @@
 
   /// Returns info about a .pb.dart being imported,
   /// based on the filename given to us by protoc.
-  FileGenerator getImportedProtoFile(String name) => _files[name];
+  FileGenerator? getImportedProtoFile(String name) => _files[name];
 
   /// Returns info about the type of a message, group, or enum field,
   /// based on the fully qualified name given to us by protoc.
-  ProtobufContainer getFieldType(String name) => _typeRegistry[name];
+  ProtobufContainer? getFieldType(String name) => _typeRegistry[name];
 }
diff --git a/protoc_plugin/lib/src/message_generator.dart b/protoc_plugin/lib/src/message_generator.dart
index acf3bfa..a2efc02 100644
--- a/protoc_plugin/lib/src/message_generator.dart
+++ b/protoc_plugin/lib/src/message_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 /// Generates the Dart enum corresponding to a oneof declaration.
@@ -15,7 +13,7 @@
       IndentingWriter out, String classname, List<ProtobufField> fields) {
     out.addBlock('enum $classname {', '}\n', () {
       for (var field in fields) {
-        final name = oneofEnumMemberName(field.memberNames.fieldName);
+        final name = oneofEnumMemberName(field.memberNames!.fieldName);
         out.println('$name, ');
       }
       out.println('notSet');
@@ -51,10 +49,11 @@
   String get messageName =>
       fullName.substring(package.isEmpty ? 0 : package.length + 1);
 
-  PbMixin mixin;
+  PbMixin? mixin;
 
   @override
-  final ProtobufContainer parent;
+  final ProtobufContainer? parent;
+
   final DescriptorProto _descriptor;
   final List<EnumGenerator> _enumGenerators = <EnumGenerator>[];
   final List<MessageGenerator> _messageGenerators = <MessageGenerator>[];
@@ -64,18 +63,18 @@
   /// by the index in the containing types's oneof_decl list.
   /// Only contains the 'real' oneofs.
   final List<List<ProtobufField>> _oneofFields;
-  List<OneofNames> _oneofNames;
+  late List<OneofNames> _oneofNames;
 
-  List<int> _fieldPath;
   final List<int> _fieldPathSegment;
 
   /// See [[ProtobufContainer]
   @override
-  List<int> get fieldPath =>
-      _fieldPath ??= List.from(parent.fieldPath)..addAll(_fieldPathSegment);
+  late final List<int> fieldPath = List.from(parent!.fieldPath!)
+    ..addAll(_fieldPathSegment);
 
   // populated by resolve()
-  List<ProtobufField> _fieldList;
+  late List<ProtobufField> _fieldList;
+  bool _resolved = false;
 
   Set<String> _usedTopLevelNames;
 
@@ -83,7 +82,7 @@
       DescriptorProto descriptor,
       this.parent,
       Map<String, PbMixin> declaredMixins,
-      PbMixin defaultMixin,
+      PbMixin? defaultMixin,
       this._usedTopLevelNames,
       int repeatedFieldIndex,
       int fieldIdTag)
@@ -92,7 +91,7 @@
         classname = messageOrEnumClassName(descriptor.name, _usedTopLevelNames,
             parent: parent?.classname ?? ''),
         assert(parent != null),
-        fullName = parent.fullName == ''
+        fullName = parent!.fullName == ''
             ? descriptor.name
             : '${parent.fullName}.${descriptor.name}',
         _oneofFields =
@@ -127,7 +126,7 @@
       DescriptorProto descriptor,
       ProtobufContainer parent,
       Map<String, PbMixin> declaredMixins,
-      PbMixin defaultMixin,
+      PbMixin? defaultMixin,
       Set<String> usedNames,
       int repeatedFieldIndex)
       : this._(descriptor, parent, declaredMixins, defaultMixin, usedNames,
@@ -137,22 +136,22 @@
       DescriptorProto descriptor,
       ProtobufContainer parent,
       Map<String, PbMixin> declaredMixins,
-      PbMixin defaultMixin,
+      PbMixin? defaultMixin,
       Set<String> usedNames,
       int repeatedFieldIndex)
       : this._(descriptor, parent, declaredMixins, defaultMixin, usedNames,
             repeatedFieldIndex, _nestedMessageTag);
 
   @override
-  String get package => parent.package;
+  String get package => parent!.package;
 
   /// The generator of the .pb.dart file that will declare this type.
   @override
-  FileGenerator get fileGen => parent.fileGen;
+  FileGenerator get fileGen => parent!.fileGen!;
 
   /// Throws an exception if [resolve] hasn't been called yet.
   void checkResolved() {
-    if (_fieldList == null) {
+    if (!_resolved) {
       throw StateError('message not resolved: $fullName');
     }
   }
@@ -170,7 +169,7 @@
   /// Adds all mixins used in this message and any submessages.
   void addMixinsTo(Set<PbMixin> output) {
     if (mixin != null) {
-      output.addAll(mixin.findMixinsToApply());
+      output.addAll(mixin!.findMixinsToApply());
     }
     for (var m in _messageGenerators) {
       m.addMixinsTo(output);
@@ -190,7 +189,8 @@
 
   // Creates fields and resolves extension targets.
   void resolve(GenerationContext ctx) {
-    if (_fieldList != null) throw StateError('message already resolved');
+    if (_resolved) throw StateError('message already resolved');
+    _resolved = true;
 
     var reserved = mixin?.findReservedNames() ?? const <String>[];
     var members = messageMemberNames(_descriptor, classname, _usedTopLevelNames,
@@ -216,7 +216,7 @@
   }
 
   bool get needsFixnumImport {
-    if (_fieldList == null) throw StateError('message not resolved');
+    checkResolved();
     for (var field in _fieldList) {
       if (field.needsFixnumImport) return true;
     }
@@ -235,13 +235,13 @@
   /// add its generator.
   void addImportsTo(
       Set<FileGenerator> imports, Set<FileGenerator> enumImports) {
-    if (_fieldList == null) throw StateError('message not resolved');
+    checkResolved();
     for (var field in _fieldList) {
       var typeGen = field.baseType.generator;
       if (typeGen is EnumGenerator) {
-        enumImports.add(typeGen.fileGen);
+        enumImports.add(typeGen.fileGen!);
       } else if (typeGen != null) {
-        imports.add(typeGen.fileGen);
+        imports.add(typeGen.fileGen!);
       }
     }
     for (var m in _messageGenerators) {
@@ -266,7 +266,7 @@
   /// For each .pbjson.dart file that the generated code needs to import,
   /// add its generator.
   void addConstantImportsTo(Set<FileGenerator> imports) {
-    if (_fieldList == null) throw StateError('message not resolved');
+    checkResolved();
     for (var m in _messageGenerators) {
       m.addConstantImportsTo(imports);
     }
@@ -293,7 +293,7 @@
     var mixinClause = '';
     if (mixin != null) {
       var mixinNames =
-          mixin.findMixinsToApply().map((m) => '$mixinImportPrefix.${m.name}');
+          mixin!.findMixinsToApply().map((m) => '$mixinImportPrefix.${m.name}');
       mixinClause = ' with ${mixinNames.join(", ")}';
     }
 
@@ -304,8 +304,8 @@
     var packageClause =
         package == '' ? '' : ', package: $conditionalPackageName';
     var proto3JsonClause = (mixin?.hasProto3JsonHelpers ?? false)
-        ? ', toProto3Json: $mixinImportPrefix.${mixin.name}.toProto3JsonHelper, '
-            'fromProto3Json: $mixinImportPrefix.${mixin.name}.fromProto3JsonHelper'
+        ? ', toProto3Json: $mixinImportPrefix.${mixin!.name}.toProto3JsonHelper, '
+            'fromProto3Json: $mixinImportPrefix.${mixin!.name}.fromProto3JsonHelper'
         : '';
     out.addAnnotatedBlock(
         'class $classname extends $protobufImportPrefix.GeneratedMessage$mixinClause {',
@@ -319,7 +319,7 @@
             '};', () {
           for (var field in _oneofFields[oneof.index]) {
             final oneofMemberName =
-                oneofEnumMemberName(field.memberNames.fieldName);
+                oneofEnumMemberName(field.memberNames!.fieldName);
             out.println(
                 '${field.number} : ${oneof.oneofEnumName}.$oneofMemberName,');
           }
@@ -342,7 +342,7 @@
         }
 
         for (var field in _fieldList) {
-          out.println(field.generateBuilderInfoCall(fileGen, package));
+          out.println(field.generateBuilderInfoCall(package));
         }
 
         if (_descriptor.extensionRange.isNotEmpty) {
@@ -369,10 +369,10 @@
           _emitDeprecatedIf(field.isDeprecated, out);
           if (field.isRepeated && !field.isMapField) {
             out.println(
-                '  ${field.baseType.getRepeatedDartTypeIterable(fileGen)}? ${field.memberNames.fieldName},');
+                '  ${field.baseType.getRepeatedDartTypeIterable(fileGen)}? ${field.memberNames!.fieldName},');
           } else {
             out.println(
-                '  ${field.getDartType(fileGen)}? ${field.memberNames.fieldName},');
+                '  ${field.getDartType()}? ${field.memberNames!.fieldName},');
           }
         }
         out.print('}');
@@ -381,17 +381,17 @@
         out.println(') {');
         out.println('  final _result = create();');
         for (final field in _fieldList) {
-          out.println('  if (${field.memberNames.fieldName} != null) {');
+          out.println('  if (${field.memberNames!.fieldName} != null) {');
           if (field.isDeprecated) {
             out.println(
                 '    // ignore: deprecated_member_use_from_same_package');
           }
           if (field.isRepeated || field.isMapField) {
             out.println(
-                '    _result.${field.memberNames.fieldName}.addAll(${field.memberNames.fieldName});');
+                '    _result.${field.memberNames!.fieldName}.addAll(${field.memberNames!.fieldName});');
           } else {
             out.println(
-                '    _result.${field.memberNames.fieldName} = ${field.memberNames.fieldName};');
+                '    _result.${field.memberNames!.fieldName} = ${field.memberNames!.fieldName};');
           }
           out.println('  }');
         }
@@ -452,7 +452,7 @@
   // already_seen is used to avoid checking the same type multiple times
   // (and also to protect against unbounded recursion).
   bool _hasRequiredFields(MessageGenerator type, Set alreadySeen) {
-    if (type._fieldList == null) throw StateError('message not resolved');
+    checkResolved();
 
     if (alreadySeen.contains(type.fullName)) {
       // The type is already in cache.  This means that either:
@@ -495,7 +495,7 @@
     for (var field in _fieldList) {
       out.println();
       var memberFieldPath = List<int>.from(fieldPath)
-        ..addAll([_messageFieldTag, field.sourcePosition]);
+        ..addAll([_messageFieldTag, field.sourcePosition!]);
       generateFieldAccessorsMutators(field, out, memberFieldPath);
     }
   }
@@ -510,17 +510,17 @@
 
   void generateFieldAccessorsMutators(
       ProtobufField field, IndentingWriter out, List<int> memberFieldPath) {
-    var fieldTypeString = field.getDartType(fileGen);
+    var fieldTypeString = field.getDartType();
     var defaultExpr = field.getDefaultExpr();
     var names = field.memberNames;
 
     _emitDeprecatedIf(field.isDeprecated, out);
     _emitOverrideIf(field.overridesGetter, out);
     _emitIndexAnnotation(field.number, out);
-    final getterExpr = _getterExpression(fieldTypeString, field.index,
+    final getterExpr = _getterExpression(fieldTypeString, field.index!,
         defaultExpr, field.isRepeated, field.isMapField);
     out.printlnAnnotated(
-        '$fieldTypeString get ${names.fieldName} => $getterExpr;', [
+        '$fieldTypeString get ${names!.fieldName} => $getterExpr;', [
       NamedLocation(
           name: names.fieldName,
           fieldPathSegment: memberFieldPath,
@@ -579,7 +579,7 @@
             ' \$_has(${field.index});',
             [
               NamedLocation(
-                  name: names.hasMethodName,
+                  name: names.hasMethodName!,
                   fieldPathSegment: memberFieldPath,
                   start: '$coreImportPrefix.bool '.length)
             ]);
@@ -592,7 +592,7 @@
           ' clearField(${field.number});',
           [
             NamedLocation(
-                name: names.clearMethodName,
+                name: names.clearMethodName!,
                 fieldPathSegment: memberFieldPath,
                 start: 'void '.length)
           ]);
@@ -604,7 +604,7 @@
             '\$_ensure(${field.index});',
             <NamedLocation>[
               NamedLocation(
-                  name: names.ensureMethodName,
+                  name: names.ensureMethodName!,
                   fieldPathSegment: memberFieldPath,
                   start: '$fieldTypeString '.length)
             ]);
@@ -677,8 +677,8 @@
   void generateConstants(IndentingWriter out) {
     const nestedTypeTag = 3;
     const enumTypeTag = 4;
-    assert(_descriptor.info_.fieldInfo[nestedTypeTag].name == 'nestedType');
-    assert(_descriptor.info_.fieldInfo[enumTypeTag].name == 'enumType');
+    assert(_descriptor.info_.fieldInfo[nestedTypeTag]!.name == 'nestedType');
+    assert(_descriptor.info_.fieldInfo[enumTypeTag]!.name == 'enumType');
 
     var name = getJsonConstant(fileGen);
     var json = _descriptor.writeToJsonMap();
@@ -688,7 +688,7 @@
         _enumGenerators.map((e) => e.getJsonConstant(fileGen)).toList();
 
     out.println('@$coreImportPrefix.Deprecated'
-        '(\'Use ${toplevelParent.binaryDescriptorName} instead\')');
+        '(\'Use ${toplevelParent!.binaryDescriptorName} instead\')');
     out.addBlock('const $name = const {', '};', () {
       for (var key in json.keys) {
         out.print("'$key': ");
@@ -718,9 +718,10 @@
 
   /// Returns the mixin for this message, or null if none.
   ///
-  /// First searches [wellKnownMixins], then [declaredMixins],
+  /// First searches [_wellKnownMixins], then [declaredMixins],
   /// then internal mixins declared by [findMixin].
-  PbMixin _getMixin(Map<String, PbMixin> declaredMixins, PbMixin defaultMixin) {
+  PbMixin? _getMixin(
+      Map<String, PbMixin> declaredMixins, PbMixin? defaultMixin) {
     var wellKnownMixin = wellKnownMixinForFullName(fullName);
     if (wellKnownMixin != null) return wellKnownMixin;
     if (!_descriptor.hasOptions() ||
@@ -732,7 +733,7 @@
     if (name.isEmpty) return null; // don't use any mixins (override default)
     var mixin = declaredMixins[name] ?? findMixin(name);
     if (mixin == null) {
-      throw '${_descriptor.name} in ${parent.fileGen.descriptor.name}: mixin "$name" not found';
+      throw '${_descriptor.name} in ${parent!.fileGen!.descriptor.name}: mixin "$name" not found';
     }
     return mixin;
   }
diff --git a/protoc_plugin/lib/src/options.dart b/protoc_plugin/lib/src/options.dart
index 55323ba..aa7b463 100644
--- a/protoc_plugin/lib/src/options.dart
+++ b/protoc_plugin/lib/src/options.dart
@@ -92,7 +92,7 @@
 }
 
 /// Parser used by the compiler, which supports the `rpc` option (see
-/// [RpcOptionParser]) and any additional option added in [parsers]. If
+/// [GrpcOptionParser]) and any additional option added in [parsers]. If
 /// [parsers] has a key for `rpc`, it will be ignored.
 GenerationOptions? parseGenerationOptions(
     CodeGeneratorRequest request, CodeGeneratorResponse response,
diff --git a/protoc_plugin/lib/src/protobuf_field.dart b/protoc_plugin/lib/src/protobuf_field.dart
index 7f8dad6..b8f5b3f 100644
--- a/protoc_plugin/lib/src/protobuf_field.dart
+++ b/protoc_plugin/lib/src/protobuf_field.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 class ProtobufField {
@@ -22,10 +20,11 @@
   final FieldDescriptorProto descriptor;
 
   /// Dart names within a GeneratedMessage or `null` for an extension.
-  final FieldNames memberNames;
+  final FieldNames? memberNames;
 
   final String fullName;
   final BaseType baseType;
+  final ProtobufContainer parent;
 
   ProtobufField.message(
       FieldNames names, ProtobufContainer parent, GenerationContext ctx)
@@ -35,8 +34,8 @@
       ProtobufContainer parent, GenerationContext ctx)
       : this._(descriptor, null, parent, ctx);
 
-  ProtobufField._(this.descriptor, FieldNames dartNames,
-      ProtobufContainer parent, GenerationContext ctx)
+  ProtobufField._(this.descriptor, FieldNames? dartNames, this.parent,
+      GenerationContext ctx)
       : memberNames = dartNames,
         fullName = '${parent.fullName}.${descriptor.name}',
         baseType = BaseType(descriptor, ctx);
@@ -44,18 +43,18 @@
   /// The index of this field in MessageGenerator.fieldList.
   ///
   /// `null` for an extension.
-  int get index => memberNames?.index;
+  int? get index => memberNames?.index;
 
-  String get quotedProtoName =>
+  String? get quotedProtoName =>
       (_unCamelCase(descriptor.jsonName) == descriptor.name)
           ? null
           : "'${descriptor.name}'";
 
   /// The position of this field as it appeared in the original DescriptorProto.
-  int get sourcePosition => memberNames.sourcePosition;
+  int? get sourcePosition => memberNames?.sourcePosition;
 
   /// True if the field is to be encoded with [deprecated = true] encoding.
-  bool get isDeprecated => descriptor.options?.deprecated;
+  bool get isDeprecated => descriptor.options.deprecated;
 
   bool get isRequired =>
       descriptor.label == FieldDescriptorProto_Label.LABEL_REQUIRED;
@@ -63,9 +62,33 @@
   bool get isRepeated =>
       descriptor.label == FieldDescriptorProto_Label.LABEL_REPEATED;
 
-  /// True if the field is to be encoded with [packed=true] encoding.
-  bool get isPacked =>
-      isRepeated && descriptor.options != null && descriptor.options.packed;
+  /// True if a numeric field is repeated and it should be encoded with packed
+  /// encoding. In proto3 repeated fields are encoded as packed by default.
+  /// proto2 requires `[packed=true]` option.
+  bool get isPacked {
+    if (!isRepeated) {
+      return false; // only repeated fields can be packed
+    }
+
+    if (!baseType.isPackable) {
+      return false;
+    }
+
+    switch (parent.fileGen!.syntax) {
+      case ProtoSyntax.proto3:
+        if (!descriptor.hasOptions()) {
+          return true; // packed by default in proto3
+        } else {
+          return !descriptor.options.hasPacked() || descriptor.options.packed;
+        }
+      case ProtoSyntax.proto2:
+        if (!descriptor.hasOptions()) {
+          return false; // not packed by default in proto3
+        } else {
+          return descriptor.options.packed;
+        }
+    }
+  }
 
   /// Whether the field has the `overrideGetter` annotation set to true.
   bool get overridesGetter => _hasBooleanOption(Dart_options.overrideGetter);
@@ -114,16 +137,15 @@
   /// Returns the expression to use for the Dart type.
   ///
   /// This will be a List for repeated types.
-  /// [fileGen] represents the .proto file where we are generating code.
-  String getDartType(FileGenerator fileGen) {
+  String getDartType() {
     if (isMapField) {
       final d = baseType.generator as MessageGenerator;
-      var keyType = d._fieldList[0].baseType.getDartType(fileGen);
-      var valueType = d._fieldList[1].baseType.getDartType(fileGen);
+      var keyType = d._fieldList[0].baseType.getDartType(parent.fileGen!);
+      var valueType = d._fieldList[1].baseType.getDartType(parent.fileGen!);
       return '$coreImportPrefix.Map<$keyType, $valueType>';
     }
-    if (isRepeated) return baseType.getRepeatedDartType(fileGen);
-    return baseType.getDartType(fileGen);
+    if (isRepeated) return baseType.getRepeatedDartType(parent.fileGen!);
+    return baseType.getDartType(parent.fileGen!);
   }
 
   /// Returns the tag number of the underlying proto field.
@@ -145,7 +167,7 @@
   }
 
   static String _formatArguments(
-      List<String> positionals, Map<String, String> named) {
+      List<String?> positionals, Map<String, String?> named) {
     final args = positionals.toList();
     while (args.last == null) {
       args.removeLast();
@@ -165,20 +187,19 @@
 
   /// Returns Dart code adding this field to a BuilderInfo object.
   /// The call will start with ".." and a method name.
-  /// [fileGen] represents the .proto file where the code will be evaluated.
-  String generateBuilderInfoCall(FileGenerator fileGen, String package) {
+  String generateBuilderInfoCall(String package) {
     assert(descriptor.hasJsonName());
     var quotedName = configurationDependent(
       'protobuf.omit_field_names',
       quoted(descriptor.jsonName),
     );
 
-    var type = baseType.getDartType(fileGen);
+    var type = baseType.getDartType(parent.fileGen!);
 
     String invocation;
 
     var args = <String>[];
-    var named = <String, String>{'protoName': quotedProtoName};
+    var named = <String, String?>{'protoName': quotedProtoName};
     args.add('$number');
     args.add(quotedName);
 
@@ -186,8 +207,8 @@
       final generator = baseType.generator as MessageGenerator;
       var key = generator._fieldList[0];
       var value = generator._fieldList[1];
-      var keyType = key.baseType.getDartType(fileGen);
-      var valueType = value.baseType.getDartType(fileGen);
+      var keyType = key.baseType.getDartType(parent.fileGen!);
+      var valueType = value.baseType.getDartType(parent.fileGen!);
 
       invocation = 'm<$keyType, $valueType>';
 
@@ -200,7 +221,7 @@
       if (value.baseType.isEnum) {
         named['valueOf'] = '$valueType.valueOf';
         named['enumValues'] = '$valueType.values';
-        named['defaultEnumValue'] = value.generateDefaultFunction(fileGen);
+        named['defaultEnumValue'] = value.generateDefaultFunction();
       }
       if (package != '') {
         named['packageName'] =
@@ -222,12 +243,12 @@
         } else if (baseType.isEnum) {
           named['valueOf'] = '$type.valueOf';
           named['enumValues'] = '$type.values';
-          named['defaultEnumValue'] = generateDefaultFunction(fileGen);
+          named['defaultEnumValue'] = generateDefaultFunction();
         }
       }
     } else {
       // Singular field.
-      var makeDefault = generateDefaultFunction(fileGen);
+      var makeDefault = generateDefaultFunction();
 
       if (baseType.isEnum) {
         args.add(typeConstant);
@@ -279,7 +300,6 @@
         }
       }
     }
-    assert(invocation != null);
     return '..$invocation(${_formatArguments(args, named)})';
   }
 
@@ -291,25 +311,22 @@
     if (isRepeated) return 'null';
     switch (descriptor.type) {
       case FieldDescriptorProto_Type.TYPE_BOOL:
-        return _getDefaultAsBoolExpr('false');
+        return _getDefaultAsBoolExpr('false')!;
       case FieldDescriptorProto_Type.TYPE_INT32:
       case FieldDescriptorProto_Type.TYPE_UINT32:
       case FieldDescriptorProto_Type.TYPE_SINT32:
       case FieldDescriptorProto_Type.TYPE_FIXED32:
       case FieldDescriptorProto_Type.TYPE_SFIXED32:
-        return _getDefaultAsInt32Expr('0');
+        return _getDefaultAsInt32Expr('0')!;
       case FieldDescriptorProto_Type.TYPE_STRING:
-        return _getDefaultAsStringExpr("''");
+        return _getDefaultAsStringExpr("''")!;
       default:
         return 'null';
     }
   }
 
   /// Returns a function expression that returns the field's default value.
-  ///
-  /// [fileGen] represents the .proto file where the expression will be
-  /// evaluated.
-  String generateDefaultFunction(FileGenerator fileGen) {
+  String? generateDefaultFunction() {
     assert(!isRepeated);
     switch (descriptor.type) {
       case FieldDescriptorProto_Type.TYPE_BOOL:
@@ -363,9 +380,9 @@
         return '() => <$coreImportPrefix.int>[$byteList]';
       case FieldDescriptorProto_Type.TYPE_GROUP:
       case FieldDescriptorProto_Type.TYPE_MESSAGE:
-        return '${baseType.getDartType(fileGen)}.getDefault';
+        return '${baseType.getDartType(parent.fileGen!)}.getDefault';
       case FieldDescriptorProto_Type.TYPE_ENUM:
-        var className = baseType.getDartType(fileGen);
+        var className = baseType.getDartType(parent.fileGen!);
         final gen = baseType.generator as EnumGenerator;
         if (descriptor.hasDefaultValue() &&
             descriptor.defaultValue.isNotEmpty) {
@@ -379,14 +396,14 @@
     }
   }
 
-  String _getDefaultAsBoolExpr(String noDefault) {
+  String? _getDefaultAsBoolExpr(String? noDefault) {
     if (descriptor.hasDefaultValue() && 'false' != descriptor.defaultValue) {
       return descriptor.defaultValue;
     }
     return noDefault;
   }
 
-  String _getDefaultAsStringExpr(String noDefault) {
+  String? _getDefaultAsStringExpr(String? noDefault) {
     if (!descriptor.hasDefaultValue() || descriptor.defaultValue.isEmpty) {
       return noDefault;
     }
@@ -394,7 +411,7 @@
     return quoted(descriptor.defaultValue);
   }
 
-  String _getDefaultAsInt32Expr(String noDefault) {
+  String? _getDefaultAsInt32Expr(String? noDefault) {
     if (descriptor.hasDefaultValue() && '0' != descriptor.defaultValue) {
       return descriptor.defaultValue;
     }
@@ -402,7 +419,7 @@
   }
 
   bool _hasBooleanOption(Extension extension) =>
-      descriptor?.options?.getExtension(extension) as bool ?? false;
+      descriptor.options.getExtension(extension) as bool? ?? false;
 
   String get _invalidDefaultValue => 'dart-protoc-plugin:'
       ' invalid default value (${descriptor.defaultValue})'
@@ -416,6 +433,6 @@
 
   static String _unCamelCase(String name) {
     return name.replaceAllMapped(
-        _upperCase, (match) => '_${match.group(0).toLowerCase()}');
+        _upperCase, (match) => '_${match.group(0)!.toLowerCase()}');
   }
 }
diff --git a/protoc_plugin/lib/src/service_generator.dart b/protoc_plugin/lib/src/service_generator.dart
index 3fcd1a9..53ef39c 100644
--- a/protoc_plugin/lib/src/service_generator.dart
+++ b/protoc_plugin/lib/src/service_generator.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
 class ServiceGenerator {
@@ -68,7 +66,7 @@
   void _addDependency(GenerationContext ctx, String fqname, String location) {
     if (_deps.containsKey(fqname)) return; // Already added.
 
-    final mg = ctx.getFieldType(fqname) as MessageGenerator;
+    final mg = ctx.getFieldType(fqname) as MessageGenerator?;
     if (mg == null) {
       _undefinedDeps[fqname] = location;
       return;
@@ -226,7 +224,7 @@
 
     var typeConstants = <String, String>{};
     for (var key in _transitiveDeps.keys) {
-      typeConstants[key] = _transitiveDeps[key].getJsonConstant(fileGen);
+      typeConstants[key] = _transitiveDeps[key]!.getJsonConstant(fileGen);
     }
 
     out.println('@$coreImportPrefix.Deprecated'
diff --git a/protoc_plugin/lib/src/well_known_types.dart b/protoc_plugin/lib/src/well_known_types.dart
index 7fabd87..24e360b 100644
--- a/protoc_plugin/lib/src/well_known_types.dart
+++ b/protoc_plugin/lib/src/well_known_types.dart
@@ -2,11 +2,9 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 part of '../protoc.dart';
 
-PbMixin wellKnownMixinForFullName(String qualifiedName) =>
+PbMixin? wellKnownMixinForFullName(String qualifiedName) =>
     _wellKnownMixins[qualifiedName];
 
 const _wellKnownImportPath =
diff --git a/protoc_plugin/mono_pkg.yaml b/protoc_plugin/mono_pkg.yaml
index efa3a9e..ba38d24 100644
--- a/protoc_plugin/mono_pkg.yaml
+++ b/protoc_plugin/mono_pkg.yaml
@@ -6,10 +6,10 @@
       - command: ./../tool/setup.sh
       - command: make protos
       - analyze: --fatal-infos
-      dart: [dev]
+      sdk: [dev]
   - run_tests:
     - group:
       - command: ./../tool/setup.sh
       - command: make protos
       - test
-      dart: [2.12.0, dev]
+      sdk: [2.12.0, dev]
diff --git a/protoc_plugin/pubspec.yaml b/protoc_plugin/pubspec.yaml
index bcdd56f..2febd42 100644
--- a/protoc_plugin/pubspec.yaml
+++ b/protoc_plugin/pubspec.yaml
@@ -1,7 +1,7 @@
 name: protoc_plugin
 version: 20.0.1-dev
 description: Protoc compiler plugin to generate Dart code
-homepage: https://github.com/google/protobuf.dart
+repository: https://github.com/google/protobuf.dart/tree/master/protoc_plugin
 
 environment:
   sdk: '>=2.12.0 <3.0.0'
diff --git a/protoc_plugin/test/all_tests.dart b/protoc_plugin/test/all_tests.dart
deleted file mode 100755
index d85093f..0000000
--- a/protoc_plugin/test/all_tests.dart
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/usr/bin/env dart
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart=2.11
-
-import 'any_test.dart' as any;
-import 'bazel_test.dart' as bazel;
-import 'client_generator_test.dart' as client_generator;
-import 'const_generator_test.dart' as const_generator;
-import 'default_value_escape_test.dart' as default_value_escape;
-import 'descriptor_test.dart' as descriptor_test;
-import 'enum_generator_test.dart' as enum_generator;
-import 'extension_generator_test.dart' as extension_generator_test;
-import 'extension_test.dart' as extension_test;
-import 'file_generator_test.dart' as file_generator;
-import 'generated_message_test.dart' as generated_message;
-import 'hash_code_test.dart' as hash_code;
-import 'high_tagnumber_test.dart' as high_tagnumber;
-import 'import_public_test.dart' as import_public;
-import 'import_test.dart' as import_prefix;
-import 'indenting_writer_test.dart' as indenting_writer;
-import 'json_test.dart' as json;
-import 'leading_underscores_test.dart' as leading_underscores;
-import 'map_field_test.dart' as map_field;
-import 'map_test.dart' as map;
-import 'merge_test.dart' as merge;
-import 'message_generator_test.dart' as message_generator;
-import 'message_test.dart' as message;
-import 'mixin_test.dart' as mixin_test;
-import 'names_test.dart' as names;
-import 'omit_enum_names_test.dart' as omit_enum_names;
-import 'omit_field_names_test.dart' as omit_field_names;
-import 'omit_message_names_test.dart' as omit_message_names;
-import 'oneof_test.dart' as oneof;
-import 'proto3_optional_test.dart' as proto3_optional;
-import 'protoc_options_test.dart' as protoc_options;
-import 'repeated_field_test.dart' as repeated_field;
-import 'reserved_names_test.dart' as reserved_names;
-import 'send_protos_via_sendports_test.dart' as send_protos_via_sendports_test;
-import 'service_generator_test.dart' as service_generator;
-import 'service_test.dart' as service;
-import 'to_builder_test.dart' as to_builder;
-import 'unknown_field_set_test.dart' as unknown_field_set;
-import 'validate_fail_test.dart' as validate_fail;
-import 'wire_format_test.dart' as wire_format;
-
-void main() {
-  any.main();
-  bazel.main();
-  client_generator.main();
-  const_generator.main();
-  default_value_escape.main();
-  descriptor_test.main();
-  enum_generator.main();
-  extension_generator_test.main();
-  extension_test.main();
-  file_generator.main();
-  generated_message.main();
-  hash_code.main();
-  high_tagnumber.main();
-  import_prefix.main();
-  import_public.main();
-  indenting_writer.main();
-  json.main();
-  leading_underscores.main();
-  map.main();
-  map_field.main();
-  merge.main();
-  message.main();
-  message_generator.main();
-  mixin_test.main();
-  names.main();
-  omit_enum_names.main();
-  omit_field_names.main();
-  omit_message_names.main();
-  oneof.main();
-  proto3_optional.main();
-  protoc_options.main();
-  repeated_field.main();
-  reserved_names.main();
-  send_protos_via_sendports_test.main();
-  service.main();
-  service_generator.main();
-  to_builder.main();
-  unknown_field_set.main();
-  validate_fail.main();
-  wire_format.main();
-}
diff --git a/protoc_plugin/test/any_test.dart b/protoc_plugin/test/any_test.dart
index a29d6d1..df1cb12 100644
--- a/protoc_plugin/test/any_test.dart
+++ b/protoc_plugin/test/any_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protobuf/protobuf.dart';
 import 'package:test/test.dart';
 
diff --git a/protoc_plugin/test/bazel_test.dart b/protoc_plugin/test/bazel_test.dart
index e1ef89c..0cca05b 100644
--- a/protoc_plugin/test/bazel_test.dart
+++ b/protoc_plugin/test/bazel_test.dart
@@ -2,17 +2,15 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/bazel.dart';
 import 'package:test/test.dart';
 
 void main() {
   group('BazelOptionParser', () {
-    BazelOptionParser optionParser;
-    Map<String, BazelPackage> packages;
-    List<String> errors;
-
+    late BazelOptionParser optionParser;
+    late Map<String, BazelPackage> packages;
+    late List<String> errors;
+    final optionName = 'name';
     setUp(() {
       packages = {};
       optionParser = BazelOptionParser(packages);
@@ -24,95 +22,95 @@
     }
 
     test('should call onError for null values', () {
-      optionParser.parse(null, null, _onError);
+      optionParser.parse(optionName, null, _onError);
       expect(errors, isNotEmpty);
     });
 
     test('should call onError for empty values', () {
-      optionParser.parse(null, '', _onError);
+      optionParser.parse(optionName, '', _onError);
       expect(errors, isNotEmpty);
     });
 
     test('should call onError for malformed entries', () {
-      optionParser.parse(null, 'foo', _onError);
-      optionParser.parse(null, 'foo|bar', _onError);
-      optionParser.parse(null, 'foo|bar|baz|quux', _onError);
+      optionParser.parse(optionName, 'foo', _onError);
+      optionParser.parse(optionName, 'foo|bar', _onError);
+      optionParser.parse(optionName, 'foo|bar|baz|quux', _onError);
       expect(errors.length, 3);
       expect(packages, isEmpty);
     });
 
     test('should handle a single package|path entry', () {
-      optionParser.parse(null, 'foo|bar/baz|wibble/wobble', _onError);
+      optionParser.parse(optionName, 'foo|bar/baz|wibble/wobble', _onError);
       expect(errors, isEmpty);
       expect(packages.length, 1);
-      expect(packages['bar/baz'].name, 'foo');
-      expect(packages['bar/baz'].inputRoot, 'bar/baz');
-      expect(packages['bar/baz'].outputRoot, 'wibble/wobble');
+      expect(packages['bar/baz']!.name, 'foo');
+      expect(packages['bar/baz']!.inputRoot, 'bar/baz');
+      expect(packages['bar/baz']!.outputRoot, 'wibble/wobble');
     });
 
     test('should handle multiple package|path entries', () {
       optionParser.parse(
-          null,
+          optionName,
           'foo|bar/baz|wibble/wobble;a|b/c/d|e/f;one.two|three|four/five',
           _onError);
       expect(errors, isEmpty);
       expect(packages.length, 3);
-      expect(packages['bar/baz'].name, 'foo');
-      expect(packages['bar/baz'].inputRoot, 'bar/baz');
-      expect(packages['bar/baz'].outputRoot, 'wibble/wobble');
-      expect(packages['b/c/d'].name, 'a');
-      expect(packages['b/c/d'].inputRoot, 'b/c/d');
-      expect(packages['b/c/d'].outputRoot, 'e/f');
-      expect(packages['three'].name, 'one.two');
-      expect(packages['three'].inputRoot, 'three');
-      expect(packages['three'].outputRoot, 'four/five');
+      expect(packages['bar/baz']!.name, 'foo');
+      expect(packages['bar/baz']!.inputRoot, 'bar/baz');
+      expect(packages['bar/baz']!.outputRoot, 'wibble/wobble');
+      expect(packages['b/c/d']!.name, 'a');
+      expect(packages['b/c/d']!.inputRoot, 'b/c/d');
+      expect(packages['b/c/d']!.outputRoot, 'e/f');
+      expect(packages['three']!.name, 'one.two');
+      expect(packages['three']!.inputRoot, 'three');
+      expect(packages['three']!.outputRoot, 'four/five');
     });
 
     test('should skip and continue past malformed entries', () {
-      optionParser.parse(null,
+      optionParser.parse(optionName,
           'foo|bar/baz|wibble/wobble;fizz;a.b|c/d|e/f;x|y|zz|y', _onError);
       expect(errors.length, 2);
       expect(packages.length, 2);
-      expect(packages['bar/baz'].name, 'foo');
-      expect(packages['c/d'].name, 'a.b');
+      expect(packages['bar/baz']!.name, 'foo');
+      expect(packages['c/d']!.name, 'a.b');
     });
 
     test('should emit error for conflicting package names', () {
-      optionParser.parse(null,
+      optionParser.parse(optionName,
           'foo|bar/baz|wibble/wobble;flob|bar/baz|wibble/wobble', _onError);
       expect(errors.length, 1);
       expect(packages.length, 1);
-      expect(packages['bar/baz'].name, 'foo');
+      expect(packages['bar/baz']!.name, 'foo');
     });
 
-    test('should emit error for conflicting output_roots', () {
-      optionParser.parse(null,
+    test('should emit error for conflicting outputRoots', () {
+      optionParser.parse(optionName,
           'foo|bar/baz|wibble/wobble;foo|bar/baz|womble/wumble', _onError);
       expect(errors.length, 1);
       expect(packages.length, 1);
-      expect(packages['bar/baz'].outputRoot, 'wibble/wobble');
+      expect(packages['bar/baz']!.outputRoot, 'wibble/wobble');
     });
 
     test('should normalize paths', () {
-      optionParser.parse(
-          null, 'foo|bar//baz/|quux/;a|b/|c;c|d//e/f///|g//h//', _onError);
+      optionParser.parse(optionName,
+          'foo|bar//baz/|quux/;a|b/|c;c|d//e/f///|g//h//', _onError);
       expect(errors, isEmpty);
       expect(packages.length, 3);
-      expect(packages['bar/baz'].name, 'foo');
-      expect(packages['bar/baz'].inputRoot, 'bar/baz');
-      expect(packages['bar/baz'].outputRoot, 'quux');
-      expect(packages['b'].name, 'a');
-      expect(packages['b'].inputRoot, 'b');
-      expect(packages['b'].outputRoot, 'c');
-      expect(packages['d/e/f'].name, 'c');
-      expect(packages['d/e/f'].inputRoot, 'd/e/f');
-      expect(packages['d/e/f'].outputRoot, 'g/h');
+      expect(packages['bar/baz']!.name, 'foo');
+      expect(packages['bar/baz']!.inputRoot, 'bar/baz');
+      expect(packages['bar/baz']!.outputRoot, 'quux');
+      expect(packages['b']!.name, 'a');
+      expect(packages['b']!.inputRoot, 'b');
+      expect(packages['b']!.outputRoot, 'c');
+      expect(packages['d/e/f']!.name, 'c');
+      expect(packages['d/e/f']!.inputRoot, 'd/e/f');
+      expect(packages['d/e/f']!.outputRoot, 'g/h');
     });
   });
 
   group('BazelOutputConfiguration', () {
     Map<String, BazelPackage> packages;
-    BazelOutputConfiguration config;
+    late BazelOutputConfiguration config;
 
     setUp(() {
       packages = {
diff --git a/protoc_plugin/test/client_generator_test.dart b/protoc_plugin/test/client_generator_test.dart
index 157805c..fbe2398 100644
--- a/protoc_plugin/test/client_generator_test.dart
+++ b/protoc_plugin/test/client_generator_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/indenting_writer.dart';
 import 'package:protoc_plugin/protoc.dart';
 import 'package:protoc_plugin/src/linker.dart';
diff --git a/protoc_plugin/test/coded_buffer_test.dart b/protoc_plugin/test/coded_buffer_test.dart
new file mode 100644
index 0000000..e171f06
--- /dev/null
+++ b/protoc_plugin/test/coded_buffer_test.dart
@@ -0,0 +1,30 @@
+#!/usr/bin/env dart
+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+import '../out/protos/bytes.pb.dart';
+
+void main() {
+  test('Does not reuse input buffer for bytes fields', () {
+    var message = BytesEntity()..value = [1, 2, 3];
+    var bytes = message.writeToBuffer();
+    var deserialized1 = BytesEntity()..mergeFromBuffer(bytes);
+    var deserialized2 = BytesEntity()..mergeFromBuffer(bytes);
+    deserialized1.value[0] = 100;
+    expect(deserialized1.value[0], 100);
+    expect(deserialized2.value[0], 1);
+  });
+
+  test('Does not reuse input buffer for repeated bytes fields', () {
+    var message = BytesEntity()..values.add([1, 2, 3]);
+    var bytes = message.writeToBuffer();
+    var deserialized1 = BytesEntity()..mergeFromBuffer(bytes);
+    var deserialized2 = BytesEntity()..mergeFromBuffer(bytes);
+    deserialized1.values.first[0] = 100;
+    expect(deserialized1.values.first[0], 100);
+    expect(deserialized2.values.first[0], 1);
+  });
+}
diff --git a/protoc_plugin/test/default_value_escape_test.dart b/protoc_plugin/test/default_value_escape_test.dart
index 2894c50..b71a01b 100644
--- a/protoc_plugin/test/default_value_escape_test.dart
+++ b/protoc_plugin/test/default_value_escape_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:test/test.dart';
 
 import '../out/protos/default_value_escape.pb.dart';
diff --git a/protoc_plugin/test/descriptor_test.dart b/protoc_plugin/test/descriptor_test.dart
index cc97675..34085cf 100644
--- a/protoc_plugin/test/descriptor_test.dart
+++ b/protoc_plugin/test/descriptor_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protobuf/protobuf.dart';
 import 'package:protoc_plugin/src/generated/descriptor.pb.dart';
 import 'package:test/test.dart';
diff --git a/protoc_plugin/test/enum_generator_test.dart b/protoc_plugin/test/enum_generator_test.dart
index a2b7b02..715df7e 100755
--- a/protoc_plugin/test/enum_generator_test.dart
+++ b/protoc_plugin/test/enum_generator_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/indenting_writer.dart';
 import 'package:protoc_plugin/protoc.dart';
 import 'package:protoc_plugin/src/generated/descriptor.pb.dart';
diff --git a/protoc_plugin/test/extension_generator_test.dart b/protoc_plugin/test/extension_generator_test.dart
index b49ff6d..ceb0696 100644
--- a/protoc_plugin/test/extension_generator_test.dart
+++ b/protoc_plugin/test/extension_generator_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/indenting_writer.dart';
 import 'package:protoc_plugin/protoc.dart';
 import 'package:protoc_plugin/src/generated/descriptor.pb.dart' as pb;
diff --git a/protoc_plugin/test/extension_test.dart b/protoc_plugin/test/extension_test.dart
index 56b7408..5925251 100644
--- a/protoc_plugin/test/extension_test.dart
+++ b/protoc_plugin/test/extension_test.dart
@@ -3,8 +3,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
+import 'dart:typed_data';
 import 'package:protobuf/protobuf.dart';
 import 'package:test/test.dart';
 
@@ -17,7 +16,8 @@
 import '../out/protos/non_nested_extension.pb.dart';
 import 'test_util.dart';
 
-Matcher throwsArgError(String expectedMessage) => throwsA(predicate((x) {
+Matcher throwsArgError(String expectedMessage) =>
+    throwsA(predicate((dynamic x) {
       expect(x, isArgumentError);
       expect(x.message, expectedMessage);
       return true;
@@ -174,14 +174,14 @@
   });
 
   test('can extend a message with a message field with a different type', () {
-    expect(Non_nested_extension.nonNestedExtension.makeDefault(),
+    expect(Non_nested_extension.nonNestedExtension.makeDefault!(),
         TypeMatcher<MyNonNestedExtension>());
     expect(Non_nested_extension.nonNestedExtension.name, 'nonNestedExtension');
   });
 
   test('can extend a message with a message field of the same type', () {
     expect(
-        MyNestedExtension.recursiveExtension.makeDefault()
+        MyNestedExtension.recursiveExtension.makeDefault!()
             is MessageToBeExtended,
         isTrue);
     expect(MyNestedExtension.recursiveExtension.name, 'recursiveExtension');
@@ -532,7 +532,7 @@
     final withUnknownFields = Outer.fromBuffer(original.writeToBuffer());
 
     expect(
-        withUnknownFields.innerMap[0]
+        withUnknownFields.innerMap[0]!
             .hasExtension(Extend_unittest.innerExtensionString),
         isFalse);
 
@@ -540,11 +540,23 @@
     final reparsed = r.reparseMessage(withUnknownFields);
 
     expect(
-        reparsed.innerMap[0].hasExtension(Extend_unittest.innerExtensionString),
+        reparsed.innerMap[0]!
+            .hasExtension(Extend_unittest.innerExtensionString),
         isTrue);
     expect(
         identical(withUnknownFields.innerMap[1], reparsed.innerMap[1]), isTrue);
     expect(withUnknownFields.stringMap.length, reparsed.stringMap.length);
     expect(withUnknownFields.stringMap[0], reparsed.stringMap[0]);
   });
+
+  test('consistent hashcode for reparsed messages with extensions', () {
+    final r = ExtensionRegistry()..add(Extend_unittest.outer);
+    final m = TestAllExtensions()
+      ..setExtension(
+          Extend_unittest.outer, Outer()..inner = (Inner()..value = 'hello'));
+    final Uint8List b = m.writeToBuffer();
+    final c = TestAllExtensions.fromBuffer(b);
+    final d = r.reparseMessage(c);
+    expect(m.hashCode, d.hashCode);
+  });
 }
diff --git a/protoc_plugin/test/extension_unknown_interaction_test.dart b/protoc_plugin/test/extension_unknown_interaction_test.dart
new file mode 100644
index 0000000..9acb389
--- /dev/null
+++ b/protoc_plugin/test/extension_unknown_interaction_test.dart
@@ -0,0 +1,23 @@
+#!/usr/bin/env dart
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:protobuf/protobuf.dart';
+import 'package:test/test.dart';
+
+import '../out/protos/google/protobuf/unittest.pb.dart';
+
+void main() {
+  test('setExtension clears unknown field with same tag number', () {
+    final m = TestAllExtensions();
+    m.unknownFields.addField(Unittest.optionalInt32Extension.tagNumber,
+        UnknownFieldSetField()..addFixed32(33));
+    expect(m.unknownFields.hasField(Unittest.optionalInt32Extension.tagNumber),
+        isTrue);
+    m.setExtension(Unittest.optionalInt32Extension, 42);
+    expect(m.getExtension(Unittest.optionalInt32Extension), 42);
+    expect(m.unknownFields.hasField(Unittest.optionalInt32Extension.tagNumber),
+        isFalse);
+  });
+}
diff --git a/protoc_plugin/test/file_generator_test.dart b/protoc_plugin/test/file_generator_test.dart
index 84c733a..6763579 100644
--- a/protoc_plugin/test/file_generator_test.dart
+++ b/protoc_plugin/test/file_generator_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/indenting_writer.dart';
 import 'package:protoc_plugin/protoc.dart';
 import 'package:protoc_plugin/src/generated/descriptor.pb.dart';
@@ -95,8 +93,8 @@
   test('FileGenerator outputs a .pb.dart file for a proto with one message',
       () {
     var fd = buildFileDescriptor();
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
     expectMatchesGoldenFile(
@@ -105,8 +103,8 @@
 
   test('FileGenerator outputs a .pb.dart file for an Int64 message', () {
     var fd = createInt64Proto();
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
     expectMatchesGoldenFile(
@@ -119,7 +117,7 @@
     var fd = buildFileDescriptor();
     var options = parseGenerationOptions(
         CodeGeneratorRequest()..parameter = 'generate_kythe_info',
-        CodeGeneratorResponse());
+        CodeGeneratorResponse())!;
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
     expectMatchesGoldenFile(fg.generateMainFile().sourceLocationInfo.toString(),
@@ -129,8 +127,8 @@
   test('FileGenerator outputs a pbjson.dart file for a proto with one message',
       () {
     var fd = buildFileDescriptor();
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
     expectMatchesGoldenFile(
@@ -139,8 +137,8 @@
 
   test('FileGenerator generates files for a top-level enum', () {
     var fd = buildFileDescriptor(phoneNumber: false, topLevelEnum: true);
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
 
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
@@ -154,7 +152,7 @@
     var fd = buildFileDescriptor(phoneNumber: false, topLevelEnum: true);
     var options = parseGenerationOptions(
         CodeGeneratorRequest()..parameter = 'generate_kythe_info',
-        CodeGeneratorResponse());
+        CodeGeneratorResponse())!;
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
 
@@ -166,8 +164,8 @@
 
   test('FileGenerator generates a .pbjson.dart file for a top-level enum', () {
     var fd = buildFileDescriptor(phoneNumber: false, topLevelEnum: true);
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
 
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
@@ -178,8 +176,8 @@
   test('FileGenerator outputs library for a .proto in a package', () {
     var fd = buildFileDescriptor();
     fd.package = 'pb_library';
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
 
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
@@ -203,8 +201,8 @@
             ..type = FieldDescriptorProto_Type.TYPE_INT64
         ]));
 
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
 
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
@@ -230,8 +228,8 @@
       ..messageType.add(empty)
       ..service.add(sd);
 
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
 
     var fg = FileGenerator(fd, options);
     link(options, [fg]);
@@ -420,7 +418,7 @@
     fd.dependency.addAll(['package1.proto', 'package2.proto']);
     var request = CodeGeneratorRequest();
     var response = CodeGeneratorResponse();
-    var options = parseGenerationOptions(request, response);
+    var options = parseGenerationOptions(request, response)!;
 
     var fg = FileGenerator(fd, options);
     link(options,
diff --git a/protoc_plugin/test/goldens/grpc_service.pb b/protoc_plugin/test/goldens/grpc_service.pb
index bb87200..40d318a 100644
--- a/protoc_plugin/test/goldens/grpc_service.pb
+++ b/protoc_plugin/test/goldens/grpc_service.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/grpc_service.pbgrpc b/protoc_plugin/test/goldens/grpc_service.pbgrpc
index e03a826..a221d1f 100644
--- a/protoc_plugin/test/goldens/grpc_service.pbgrpc
+++ b/protoc_plugin/test/goldens/grpc_service.pbgrpc
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:async' as $async;
 
diff --git a/protoc_plugin/test/goldens/header_in_package.pb b/protoc_plugin/test/goldens/header_in_package.pb
index ee271f7..b102c15 100644
--- a/protoc_plugin/test/goldens/header_in_package.pb
+++ b/protoc_plugin/test/goldens/header_in_package.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/header_with_fixnum.pb b/protoc_plugin/test/goldens/header_with_fixnum.pb
index 3eb7a37..c9dfa5d 100644
--- a/protoc_plugin/test/goldens/header_with_fixnum.pb
+++ b/protoc_plugin/test/goldens/header_with_fixnum.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/imports.pb b/protoc_plugin/test/goldens/imports.pb
index 47e7a8b..275c153 100644
--- a/protoc_plugin/test/goldens/imports.pb
+++ b/protoc_plugin/test/goldens/imports.pb
@@ -3,7 +3,7 @@
 //  source: test.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/imports.pbjson b/protoc_plugin/test/goldens/imports.pbjson
index baf0213..66934b0 100644
--- a/protoc_plugin/test/goldens/imports.pbjson
+++ b/protoc_plugin/test/goldens/imports.pbjson
@@ -3,5 +3,5 @@
 //  source: test.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
diff --git a/protoc_plugin/test/goldens/int64.pb b/protoc_plugin/test/goldens/int64.pb
index 2a1aff1..3d4184a 100644
--- a/protoc_plugin/test/goldens/int64.pb
+++ b/protoc_plugin/test/goldens/int64.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/oneMessage.pb b/protoc_plugin/test/goldens/oneMessage.pb
index 3f44fbb..f47d665 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb
+++ b/protoc_plugin/test/goldens/oneMessage.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/oneMessage.pb.meta b/protoc_plugin/test/goldens/oneMessage.pb.meta
index a287f25..2b7dcac 100644
--- a/protoc_plugin/test/goldens/oneMessage.pb.meta
+++ b/protoc_plugin/test/goldens/oneMessage.pb.meta
@@ -2,15 +2,15 @@
   path: 4
   path: 0
   sourceFile: test
-  begin: 420
-  end: 431
+  begin: 439
+  end: 450
 }
 annotation: {
   path: 4
   path: 0
   sourceFile: test
-  begin: 992
-  end: 1003
+  begin: 1011
+  end: 1022
 }
 annotation: {
   path: 4
@@ -18,8 +18,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 2732
-  end: 2738
+  begin: 2751
+  end: 2757
 }
 annotation: {
   path: 4
@@ -27,8 +27,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 2780
-  end: 2786
+  begin: 2799
+  end: 2805
 }
 annotation: {
   path: 4
@@ -36,8 +36,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 2859
-  end: 2868
+  begin: 2878
+  end: 2887
 }
 annotation: {
   path: 4
@@ -45,8 +45,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 2911
-  end: 2922
+  begin: 2930
+  end: 2941
 }
 annotation: {
   path: 4
@@ -54,8 +54,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 2980
-  end: 2984
+  begin: 2999
+  end: 3003
 }
 annotation: {
   path: 4
@@ -63,8 +63,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 3026
-  end: 3030
+  begin: 3045
+  end: 3049
 }
 annotation: {
   path: 4
@@ -72,8 +72,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 3105
-  end: 3112
+  begin: 3124
+  end: 3131
 }
 annotation: {
   path: 4
@@ -81,8 +81,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 3155
-  end: 3164
+  begin: 3174
+  end: 3183
 }
 annotation: {
   path: 4
@@ -90,8 +90,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 3225
-  end: 3229
+  begin: 3244
+  end: 3248
 }
 annotation: {
   path: 4
@@ -99,8 +99,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 3276
-  end: 3280
+  begin: 3295
+  end: 3299
 }
 annotation: {
   path: 4
@@ -108,8 +108,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 3353
-  end: 3360
+  begin: 3372
+  end: 3379
 }
 annotation: {
   path: 4
@@ -117,6 +117,6 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 3403
-  end: 3412
+  begin: 3422
+  end: 3431
 }
diff --git a/protoc_plugin/test/goldens/oneMessage.pbjson b/protoc_plugin/test/goldens/oneMessage.pbjson
index 2ee61f2..1bf29b9 100644
--- a/protoc_plugin/test/goldens/oneMessage.pbjson
+++ b/protoc_plugin/test/goldens/oneMessage.pbjson
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
diff --git a/protoc_plugin/test/goldens/service.pb b/protoc_plugin/test/goldens/service.pb
index 562d64f..ef803ec 100644
--- a/protoc_plugin/test/goldens/service.pb
+++ b/protoc_plugin/test/goldens/service.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:async' as $async;
 import 'dart:core' as $core;
diff --git a/protoc_plugin/test/goldens/service.pbserver b/protoc_plugin/test/goldens/service.pbserver
index 56ea3aa..51dad74 100644
--- a/protoc_plugin/test/goldens/service.pbserver
+++ b/protoc_plugin/test/goldens/service.pbserver
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:async' as $async;
 
diff --git a/protoc_plugin/test/goldens/serviceGenerator.pb.json b/protoc_plugin/test/goldens/serviceGenerator.pb.json
index ad1b9e2..419d00e 100644
--- a/protoc_plugin/test/goldens/serviceGenerator.pb.json
+++ b/protoc_plugin/test/goldens/serviceGenerator.pb.json
@@ -3,7 +3,7 @@
 //  source: testpkg.proto
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
diff --git a/protoc_plugin/test/goldens/topLevelEnum.pb b/protoc_plugin/test/goldens/topLevelEnum.pb
index 2a5f821..d82fcc5 100644
--- a/protoc_plugin/test/goldens/topLevelEnum.pb
+++ b/protoc_plugin/test/goldens/topLevelEnum.pb
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 
diff --git a/protoc_plugin/test/goldens/topLevelEnum.pbenum b/protoc_plugin/test/goldens/topLevelEnum.pbenum
index ec6cf92..326cbe0 100644
--- a/protoc_plugin/test/goldens/topLevelEnum.pbenum
+++ b/protoc_plugin/test/goldens/topLevelEnum.pbenum
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 // ignore_for_file: UNDEFINED_SHOWN_NAME
 import 'dart:core' as $core;
diff --git a/protoc_plugin/test/goldens/topLevelEnum.pbenum.meta b/protoc_plugin/test/goldens/topLevelEnum.pbenum.meta
index ed85783..92298cb 100644
--- a/protoc_plugin/test/goldens/topLevelEnum.pbenum.meta
+++ b/protoc_plugin/test/goldens/topLevelEnum.pbenum.meta
@@ -2,8 +2,8 @@
   path: 5
   path: 0
   sourceFile: test
-  begin: 460
-  end: 469
+  begin: 479
+  end: 488
 }
 annotation: {
   path: 5
@@ -11,8 +11,8 @@
   path: 2
   path: 0
   sourceFile: test
-  begin: 522
-  end: 528
+  begin: 541
+  end: 547
 }
 annotation: {
   path: 5
@@ -20,8 +20,8 @@
   path: 2
   path: 1
   sourceFile: test
-  begin: 650
-  end: 654
+  begin: 669
+  end: 673
 }
 annotation: {
   path: 5
@@ -29,8 +29,8 @@
   path: 2
   path: 2
   sourceFile: test
-  begin: 774
-  end: 778
+  begin: 793
+  end: 797
 }
 annotation: {
   path: 5
@@ -38,6 +38,6 @@
   path: 2
   path: 3
   sourceFile: test
-  begin: 899
-  end: 907
+  begin: 918
+  end: 926
 }
diff --git a/protoc_plugin/test/goldens/topLevelEnum.pbjson b/protoc_plugin/test/goldens/topLevelEnum.pbjson
index 5ade8bc..f00b03d 100644
--- a/protoc_plugin/test/goldens/topLevelEnum.pbjson
+++ b/protoc_plugin/test/goldens/topLevelEnum.pbjson
@@ -3,7 +3,7 @@
 //  source: test
 //
 // @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_this,unused_import,unused_shown_name
+// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
 
 import 'dart:core' as $core;
 import 'dart:convert' as $convert;
diff --git a/protoc_plugin/test/json_test.dart b/protoc_plugin/test/json_test.dart
index 15c9326..8e5f6f1 100755
--- a/protoc_plugin/test/json_test.dart
+++ b/protoc_plugin/test/json_test.dart
@@ -14,19 +14,19 @@
 
 void main() {
   final testAllJsonTypes = '{"1":101,"2":"102","3":103,"4":"104",'
-      '"5":105,"6":"106","7":107,"8":"108","9":109,"10":"110","11":111.0,'
-      '"12":112.0,"13":true,"14":"115","15":"MTE2","16":{"17":117},'
+      '"5":105,"6":"106","7":107,"8":"108","9":109,"10":"110","11":111,'
+      '"12":112,"13":true,"14":"115","15":"MTE2","16":{"17":117},'
       '"18":{"1":118},"19":{"1":119},"20":{"1":120},"21":3,"22":6,"23":9,'
       '"24":"124","25":"125","31":[201,301],"32":["202","302"],'
       '"33":[203,303],"34":["204","304"],"35":[205,305],"36":["206","306"],'
       '"37":[207,307],"38":["208","308"],"39":[209,309],"40":["210","310"],'
-      '"41":[211.0,311.0],"42":[212.0,312.0],"43":[true,false],'
+      '"41":[211,311],"42":[212,312],"43":[true,false],'
       '"44":["215","315"],"45":["MjE2","MzE2"],"46":[{"47":217},{"47":317}],'
       '"48":[{"1":218},{"1":318}],"49":[{"1":219},{"1":319}],'
       '"50":[{"1":220},{"1":320}],"51":[2,3],"52":[5,6],"53":[8,9],'
       '"54":["224","324"],"55":["225","325"],"61":401,"62":"402","63":403,'
       '"64":"404","65":405,"66":"406","67":407,"68":"408","69":409,'
-      '"70":"410","71":411.0,"72":412.0,"73":false,"74":"415","75":"NDE2",'
+      '"70":"410","71":411,"72":412,"73":false,"74":"415","75":"NDE2",'
       '"81":1,"82":4,"83":7,"84":"424","85":"425"}';
 
   // Checks that message once serialized to JSON
@@ -128,6 +128,37 @@
     expect(parsed, expected);
   });
 
+  group('testConvertDouble', () {
+    test('WithDecimal', () {
+      final json = '{"12":1.2}';
+      TestAllTypes proto = TestAllTypes()..optionalDouble = 1.2;
+      expect(TestAllTypes.fromJson(json), proto);
+      expect(proto.writeToJson(), json);
+    });
+
+    test('WholeNumber', () {
+      final json = '{"12":5}';
+      TestAllTypes proto = TestAllTypes()..optionalDouble = 5.0;
+      expect(TestAllTypes.fromJson(json), proto);
+      expect(proto.writeToJson(), json);
+    });
+
+    test('Infinity', () {
+      final json = '{"12":"Infinity"}';
+      TestAllTypes proto = TestAllTypes()..optionalDouble = double.infinity;
+      expect(TestAllTypes.fromJson(json), proto);
+      expect(proto.writeToJson(), json);
+    });
+
+    test('NegativeInfinity', () {
+      final json = '{"12":"-Infinity"}';
+      TestAllTypes proto = TestAllTypes()
+        ..optionalDouble = double.negativeInfinity;
+      expect(TestAllTypes.fromJson(json), proto);
+      expect(proto.writeToJson(), json);
+    });
+  });
+
   test('testParseUnsignedLegacy', () {
     var parsed = TestAllTypes.fromJson(
         '{"4":"-1152921500311945216","8":"-1152921500311945215"}');
diff --git a/protoc_plugin/test/map_field_test.dart b/protoc_plugin/test/map_field_test.dart
index 75f1fbd..eae4cfb 100644
--- a/protoc_plugin/test/map_field_test.dart
+++ b/protoc_plugin/test/map_field_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'dart:convert';
 
 import 'package:protobuf/protobuf.dart';
@@ -83,9 +81,9 @@
     expect(testMap.int32ToEnumField[2], TestMap_EnumValue.BAR);
     expect(testMap.int32ToEnumField[3], TestMap_EnumValue.BAZ);
 
-    expect(testMap.int32ToMessageField[1].value, 11);
-    expect(testMap.int32ToMessageField[2].value, 22);
-    expect(testMap.int32ToMessageField[3].value, 33);
+    expect(testMap.int32ToMessageField[1]!.value, 11);
+    expect(testMap.int32ToMessageField[2]!.value, 22);
+    expect(testMap.int32ToMessageField[3]!.value, 33);
 
     expect(testMap.stringToInt32Field['1'], 11);
     expect(testMap.stringToInt32Field['2'], 22);
@@ -114,9 +112,9 @@
     expect(testMap.int32ToEnumField[4], TestMap_EnumValue.ZOP);
 
     expect(testMap.int32ToMessageField.length, 3);
-    expect(testMap.int32ToMessageField[1].value, 111);
-    expect(testMap.int32ToMessageField[3].value, 33);
-    expect(testMap.int32ToMessageField[4].value, 44);
+    expect(testMap.int32ToMessageField[1]!.value, 111);
+    expect(testMap.int32ToMessageField[3]!.value, 33);
+    expect(testMap.int32ToMessageField[4]!.value, 44);
 
     expect(testMap.stringToInt32Field.length, 3);
     expect(testMap.stringToInt32Field['1'], 111);
@@ -142,45 +140,6 @@
     _expectMapValuesUpdated(testMap);
   });
 
-  test('null keys and value are not allowed', () {
-    var testMap = TestMap();
-
-    try {
-      testMap.stringToInt32Field[null] = 1;
-      fail('Should have thrown an exception.');
-    } on ArgumentError catch (e) {
-      expect(e.message, "Can't add a null to a map field");
-    }
-
-    try {
-      testMap.int32ToBytesField[1] = null;
-      fail('Should have thrown an exception.');
-    } on ArgumentError catch (e) {
-      expect(e.message, "Can't add a null to a map field");
-    }
-
-    try {
-      testMap.int32ToStringField[1] = null;
-      fail('Should have thrown an exception.');
-    } on ArgumentError catch (e) {
-      expect(e.message, "Can't add a null to a map field");
-    }
-
-    try {
-      testMap.int32ToEnumField[1] = null;
-      fail('Should have thrown an exception.');
-    } on ArgumentError catch (e) {
-      expect(e.message, "Can't add a null to a map field");
-    }
-
-    try {
-      testMap.int32ToMessageField[1] = null;
-      fail('Should have thrown an exception.');
-    } on ArgumentError catch (e) {
-      expect(e.message, "Can't add a null to a map field");
-    }
-  });
-
   test('Serialize and parse map', () {
     var testMap = TestMap();
     _setValues(testMap);
@@ -255,9 +214,9 @@
       ..int32ToMessageField[1] = (TestMap_MessageValue()..secondValue = 43);
     testMap.mergeFromMessage(other);
 
-    expect(testMap.int32ToMessageField[1].value, 0);
-    expect(testMap.int32ToMessageField[1].secondValue, 43);
-    expect(testMap.int32ToMessageField[2].value, 44);
+    expect(testMap.int32ToMessageField[1]!.value, 0);
+    expect(testMap.int32ToMessageField[1]!.secondValue, 43);
+    expect(testMap.int32ToMessageField[2]!.value, 44);
   });
 
   test('parse duplicate keys', () {
@@ -286,12 +245,12 @@
 
   test('retain explicit default values of sub-messages', () {
     var testMap = TestMap()..int32ToMessageField[1] = TestMap_MessageValue();
-    expect(testMap.int32ToMessageField[1].secondValue, 42);
+    expect(testMap.int32ToMessageField[1]!.secondValue, 42);
 
     var testMap2 = TestMap()..int32ToMessageField[2] = TestMap_MessageValue();
 
     testMap.mergeFromBuffer(testMap2.writeToBuffer());
-    expect(testMap.int32ToMessageField[2].secondValue, 42);
+    expect(testMap.int32ToMessageField[2]!.secondValue, 42);
   });
 
   test('Freeze message with map field', () {
@@ -301,7 +260,7 @@
 
     expect(() => _updateValues(testMap),
         throwsA(const TypeMatcher<UnsupportedError>()));
-    expect(() => testMap.int32ToMessageField[1].value = 42,
+    expect(() => testMap.int32ToMessageField[1]!.value = 42,
         throwsA(const TypeMatcher<UnsupportedError>()));
     expect(() => testMap.int32ToStringField.remove(1),
         throwsA(const TypeMatcher<UnsupportedError>()));
@@ -315,8 +274,8 @@
     testMap.int32ToMessageField[2] = (TestMap_MessageValue()..secondValue = 12);
 
     void testValues(TestMap candidate) {
-      final message1 = candidate.int32ToMessageField[1];
-      final message2 = candidate.int32ToMessageField[2];
+      final message1 = candidate.int32ToMessageField[1]!;
+      final message2 = candidate.int32ToMessageField[2]!;
 
       expect(message1.hasValue(), true);
       expect(message1.value, 11);
@@ -342,7 +301,7 @@
     expect(value is Map<int, List<int>>, true);
   });
 
-  test('named optional arguments in cosntructor', () {
+  test('named optional arguments in constructor', () {
     final testMap = TestMap(
       int32ToInt32Field: {1: 11, 2: 22, 3: 33},
       int32ToStringField: {1: '11', 2: '22', 3: '33'},
@@ -365,4 +324,36 @@
     );
     _expectMapValuesSet(testMap);
   });
+
+  test('Parses null keys and values', () {
+    // Use a desugared version of the message to create missing
+    // values in the serialized form.
+    final d = Desugared(int32ToStringField: [
+      Desugared_Int32ToString(key: null, value: 'abc'),
+      Desugared_Int32ToString(key: 42, value: null),
+      Desugared_Int32ToString(key: 11, value: 'def'),
+    ], stringToInt32Field: [
+      Desugared_StringToInt32(key: null, value: 11),
+      Desugared_StringToInt32(key: 'abc', value: null),
+      Desugared_StringToInt32(key: 'def', value: 42),
+    ]);
+
+    final m = TestMap.fromBuffer(d.writeToBuffer());
+    expect(m.int32ToStringField[0], 'abc');
+    expect(m.int32ToStringField[42], '');
+    expect(m.int32ToStringField[11], 'def');
+    expect(m.stringToInt32Field['abc'], 0);
+    expect(m.stringToInt32Field[''], 11);
+    expect(m.stringToInt32Field['def'], 42);
+  });
+
+  test('Map field reads should not affect equality or hash of message', () {
+    final m1 = TestMap.create();
+    final m2 = TestMap.create();
+    expect(m1, equals(m2));
+    expect(m1.hashCode, equals(m2.hashCode));
+    m1.int32ToStringField; // read a map field
+    expect(m1, equals(m2));
+    expect(m1.hashCode, equals(m2.hashCode));
+  });
 }
diff --git a/protoc_plugin/test/message_generator_test.dart b/protoc_plugin/test/message_generator_test.dart
index 1cf2c4a..e8b6d2e 100755
--- a/protoc_plugin/test/message_generator_test.dart
+++ b/protoc_plugin/test/message_generator_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'dart:collection';
 
 import 'package:collection/collection.dart';
@@ -19,9 +17,9 @@
 import 'golden_file.dart';
 
 void main() {
-  FileDescriptorProto fd;
+  late FileDescriptorProto fd;
   EnumDescriptorProto ed;
-  DescriptorProto md;
+  late DescriptorProto md;
   setUp(() async {
     fd = FileDescriptorProto();
     ed = EnumDescriptorProto()
@@ -76,11 +74,12 @@
       ..enumType.add(ed);
   });
   test('testMessageGenerator', () {
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
 
-    var fg = FileGenerator(fd, options);
-    var mg = MessageGenerator.topLevel(md, fg, {}, null, <String>{}, 0);
+    FileGenerator fg = FileGenerator(fd, options);
+    MessageGenerator mg =
+        MessageGenerator.topLevel(md, fg, {}, null, <String>{}, 0);
 
     var ctx = GenerationContext(options);
     mg.register(ctx);
@@ -101,10 +100,11 @@
   });
 
   test('testMetadataIndices', () {
-    var options =
-        parseGenerationOptions(CodeGeneratorRequest(), CodeGeneratorResponse());
-    var fg = FileGenerator(fd, options);
-    var mg = MessageGenerator.topLevel(md, fg, {}, null, <String>{}, 0);
+    var options = parseGenerationOptions(
+        CodeGeneratorRequest(), CodeGeneratorResponse())!;
+    FileGenerator fg = FileGenerator(fd, options);
+    MessageGenerator mg =
+        MessageGenerator.topLevel(md, fg, {}, null, <String>{}, 0);
 
     var ctx = GenerationContext(options);
     mg.register(ctx);
diff --git a/protoc_plugin/test/mirror_util.dart b/protoc_plugin/test/mirror_util.dart
index 959a4f5..70a7487 100644
--- a/protoc_plugin/test/mirror_util.dart
+++ b/protoc_plugin/test/mirror_util.dart
@@ -4,8 +4,8 @@
 
 import 'dart:mirrors';
 
-/// Returns the names of the public properties and methods on a class.
-/// (Also visits its superclasses, recursively.)
+/// Returns the names of the public properties and non-static methods of a
+/// class. Also visits its superclasses, recursively.
 Set<String> findMemberNames(String importName, Symbol classSymbol) {
   var lib = currentMirrorSystem().libraries[Uri.parse(importName)]!;
   var cls = lib.declarations[classSymbol] as ClassMirror?;
@@ -26,7 +26,8 @@
     for (var decl in cls.declarations.values) {
       if (!decl.isPrivate &&
           decl is! VariableMirror &&
-          decl is! TypeVariableMirror) {
+          decl is! TypeVariableMirror &&
+          !(decl is MethodMirror && decl.isStatic)) {
         result.add(chooseName(decl.simpleName));
       }
     }
diff --git a/protoc_plugin/test/mixin_test.dart b/protoc_plugin/test/mixin_test.dart
index e4b6641..16bc720 100644
--- a/protoc_plugin/test/mixin_test.dart
+++ b/protoc_plugin/test/mixin_test.dart
@@ -12,8 +12,8 @@
     });
 
     test('is a Mixin1', () {
-      expect(proto is Mixin1, isTrue);
-      expect(proto is Mixin2, isFalse);
+      expect(proto, isA<Mixin1>());
+      expect(proto, isNot(isA<Mixin2>()));
     });
 
     test('implements interface defined by mixins', () {
@@ -59,9 +59,9 @@
     });
 
     test('is both Mixin1 (from parent) and Mixin3', () {
-      expect(proto is Mixin1, isTrue);
-      expect(proto is Mixin2, isFalse);
-      expect(proto is Mixin3, isTrue);
+      expect(proto, isA<Mixin1>());
+      expect(proto, isNot(isA<Mixin2>()));
+      expect(proto, isA<Mixin3>());
     });
   });
 }
diff --git a/protoc_plugin/test/names_test.dart b/protoc_plugin/test/names_test.dart
index 4e1adf3..a9ac9ce 100644
--- a/protoc_plugin/test/names_test.dart
+++ b/protoc_plugin/test/names_test.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/names.dart' as names;
 import 'package:protoc_plugin/src/generated/dart_options.pb.dart';
 import 'package:protoc_plugin/src/generated/descriptor.pb.dart';
@@ -32,6 +30,12 @@
     expect(msg.hasRenamedField(), false);
   });
 
+  test('Can access a filed started with underscore and digit', () {
+    var msg = pb.UnderscoreDigitName();
+    msg.x3d = 'one';
+    expect(msg.getField(1), 'one');
+  });
+
   test('Can swap field names using dart_name option', () {
     var msg = pb.SwapNames();
     msg.first = 'one';
@@ -220,7 +224,7 @@
   });
 
   test('The proto name is set correctly', () {
-    expect(json_name.JsonNamedMessage().info_.byName['barName'].protoName,
+    expect(json_name.JsonNamedMessage().info_.byName['barName']!.protoName,
         'foo_name');
   });
 
diff --git a/protoc_plugin/test/omit_enum_names_test.dart b/protoc_plugin/test/omit_enum_names_test.dart
index a9af671..341ba45 100644
--- a/protoc_plugin/test/omit_enum_names_test.dart
+++ b/protoc_plugin/test/omit_enum_names_test.dart
@@ -16,6 +16,11 @@
         const bool.fromEnvironment('protobuf.omit_enum_names')
             ? ''
             : 'FOREIGN_FOO');
+    expect(
+        ForeignEnum.FOREIGN_FOO.toString(),
+        const bool.fromEnvironment('protobuf.omit_enum_names')
+            ? '4'
+            : 'FOREIGN_FOO');
     expect(constant(), 'SHOULD_BE_PRESENT');
   });
 }
diff --git a/protoc_plugin/test/omit_field_names_test.dart b/protoc_plugin/test/omit_field_names_test.dart
index 3673f3a..7dba8b2 100644
--- a/protoc_plugin/test/omit_field_names_test.dart
+++ b/protoc_plugin/test/omit_field_names_test.dart
@@ -12,10 +12,11 @@
 Future<void> main() async {
   test('enum name available depending on environment', () {
     var proto = TestAllTypes()..optionalForeignMessage = ForeignMessage();
+
     expect(
         proto.toString(),
         const bool.fromEnvironment('protobuf.omit_field_names')
-            ? ': {\n}\n'
+            ? '19: {\n}\n'
             : 'optionalForeignMessage: {\n}\n');
     expect(constant(), 'SHOULD_BE_PRESENT');
   });
diff --git a/protoc_plugin/test/oneof_test.dart b/protoc_plugin/test/oneof_test.dart
index de1c00a..dcd65fa 100644
--- a/protoc_plugin/test/oneof_test.dart
+++ b/protoc_plugin/test/oneof_test.dart
@@ -161,11 +161,15 @@
   test('copyWith preserves oneof state', () {
     var foo = Foo();
     expectOneofNotSet(foo);
-    var copy1 = foo.deepCopy().freeze().rebuild((_) {}) as Foo;
+    // `ignore` below to work around https://github.com/dart-lang/sdk/issues/48879
+    var copy1 =
+        foo.deepCopy().freeze().rebuild((_) {}) as Foo; // ignore: unused_result
     expectOneofNotSet(copy1);
     foo.first = 'oneof';
     expectFirstSet(foo);
-    var copy2 = foo.deepCopy().freeze().rebuild((_) {}) as Foo;
+    // `ignore` below to work around https://github.com/dart-lang/sdk/issues/48879
+    var copy2 =
+        foo.deepCopy().freeze().rebuild((_) {}) as Foo; // ignore: unused_result
     expectFirstSet(copy2);
   });
 
diff --git a/protoc_plugin/test/proto3_json_test.dart b/protoc_plugin/test/proto3_json_test.dart
index 02fb71a..e0a2f4e 100644
--- a/protoc_plugin/test/proto3_json_test.dart
+++ b/protoc_plugin/test/proto3_json_test.dart
@@ -17,6 +17,7 @@
 import '../out/protos/google/protobuf/struct.pb.dart';
 import '../out/protos/google/protobuf/timestamp.pb.dart';
 import '../out/protos/google/protobuf/unittest.pb.dart';
+import '../out/protos/google/protobuf/unittest_nested_any.pb.dart';
 import '../out/protos/google/protobuf/unittest_well_known_types.pb.dart';
 import '../out/protos/google/protobuf/wrappers.pb.dart';
 import '../out/protos/map_field.pb.dart';
@@ -270,6 +271,26 @@
           throwsA(TypeMatcher<ArgumentError>()));
     });
 
+    test('Nested Any', () {
+      final packedOne = Any.pack(AnyMessage1()..value = '1');
+      final packedTwo = Any.pack(AnyMessage2()
+        ..value = '2'
+        ..anyField2 = packedOne);
+      expect(
+          packedTwo.toProto3Json(
+              typeRegistry: TypeRegistry([AnyMessage1(), AnyMessage2()])),
+          {
+            'anyField2': {
+              'value': '1',
+              '@type':
+                  'type.googleapis.com/protobuf_unittest_nested_any.AnyMessage1'
+            },
+            'value': '2',
+            '@type':
+                'type.googleapis.com/protobuf_unittest_nested_any.AnyMessage2'
+          });
+    });
+
     test('struct', () {
       final s = Struct()
         ..fields['null'] = (Value()..nullValue = NullValue.NULL_VALUE)
@@ -775,61 +796,47 @@
             ..optionalUint64 =
                 Int64.fromBytes([255, 255, 255, 255, 255, 255, 255, 255]));
 
-      expect(
-          () => TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalUint32': '-1',
-            }),
-          parseFailure(['optionalUint32']));
+      void expectRoundTrip(String typeName, int value) {
+        final t = TestAllTypes()
+          ..mergeFromProto3Json({
+            typeName: value,
+          });
+        expect(t.getField(t.getTagNumber(typeName)!), value);
+        final t2 = TestAllTypes()
+          ..mergeFromProto3Json({
+            typeName: value.toString(),
+          });
+        expect(t2.getField(t2.getTagNumber(typeName)!), value);
+      }
 
-      expect(
-          () => TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalUint32': -1,
-            }),
-          parseFailure(['optionalUint32']));
+      void expectFailure(String typeName, int value) {
+        expect(
+            () => TestAllTypes()..mergeFromProto3Json({typeName: -2147483649}),
+            parseFailure([typeName]));
+      }
 
-      expect(
-          () => TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalUint32': 0xFFFFFFFF + 1,
-            }),
-          parseFailure(['optionalUint32']));
+      void expectSigned32(String typeName) {
+        expectRoundTrip(typeName, 1);
+        expectRoundTrip(typeName, 0);
+        expectRoundTrip(typeName, 2147483647);
+        expectRoundTrip(typeName, -2147483648);
+        expectFailure(typeName, 2147483648);
+        expectFailure(typeName, -2147483649);
+      }
 
-      expect(
-          TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalInt32': '2147483647',
-            }),
-          TestAllTypes()..optionalInt32 = 2147483647);
+      void expectUnsigned32(String typeName) {
+        expectRoundTrip(typeName, 1);
+        expectRoundTrip(typeName, 0);
+        expectRoundTrip(typeName, 0xFFFFFFFF);
+        expectFailure(typeName, 0xFFFFFFFF + 1);
+        expectFailure(typeName, -1);
+      }
 
-      expect(
-          TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalInt32': '-2147483648',
-            }),
-          TestAllTypes()..optionalInt32 = -2147483648);
-
-      expect(
-          () => TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalInt32': 2147483647 + 1,
-            }),
-          parseFailure(['optionalInt32']));
-
-      expect(
-          () => TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalInt32': (2147483647 + 1).toString(),
-            }),
-          parseFailure(['optionalInt32']));
-
-      expect(
-          () => TestAllTypes()
-            ..mergeFromProto3Json({
-              'optionalInt32': -2147483648 - 1,
-            }),
-          parseFailure(['optionalInt32']));
+      expectUnsigned32('optionalFixed32');
+      expectUnsigned32('optionalUint32');
+      expectSigned32('optionalInt32');
+      expectSigned32('optionalSfixed32');
+      expectSigned32('optionalSint32');
     });
 
     test('unknown fields', () {
@@ -852,6 +859,7 @@
       expect(t, TestAllTypes());
       expect(t.unknownFields.isEmpty, isTrue);
     });
+
     test('Any', () {
       final m1 = Any()
         ..mergeFromProto3Json({
@@ -929,6 +937,28 @@
           parseFailure([]));
     });
 
+    test('Nested Any', () {
+      final m1 = Any()
+        ..mergeFromProto3Json({
+          'anyField2': {
+            'value': '1',
+            '@type':
+                'type.googleapis.com/protobuf_unittest_nested_any.AnyMessage1'
+          },
+          'value': '2',
+          '@type':
+              'type.googleapis.com/protobuf_unittest_nested_any.AnyMessage2'
+        }, typeRegistry: TypeRegistry([AnyMessage1(), AnyMessage2()]));
+
+      expect(
+          m1
+              .unpackInto(AnyMessage2())
+              .anyField2
+              .unpackInto(AnyMessage1())
+              .value,
+          '1');
+    });
+
     test('Duration', () {
       expect(
           Duration()..mergeFromProto3Json('0s'),
@@ -1214,4 +1244,35 @@
     expectSecondSet(Foo()..mergeFromProto3Json({'second': 1}));
     expectOneofNotSet(Foo()..mergeFromProto3Json({}));
   });
+
+  group('Convert Double', () {
+    test('With Decimal', () {
+      final json = {'optionalDouble': 1.2};
+      TestAllTypes proto = TestAllTypes()..optionalDouble = 1.2;
+      expect(TestAllTypes()..mergeFromProto3Json(json), proto);
+      expect(proto.toProto3Json(), json);
+    });
+
+    test('Whole Number', () {
+      final json = {'optionalDouble': 5};
+      TestAllTypes proto = TestAllTypes()..optionalDouble = 5.0;
+      expect(TestAllTypes()..mergeFromProto3Json(json), proto);
+      expect(proto.toProto3Json(), json);
+    });
+
+    test('Infinity', () {
+      final json = {'optionalDouble': 'Infinity'};
+      TestAllTypes proto = TestAllTypes()..optionalDouble = double.infinity;
+      expect(TestAllTypes()..mergeFromProto3Json(json), proto);
+      expect(proto.toProto3Json(), json);
+    });
+
+    test('Negative Infinity', () {
+      final json = {'optionalDouble': '-Infinity'};
+      TestAllTypes proto = TestAllTypes()
+        ..optionalDouble = double.negativeInfinity;
+      expect(TestAllTypes()..mergeFromProto3Json(json), proto);
+      expect(proto.toProto3Json(), json);
+    });
+  });
 }
diff --git a/protoc_plugin/test/protoc_options_test.dart b/protoc_plugin/test/protoc_options_test.dart
index f463b94..f197efb 100644
--- a/protoc_plugin/test/protoc_options_test.dart
+++ b/protoc_plugin/test/protoc_options_test.dart
@@ -3,15 +3,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/src/generated/plugin.pb.dart';
 import 'package:protoc_plugin/src/options.dart';
 import 'package:test/test.dart';
 
 void main() {
   test('testValidGeneratorOptions', () {
-    void checkValid(String parameter) {
+    void checkValid(String? parameter) {
       var request = CodeGeneratorRequest();
       if (parameter != null) request.parameter = parameter;
       var response = CodeGeneratorResponse();
@@ -28,9 +26,9 @@
   });
 
   test('testInvalidGeneratorOptions', () {
-    void checkInvalid(String parameter) {
+    checkInvalid(String parameter) {
       var request = CodeGeneratorRequest();
-      if (parameter != null) request.parameter = parameter;
+      request.parameter = parameter;
       var response = CodeGeneratorResponse();
       var options = parseGenerationOptions(request, response);
       expect(options, isNull);
diff --git a/protoc_plugin/test/protos/bytes.proto b/protoc_plugin/test/protos/bytes.proto
new file mode 100644
index 0000000..4ba43f3
--- /dev/null
+++ b/protoc_plugin/test/protos/bytes.proto
@@ -0,0 +1,6 @@
+syntax = "proto2";
+
+message BytesEntity {
+  optional bytes value = 1;
+  repeated bytes values = 2;
+}
diff --git a/protoc_plugin/test/protos/dart_name.proto b/protoc_plugin/test/protos/dart_name.proto
index 533d43c..a09c742 100644
--- a/protoc_plugin/test/protos/dart_name.proto
+++ b/protoc_plugin/test/protos/dart_name.proto
@@ -37,3 +37,7 @@
 message Function_ {
   optional string fun1 = 1;
 }
+
+message UnderscoreDigitName {
+  optional string _3d = 1;
+}
diff --git a/protoc_plugin/test/protos/google/protobuf/unittest_nested_any.proto b/protoc_plugin/test/protos/google/protobuf/unittest_nested_any.proto
new file mode 100644
index 0000000..d929b54
--- /dev/null
+++ b/protoc_plugin/test/protos/google/protobuf/unittest_nested_any.proto
@@ -0,0 +1,15 @@
+syntax = "proto3";
+
+package protobuf_unittest_nested_any;
+
+import "google/protobuf/any.proto";
+
+message AnyMessage1 {
+  google.protobuf.Any any_field1 = 1;
+  string value = 2;
+}
+
+message AnyMessage2 {
+  google.protobuf.Any any_field2 = 1;
+  string value = 2;
+}
diff --git a/protoc_plugin/test/protos/map_field.proto b/protoc_plugin/test/protos/map_field.proto
index 0017d72..8236376 100644
--- a/protoc_plugin/test/protos/map_field.proto
+++ b/protoc_plugin/test/protos/map_field.proto
@@ -37,3 +37,16 @@
 message Outer {
     optional Inner i = 1;
 }
+
+message Desugared {
+    message Int32ToString {
+        optional int32 key = 1;
+        optional string value = 2;
+    }
+    message StringToInt32 {
+        optional string key = 1;
+        optional int32 value = 2;
+    }
+    repeated Int32ToString int32_to_string_field = 2;
+    repeated StringToInt32 string_to_int32_field = 6;
+}
diff --git a/protoc_plugin/test/protos/proto2_repeated.proto b/protoc_plugin/test/protos/proto2_repeated.proto
new file mode 100644
index 0000000..ee3b156
--- /dev/null
+++ b/protoc_plugin/test/protos/proto2_repeated.proto
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+syntax = "proto2";
+
+message Proto2Repeated {
+  repeated int32 ints_default = 1;
+  repeated int32 ints_packed = 2 [packed=true];
+  repeated int32 ints_not_packed = 3 [packed=false];
+}
diff --git a/protoc_plugin/test/protos/proto3_repeated.proto b/protoc_plugin/test/protos/proto3_repeated.proto
new file mode 100644
index 0000000..723d154
--- /dev/null
+++ b/protoc_plugin/test/protos/proto3_repeated.proto
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+syntax = "proto3";
+
+message Proto3Repeated {
+  repeated int32 ints_default = 1;
+  repeated int32 ints_packed = 2 [packed=true];
+  repeated int32 ints_not_packed = 3 [packed=false];
+}
diff --git a/protoc_plugin/test/repeated_encoding_test.dart b/protoc_plugin/test/repeated_encoding_test.dart
new file mode 100644
index 0000000..17a990a
--- /dev/null
+++ b/protoc_plugin/test/repeated_encoding_test.dart
@@ -0,0 +1,50 @@
+#!/usr/bin/env dart
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+
+import '../out/protos/proto2_repeated.pb.dart';
+import '../out/protos/proto3_repeated.pb.dart';
+
+void main() {
+  test('check proto2 and proto3 repeated field encodings', () {
+    final proto2 = Proto2Repeated(
+        intsDefault: [1, 2], intsPacked: [1, 2], intsNotPacked: [1, 2]);
+    final proto2Encoded = proto2.writeToBuffer();
+    expect(
+        proto2Encoded.toList(),
+        equals([
+          8, // field = 1, type = varint
+          1, // value = 1
+          8, // field = 1, type = varint
+          2, // value = 2
+          18, // field = 2, type = length delimited
+          2, // length = 2
+          1, 2, // values = [1, 2]
+          24, // field = 3, type = varint
+          1, // value = 1
+          24, // field = 3, type = varint
+          2, // value = 2
+        ]));
+
+    final proto3 = Proto3Repeated(
+        intsDefault: [1, 2], intsPacked: [1, 2], intsNotPacked: [1, 2]);
+    final proto3Encoded = proto3.writeToBuffer();
+    expect(
+        proto3Encoded.toList(),
+        equals([
+          10, // field = 1, type = length delimited
+          2, // length = 2
+          1, 2, // values = [1, 2]
+          18, // field = 2, type = length delimited
+          2, // length = 2
+          1, 2, // values = [1, 2]
+          24, // field = 3, type = varint
+          1, // value = 1
+          24, // field = 3, type = varint
+          2, // value = 2
+        ]));
+  });
+}
diff --git a/protoc_plugin/test/repeated_field_test.dart b/protoc_plugin/test/repeated_field_test.dart
index 2683745..d62ccf4 100644
--- a/protoc_plugin/test/repeated_field_test.dart
+++ b/protoc_plugin/test/repeated_field_test.dart
@@ -32,4 +32,16 @@
             .check,
         isNotNull);
   });
+
+  test('check read-only default list type', () {
+    final msg = TestAllTypes()..freeze();
+    final list = msg.repeatedInt32;
+    expect(
+      list.firstWhere(
+        (_msg) => false,
+        orElse: () => 123,
+      ),
+      123,
+    );
+  });
 }
diff --git a/protoc_plugin/test/reserved_names_test.dart b/protoc_plugin/test/reserved_names_test.dart
index dbac073..43c9eff 100755
--- a/protoc_plugin/test/reserved_names_test.dart
+++ b/protoc_plugin/test/reserved_names_test.dart
@@ -22,13 +22,26 @@
 
 import 'mirror_util.dart' show findMemberNames;
 
+// These names are no longer reserved but we keep them in
+// `GeneratedMessage_reservedNames` to keep generated code backwards
+// compatible. Remove in next major release.
+const List<String> oldGeneratedMessageReservedNames = [
+  'fromBuffer',
+  'fromJson',
+  r'$_defaultFor',
+];
+
+// These names are no longer reserved but we keep them in
+// `ProtobufEnum_reservedNames` to keep generated code backwards compatible.
+// Remove in next major release.
+const List<String> oldProtobufEnumReservedNames = ['initByValue'];
+
 void main() {
   test('GeneratedMessage reserved names are up to date', () {
     var actual = Set<String>.from(GeneratedMessage_reservedNames);
     var expected =
         findMemberNames('package:protobuf/protobuf.dart', #GeneratedMessage)
-          // TODO: see https://github.com/google/protobuf.dart/issues/527
-          ..removeAll(_oneOffNames);
+          ..addAll(oldGeneratedMessageReservedNames);
 
     expect(actual.toList()..sort(), equals(expected.toList()..sort()));
   });
@@ -37,17 +50,14 @@
     var actual = Set<String>.from(ProtobufEnum_reservedNames);
     var expected =
         findMemberNames('package:protobuf/protobuf.dart', #ProtobufEnum)
-          // TODO: see https://github.com/google/protobuf.dart/issues/527
-          ..removeAll(_oneOffNames);
+          ..addAll(oldProtobufEnumReservedNames);
 
     expect(actual.toList()..sort(), equals(expected.toList()..sort()));
   });
 
   test("ReadonlyMessageMixin doesn't add any reserved names", () {
-    var mixinNames =
-        findMemberNames('package:protobuf/protobuf.dart', #ReadonlyMessageMixin)
-          // TODO: see https://github.com/google/protobuf.dart/issues/527
-          ..removeAll(_oneOffNames);
+    var mixinNames = findMemberNames(
+        'package:protobuf/protobuf.dart', #ReadonlyMessageMixin);
     var reservedNames = Set<String>.from(GeneratedMessage_reservedNames);
     for (var name in mixinNames) {
       if (name == 'ReadonlyMessageMixin' || name == 'unknownFields') continue;
@@ -63,9 +73,7 @@
 
     var expected = findMemberNames(meta.importFrom, #PbMapMixin)
       ..addAll(findMemberNames('dart:collection', #MapMixin))
-      ..removeAll(GeneratedMessage_reservedNames)
-      // TODO: see https://github.com/google/protobuf.dart/issues/527
-      ..removeAll(_oneOffNames);
+      ..removeAll(GeneratedMessage_reservedNames);
 
     expect(
         actual.toList()..sort(), containsAllInOrder(expected.toList()..sort()));
@@ -76,17 +84,8 @@
     var actual = Set<String>.from(meta.findReservedNames());
 
     var expected = findMemberNames(meta.importFrom, #PbEventMixin)
-      ..removeAll(GeneratedMessage_reservedNames)
-      // TODO: see https://github.com/google/protobuf.dart/issues/527
-      ..removeAll(_oneOffNames);
+      ..removeAll(GeneratedMessage_reservedNames);
 
     expect(actual.toList()..sort(), equals(expected.toList()..sort()));
   });
 }
-
-// See https://github.com/google/protobuf.dart/issues/527
-const _oneOffNames = {
-  'hash',
-  'hashAll',
-  'hashAllUnordered',
-};
diff --git a/protoc_plugin/test/service_generator_test.dart b/protoc_plugin/test/service_generator_test.dart
index 5dc0214..388a78a 100644
--- a/protoc_plugin/test/service_generator_test.dart
+++ b/protoc_plugin/test/service_generator_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:protoc_plugin/indenting_writer.dart';
 import 'package:protoc_plugin/protoc.dart';
 import 'package:protoc_plugin/src/linker.dart';
diff --git a/protoc_plugin/test/timestamp_test.dart b/protoc_plugin/test/timestamp_test.dart
index 5d02e21..6203139 100644
--- a/protoc_plugin/test/timestamp_test.dart
+++ b/protoc_plugin/test/timestamp_test.dart
@@ -23,6 +23,17 @@
     expect(fromProto, dateTime);
   });
 
+  test('negative Timestamp', () {
+    final secondBeforeEpoch = Timestamp(seconds: Int64(-1), nanos: 1000000);
+    var dateTime = DateTime.fromMillisecondsSinceEpoch(-999, isUtc: true);
+
+    expect(secondBeforeEpoch.toDateTime().millisecondsSinceEpoch,
+        dateTime.millisecondsSinceEpoch);
+    expect(secondBeforeEpoch.toDateTime(), dateTime);
+    expect(Timestamp.fromDateTime(dateTime).nanos, 1000000);
+    expect(Timestamp.fromDateTime(dateTime).seconds, Int64(-1));
+  });
+
   test('local datetime -> timestamp -> datetime', () {
     var dateTime = DateTime(2019, 02, 15, 10, 21, 25, 5, 5);
     var fromProto = Timestamp.fromDateTime(dateTime).toDateTime();
diff --git a/protoc_plugin/test/unknown_field_set_test.dart b/protoc_plugin/test/unknown_field_set_test.dart
index e8b36b6..7ca5efd 100755
--- a/protoc_plugin/test/unknown_field_set_test.dart
+++ b/protoc_plugin/test/unknown_field_set_test.dart
@@ -316,4 +316,13 @@
 
     _checkNotEqual(f1, f2);
   });
+
+  test(
+      'consistent hashcode for messages with no unknown fields set and an empty unknown field set',
+      () {
+    final m = TestAllExtensions();
+    // Force an unknown field set.
+    final m2 = TestAllExtensions()..unknownFields;
+    expect(m.hashCode, m2.hashCode);
+  });
 }
diff --git a/protoc_plugin/test/validate_fail_test.dart b/protoc_plugin/test/validate_fail_test.dart
index 5c04584..5dde01e 100755
--- a/protoc_plugin/test/validate_fail_test.dart
+++ b/protoc_plugin/test/validate_fail_test.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart=2.11
-
 import 'package:fixnum/fixnum.dart';
 import 'package:test/test.dart';
 
@@ -25,10 +23,6 @@
     var builder = TestAllTypes();
 
     expect(() {
-      builder.optionalInt32 = null;
-    }, throwsArgumentError);
-
-    expect(() {
       builder.optionalInt32 = cast('101') as int;
     }, badArgument);
     expect(() {
diff --git a/query_benchmark/analysis_options.yaml b/query_benchmark/analysis_options.yaml
new file mode 100644
index 0000000..e841247
--- /dev/null
+++ b/query_benchmark/analysis_options.yaml
@@ -0,0 +1,9 @@
+include: package:lints/recommended.yaml
+
+linter:
+  rules:
+    - comment_references
+    - directives_ordering
+    - prefer_relative_imports
+    - prefer_single_quotes
+    - prefer_spread_collections
diff --git a/query_benchmark/bin/set_nested_value.dart b/query_benchmark/bin/set_nested_value.dart
index 6df654f..0b7afb6 100644
--- a/query_benchmark/bin/set_nested_value.dart
+++ b/query_benchmark/bin/set_nested_value.dart
@@ -20,7 +20,7 @@
       title: 'protobuf_decode',
       duration: measure(() => a.rebuild((f0.A0 a0Builder) {
             a0Builder.a4.last = a0Builder.a4.last.rebuild((f2.A1 a1builder) {
-              a1builder.a378.rebuild(
+              a1builder.a378 = a1builder.a378.rebuild(
                   (f19.A220 a220builder) => a220builder.a234 = 'new_value');
             });
           })),
diff --git a/query_benchmark/mono_pkg.yaml b/query_benchmark/mono_pkg.yaml
index e822136..b23af8b 100644
--- a/query_benchmark/mono_pkg.yaml
+++ b/query_benchmark/mono_pkg.yaml
@@ -7,4 +7,4 @@
       - command: ./../tool/setup.sh
       - command: ./compile_protos.sh
       - analyze: --fatal-infos
-      dart: dev
+      sdk: dev
diff --git a/query_benchmark/pubspec.yaml b/query_benchmark/pubspec.yaml
index f264e55..1b4d85d 100644
--- a/query_benchmark/pubspec.yaml
+++ b/query_benchmark/pubspec.yaml
@@ -5,13 +5,17 @@
 name: query_benchmark
 description: Measuring encoding and decoding of a "real-world" protobuf.
 
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
+
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.12.0 <3.0.0'
 
 dependencies:
-  protobuf:
+  protobuf: any
 
 dev_dependencies:
+  lints: ^1.0.0
   protoc_plugin:
     path: "../protoc_plugin"
 
diff --git a/tool/ci.sh b/tool/ci.sh
index c323e5c..3251acc 100755
--- a/tool/ci.sh
+++ b/tool/ci.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Created with package:mono_repo v5.0.1
+# Created with package:mono_repo v6.2.0
 
 # Support built in commands on windows out of the box.
 # When it is a flutter repo (check the pubspec.yaml for "sdk: flutter")