| // Copyright (c) 2024, 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:analysis_server_plugin/edit/correction_utils.dart'; | |
| import 'package:analysis_server_plugin/edit/fix/fix_context.dart'; | |
| import 'package:analysis_server_plugin/src/correction/change_workspace.dart'; | |
| import 'package:analyzer/dart/analysis/results.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:analyzer/diagnostic/diagnostic.dart'; | |
| import 'package:analyzer/instrumentation/service.dart'; | |
| import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart'; | |
| import 'package:analyzer/src/dart/analysis/file_state_filter.dart'; | |
| import 'package:analyzer/src/dart/resolver/applicable_extensions.dart'; | |
| import 'package:analyzer/src/services/top_level_declarations.dart'; | |
| import 'package:analyzer/utilities/extensions/element.dart'; | |
| /// An object used to provide context information for Dart fix contributors. | |
| /// | |
| /// Clients may not extend, implement or mix-in this class. | |
| class DartFixContext implements FixContext { | |
| /// Whether fixes were triggered automatically (for example by a save | |
| /// operation). | |
| /// | |
| /// Some fixes may be excluded when running automatically. For example | |
| /// removing unused imports or parameters is less acceptable while the code is | |
| /// incomplete and being worked on than when manually executing fixes ready | |
| /// for committing. | |
| final bool autoTriggered; | |
| /// The instrumentation service used to report errors that prevent a fix from | |
| /// being composed. | |
| final InstrumentationService instrumentationService; | |
| /// The library result in which the fix operates. | |
| final ResolvedLibraryResult libraryResult; | |
| /// The unit result in which the fix operates. | |
| final ResolvedUnitResult unitResult; | |
| // The [CorrectionUtils] for the unit result. | |
| final CorrectionUtils correctionUtils; | |
| /// The workspace in which the fix contributor operates. | |
| final ChangeWorkspace workspace; | |
| /// Cache of previously computed [getTopLevelDeclarations] responses. | |
| /// | |
| /// It's been observed that the same request is fired multiple times for at | |
| /// least some getFixes requsts. Caching the response can speed up such | |
| /// requests. | |
| final Map<String, Future<Map<LibraryElement, Element>>> | |
| _cachedTopLevelDeclarations = {}; | |
| @override | |
| final Diagnostic diagnostic; | |
| DartFixContext({ | |
| required this.instrumentationService, | |
| required this.workspace, | |
| required this.libraryResult, | |
| required this.unitResult, | |
| // TODO(srawlins): Rename to `diagnostic`. | |
| required Diagnostic error, | |
| this.autoTriggered = false, | |
| CorrectionUtils? correctionUtils, | |
| }) : diagnostic = error, | |
| correctionUtils = correctionUtils ?? CorrectionUtils(unitResult); | |
| @override | |
| Diagnostic get error => diagnostic; | |
| /// Returns the mapping from each library (that is available to this context) | |
| /// to a top-level declaration that is exported (not necessary declared) by | |
| /// this library, and has the requested base name. | |
| /// | |
| /// For getters and setters the corresponding top-level variable is returned. | |
| Future<Map<LibraryElement, Element>> getTopLevelDeclarations(String name) { | |
| var cachedResult = _cachedTopLevelDeclarations[name]; | |
| if (cachedResult != null) return cachedResult; | |
| var result = TopLevelDeclarations(unitResult).withName(name); | |
| _cachedTopLevelDeclarations[name] = result; | |
| return result; | |
| } | |
| /// Returns libraries with extensions that declare non-static public | |
| /// extension members with the [memberName]. | |
| Stream<LibraryElement> librariesWithExtensions(Name memberName) async* { | |
| var analysisContext = unitResult.session.analysisContext; | |
| if (analysisContext is! DriverBasedAnalysisContext) { | |
| return; | |
| } | |
| var analysisDriver = analysisContext.driver; | |
| var fsState = analysisDriver.fsState; | |
| var targetFile = fsState.getFileForPath(unitResult.path); | |
| var filter = FileStateFilter(targetFile); | |
| await analysisDriver.discoverAvailableFilesFor(targetFile); | |
| for (var file in fsState.knownFiles.toList()) { | |
| if (!filter.shouldInclude(file)) { | |
| continue; | |
| } | |
| var elementResult = await analysisDriver.getLibraryByUri(file.uriStr); | |
| if (elementResult is! LibraryElementResult) { | |
| continue; | |
| } | |
| if (elementResult.element.exportedExtensions | |
| .havingMemberWithBaseName(memberName) | |
| .isNotEmpty) { | |
| yield elementResult.element; | |
| } | |
| } | |
| } | |
| } |