blob: e54d8a9b17b3c1449eb2d611fb5614d1c41c3bc2 [file] [log] [blame]
// 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.
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/context/source.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart' as file_state;
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/testing_data.dart';
import 'package:analyzer/src/dart/analysis/unit_analysis.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/constant/compute.dart';
import 'package:analyzer/src/dart/constant/constant_verifier.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
import 'package:analyzer/src/dart/constant/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type_constraint_gatherer.dart';
import 'package:analyzer/src/dart/element/type_provider.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
import 'package:analyzer/src/dart/resolver/resolution_visitor.dart';
import 'package:analyzer/src/error/best_practices_verifier.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/error/constructor_fields_verifier.dart';
import 'package:analyzer/src/error/dead_code_verifier.dart';
import 'package:analyzer/src/error/duplicate_definition_verifier.dart';
import 'package:analyzer/src/error/ignore_validator.dart';
import 'package:analyzer/src/error/imports_verifier.dart';
import 'package:analyzer/src/error/inheritance_override.dart';
import 'package:analyzer/src/error/language_version_override_verifier.dart';
import 'package:analyzer/src/error/override_verifier.dart';
import 'package:analyzer/src/error/redeclare_verifier.dart';
import 'package:analyzer/src/error/todo_finder.dart';
import 'package:analyzer/src/error/unicode_text_verifier.dart';
import 'package:analyzer/src/error/unused_local_elements_verifier.dart';
import 'package:analyzer/src/generated/element_walker.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error_verifier.dart';
import 'package:analyzer/src/generated/ffi_verifier.dart';
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/hint/sdk_constraint_verifier.dart';
import 'package:analyzer/src/ignore_comments/ignore_info.dart';
import 'package:analyzer/src/lint/lint_rule_timers.dart';
import 'package:analyzer/src/lint/linter.dart';
import 'package:analyzer/src/lint/linter_visitor.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:analyzer/src/utilities/extensions/version.dart';
import 'package:analyzer/src/workspace/pub.dart';
import 'package:analyzer/src/workspace/workspace.dart';
import 'package:collection/collection.dart';
class AnalysisForCompletionResult {
final FileState fileState;
final CompilationUnit parsedUnit;
final List<AstNode> resolvedNodes;
AnalysisForCompletionResult({
required this.fileState,
required this.parsedUnit,
required this.resolvedNodes,
});
}
/// Analyzer of a single library.
class LibraryAnalyzer {
final AnalysisOptionsImpl _analysisOptions;
final DeclaredVariables _declaredVariables;
final LibraryFileKind _library;
final LibraryResolutionContext libraryResolutionContext =
LibraryResolutionContext();
final InheritanceManager3 _inheritance;
final LibraryElementImpl _libraryElement;
final Map<FileState, UnitAnalysis> _libraryUnits = {};
late final LibraryVerificationContext _libraryVerificationContext;
final TestingData? _testingData;
final TypeSystemOperations _typeSystemOperations;
LibraryAnalyzer(this._analysisOptions, this._declaredVariables,
this._libraryElement, this._inheritance, this._library,
{TestingData? testingData,
required TypeSystemOperations typeSystemOperations})
: _testingData = testingData,
_typeSystemOperations = typeSystemOperations {
_libraryVerificationContext = LibraryVerificationContext(
libraryKind: _library,
constructorFieldsVerifier: ConstructorFieldsVerifier(
typeSystem: _typeSystem,
),
units: _libraryUnits,
);
}
TypeProviderImpl get _typeProvider => _libraryElement.typeProvider;
TypeSystemImpl get _typeSystem => _libraryElement.typeSystem;
/// Compute analysis results for all units of the library.
List<UnitAnalysisResult> analyze() {
_parseAndResolve();
_computeDiagnostics();
// Return full results.
var results = <UnitAnalysisResult>[];
for (var unitAnalysis in _libraryUnits.values) {
var errors = unitAnalysis.errorListener.errors;
errors = _filterIgnoredErrors(unitAnalysis, errors);
results.add(
UnitAnalysisResult(
unitAnalysis.file,
unitAnalysis.unit,
errors,
),
);
}
return results;
}
/// Analyze [file] for a completion result.
///
/// This method aims to avoid work that [analyze] does which would be
/// unnecessary for a completion request.
AnalysisForCompletionResult analyzeForCompletion({
required FileState file,
required int offset,
required CompilationUnitElementImpl unitElement,
required OperationPerformanceImpl performance,
}) {
var unitAnalysis = performance.run('parse', (performance) {
return _parse(file);
});
var parsedUnit = unitAnalysis.unit;
parsedUnit.declaredElement = unitElement;
var node = NodeLocator(offset).searchWithin(parsedUnit);
var errorListener = RecordingErrorListener();
return performance.run('resolve', (performance) {
TypeConstraintGenerationDataForTesting? inferenceDataForTesting =
_testingData != null
? TypeConstraintGenerationDataForTesting()
: null;
// TODO(scheglov): We don't need to do this for the whole unit.
parsedUnit.accept(
ResolutionVisitor(
unitElement: unitElement,
errorListener: errorListener,
nameScope: _libraryElement.scope,
strictInference: _analysisOptions.strictInference,
strictCasts: _analysisOptions.strictCasts,
elementWalker: ElementWalker.forCompilationUnit(
unitElement,
libraryFilePath: _library.file.path,
unitFilePath: file.path,
),
dataForTesting: inferenceDataForTesting,
),
);
_testingData?.recordTypeConstraintGenerationDataForTesting(
file.uri, inferenceDataForTesting!);
// TODO(scheglov): We don't need to do this for the whole unit.
parsedUnit.accept(ScopeResolverVisitor(
_libraryElement, file.source, _typeProvider, errorListener,
nameScope: _libraryElement.scope));
FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(
_testingData != null, _libraryElement.featureSet,
typeSystemOperations: _typeSystemOperations);
_testingData?.recordFlowAnalysisDataForTesting(
file.uri, flowAnalysisHelper.dataForTesting!);
var resolverVisitor = ResolverVisitor(_inheritance, _libraryElement,
libraryResolutionContext, file.source, _typeProvider, errorListener,
featureSet: _libraryElement.featureSet,
analysisOptions: _library.file.analysisOptions,
flowAnalysisHelper: flowAnalysisHelper);
_testingData?.recordTypeConstraintGenerationDataForTesting(
file.uri, resolverVisitor.inferenceHelper.dataForTesting!);
var nodeToResolve = node?.thisOrAncestorMatching((e) {
return e.parent is ClassDeclaration ||
e.parent is CompilationUnit ||
e.parent is ExtensionDeclaration ||
e.parent is MixinDeclaration;
});
if (nodeToResolve != null && nodeToResolve is! Directive) {
var canResolveNode = resolverVisitor.prepareForResolving(nodeToResolve);
if (canResolveNode) {
nodeToResolve.accept(resolverVisitor);
resolverVisitor.checkIdle();
return AnalysisForCompletionResult(
fileState: file,
parsedUnit: parsedUnit,
resolvedNodes: [nodeToResolve],
);
}
}
_parseAndResolve();
var unit = _libraryUnits.values.first.unit;
return AnalysisForCompletionResult(
fileState: file,
parsedUnit: unit,
resolvedNodes: [unit],
);
});
}
void _checkForInconsistentLanguageVersionOverride() {
var libraryUnitAnalysis = _libraryUnits.values.first;
var libraryUnit = libraryUnitAnalysis.unit;
var libraryOverrideToken = libraryUnit.languageVersionToken;
var elementToUnit = <CompilationUnitElement, CompilationUnit>{};
for (var unitAnalysis in _libraryUnits.values) {
elementToUnit[unitAnalysis.element] = unitAnalysis.unit;
}
for (var directive in libraryUnit.directives) {
if (directive is PartDirectiveImpl) {
var elementUri = directive.element?.uri;
if (elementUri is DirectiveUriWithUnit) {
var partUnit = elementToUnit[elementUri.unit];
if (partUnit != null) {
var shouldReport = false;
var partOverrideToken = partUnit.languageVersionToken;
if (libraryOverrideToken != null) {
if (partOverrideToken != null) {
if (partOverrideToken.major != libraryOverrideToken.major ||
partOverrideToken.minor != libraryOverrideToken.minor) {
shouldReport = true;
}
} else {
shouldReport = true;
}
} else if (partOverrideToken != null) {
shouldReport = true;
}
if (shouldReport) {
libraryUnitAnalysis.errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.INCONSISTENT_LANGUAGE_VERSION_OVERRIDE,
);
}
}
}
}
}
}
void _computeConstantErrors(UnitAnalysis unitAnalysis) {
ConstantVerifier constantVerifier = ConstantVerifier(
unitAnalysis.errorReporter, _libraryElement, _declaredVariables,
retainDataForTesting: _testingData != null);
unitAnalysis.unit.accept(constantVerifier);
_testingData?.recordExhaustivenessDataForTesting(
unitAnalysis.file.uri, constantVerifier.exhaustivenessDataForTesting!);
}
/// Compute constants in all units.
void _computeConstants() {
var configuration = ConstantEvaluationConfiguration();
var constants = [
for (var unitAnalysis in _libraryUnits.values)
..._findConstants(
unit: unitAnalysis.unit,
configuration: configuration,
),
];
computeConstants(
declaredVariables: _declaredVariables,
constants: constants,
featureSet: _libraryElement.featureSet,
configuration: configuration,
);
}
/// Compute diagnostics in [_libraryUnits], including errors and warnings,
/// lints, and a few other cases.
void _computeDiagnostics() {
for (var unitAnalysis in _libraryUnits.values) {
_computeVerifyErrors(unitAnalysis);
}
MemberDuplicateDefinitionVerifier.checkLibrary(
inheritance: _inheritance,
libraryVerificationContext: _libraryVerificationContext,
libraryElement: _libraryElement,
units: _libraryUnits,
);
_libraryVerificationContext.constructorFieldsVerifier.report();
if (_analysisOptions.warning) {
var usedImportedElements = <UsedImportedElements>[];
var usedLocalElements = <UsedLocalElements>[];
for (var unitAnalysis in _libraryUnits.values) {
{
var visitor = GatherUsedLocalElementsVisitor(_libraryElement);
unitAnalysis.unit.accept(visitor);
usedLocalElements.add(visitor.usedElements);
}
{
var visitor = GatherUsedImportedElementsVisitor(_libraryElement);
unitAnalysis.unit.accept(visitor);
usedImportedElements.add(visitor.usedElements);
}
}
var usedElements = UsedLocalElements.merge(usedLocalElements);
for (var unitAnalysis in _libraryUnits.values) {
_computeWarnings(
unitAnalysis,
usedImportedElements: usedImportedElements,
usedElements: usedElements,
);
}
}
if (_analysisOptions.lint) {
_computeLints();
}
_checkForInconsistentLanguageVersionOverride();
// This must happen after all other diagnostics have been computed but
// before the list of diagnostics has been filtered.
for (var unitAnalysis in _libraryUnits.values) {
IgnoreValidator(
unitAnalysis.errorReporter,
unitAnalysis.errorListener.errors,
unitAnalysis.ignoreInfo,
unitAnalysis.unit.lineInfo,
_analysisOptions.unignorableNames,
).reportErrors();
}
}
void _computeLints() {
var definingUnit = _libraryElement.definingCompilationUnit;
var analysesToContextUnits = <UnitAnalysis, LinterContextUnit>{};
LinterContextUnit? definingContextUnit;
WorkspacePackage? workspacePackage;
for (var unitAnalysis in _libraryUnits.values) {
var linterContextUnit = LinterContextUnit(
unitAnalysis.file.content,
unitAnalysis.unit,
unitAnalysis.errorReporter,
);
analysesToContextUnits[unitAnalysis] = linterContextUnit;
if (unitAnalysis.unit.declaredElement == definingUnit) {
definingContextUnit = linterContextUnit;
workspacePackage = unitAnalysis.file.workspacePackage;
}
}
var allUnits = analysesToContextUnits.values.toList();
definingContextUnit ??= allUnits.first;
var enableTiming = _analysisOptions.enableTiming;
var nodeRegistry = NodeLintRegistry(enableTiming);
var context = LinterContextImpl(
allUnits,
definingContextUnit,
_typeProvider,
_typeSystem,
_inheritance,
workspacePackage,
);
for (var linter in _analysisOptions.lintRules) {
var timer = enableTiming ? lintRuleTimers.getTimer(linter) : null;
timer?.start();
linter.registerNodeProcessors(nodeRegistry, context);
timer?.stop();
}
var logException = LinterExceptionHandler(
propagateExceptions: _analysisOptions.propagateLinterExceptions,
).logException;
for (var MapEntry(key: unitAnalysis, value: currentUnit)
in analysesToContextUnits.entries) {
// Skip computing lints on macro generated augmentations.
// See: https://github.com/dart-lang/sdk/issues/54875
if (unitAnalysis.file.isMacroAugmentation) return;
var unit = currentUnit.unit;
var errorReporter = currentUnit.errorReporter;
for (var linter in _analysisOptions.lintRules) {
linter.reporter = errorReporter;
}
// Run lint rules that handle specific node types.
unit.accept(
LinterVisitor(nodeRegistry, logException),
);
}
// Now that all lint rules have visited the code in each of the compilation
// units, we can accept each lint rule's `afterLibrary` hook.
LinterVisitor(nodeRegistry, logException).afterLibrary();
}
void _computeVerifyErrors(UnitAnalysis unitAnalysis) {
var errorReporter = unitAnalysis.errorReporter;
var unit = unitAnalysis.unit;
//
// Use the ConstantVerifier to compute errors.
//
_computeConstantErrors(unitAnalysis);
//
// Compute inheritance and override errors.
//
InheritanceOverrideVerifier(
_typeSystem,
_inheritance,
errorReporter,
).verifyUnit(unit);
//
// Use the ErrorVerifier to compute errors.
//
ErrorVerifier errorVerifier = ErrorVerifier(
errorReporter,
_libraryElement,
unit.declaredElement!,
_typeProvider,
_inheritance,
_libraryVerificationContext,
_analysisOptions,
typeSystemOperations: _typeSystemOperations,
);
unit.accept(errorVerifier);
// Verify constraints on FFI uses. The CFE enforces these constraints as
// compile-time errors and so does the analyzer.
unit.accept(FfiVerifier(_typeSystem, errorReporter,
strictCasts: _analysisOptions.strictCasts));
}
void _computeWarnings(
UnitAnalysis unitAnalysis, {
required List<UsedImportedElements> usedImportedElements,
required UsedLocalElements usedElements,
}) {
var errorReporter = unitAnalysis.errorReporter;
var unit = unitAnalysis.unit;
UnicodeTextVerifier(errorReporter).verify(unit, unitAnalysis.file.content);
unit.accept(DeadCodeVerifier(errorReporter, _libraryElement));
unit.accept(
BestPracticesVerifier(
errorReporter,
_typeProvider,
_libraryElement,
unit,
typeSystem: _typeSystem,
inheritanceManager: _inheritance,
analysisOptions: _analysisOptions,
workspacePackage: _library.file.workspacePackage,
),
);
unit.accept(OverrideVerifier(
_inheritance,
_libraryElement,
errorReporter,
));
unit.accept(RedeclareVerifier(
_inheritance,
_libraryElement,
errorReporter,
));
TodoFinder(errorReporter).findIn(unit);
LanguageVersionOverrideVerifier(errorReporter).verify(unit);
// Verify imports.
{
ImportsVerifier verifier = ImportsVerifier();
verifier.addImports(unit);
usedImportedElements.forEach(verifier.removeUsedElements);
verifier.generateDuplicateExportWarnings(errorReporter);
verifier.generateDuplicateImportWarnings(errorReporter);
verifier.generateDuplicateShownHiddenNameWarnings(errorReporter);
verifier.generateUnusedImportHints(errorReporter);
verifier.generateUnusedShownNameHints(errorReporter);
verifier.generateUnnecessaryImportHints(
errorReporter, usedImportedElements);
}
// Unused local elements.
unit.accept(
UnusedLocalElementsVerifier(
unitAnalysis.errorListener,
usedElements,
_inheritance,
_libraryElement,
),
);
//
// Find code that uses features from an SDK version that does not satisfy
// the SDK constraints specified in analysis options.
//
var package = unitAnalysis.file.workspacePackage;
var sdkVersionConstraint =
(package is PubPackage) ? package.sdkVersionConstraint : null;
if (sdkVersionConstraint != null) {
SdkConstraintVerifier verifier = SdkConstraintVerifier(
errorReporter,
sdkVersionConstraint.withoutPreRelease,
);
unit.accept(verifier);
}
}
/// Return a subset of the given [errors] that are not marked as ignored in
/// the [file].
List<AnalysisError> _filterIgnoredErrors(
UnitAnalysis unitAnalysis,
List<AnalysisError> errors,
) {
if (errors.isEmpty) {
return errors;
}
IgnoreInfo ignoreInfo = unitAnalysis.ignoreInfo;
if (!ignoreInfo.hasIgnores) {
return errors;
}
var unignorableCodes = _analysisOptions.unignorableNames;
bool isIgnored(AnalysisError error) {
var code = error.errorCode;
// Don't allow un-ignorable codes to be ignored.
if (unignorableCodes.contains(code.name) ||
unignorableCodes.contains(code.uniqueName) ||
// Lint rules have lower case names.
unignorableCodes.contains(code.name.toUpperCase())) {
return false;
}
return ignoreInfo.ignored(error);
}
return errors.where((AnalysisError e) => !isIgnored(e)).toList();
}
/// Find constants in [unit] to compute.
List<ConstantEvaluationTarget> _findConstants({
required CompilationUnit unit,
required ConstantEvaluationConfiguration configuration,
}) {
ConstantFinder constantFinder = ConstantFinder(
configuration: configuration,
);
unit.accept(constantFinder);
var dependenciesFinder = ConstantExpressionsDependenciesFinder();
unit.accept(dependenciesFinder);
return [
...constantFinder.constantsToCompute,
...dependenciesFinder.dependencies,
];
}
/// Return a new parsed unresolved [CompilationUnit].
UnitAnalysis _parse(FileState file) {
var errorListener = RecordingErrorListener();
var unit = file.parse(
errorListener: errorListener,
performance: OperationPerformanceImpl('<root>'),
);
// TODO(scheglov): Store [IgnoreInfo] as unlinked data.
var result = UnitAnalysis(
file: file,
errorListener: errorListener,
unit: unit,
);
_libraryUnits[file] = result;
return result;
}
/// Parse and resolve all files in [_library].
void _parseAndResolve() {
_resolveDirectives(
containerKind: _library,
containerElement: _libraryElement,
);
for (var unitAnalysis in _libraryUnits.values) {
_resolveFile(unitAnalysis);
}
_computeConstants();
}
/// Reports URI-related import directive errors to the [errorReporter].
void _reportImportDirectiveErrors({
required ImportDirectiveImpl directive,
required LibraryImportState state,
required ErrorReporter errorReporter,
}) {
if (state is LibraryImportWithUri) {
var selectedUriStr = state.selectedUri.relativeUriStr;
if (selectedUriStr.startsWith('dart-ext:')) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.USE_OF_NATIVE_EXTENSION,
);
} else if (state.importedSource == null) {
var errorCode = state.isDocImport
? WarningCode.URI_DOES_NOT_EXIST_IN_DOC_IMPORT
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
errorReporter.atNode(
directive.uri,
errorCode,
arguments: [selectedUriStr],
);
} else if (state is LibraryImportWithFile && !state.importedFile.exists) {
var errorCode = state.isDocImport
? WarningCode.URI_DOES_NOT_EXIST_IN_DOC_IMPORT
: isGeneratedSource(state.importedSource)
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
errorReporter.atNode(
directive.uri,
errorCode,
arguments: [selectedUriStr],
);
} else if (state.importedLibrarySource == null) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.IMPORT_OF_NON_LIBRARY,
arguments: [selectedUriStr],
);
}
} else if (state is LibraryImportWithUriStr) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.INVALID_URI,
arguments: [state.selectedUri.relativeUriStr],
);
} else {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
);
}
}
void _resolveAugmentationImportDirective({
required AugmentationImportDirectiveImpl? directive,
required AugmentationImportElementImpl element,
required AugmentationImportState state,
required ErrorReporter errorReporter,
required Set<AugmentationFileKind> seenAugmentations,
}) {
directive?.element = element;
void reportOnDirective(ErrorCode errorCode, List<Object>? arguments) {
if (directive != null) {
errorReporter.atNode(
directive.uri,
errorCode,
arguments: arguments,
);
}
}
AugmentationFileKind? importedAugmentationKind;
if (state is AugmentationImportWithFile) {
importedAugmentationKind = state.importedAugmentation;
if (!state.importedFile.exists) {
reportOnDirective(
isGeneratedSource(state.importedFile.source)
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST,
[state.importedFile.uriStr],
);
return;
} else if (importedAugmentationKind == null) {
reportOnDirective(
CompileTimeErrorCode.IMPORT_OF_NOT_AUGMENTATION,
[state.importedFile.uriStr],
);
return;
} else if (!seenAugmentations.add(importedAugmentationKind)) {
reportOnDirective(
CompileTimeErrorCode.DUPLICATE_AUGMENTATION_IMPORT,
[state.importedFile.uriStr],
);
return;
}
} else if (state is AugmentationImportWithUri) {
reportOnDirective(
CompileTimeErrorCode.URI_DOES_NOT_EXIST,
[state.uri.relativeUriStr],
);
return;
} else if (state is AugmentationImportWithUriStr) {
reportOnDirective(
CompileTimeErrorCode.INVALID_URI,
[state.uri.relativeUriStr],
);
return;
} else {
reportOnDirective(
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
null,
);
return;
}
var augmentationFile = importedAugmentationKind.file;
var augmentationUnitAnalysis = _parse(augmentationFile);
var importedAugmentation = element.importedAugmentation!;
augmentationUnitAnalysis.unit.declaredElement =
importedAugmentation.definingCompilationUnit;
for (var directive in augmentationUnitAnalysis.unit.directives) {
if (directive is AugmentationImportDirectiveImpl) {
directive.element = importedAugmentation;
}
}
_resolveDirectives(
containerKind: importedAugmentationKind,
containerElement: importedAugmentation,
);
}
/// Parses the file of [containerKind], and resolves directives.
/// Recursively parses augmentations and parts.
void _resolveDirectives({
required LibraryOrAugmentationFileKind containerKind,
required LibraryOrAugmentationElementImpl containerElement,
}) {
var containerFile = containerKind.file;
var containerUnitAnalysis = _parse(containerFile);
var containerUnit = containerUnitAnalysis.unit;
var containerUnitElement = containerElement.definingCompilationUnit;
containerUnit.declaredElement = containerUnitElement;
var containerErrorReporter = containerUnitAnalysis.errorReporter;
containerUnitAnalysis.element = containerUnitElement;
var augmentationImportIndex = 0;
var libraryExportIndex = 0;
var libraryImportIndex = 0;
var partIndex = 0;
LibraryIdentifier? libraryNameNode;
var seenAugmentations = <AugmentationFileKind>{};
var seenPartSources = <Source>{};
for (Directive directive in containerUnit.directives) {
if (directive is AugmentationImportDirectiveImpl) {
var index = augmentationImportIndex++;
_resolveAugmentationImportDirective(
directive: directive,
element: containerElement.augmentationImports[index],
state: containerKind.augmentationImports[index],
errorReporter: containerErrorReporter,
seenAugmentations: seenAugmentations,
);
} else if (directive is ExportDirectiveImpl) {
var index = libraryExportIndex++;
_resolveLibraryExportDirective(
directive: directive,
element: containerElement.libraryExports[index],
state: containerKind.libraryExports[index],
errorReporter: containerErrorReporter,
);
} else if (directive is ImportDirectiveImpl) {
var index = libraryImportIndex++;
_resolveLibraryImportDirective(
directive: directive,
element: containerElement.libraryImports[index],
state: containerKind.libraryImports[index],
errorReporter: containerErrorReporter,
);
} else if (directive is LibraryAugmentationDirectiveImpl) {
_resolveLibraryAugmentationDirective(
directive: directive,
containerKind: containerKind,
containerElement: containerElement,
containerErrorReporter: containerErrorReporter,
);
} else if (directive is LibraryDirectiveImpl) {
if (containerElement is LibraryElementImpl) {
directive.element = containerElement;
libraryNameNode = directive.name2;
}
} else if (directive is PartDirectiveImpl) {
if (containerKind is LibraryFileKind &&
containerElement is LibraryElementImpl) {
var index = partIndex++;
_resolvePartDirective(
directive: directive,
partState: containerKind.parts[index],
partElement: containerElement.parts[index],
errorReporter: containerErrorReporter,
libraryNameNode: libraryNameNode,
seenPartSources: seenPartSources,
);
}
}
}
// The macro augmentation does not have an explicit `import` directive.
// So, we look into the file augmentation imports.
var macroImport = containerKind.augmentationImports.lastOrNull;
if (macroImport is AugmentationImportWithFile) {
var importedFile = macroImport.importedFile;
if (importedFile.isMacroAugmentation) {
_resolveAugmentationImportDirective(
directive: null,
element: _libraryElement.augmentationImports.last,
state: macroImport,
errorReporter: containerErrorReporter,
seenAugmentations: seenAugmentations,
);
}
}
var docImports = containerUnit.directives
.whereType<LibraryDirective>()
.firstOrNull
?.documentationComment
?.docImports;
if (docImports != null) {
for (var i = 0; i < docImports.length; i++) {
_resolveLibraryDocImportDirective(
directive: docImports[i].import as ImportDirectiveImpl,
state: containerKind.docImports[i],
errorReporter: containerErrorReporter,
);
}
}
}
void _resolveFile(UnitAnalysis unitAnalysis) {
var source = unitAnalysis.file.source;
var errorListener = unitAnalysis.errorListener;
var unit = unitAnalysis.unit;
var unitElement = unitAnalysis.element;
TypeConstraintGenerationDataForTesting? inferenceDataForTesting =
_testingData != null ? TypeConstraintGenerationDataForTesting() : null;
unit.accept(
ResolutionVisitor(
unitElement: unitElement,
errorListener: errorListener,
nameScope: unitElement.enclosingElement.scope,
strictInference: _analysisOptions.strictInference,
strictCasts: _analysisOptions.strictCasts,
elementWalker: ElementWalker.forCompilationUnit(
unitElement,
libraryFilePath: _library.file.path,
unitFilePath: unitAnalysis.file.path,
),
dataForTesting: inferenceDataForTesting,
),
);
_testingData?.recordTypeConstraintGenerationDataForTesting(
unitAnalysis.file.uri, inferenceDataForTesting!);
var docImportLibraries = [
for (var import in _library.docImports)
if (import is LibraryImportWithFile)
_libraryElement.session.elementFactory
.libraryOfUri2(import.importedFile.uri)
];
unit.accept(ScopeResolverVisitor(
_libraryElement,
source,
_typeProvider,
errorListener,
nameScope: unitElement.enclosingElement.scope,
docImportLibraries: docImportLibraries,
));
// Nothing for RESOLVED_UNIT8?
// Nothing for RESOLVED_UNIT9?
// Nothing for RESOLVED_UNIT10?
FlowAnalysisHelper flowAnalysisHelper = FlowAnalysisHelper(
_testingData != null, unit.featureSet,
typeSystemOperations: _typeSystemOperations);
_testingData?.recordFlowAnalysisDataForTesting(
unitAnalysis.file.uri, flowAnalysisHelper.dataForTesting!);
var resolver = ResolverVisitor(_inheritance, _libraryElement,
libraryResolutionContext, source, _typeProvider, errorListener,
analysisOptions: _library.file.analysisOptions,
featureSet: unit.featureSet,
flowAnalysisHelper: flowAnalysisHelper);
unit.accept(resolver);
_testingData?.recordTypeConstraintGenerationDataForTesting(
unitAnalysis.file.uri, resolver.inferenceHelper.dataForTesting!);
}
void _resolveLibraryAugmentationDirective({
required LibraryAugmentationDirectiveImpl directive,
required LibraryOrAugmentationFileKind containerKind,
required LibraryOrAugmentationElementImpl containerElement,
required ErrorReporter containerErrorReporter,
}) {
directive.element = containerElement;
// If we had to treat this augmentation as a library.
if (containerKind is! LibraryFileKind) {
return;
}
// We should recover from an augmentation.
var recoveredFrom = containerKind.recoveredFrom;
if (recoveredFrom is! AugmentationFileKind) {
return;
}
var targetUri = recoveredFrom.uri;
if (targetUri is DirectiveUriWithFile) {
var targetFile = targetUri.file;
if (!targetFile.exists) {
containerErrorReporter.atNode(
directive.uri,
CompileTimeErrorCode.URI_DOES_NOT_EXIST,
arguments: [targetUri.relativeUriStr],
);
return;
}
var targetFileKind = targetFile.kind;
if (targetFileKind is LibraryFileKind) {
containerErrorReporter.atNode(
directive.uri,
CompileTimeErrorCode.AUGMENTATION_WITHOUT_IMPORT,
);
return;
}
}
// Otherwise, there are many other problems with the URI.
containerErrorReporter.atNode(
directive.uri,
CompileTimeErrorCode.AUGMENTATION_WITHOUT_LIBRARY,
);
}
/// Resolves the `@docImport` directive URI and reports any import errors of
/// the [directive] to the [errorReporter].
void _resolveLibraryDocImportDirective({
required ImportDirectiveImpl directive,
required LibraryImportState state,
required ErrorReporter errorReporter,
}) {
_resolveNamespaceDirective(
configurationNodes: directive.configurations,
configurationUris: state.uris.configurations,
);
_reportImportDirectiveErrors(
directive: directive,
state: state,
errorReporter: errorReporter,
);
}
void _resolveLibraryExportDirective({
required ExportDirectiveImpl directive,
required LibraryExportElementImpl element,
required LibraryExportState state,
required ErrorReporter errorReporter,
}) {
directive.element = element;
_resolveNamespaceDirective(
configurationNodes: directive.configurations,
configurationUris: state.uris.configurations,
);
if (state is LibraryExportWithUri) {
var selectedUriStr = state.selectedUri.relativeUriStr;
if (selectedUriStr.startsWith('dart-ext:')) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.USE_OF_NATIVE_EXTENSION,
);
} else if (state.exportedSource == null) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.URI_DOES_NOT_EXIST,
arguments: [selectedUriStr],
);
} else if (state is LibraryExportWithFile && !state.exportedFile.exists) {
var errorCode = isGeneratedSource(state.exportedSource)
? CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED
: CompileTimeErrorCode.URI_DOES_NOT_EXIST;
errorReporter.atNode(
directive.uri,
errorCode,
arguments: [selectedUriStr],
);
} else if (state.exportedLibrarySource == null) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.EXPORT_OF_NON_LIBRARY,
arguments: [selectedUriStr],
);
}
} else if (state is LibraryExportWithUriStr) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.INVALID_URI,
arguments: [state.selectedUri.relativeUriStr],
);
} else {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
);
}
}
void _resolveLibraryImportDirective({
required ImportDirectiveImpl directive,
required LibraryImportElementImpl element,
required LibraryImportState state,
required ErrorReporter errorReporter,
}) {
directive.element = element;
directive.prefix?.staticElement = element.prefix?.element;
_resolveNamespaceDirective(
configurationNodes: directive.configurations,
configurationUris: state.uris.configurations,
);
_reportImportDirectiveErrors(
directive: directive,
state: state,
errorReporter: errorReporter,
);
}
void _resolveNamespaceDirective({
required List<Configuration> configurationNodes,
required List<file_state.DirectiveUri> configurationUris,
}) {
for (var i = 0; i < configurationNodes.length; i++) {
var node = configurationNodes[i] as ConfigurationImpl;
node.resolvedUri = configurationUris[i].asDirectiveUri;
}
}
void _resolvePartDirective({
required PartDirectiveImpl directive,
required PartState partState,
required PartElementImpl partElement,
required ErrorReporter errorReporter,
required LibraryIdentifier? libraryNameNode,
required Set<Source> seenPartSources,
}) {
StringLiteral partUri = directive.uri;
directive.element = partElement;
if (partState is! PartWithUriStr) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.URI_WITH_INTERPOLATION,
);
return;
}
if (partState is! PartWithUri) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.INVALID_URI,
arguments: [partState.uri.relativeUriStr],
);
return;
}
if (partState is! PartWithFile) {
errorReporter.atNode(
directive.uri,
CompileTimeErrorCode.URI_DOES_NOT_EXIST,
arguments: [partState.uri.relativeUriStr],
);
return;
}
var includedFile = partState.includedFile;
var includedKind = includedFile.kind;
if (includedKind is! PartFileKind) {
ErrorCode errorCode;
if (includedFile.exists) {
errorCode = CompileTimeErrorCode.PART_OF_NON_PART;
} else if (isGeneratedSource(includedFile.source)) {
errorCode = CompileTimeErrorCode.URI_HAS_NOT_BEEN_GENERATED;
} else {
errorCode = CompileTimeErrorCode.URI_DOES_NOT_EXIST;
}
errorReporter.atNode(
partUri,
errorCode,
arguments: [includedFile.uriStr],
);
return;
}
if (includedKind is PartOfNameFileKind) {
if (!includedKind.libraries.contains(_library)) {
var name = includedKind.unlinked.name;
if (libraryNameNode == null) {
errorReporter.atNode(
partUri,
CompileTimeErrorCode.PART_OF_UNNAMED_LIBRARY,
arguments: [name],
);
} else {
errorReporter.atNode(
partUri,
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
arguments: [libraryNameNode.name, name],
);
}
return;
}
} else if (includedKind.library != _library) {
errorReporter.atNode(
partUri,
CompileTimeErrorCode.PART_OF_DIFFERENT_LIBRARY,
arguments: [_library.file.uriStr, includedFile.uriStr],
);
return;
}
var partUnitAnalysis = _parse(includedFile);
var partElementUri = partElement.uri;
if (partElementUri is DirectiveUriWithUnitImpl) {
partUnitAnalysis.element = partElementUri.unit;
partUnitAnalysis.unit.declaredElement = partElementUri.unit;
}
var partSource = includedKind.file.source;
for (var directive in partUnitAnalysis.unit.directives) {
if (directive is PartOfDirectiveImpl) {
directive.element = _libraryElement;
}
}
//
// Validate that the part source is unique in the library.
//
if (!seenPartSources.add(partSource)) {
errorReporter.atNode(
partUri,
CompileTimeErrorCode.DUPLICATE_PART,
arguments: [partSource.uri],
);
}
}
}
/// Analysis result for single file.
class UnitAnalysisResult {
final FileState file;
final CompilationUnit unit;
final List<AnalysisError> errors;
UnitAnalysisResult(this.file, this.unit, this.errors);
}
extension on file_state.DirectiveUri {
DirectiveUriImpl get asDirectiveUri {
var self = this;
if (self is file_state.DirectiveUriWithSource) {
return DirectiveUriWithSourceImpl(
relativeUriString: self.relativeUriStr,
relativeUri: self.relativeUri,
source: self.source,
);
} else if (self is file_state.DirectiveUriWithUri) {
return DirectiveUriWithRelativeUriImpl(
relativeUriString: self.relativeUriStr,
relativeUri: self.relativeUri,
);
} else if (self is file_state.DirectiveUriWithString) {
return DirectiveUriWithRelativeUriStringImpl(
relativeUriString: self.relativeUriStr,
);
}
return DirectiveUriImpl();
}
}