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);
}