Version 2.14.0-6.0.dev

Merge commit '3c597080546a5990dee18473c8065bc89308d951' into 'dev'
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 2a97366..0233e2e 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.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.9
-
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart'
     show SemanticTokenTypes, SemanticTokenModifiers;
 import 'package:analysis_server/src/lsp/constants.dart'
@@ -24,7 +22,7 @@
 /// A computer for [HighlightRegion]s and LSP [SemanticTokenInfo] in a Dart [CompilationUnit].
 class DartUnitHighlightsComputer {
   final CompilationUnit _unit;
-  final SourceRange range;
+  final SourceRange? range;
 
   final _regions = <HighlightRegion>[];
   final _semanticTokens = <SemanticTokenInfo>[];
@@ -57,11 +55,11 @@
   }
 
   void _addCommentRanges() {
-    var token = _unit.beginToken;
+    Token? token = _unit.beginToken;
     while (token != null) {
-      Token commentToken = token.precedingComments;
+      Token? commentToken = token.precedingComments;
       while (commentToken != null) {
-        HighlightRegionType highlightType;
+        HighlightRegionType? highlightType;
         if (commentToken.type == TokenType.MULTI_LINE_COMMENT) {
           if (commentToken.lexeme.startsWith('/**')) {
             highlightType = HighlightRegionType.COMMENT_DOCUMENTATION;
@@ -155,15 +153,16 @@
     if (element is! ClassElement) {
       return false;
     }
-    ClassElement classElement = element;
     // prepare type
     HighlightRegionType type;
-    if (node.parent is TypeName &&
-        node.parent.parent is ConstructorName &&
-        node.parent.parent.parent is InstanceCreationExpression) {
+    var parent = node.parent;
+    var grandParent = parent?.parent;
+    if (parent is TypeName &&
+        grandParent is ConstructorName &&
+        grandParent.parent is InstanceCreationExpression) {
       // new Class()
       type = HighlightRegionType.CONSTRUCTOR;
-    } else if (classElement.isEnum) {
+    } else if (element.isEnum) {
       type = HighlightRegionType.ENUM;
     } else {
       type = HighlightRegionType.CLASS;
@@ -184,7 +183,7 @@
     var element = node.writeOrReadElement;
     if (element is LocalVariableElement) {
       var elementType = element.type;
-      if (elementType?.isDynamic == true) {
+      if (elementType.isDynamic) {
         var type = node.inDeclarationContext()
             ? HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_DECLARATION
             : HighlightRegionType.DYNAMIC_LOCAL_VARIABLE_REFERENCE;
@@ -193,7 +192,7 @@
     }
     if (element is ParameterElement) {
       var elementType = element.type;
-      if (elementType?.isDynamic == true) {
+      if (elementType.isDynamic) {
         var type = node.inDeclarationContext()
             ? HighlightRegionType.DYNAMIC_PARAMETER_DECLARATION
             : HighlightRegionType.DYNAMIC_PARAMETER_REFERENCE;
@@ -207,11 +206,11 @@
     var element = node.writeOrReadElement;
     if (element is FieldFormalParameterElement) {
       if (node.parent is FieldFormalParameter) {
-        element = (element as FieldFormalParameterElement).field;
+        element = element.field;
       }
     }
     // prepare type
-    HighlightRegionType type;
+    HighlightRegionType? type;
     if (element is FieldElement) {
       var enclosingElement = element.enclosingElement;
       if (enclosingElement is ClassElement && enclosingElement.isEnum) {
@@ -283,13 +282,12 @@
       return false;
     }
     // getter or setter
-    var propertyAccessorElement = element as PropertyAccessorElement;
     var isTopLevel = element.enclosingElement is CompilationUnitElement;
     HighlightRegionType type;
-    if (propertyAccessorElement.isGetter) {
+    if (element.isGetter) {
       if (isTopLevel) {
         type = HighlightRegionType.TOP_LEVEL_GETTER_DECLARATION;
-      } else if (propertyAccessorElement.isStatic) {
+      } else if (element.isStatic) {
         type = HighlightRegionType.STATIC_GETTER_DECLARATION;
       } else {
         type = HighlightRegionType.INSTANCE_GETTER_DECLARATION;
@@ -297,7 +295,7 @@
     } else {
       if (isTopLevel) {
         type = HighlightRegionType.TOP_LEVEL_SETTER_DECLARATION;
-      } else if (propertyAccessorElement.isStatic) {
+      } else if (element.isStatic) {
         type = HighlightRegionType.STATIC_SETTER_DECLARATION;
       } else {
         type = HighlightRegionType.INSTANCE_SETTER_DECLARATION;
@@ -347,8 +345,7 @@
     if (element is! MethodElement) {
       return false;
     }
-    var methodElement = element as MethodElement;
-    var isStatic = methodElement.isStatic;
+    var isStatic = element.isStatic;
     // OK
     HighlightRegionType type;
     if (node.inDeclarationContext()) {
@@ -443,9 +440,10 @@
     int offset,
     int length,
     HighlightRegionType type, {
-    SemanticTokenTypes semanticTokenType,
-    Set<SemanticTokenModifiers> semanticTokenModifiers,
+    SemanticTokenTypes? semanticTokenType,
+    Set<SemanticTokenModifiers>? semanticTokenModifiers,
   }) {
+    final range = this.range;
     if (range != null) {
       final end = offset + length;
       // Skip token if it ends before the range of starts after the range.
@@ -470,8 +468,8 @@
   bool _addRegion_node(
     AstNode node,
     HighlightRegionType type, {
-    SemanticTokenTypes semanticTokenType,
-    Set<SemanticTokenModifiers> semanticTokenModifiers,
+    SemanticTokenTypes? semanticTokenType,
+    Set<SemanticTokenModifiers>? semanticTokenModifiers,
   }) {
     var offset = node.offset;
     var length = node.length;
@@ -493,10 +491,10 @@
   }
 
   void _addRegion_token(
-    Token token,
+    Token? token,
     HighlightRegionType type, {
-    SemanticTokenTypes semanticTokenType,
-    Set<SemanticTokenModifiers> semanticTokenModifiers,
+    SemanticTokenTypes? semanticTokenType,
+    Set<SemanticTokenModifiers>? semanticTokenModifiers,
   }) {
     if (token != null) {
       var offset = token.offset;
diff --git a/pkg/analysis_server/lib/src/utilities/mocks.dart b/pkg/analysis_server/lib/src/utilities/mocks.dart
index 6c4134a..5022dcd 100644
--- a/pkg/analysis_server/lib/src/utilities/mocks.dart
+++ b/pkg/analysis_server/lib/src/utilities/mocks.dart
@@ -2,12 +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.9
-
 import 'dart:async';
 
 import 'package:analysis_server/protocol/protocol.dart';
-import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/channel/channel.dart';
 import 'package:analysis_server/src/plugin/notification_manager.dart';
 import 'package:analysis_server/src/plugin/plugin_manager.dart';
@@ -27,14 +24,14 @@
       StreamController<Response>.broadcast();
   StreamController<Notification> notificationController =
       StreamController<Notification>(sync: true);
-  Completer<Response> errorCompleter;
+  Completer<Response>? errorCompleter;
 
   List<Response> responsesReceived = [];
   List<Notification> notificationsReceived = [];
 
   bool _closed = false;
 
-  String name;
+  String? name;
 
   MockServerChannel();
 
@@ -50,7 +47,7 @@
 
   @override
   void listen(void Function(Request request) onRequest,
-      {Function onError, void Function() onDone}) {
+      {Function? onError, void Function()? onDone}) {
     requestController.stream
         .listen(onRequest, onError: onError, onDone: onDone);
   }
@@ -62,11 +59,12 @@
       return;
     }
     notificationsReceived.add(notification);
+    final errorCompleter = this.errorCompleter;
     if (errorCompleter != null && notification.event == 'server.error') {
-      print(
-          '[server.error] test: $name message: ${notification.params['message']}');
-      errorCompleter.completeError(ServerError(notification.params['message']),
-          StackTrace.fromString(notification.params['stackTrace']));
+      var params = notification.params!;
+      print('[server.error] test: $name message: ${params['message']}');
+      errorCompleter.completeError(ServerError(params['message'] as String),
+          StackTrace.fromString(params['stackTrace'] as String));
     }
     // Wrap send notification in future to simulate websocket
     // TODO(scheglov) ask Dan why and decide what to do
@@ -118,9 +116,9 @@
     var response =
         responseController.stream.firstWhere((response) => response.id == id);
     if (throwOnError) {
-      errorCompleter = Completer<Response>();
+      var completer = errorCompleter = Completer<Response>();
       try {
-        return Future.any([response, errorCompleter.future]);
+        return Future.any([response, completer.future]);
       } finally {
         errorCompleter = null;
       }
@@ -143,11 +141,11 @@
 /// A plugin manager that simulates broadcasting requests to plugins by
 /// hard-coding the responses.
 class TestPluginManager implements PluginManager {
-  plugin.AnalysisSetPriorityFilesParams analysisSetPriorityFilesParams;
-  plugin.AnalysisSetSubscriptionsParams analysisSetSubscriptionsParams;
-  plugin.AnalysisUpdateContentParams analysisUpdateContentParams;
-  plugin.RequestParams broadcastedRequest;
-  Map<PluginInfo, Future<plugin.Response>> broadcastResults;
+  plugin.AnalysisSetPriorityFilesParams? analysisSetPriorityFilesParams;
+  plugin.AnalysisSetSubscriptionsParams? analysisSetSubscriptionsParams;
+  plugin.AnalysisUpdateContentParams? analysisUpdateContentParams;
+  plugin.RequestParams? broadcastedRequest;
+  Map<PluginInfo, Future<plugin.Response>>? broadcastResults;
 
   @override
   List<PluginInfo> plugins = [];
@@ -192,7 +190,7 @@
   @override
   Map<PluginInfo, Future<plugin.Response>> broadcastRequest(
       plugin.RequestParams params,
-      {analyzer.ContextRoot contextRoot}) {
+      {analyzer.ContextRoot? contextRoot}) {
     broadcastedRequest = params;
     return broadcastResults ?? <PluginInfo, Future<plugin.Response>>{};
   }
@@ -209,7 +207,7 @@
   }
 
   @override
-  List<PluginInfo> pluginsForContextRoot(analyzer.ContextRoot contextRoot) {
+  List<PluginInfo> pluginsForContextRoot(analyzer.ContextRoot? contextRoot) {
     fail('Unexpected invocation of pluginsForContextRoot');
   }
 
diff --git a/pkg/analysis_server/test/src/computer/highlights_computer_test.dart b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
index 3b3aacb..08682eb 100644
--- a/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/highlights_computer_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.9
-
 import 'package:analysis_server/src/computer/computer_highlights.dart';
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:test/test.dart';
@@ -19,9 +17,9 @@
 
 @reflectiveTest
 class Highlights2ComputerTest extends AbstractContextTest {
-  String sourcePath;
-  String content;
-  List<HighlightRegion> highlights;
+  late String sourcePath;
+  late String content;
+  late List<HighlightRegion> highlights;
 
   @override
   void setUp() {
@@ -122,7 +120,7 @@
       expect(result.errors, isEmpty);
     }
 
-    var computer = DartUnitHighlightsComputer(result.unit);
+    var computer = DartUnitHighlightsComputer(result.unit!);
     highlights = computer.compute();
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 35f7890..6024381 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -632,7 +632,7 @@
           throw ArgumentError('$uri is not a library.');
         }
 
-        var unitResult = await getUnitElement(file.path!);
+        var unitResult = await getUnitElement(file.path);
         return unitResult.element.library;
       },
       (externalLibrary) async {
@@ -662,10 +662,8 @@
     var units = <ParsedUnitResult>[];
     for (var unitFile in file.libraryFiles) {
       var unitPath = unitFile.path;
-      if (unitPath != null) {
-        var unitResult = parseFileSync(unitPath);
-        units.add(unitResult);
-      }
+      var unitResult = parseFileSync(unitPath);
+      units.add(unitResult);
     }
 
     return ParsedLibraryResultImpl(currentSession, path, file.uri, units);
@@ -685,7 +683,7 @@
         if (file.isPart) {
           throw ArgumentError('Is a part: $uri');
         }
-        return getParsedLibrary(file.path!);
+        return getParsedLibrary(file.path);
       },
       (externalLibrary) {
         return ParsedLibraryResultImpl.external(currentSession, uri);
@@ -759,7 +757,7 @@
         if (file.isPart) {
           throw ArgumentError('Is a part: $uri');
         }
-        return getResolvedLibrary(file.path!);
+        return getResolvedLibrary(file.path);
       },
       (externalLibrary) async {
         return ResolvedLibraryResultImpl.external(currentSession, uri);
@@ -925,7 +923,7 @@
     FileState file = _fileTracker.getFile(path);
     RecordingErrorListener listener = RecordingErrorListener();
     CompilationUnit unit = file.parse(listener);
-    return ParsedUnitResultImpl(currentSession, file.path!, file.uri,
+    return ParsedUnitResultImpl(currentSession, file.path, file.uri,
         file.content, file.lineInfo, file.isPart, unit, listener.errors);
   }
 
@@ -1403,28 +1401,27 @@
       Map<FileState, UnitAnalysisResult> unitResults = analyzer.analyze();
       var resolvedUnits = <ResolvedUnitResult>[];
 
-      for (var unitFile in unitResults.keys) {
-        if (unitFile.path != null) {
-          var unitResult = unitResults[unitFile]!;
-          resolvedUnits.add(
-            ResolvedUnitResultImpl(
-              currentSession,
-              unitFile.path!,
-              unitFile.uri,
-              unitFile.exists,
-              unitFile.content,
-              unitFile.lineInfo,
-              unitFile.isPart,
-              unitResult.unit,
-              unitResult.errors,
-            ),
-          );
-        }
+      for (var entry in unitResults.entries) {
+        var unitFile = entry.key;
+        var unitResult = entry.value;
+        resolvedUnits.add(
+          ResolvedUnitResultImpl(
+            currentSession,
+            unitFile.path,
+            unitFile.uri,
+            unitFile.exists,
+            unitFile.content,
+            unitFile.lineInfo,
+            unitFile.isPart,
+            unitResult.unit,
+            unitResult.errors,
+          ),
+        );
       }
 
       return ResolvedLibraryResultImpl(
         currentSession,
-        library.path!,
+        library.path,
         library.uri,
         resolvedUnits.first.libraryElement,
         resolvedUnits,
@@ -1610,7 +1607,7 @@
     _updateHasErrorOrWarningFlag(file, errors);
     return AnalysisResult(
         currentSession,
-        file.path!,
+        file.path,
         file.uri,
         file.exists,
         content,
@@ -1627,7 +1624,7 @@
       FileState file, List<AnalysisDriverUnitError> serialized) {
     List<AnalysisError> errors = <AnalysisError>[];
     for (AnalysisDriverUnitError error in serialized) {
-      var analysisError = ErrorEncoding.decode(file.source!, error);
+      var analysisError = ErrorEncoding.decode(file.source, error);
       if (analysisError != null) {
         errors.add(analysisError);
       }
@@ -1666,7 +1663,7 @@
     // TODO(scheglov) Find a better way to report this.
     return AnalysisResult(
         currentSession,
-        file.path!,
+        file.path,
         file.uri,
         file.exists,
         null,
@@ -1675,7 +1672,7 @@
         'missing',
         null,
         [
-          AnalysisError(file.source!, 0, 0,
+          AnalysisError(file.source, 0, 0,
               CompileTimeErrorCode.MISSING_DART_LIBRARY, [missingUri])
         ],
         null);
@@ -1696,9 +1693,7 @@
     var libraryFile = _fsState.getFileForPath(path);
     for (var file in libraryFile.libraryFiles) {
       var path = file.path;
-      if (path != null) {
-        fileContentMap[path] = file.content;
-      }
+      fileContentMap[path] = file.content;
     }
 
     _exceptionController.add(
@@ -1737,7 +1732,7 @@
       List<AnalysisDriverExceptionFileBuilder> contextFiles = libraryFile
           .transitiveFiles
           .map((file) => AnalysisDriverExceptionFileBuilder(
-              path: file.path!, content: file.content))
+              path: file.path, content: file.content))
           .toList();
       contextFiles.sort((a, b) => a.path.compareTo(b.path));
       AnalysisDriverExceptionContextBuilder contextBuilder =
@@ -2409,7 +2404,7 @@
       }
       FileState file = filesToCheck![filesToCheckIndex++];
       if (file.referencedNames.contains(name)) {
-        referencingFiles.add(file.path!);
+        referencingFiles.add(file.path);
       }
     }
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index fbb35e3..0577376 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -92,13 +92,13 @@
   final FileSystemState _fsState;
 
   /// The absolute path of the file.
-  final String? path;
+  final String path;
 
   /// The absolute URI of the file.
   final Uri uri;
 
   /// The [Source] of the file with the [uri].
-  final Source? source;
+  final Source source;
 
   /// The [WorkspacePackage] that contains this file.
   ///
@@ -111,12 +111,12 @@
   /// possibly additional enabled experiments (from the analysis options file,
   /// or from SDK allowed experiments).
   ///
-  /// This feature set is then restricted, with the [_packageLanguageVersion],
+  /// This feature set is then restricted, with the [packageLanguageVersion],
   /// or with a `@dart` language override token in the file header.
-  final FeatureSet? _contextFeatureSet;
+  final FeatureSet _contextFeatureSet;
 
   /// The language version for the package that contains this file.
-  final Version? packageLanguageVersion;
+  final Version packageLanguageVersion;
 
   int id = fileObjectId++;
   int? refreshId;
@@ -371,7 +371,7 @@
 
   String get _invalidTransitiveSignature {
     return (ApiSignature()
-          ..addString(path!)
+          ..addString(path)
           ..addBytes(unlinkedSignature))
         .toHex();
   }
@@ -434,7 +434,7 @@
     _invalidateCurrentUnresolvedData();
 
     {
-      var rawFileState = _fsState._fileContentCache.get(path!, allowCached);
+      var rawFileState = _fsState._fileContentCache.get(path, allowCached);
       _content = rawFileState.content;
       _exists = rawFileState.exists;
       _contentHash = rawFileState.contentHash;
@@ -444,8 +444,8 @@
     {
       var signature = ApiSignature();
       signature.addUint32List(_fsState._saltForUnlinked);
-      signature.addFeatureSet(_contextFeatureSet!);
-      signature.addLanguageVersion(packageLanguageVersion!);
+      signature.addFeatureSet(_contextFeatureSet);
+      signature.addLanguageVersion(packageLanguageVersion);
       signature.addString(_contentHash!);
       signature.addBool(_exists!);
       _unlinkedSignature = signature.toByteList();
@@ -522,11 +522,7 @@
 
   @override
   String toString() {
-    if (path == null) {
-      return '<unresolved>';
-    } else {
-      return '[id: $id][rid: $refreshId]$uri = $path';
-    }
+    return '[id: $id][rid: $refreshId]$uri = $path';
   }
 
   CompilationUnitImpl _createEmptyCompilationUnit() {
@@ -534,13 +530,13 @@
     var unit = astFactory.compilationUnit(
       beginToken: token,
       endToken: token,
-      featureSet: _contextFeatureSet!,
+      featureSet: _contextFeatureSet,
     );
 
     unit.lineInfo = LineInfo(const <int>[0]);
 
     unit.languageVersion = LibraryLanguageVersion(
-      package: packageLanguageVersion!,
+      package: packageLanguageVersion,
       override: null,
     );
 
@@ -610,23 +606,19 @@
   }
 
   CompilationUnitImpl _parse(AnalysisErrorListener errorListener) {
-    if (source == null) {
-      return _createEmptyCompilationUnit();
-    }
-
     CharSequenceReader reader = CharSequenceReader(content);
-    Scanner scanner = Scanner(source!, reader, errorListener)
+    Scanner scanner = Scanner(source, reader, errorListener)
       ..configureFeatures(
-        featureSetForOverriding: _contextFeatureSet!,
-        featureSet: _contextFeatureSet!.restrictToVersion(
-          packageLanguageVersion!,
+        featureSetForOverriding: _contextFeatureSet,
+        featureSet: _contextFeatureSet.restrictToVersion(
+          packageLanguageVersion,
         ),
       );
     Token token = scanner.tokenize(reportScannerErrors: false);
     LineInfo lineInfo = LineInfo(scanner.lineStarts);
 
     Parser parser = Parser(
-      source!,
+      source,
       errorListener,
       featureSet: scanner.featureSet,
     );
@@ -635,7 +627,7 @@
     var unit = parser.parseCompilationUnit(token);
     unit.lineInfo = lineInfo;
     unit.languageVersion = LibraryLanguageVersion(
-      package: packageLanguageVersion!,
+      package: packageLanguageVersion,
       override: scanner.overrideVersion,
     );
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 1b7434b..399bc43 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -176,15 +176,13 @@
     // This must happen after all other diagnostics have been computed but
     // before the list of diagnostics has been filtered.
     for (var file in _library.libraryFiles) {
-      if (file.source != null) {
-        IgnoreValidator(
-          _getErrorReporter(file),
-          _getErrorListener(file).errors,
-          _fileToIgnoreInfo[file]!,
-          _fileToLineInfo[file]!,
-          _analysisOptions.unignorableNames,
-        ).reportErrors();
-      }
+      IgnoreValidator(
+        _getErrorReporter(file),
+        _getErrorListener(file).errors,
+        _fileToIgnoreInfo[file]!,
+        _fileToLineInfo[file]!,
+        _analysisOptions.unignorableNames,
+      ).reportErrors();
     }
 
     timerLibraryAnalyzerVerify.stop();
@@ -256,10 +254,6 @@
   }
 
   void _computeHints(FileState file, CompilationUnit unit) {
-    if (file.source == null) {
-      return;
-    }
-
     AnalysisErrorListener errorListener = _getErrorListener(file);
     ErrorReporter errorReporter = _getErrorReporter(file);
 
@@ -333,10 +327,6 @@
   void _computeLints(FileState file, LinterContextUnit currentUnit,
       List<LinterContextUnit> allUnits) {
     var unit = currentUnit.unit;
-    if (file.source == null) {
-      return;
-    }
-
     ErrorReporter errorReporter = _getErrorReporter(file);
 
     var nodeRegistry = NodeLintRegistry(_analysisOptions.enableTiming);
@@ -370,10 +360,6 @@
   }
 
   void _computeVerifyErrors(FileState file, CompilationUnit unit) {
-    if (file.source == null) {
-      return;
-    }
-
     RecordingErrorListener errorListener = _getErrorListener(file);
 
     CodeChecker checker = CodeChecker(
@@ -478,7 +464,7 @@
       RecordingErrorListener listener = _getErrorListener(file);
       return ErrorReporter(
         listener,
-        file.source!,
+        file.source,
         isNonNullableByDefault: _libraryElement.isNonNullableByDefault,
       );
     });
@@ -665,10 +651,6 @@
 
   void _resolveFile(FileState file, CompilationUnit unit) {
     var source = file.source;
-    if (source == null) {
-      return;
-    }
-
     RecordingErrorListener errorListener = _getErrorListener(file);
 
     var unitElement = unit.declaredElement as CompilationUnitElementImpl;
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index 94a44cf..7401cb1 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -138,7 +138,7 @@
         librariesLinkedTimer.start();
 
         testView.linkedCycles.add(
-          cycle.libraries.map((e) => e.path!).toSet(),
+          cycle.libraries.map((e) => e.path).toSet(),
         );
 
         timerInputLibraries.start();
@@ -146,7 +146,6 @@
         var inputLibraries = <link2.LinkInputLibrary>[];
         for (var libraryFile in cycle.libraries) {
           var librarySource = libraryFile.source;
-          if (librarySource == null) continue;
 
           var inputUnits = <link2.LinkInputUnit>[];
           var partIndex = -1;
@@ -163,7 +162,7 @@
             inputUnits.add(
               link2.LinkInputUnit(
                 partUriStr,
-                file.source!,
+                file.source,
                 isSynthetic,
                 unit,
               ),
@@ -316,7 +315,7 @@
     var fileContentMap = <String, String>{};
     for (var libraryFile in cycle.libraries) {
       for (var file in libraryFile.libraryFiles) {
-        fileContentMap[file.path!] = file.content;
+        fileContentMap[file.path] = file.content;
       }
     }
     throw CaughtExceptionWithFiles(exception, stackTrace, fileContentMap);
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index f4dcec5..d21d4e1 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -110,8 +110,8 @@
 
     // Sort libraries to produce stable signatures.
     scc.sort((first, second) {
-      var firstPath = first.file.path!;
-      var secondPath = second.file.path!;
+      var firstPath = first.file.path;
+      var secondPath = second.file.path;
       return firstPath.compareTo(secondPath);
     });
 
@@ -128,7 +128,7 @@
     for (var node in scc) {
       cycle.libraries.add(node.file);
 
-      signature.addLanguageVersion(node.file.packageLanguageVersion!);
+      signature.addLanguageVersion(node.file.packageLanguageVersion);
       signature.addString(node.file.uriStr);
 
       signature.addInt(node.file.libraryFiles.length);
diff --git a/pkg/analyzer/lib/src/dart/analysis/search.dart b/pkg/analyzer/lib/src/dart/analysis/search.dart
index 7d24cf9..9e3c4b0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/search.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/search.dart
@@ -149,8 +149,8 @@
 
     if (files != null) {
       for (FileState file in files) {
-        if (searchedFiles.add(file.path!, this)) {
-          var index = await _driver.getIndex(file.path!);
+        if (searchedFiles.add(file.path, this)) {
+          var index = await _driver.getIndex(file.path);
           if (index != null) {
             var request = _IndexRequest(index);
             request.addSubtypes(id, results, file);
@@ -174,7 +174,7 @@
 
     List<FileState> knownFiles = _driver.fsState.knownFiles.toList();
     for (FileState file in knownFiles) {
-      var unitResult = await _driver.getUnitElement(file.path!);
+      var unitResult = await _driver.getUnitElement(file.path);
       if (unitResult.state == ResultState.VALID) {
         CompilationUnitElement unitElement = unitResult.element;
         unitElement.accessors.forEach(addElement);
@@ -245,7 +245,7 @@
         FileState library = _driver.fsState.getFileForPath(libraryPath);
         for (FileState file in library.libraryFiles) {
           if (file.path == path || file.referencedNames.contains(name)) {
-            files.add(file.path!);
+            files.add(file.path);
           }
         }
       }
@@ -316,7 +316,7 @@
               const {
                 IndexRelationKind.IS_REFERENCED_BY: SearchResultKind.REFERENCE
               },
-              file.path!);
+              file.path);
         }
       }
     }
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 26ad6fe..d64b16b 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
+import 'package:analyzer/src/dart/element/member.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
@@ -245,8 +246,14 @@
 
   /// Gets the decorated type of [element] from [_variables], performing any
   /// necessary substitutions.
-  DecoratedType getOrComputeElementType(Element element,
-      {DecoratedType targetType}) {
+  ///
+  /// [node] is used as the AST node for the edge origin if any graph edges need
+  /// to be created.  [targetType], if provided, indicates the type of the
+  /// target (receiver) for a method, getter, or setter invocation.
+  /// [targetExpression], if provided, is the expression for the target
+  /// (receiver) for a method, getter, or setter invocation.
+  DecoratedType getOrComputeElementType(AstNode node, Element element,
+      {DecoratedType targetType, Expression targetExpression}) {
     Map<TypeParameterElement, DecoratedType> substitution;
     Element baseElement = element.declaration;
     if (targetType != null) {
@@ -262,19 +269,65 @@
       } else {
         assert(enclosingElement is ExtensionElement);
         final extensionElement = enclosingElement as ExtensionElement;
-        final extendedType =
-            _typeSystem.resolveToBound(extensionElement.extendedType);
-        if (extendedType is InterfaceType) {
-          if (extensionElement.typeParameters.isNotEmpty) {
-            substitution = _decoratedClassHierarchy
-                .asInstanceOf(targetType, extendedType.element)
-                .asSubstitution;
+        // The semantics of calling an extension method or extension property
+        // are essentially the same as calling a static function where the
+        // receiver is passed as an invisible `this` parameter whose type is the
+        // extension's "on" type.  If the extension declaration has type
+        // parameters, they behave like inferred type parameters of the static
+        // function.
+        //
+        // So what we need to do is (1) create a set of DecoratedTypes to
+        // represent the inferred types of the type parameters, (2) ensure that
+        // the receiver type is assignable to "on" type (with those decorated
+        // types substituted into it), and (3) substitute those decorated types
+        // into the declared type of the extension method or extension property,
+        // so that the caller will match up parameter types and the return type
+        // appropriately.
+        //
+        // Taking each step in turn:
+        // (1) create a set of decorated types to represent the inferred types
+        // of the type parameters.  Note that we must make sure each of these
+        // types satisfies its associated bound.
+        var typeParameters = extensionElement.typeParameters;
+        if (typeParameters.isNotEmpty) {
+          var preMigrationSubstitution = (element as Member).substitution.map;
+          substitution = {};
+          var target = NullabilityNodeTarget.text('extension');
+          for (int i = 0; i < typeParameters.length; i++) {
+            var typeParameter = typeParameters[i];
+            var decoratedTypeArgument = DecoratedType.forImplicitType(
+                typeProvider,
+                preMigrationSubstitution[typeParameter],
+                _graph,
+                target.typeArgument(i));
+            substitution[typeParameter] = decoratedTypeArgument;
+            var edgeOrigin =
+                InferredTypeParameterInstantiationOrigin(source, node);
+            _checkAssignment(edgeOrigin, FixReasonTarget.root,
+                source: decoratedTypeArgument,
+                destination:
+                    _variables.decoratedTypeParameterBound(typeParameter),
+                hard: true);
           }
-        } else {
-          // TODO(srawlins): Handle generic typedef. Others?
-          _unimplemented(
-              null, 'Extension on $extendedType (${extendedType.runtimeType}');
         }
+        // (2) ensure that the receiver type is assignable to "on" type (with
+        // those decorated types substituted into it)
+        var onType = _variables.decoratedElementType(extensionElement);
+        if (substitution != null) {
+          onType = onType.substitute(substitution);
+        }
+        _checkAssignment(InferredTypeParameterInstantiationOrigin(source, node),
+            FixReasonTarget.root,
+            source: targetType,
+            destination: onType,
+            hard: targetExpression == null ||
+                _postDominatedLocals.isReferenceInScope(targetExpression));
+        // (3) substitute those decorated types into the declared type of the
+        // extension method or extension property, so that the caller will match
+        // up parameter types and the return type appropriately.
+        //
+        // There's nothing more we need to do here.  The substitution below
+        // will do the job.
       }
     }
     DecoratedType decoratedBaseType;
@@ -510,8 +563,8 @@
         _dispatch(rightOperand);
         return _makeNullableDynamicType(node);
       } else {
-        var calleeType =
-            getOrComputeElementType(callee, targetType: targetType);
+        var calleeType = getOrComputeElementType(node, callee,
+            targetType: targetType, targetExpression: leftOperand);
         assert(calleeType.positionalParameters.isNotEmpty); // TODO(paulberry)
         _handleAssignment(rightOperand,
             destinationType: calleeType.positionalParameters[0]);
@@ -727,7 +780,8 @@
       ConstructorFieldInitializer node) {
     _fieldsNotInitializedByConstructor.remove(node.fieldName.staticElement);
     _handleAssignment(node.expression,
-        destinationType: getOrComputeElementType(node.fieldName.staticElement));
+        destinationType:
+            getOrComputeElementType(node, node.fieldName.staticElement));
     return null;
   }
 
@@ -756,12 +810,13 @@
         // be reached.
         _requiredHintedParameters.add(node.declaredElement);
       } else {
-        _graph.makeNullable(getOrComputeElementType(node.declaredElement).node,
+        _graph.makeNullable(
+            getOrComputeElementType(node, node.declaredElement).node,
             OptionalFormalParameterOrigin(source, node));
       }
     } else {
       _handleAssignment(defaultValue,
-          destinationType: getOrComputeElementType(node.declaredElement),
+          destinationType: getOrComputeElementType(node, node.declaredElement),
           fromDefaultValue: true);
     }
     return null;
@@ -1077,7 +1132,8 @@
       // so that we don't unnecessarily propagate nullabilities everywhere?
       result = _makeNullableDynamicType(node);
     } else {
-      var calleeType = getOrComputeElementType(callee, targetType: targetType);
+      var calleeType = getOrComputeElementType(node, callee,
+          targetType: targetType, targetExpression: target);
       // TODO(paulberry): substitute if necessary
       _handleAssignment(node.index,
           destinationType: calleeType.positionalParameters[0]);
@@ -1142,7 +1198,8 @@
         nullabilityNode, InstanceCreationOrigin(source, node));
     var createdType = DecoratedType(node.staticType, nullabilityNode,
         typeArguments: decoratedTypeArguments);
-    var calleeType = getOrComputeElementType(callee, targetType: createdType);
+    var calleeType =
+        getOrComputeElementType(node, callee, targetType: createdType);
     for (var i = 0; i < decoratedTypeArguments.length; ++i) {
       _checkAssignment(parameterEdgeOrigins?.elementAt(i),
           FixReasonTarget.root.typeArgument(i),
@@ -1281,7 +1338,8 @@
       // type of `X`.
       calleeType = targetType;
     } else if (callee != null) {
-      calleeType = getOrComputeElementType(callee, targetType: targetType);
+      calleeType = getOrComputeElementType(node, callee,
+          targetType: targetType, targetExpression: target);
       if (callee is PropertyAccessorElement) {
         calleeType = calleeType.returnType;
       }
@@ -1380,8 +1438,8 @@
         // so that we don't unnecessarily propagate nullabilities everywhere?
         writeType = _makeNullableDynamicType(node);
       } else {
-        var calleeType =
-            getOrComputeElementType(callee, targetType: targetType);
+        var calleeType = getOrComputeElementType(node, callee,
+            targetType: targetType, targetExpression: operand);
         writeType = _fixNumericTypes(calleeType.returnType, node.staticType);
       }
       if (operand is SimpleIdentifier) {
@@ -1425,8 +1483,8 @@
         // so that we don't unnecessarily propagate nullabilities everywhere?
         staticType = _makeNullableDynamicType(node);
       } else {
-        var calleeType =
-            getOrComputeElementType(callee, targetType: targetType);
+        var calleeType = getOrComputeElementType(node, callee,
+            targetType: targetType, targetExpression: operand);
         if (isIncrementOrDecrement) {
           staticType = _fixNumericTypes(calleeType.returnType, node.staticType);
         } else {
@@ -1585,7 +1643,7 @@
         var promotedType = _flowAnalysis.variableRead(node, staticElement);
         if (promotedType != null) return promotedType;
       }
-      var type = getOrComputeElementType(staticElement);
+      var type = getOrComputeElementType(node, staticElement);
       if (!node.inDeclarationContext() &&
           node.inGetterContext() &&
           !_lateHintedLocals.contains(staticElement) &&
@@ -1600,13 +1658,14 @@
       if (staticElement.enclosingElement is ClassElement) {
         targetType = _thisOrSuper(node);
       }
-      result = getOrComputeElementType(staticElement, targetType: targetType);
+      result =
+          getOrComputeElementType(node, staticElement, targetType: targetType);
     } else if (staticElement is PropertyAccessorElement) {
       if (staticElement.enclosingElement is ClassElement) {
         targetType = _thisOrSuper(node);
       }
       var elementType =
-          getOrComputeElementType(staticElement, targetType: targetType);
+          getOrComputeElementType(node, staticElement, targetType: targetType);
       result = staticElement.isGetter
           ? elementType.returnType
           : elementType.positionalParameters[0];
@@ -1619,7 +1678,7 @@
       result = _makeNullableVoidType(node);
     } else if (staticElement.enclosingElement is ClassElement &&
         (staticElement.enclosingElement as ClassElement).isEnum) {
-      result = getOrComputeElementType(staticElement);
+      result = getOrComputeElementType(node, staticElement);
     } else {
       // TODO(paulberry)
       _unimplemented(node,
@@ -1694,7 +1753,8 @@
     }
     var createdType = DecoratedType(callee.returnType, nullabilityNode,
         typeArguments: typeArguments);
-    var calleeType = getOrComputeElementType(callee, targetType: createdType);
+    var calleeType =
+        getOrComputeElementType(node, callee, targetType: createdType);
     var constructorTypeParameters = callee.enclosingElement.typeParameters;
 
     _handleInvocationArguments(
@@ -2315,7 +2375,8 @@
       }
       if (destinationLocalVariable != null) {
         _dispatch(destinationExpression);
-        destinationType = getOrComputeElementType(destinationLocalVariable);
+        destinationType = getOrComputeElementType(
+            destinationExpression, destinationLocalVariable);
       } else {
         destinationType = _dispatch(destinationExpression);
       }
@@ -2349,8 +2410,9 @@
               hard: _postDominatedLocals
                   .isReferenceInScope(assignmentExpression.leftHandSide));
           DecoratedType compoundOperatorType = getOrComputeElementType(
-              compoundOperatorMethod,
-              targetType: destinationType);
+              compoundOperatorInfo, compoundOperatorMethod,
+              targetType: destinationType,
+              targetExpression: compoundOperatorInfo.leftHandSide);
           assert(compoundOperatorType.positionalParameters.isNotEmpty);
           _checkAssignment(edgeOrigin, FixReasonTarget.root,
               source: sourceType,
@@ -2997,7 +3059,8 @@
           nullabilityNode, CallTearOffOrigin(source, node));
       calleeType = targetType.withNode(nullabilityNode);
     } else if (callee != null) {
-      calleeType = getOrComputeElementType(callee, targetType: targetType);
+      calleeType = getOrComputeElementType(node, callee,
+          targetType: targetType, targetExpression: target);
     }
     if (calleeType == null) {
       // Dynamic dispatch.
@@ -3051,10 +3114,10 @@
     } else if ((callee is MethodElement || callee is PropertyAccessorElement) &&
         callee.enclosingElement is ExtensionElement) {
       // Extension methods can be called on a `null` target, when the `on` type
-      // of the extension is nullable.
-      return _handleAssignment(target,
-          destinationType:
-              _variables.decoratedElementType(callee.enclosingElement));
+      // of the extension is nullable.  Note: we don't need to check whether the
+      // target type is assignable to the extended type; that is done in
+      // [getOrComputeElementType].
+      return _dispatch(target);
     } else {
       return _checkExpressionNotNull(target);
     }
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index f4d8542..c069791 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -2138,6 +2138,37 @@
     await _checkSingleFileChanges(content, expected, removeViaComments: true);
   }
 
+  Future<void> test_extension_complex() async {
+    var content = '''
+import 'already_migrated.dart';
+class D<V> extends C<V> {}
+abstract class Foo {
+  D<List<int>> get z;
+  List<int> test() => z.x ?? [];
+}
+''';
+    var alreadyMigrated = '''
+// @dart=2.12
+extension E<T> on C<T> {
+  T? get x => y;
+}
+class C<U> {
+  U? y;
+}
+''';
+    var expected = '''
+import 'already_migrated.dart';
+class D<V> extends C<V> {}
+abstract class Foo {
+  D<List<int>> get z;
+  List<int> test() => z.x ?? [];
+}
+''';
+    await _checkSingleFileChanges(content, expected, migratedInput: {
+      '$projectPath/lib/already_migrated.dart': alreadyMigrated
+    });
+  }
+
   Future<void> test_extension_extended_type_nullability_intent() async {
     var content = '''
 extension E on C {
@@ -2183,7 +2214,7 @@
     await _checkSingleFileChanges(content, expected);
   }
 
-  Future<void> test_extension_null_check_non_nullable() async {
+  Future<void> test_extension_null_check_non_nullable_method() async {
     var content = '''
 class C {}
 extension E on C/*!*/ {
@@ -2211,6 +2242,90 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_extension_null_check_non_nullable_binary() async {
+    var content = '''
+class C {}
+extension E on C/*!*/ {
+  void operator+(int other) {}
+}
+void f(C c, bool b) {
+  if (b) {
+    c + 0;
+  }
+}
+void g() => f(null, false);
+''';
+    var expected = '''
+class C {}
+extension E on C {
+  void operator+(int other) {}
+}
+void f(C? c, bool b) {
+  if (b) {
+    c! + 0;
+  }
+}
+void g() => f(null, false);
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_extension_null_check_non_nullable_prefix() async {
+    var content = '''
+class C {}
+extension E on C/*!*/ {
+  void operator-() {}
+}
+void f(C c, bool b) {
+  if (b) {
+    -c;
+  }
+}
+void g() => f(null, false);
+''';
+    var expected = '''
+class C {}
+extension E on C {
+  void operator-() {}
+}
+void f(C? c, bool b) {
+  if (b) {
+    -c!;
+  }
+}
+void g() => f(null, false);
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  Future<void> test_extension_null_check_non_nullable_index() async {
+    var content = '''
+class C {}
+extension E on C/*!*/ {
+  void operator[](int index) {}
+}
+void f(C c, bool b) {
+  if (b) {
+    c[0];
+  }
+}
+void g() => f(null, false);
+''';
+    var expected = '''
+class C {}
+extension E on C {
+  void operator[](int index) {}
+}
+void f(C? c, bool b) {
+  if (b) {
+    c![0];
+  }
+}
+void g() => f(null, false);
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_extension_null_check_non_nullable_generic() async {
     var content = '''
 class C {}
@@ -2480,6 +2595,25 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_extension_on_type_substitution() async {
+    var content = '''
+extension E<T> on T {
+  T get foo => this;
+}
+List<int> f(List<int/*?*/> x) => x.foo;
+''';
+    // To see that the return type of `f` must be `List<int?`, the migration
+    // tool needs to substitute the actual type argument (`T=List<int?>`) into
+    // the extension's "on" type.
+    var expected = '''
+extension E<T> on T {
+  T get foo => this;
+}
+List<int?> f(List<int?> x) => x.foo;
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_extension_override() async {
     var content = '''
 extension E on int {
@@ -2544,6 +2678,38 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_extension_use_can_imply_non_null_intent() async {
+    var content = '''
+extension E<T extends Object/*!*/> on T/*!*/ {
+  void foo() {}
+}
+f(int i) {
+  i.foo();
+}
+g(bool b, int/*?*/ j) {
+  if (b) {
+    f(j);
+  }
+}
+''';
+    // Since the extension declaration says `T extends Object/*!*/`, `i` will
+    // not be made nullable.
+    var expected = '''
+extension E<T extends Object> on T {
+  void foo() {}
+}
+f(int i) {
+  i.foo();
+}
+g(bool b, int? j) {
+  if (b) {
+    f(j!);
+  }
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_field_final_uninitalized_used() async {
     var content = '''
 class C {
diff --git a/tools/VERSION b/tools/VERSION
index ce86f3f..7c0b250 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 5
+PRERELEASE 6
 PRERELEASE_PATCH 0
\ No newline at end of file