Future nullSafetyComplianceOfPackages()

in lib/src/null_safety_analysis.dart [138:243]


  Future<NullSafetyAnalysisResult> nullSafetyComplianceOfPackages(
    Iterable<PackageId> packages,
    Package rootPackage,
  ) async {
    NullSafetyAnalysisResult? firstBadPackage;
    for (final dependencyId in packages) {
      final packageInternalAnalysis =
          await _packageInternallyGoodCache.putIfAbsent(dependencyId, () async {
        Pubspec pubspec;
        BoundSource? boundSource;
        String packageDir;
        if (dependencyId.source == null) {
          pubspec = rootPackage.pubspec;
          packageDir = rootPackage.dir;
        } else {
          boundSource = _systemCache.source(dependencyId.source);
          pubspec = await boundSource.describe(dependencyId);
          packageDir = boundSource.getDirectory(dependencyId);
        }

        if (!pubspec.languageVersion.supportsNullSafety) {
          final span =
              _tryGetSpanFromYamlMap(pubspec.fields['environment'], 'sdk');
          final where = span == null
              ? 'in the sdk constraint in the environment key in its pubspec.yaml.'
              : 'in its pubspec.yaml:\n${span.highlight()}';
          return NullSafetyAnalysisResult(
            NullSafetyCompliance.notCompliant,
            'package:${dependencyId.name} is not opted into null safety $where',
          );
        }

        if (boundSource is CachedSource) {
          // TODO(sigurdm): Consider using withDependencyType here.
          await boundSource.downloadToSystemCache(dependencyId);
        }

        final libDir =
            path.absolute(path.normalize(path.join(packageDir, 'lib')));
        if (dirExists(libDir)) {
          var contextCollection = AnalysisContextCollection(
            includedPaths: [path.normalize(packageDir)],
            resourceProvider: PhysicalResourceProvider.INSTANCE,
            sdkPath: getSdkPath(),
          );
          var analysisContext = contextCollection.contexts.first;
          var analysisSession = analysisContext.currentSession;

          for (final file in listDir(libDir,
              recursive: true, includeDirs: false, includeHidden: true)) {
            if (file.endsWith('.dart')) {
              final fileUrl =
                  'package:${dependencyId.name}/${path.relative(file, from: libDir)}';
              final someUnitResult =
                  analysisSession.getParsedUnit(path.normalize(file));
              ParsedUnitResult unitResult;
              if (someUnitResult is ParsedUnitResult) {
                unitResult = someUnitResult;
              } else {
                return NullSafetyAnalysisResult(
                    NullSafetyCompliance.analysisFailed,
                    'Could not analyze $fileUrl.');
              }
              if (unitResult.errors.isNotEmpty) {
                return NullSafetyAnalysisResult(
                    NullSafetyCompliance.analysisFailed,
                    'Could not analyze $fileUrl.');
              }
              if (unitResult.isPart) continue;
              final languageVersionToken = unitResult.unit.languageVersionToken;
              if (languageVersionToken == null) continue;
              final languageVersion = LanguageVersion.fromLanguageVersionToken(
                  languageVersionToken);
              if (!languageVersion.supportsNullSafety) {
                final sourceFile =
                    SourceFile.fromString(readTextFile(file), url: fileUrl);
                final span = sourceFile.span(languageVersionToken.offset,
                    languageVersionToken.offset + languageVersionToken.length);
                return NullSafetyAnalysisResult(
                    NullSafetyCompliance.notCompliant,
                    '$fileUrl is opting out of null safety:\n${span.highlight()}');
              }
            }
          }
        }
        return NullSafetyAnalysisResult(NullSafetyCompliance.compliant, null);
      });
      if (packageInternalAnalysis.compliance ==
          NullSafetyCompliance.analysisFailed) {
        return packageInternalAnalysis;
      }
      if (packageInternalAnalysis.compliance ==
          NullSafetyCompliance.notCompliant) {
        firstBadPackage ??= packageInternalAnalysis;
      }
    }

    if (firstBadPackage == null) {
      return NullSafetyAnalysisResult(NullSafetyCompliance.compliant, null);
    }
    if (firstBadPackage.compliance == NullSafetyCompliance.analysisFailed) {
      return firstBadPackage;
    }
    return NullSafetyAnalysisResult(
        NullSafetyCompliance.mixed, firstBadPackage.reason);
  }