| // Copyright (c) 2019, 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 'dart:io'; | |
| import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualDataMap, Id; | |
| import 'package:_fe_analyzer_shared/src/testing/id_testing.dart'; | |
| import 'package:analyzer/dart/ast/ast.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/dart/element/type.dart'; | |
| import 'package:analyzer/dart/element/type_system.dart'; | |
| import 'package:analyzer/src/dart/analysis/testing_data.dart'; | |
| import 'package:analyzer/src/util/ast_data_extractor.dart'; | |
| import '../util/id_testing_helper.dart'; | |
| main(List<String> args) { | |
| Directory dataDir = Directory.fromUri( | |
| Platform.script.resolve( | |
| '../../../_fe_analyzer_shared/test/flow_analysis/nullability/data', | |
| ), | |
| ); | |
| return runTests<String>( | |
| dataDir, | |
| args: args, | |
| createUriForFileName: createUriForFileName, | |
| onFailure: onFailure, | |
| runTest: runTestFor(const _NullabilityDataComputer(), [ | |
| analyzerDefaultConfig, | |
| ]), | |
| ); | |
| } | |
| class _NullabilityDataComputer extends DataComputer<String> { | |
| const _NullabilityDataComputer(); | |
| @override | |
| DataInterpreter<String> get dataValidator => | |
| const _NullabilityDataInterpreter(); | |
| @override | |
| void computeUnitData( | |
| TestingData testingData, | |
| CompilationUnit unit, | |
| ActualDataMap<String> actualMap, | |
| ) { | |
| var unitElement = unit.declaredFragment!; | |
| _NullabilityDataExtractor( | |
| unitElement.source.uri, | |
| actualMap, | |
| unitElement.element.typeSystem, | |
| ).run(unit); | |
| } | |
| } | |
| class _NullabilityDataExtractor extends AstDataExtractor<String> { | |
| final TypeSystem _typeSystem; | |
| _NullabilityDataExtractor(super.uri, super.actualMap, this._typeSystem); | |
| @override | |
| String? computeNodeValue(Id id, AstNode node) { | |
| Element? element; | |
| DartType? promotedType; | |
| if (node case UnqualifiedNameExpression( | |
| resolution: VariableReadResolution(element: var readElement, :var type), | |
| )) { | |
| element = readElement; | |
| promotedType = type; | |
| } else if (node is IfNullAssignment || node is CompoundAssignment) { | |
| var target = (node as AssignmentExpression2).target; | |
| if (target is UnqualifiedNameAssignmentTarget) { | |
| var readResolution = target.read; | |
| if (readResolution is VariableReadResolution) { | |
| element = readResolution.element; | |
| promotedType = readResolution.type; | |
| } | |
| } | |
| } | |
| if ((element is LocalVariableElement || | |
| element is FormalParameterElement) && | |
| promotedType != null) { | |
| var declaredType = (element as VariableElement).type; | |
| var isPromoted = promotedType != declaredType; | |
| if (isPromoted && | |
| _typeSystem.isPotentiallyNullable(declaredType) && | |
| !_typeSystem.isPotentiallyNullable(promotedType)) { | |
| return 'nonNullable'; | |
| } | |
| } | |
| return null; | |
| } | |
| } | |
| class _NullabilityDataInterpreter implements DataInterpreter<String> { | |
| const _NullabilityDataInterpreter(); | |
| @override | |
| String getText(String actualData, [String? indentation]) => actualData; | |
| @override | |
| String? isAsExpected(String actualData, String? expectedData) { | |
| if (actualData == expectedData) { | |
| return null; | |
| } else { | |
| return 'Expected $expectedData, got $actualData'; | |
| } | |
| } | |
| @override | |
| bool isEmpty(String actualData) => actualData.isEmpty; | |
| } |