|  | // Copyright (c) 2014, 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/protocol/protocol_dart.dart'; | 
|  | import 'package:analysis_server/protocol/protocol_generated.dart'; | 
|  | import 'package:analysis_server/src/services/correction/fix.dart'; | 
|  | import 'package:analysis_server/src/services/search/search_engine.dart' | 
|  | as engine; | 
|  | import 'package:analysis_server/src/utilities/extensions/element.dart'; | 
|  | import 'package:analyzer/dart/analysis/results.dart' as engine; | 
|  | import 'package:analyzer/dart/ast/ast.dart' as engine; | 
|  | import 'package:analyzer/dart/element/element.dart' as engine; | 
|  | import 'package:analyzer/dart/element/type.dart'; | 
|  | import 'package:analyzer/diagnostic/diagnostic.dart' as engine; | 
|  | import 'package:analyzer/error/error.dart' as engine; | 
|  | import 'package:analyzer/source/error_processor.dart'; | 
|  | import 'package:analyzer/src/generated/source.dart' as engine; | 
|  | import 'package:analyzer_plugin/protocol/protocol_common.dart'; | 
|  |  | 
|  | export 'package:analysis_server/plugin/protocol/protocol_dart.dart'; | 
|  | export 'package:analysis_server/protocol/protocol.dart'; | 
|  | export 'package:analysis_server/protocol/protocol_generated.dart'; | 
|  | export 'package:analyzer_plugin/protocol/protocol_common.dart'; | 
|  |  | 
|  | /// Returns a list of AnalysisErrors corresponding to the given list of Engine | 
|  | /// errors. | 
|  | List<AnalysisError> doAnalysisError_listFromEngine( | 
|  | engine.AnalysisResultWithErrors result) { | 
|  | return mapEngineErrors(result, result.errors, newAnalysisError_fromEngine); | 
|  | } | 
|  |  | 
|  | /// Adds [edit] to the file containing the given [element]. | 
|  | void doSourceChange_addElementEdit( | 
|  | SourceChange change, engine.Element element, SourceEdit edit) { | 
|  | var source = element.source!; | 
|  | doSourceChange_addSourceEdit(change, source, edit); | 
|  | } | 
|  |  | 
|  | /// Adds [edit] for the given [source] to the [change]. | 
|  | void doSourceChange_addSourceEdit( | 
|  | SourceChange change, engine.Source source, SourceEdit edit, | 
|  | {bool isNewFile = false}) { | 
|  | var file = source.fullName; | 
|  | change.addEdit(file, isNewFile ? -1 : 0, edit); | 
|  | } | 
|  |  | 
|  | String? getAliasedTypeString(engine.Element element, | 
|  | {required bool withNullability}) { | 
|  | if (element is engine.TypeAliasElement) { | 
|  | var aliasedType = element.aliasedType; | 
|  | return aliasedType.getDisplayString(withNullability: withNullability); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | String? getReturnTypeString(engine.Element element, | 
|  | {required bool withNullability}) { | 
|  | if (element is engine.ExecutableElement) { | 
|  | if (element.kind == engine.ElementKind.SETTER) { | 
|  | return null; | 
|  | } else { | 
|  | return element.returnType | 
|  | .getDisplayString(withNullability: withNullability); | 
|  | } | 
|  | } else if (element is engine.VariableElement) { | 
|  | var type = element.type; | 
|  | return type.getDisplayString(withNullability: withNullability); | 
|  | } else if (element is engine.TypeAliasElement) { | 
|  | var aliasedType = element.aliasedType; | 
|  | if (aliasedType is FunctionType) { | 
|  | var returnType = aliasedType.returnType; | 
|  | return returnType.getDisplayString(withNullability: withNullability); | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /// Translates engine errors through the ErrorProcessor. | 
|  | List<T> mapEngineErrors<T>( | 
|  | engine.AnalysisResultWithErrors result, | 
|  | List<engine.AnalysisError> errors, | 
|  | T Function( | 
|  | engine.AnalysisResultWithErrors result, engine.AnalysisError error, | 
|  | [engine.ErrorSeverity errorSeverity]) | 
|  | constructor) { | 
|  | var analysisOptions = result.session.analysisContext.analysisOptions; | 
|  | var serverErrors = <T>[]; | 
|  | for (var error in errors) { | 
|  | var processor = ErrorProcessor.getProcessor(analysisOptions, error); | 
|  | if (processor != null) { | 
|  | var severity = processor.severity; | 
|  | // Errors with null severity are filtered out. | 
|  | if (severity != null) { | 
|  | // Specified severities override. | 
|  | serverErrors.add(constructor(result, error, severity)); | 
|  | } | 
|  | } else { | 
|  | serverErrors.add(constructor(result, error)); | 
|  | } | 
|  | } | 
|  | return serverErrors; | 
|  | } | 
|  |  | 
|  | /// Construct based on error information from the analyzer engine. | 
|  | /// | 
|  | /// If an [errorSeverity] is specified, it will override the one in [error]. | 
|  | AnalysisError newAnalysisError_fromEngine( | 
|  | engine.AnalysisResultWithErrors result, engine.AnalysisError error, | 
|  | [engine.ErrorSeverity? errorSeverity]) { | 
|  | var errorCode = error.errorCode; | 
|  | // prepare location | 
|  | Location location; | 
|  | { | 
|  | var file = error.source.fullName; | 
|  | var offset = error.offset; | 
|  | var length = error.length; | 
|  | var lineInfo = result.lineInfo; | 
|  |  | 
|  | var startLocation = lineInfo.getLocation(offset); | 
|  | var startLine = startLocation.lineNumber; | 
|  | var startColumn = startLocation.columnNumber; | 
|  |  | 
|  | var endLocation = lineInfo.getLocation(offset + length); | 
|  | var endLine = endLocation.lineNumber; | 
|  | var endColumn = endLocation.columnNumber; | 
|  |  | 
|  | location = Location(file, offset, length, startLine, startColumn, | 
|  | endLine: endLine, endColumn: endColumn); | 
|  | } | 
|  |  | 
|  | // Default to the error's severity if none is specified. | 
|  | errorSeverity ??= errorCode.errorSeverity; | 
|  |  | 
|  | // done | 
|  | var severity = AnalysisErrorSeverity(errorSeverity.name); | 
|  | var type = AnalysisErrorType(errorCode.type.name); | 
|  | var message = error.message; | 
|  | var code = errorCode.name.toLowerCase(); | 
|  | List<DiagnosticMessage>? contextMessages; | 
|  | if (error.contextMessages.isNotEmpty) { | 
|  | contextMessages = error.contextMessages | 
|  | .map((message) => newDiagnosticMessage(result, message)) | 
|  | .toList(); | 
|  | } | 
|  | var correction = error.correction; | 
|  | var fix = hasFix(error.errorCode); | 
|  | var url = errorCode.url; | 
|  | return AnalysisError(severity, type, location, message, code, | 
|  | contextMessages: contextMessages, | 
|  | correction: correction, | 
|  | hasFix: fix, | 
|  | url: url); | 
|  | } | 
|  |  | 
|  | /// Create a DiagnosticMessage based on an [engine.DiagnosticMessage]. | 
|  | DiagnosticMessage newDiagnosticMessage( | 
|  | engine.AnalysisResultWithErrors result, engine.DiagnosticMessage message) { | 
|  | var file = message.filePath; | 
|  | var offset = message.offset; | 
|  | var length = message.length; | 
|  |  | 
|  | var startLocation = result.lineInfo.getLocation(offset); | 
|  | var startLine = startLocation.lineNumber; | 
|  | var startColumn = startLocation.columnNumber; | 
|  |  | 
|  | var endLocation = result.lineInfo.getLocation(offset + length); | 
|  | var endLine = endLocation.lineNumber; | 
|  | var endColumn = endLocation.columnNumber; | 
|  |  | 
|  | return DiagnosticMessage( | 
|  | message.messageText(includeUrl: true), | 
|  | Location(file, offset, length, startLine, startColumn, | 
|  | endLine: endLine, endColumn: endColumn)); | 
|  | } | 
|  |  | 
|  | /// Create a Location based on an [engine.Element]. | 
|  | Location? newLocation_fromElement(engine.Element? element) { | 
|  | if (element == null || element.source == null) { | 
|  | return null; | 
|  | } | 
|  | var offset = element.nameOffset; | 
|  | var length = element.nameLength; | 
|  | if (element is engine.CompilationUnitElement || | 
|  | (element is engine.LibraryElement && offset < 0)) { | 
|  | offset = 0; | 
|  | length = 0; | 
|  | } | 
|  | var unitElement = _getUnitElement(element); | 
|  | var range = engine.SourceRange(offset, length); | 
|  | return _locationForArgs(unitElement, range); | 
|  | } | 
|  |  | 
|  | /// Create a Location based on an [engine.SearchMatch]. | 
|  | Location newLocation_fromMatch(engine.SearchMatch match) { | 
|  | var unitElement = _getUnitElement(match.element); | 
|  | return _locationForArgs(unitElement, match.sourceRange); | 
|  | } | 
|  |  | 
|  | /// Create a Location based on an [engine.AstNode]. | 
|  | Location newLocation_fromNode(engine.AstNode node) { | 
|  | var unit = node.thisOrAncestorOfType<engine.CompilationUnit>()!; | 
|  | var unitElement = unit.declaredElement!; | 
|  | var range = engine.SourceRange(node.offset, node.length); | 
|  | return _locationForArgs(unitElement, range); | 
|  | } | 
|  |  | 
|  | /// Create a Location based on an [engine.CompilationUnit]. | 
|  | Location newLocation_fromUnit( | 
|  | engine.CompilationUnit unit, engine.SourceRange range) { | 
|  | return _locationForArgs(unit.declaredElement!, range); | 
|  | } | 
|  |  | 
|  | /// Construct based on an element from the analyzer engine. | 
|  | OverriddenMember newOverriddenMember_fromEngine(engine.Element member, | 
|  | {required bool withNullability}) { | 
|  | var element = convertElement(member, withNullability: withNullability); | 
|  | var className = member.enclosingElement!.displayName; | 
|  | return OverriddenMember(element, className); | 
|  | } | 
|  |  | 
|  | /// Construct based on a value from the search engine. | 
|  | SearchResult newSearchResult_fromMatch(engine.SearchMatch match) { | 
|  | var kind = newSearchResultKind_fromEngine(match.kind); | 
|  | var location = newLocation_fromMatch(match); | 
|  | var path = _computePath(match.element); | 
|  | return SearchResult(location, kind, !match.isResolved, path); | 
|  | } | 
|  |  | 
|  | /// Construct based on a value from the search engine. | 
|  | SearchResultKind newSearchResultKind_fromEngine(engine.MatchKind kind) { | 
|  | if (kind == engine.MatchKind.DECLARATION) { | 
|  | return SearchResultKind.DECLARATION; | 
|  | } | 
|  | if (kind == engine.MatchKind.READ) { | 
|  | return SearchResultKind.READ; | 
|  | } | 
|  | if (kind == engine.MatchKind.READ_WRITE) { | 
|  | return SearchResultKind.READ_WRITE; | 
|  | } | 
|  | if (kind == engine.MatchKind.WRITE) { | 
|  | return SearchResultKind.WRITE; | 
|  | } | 
|  | if (kind == engine.MatchKind.INVOCATION || | 
|  | kind == engine.MatchKind.INVOCATION_BY_ENUM_CONSTANT_WITHOUT_ARGUMENTS) { | 
|  | return SearchResultKind.INVOCATION; | 
|  | } | 
|  | if (kind == engine.MatchKind.REFERENCE || | 
|  | kind == engine.MatchKind.REFERENCE_BY_CONSTRUCTOR_TEAR_OFF) { | 
|  | return SearchResultKind.REFERENCE; | 
|  | } | 
|  | return SearchResultKind.UNKNOWN; | 
|  | } | 
|  |  | 
|  | /// Construct based on a SourceRange. | 
|  | SourceEdit newSourceEdit_range(engine.SourceRange range, String replacement, | 
|  | {String? id}) { | 
|  | return SourceEdit(range.offset, range.length, replacement, id: id); | 
|  | } | 
|  |  | 
|  | List<Element> _computePath(engine.Element element) { | 
|  | var path = <Element>[]; | 
|  |  | 
|  | if (element is engine.PrefixElement) { | 
|  | element = element.enclosingElement2.definingCompilationUnit; | 
|  | } | 
|  |  | 
|  | var withNullability = element.library?.isNonNullableByDefault ?? false; | 
|  | for (var e in element.withAncestors) { | 
|  | path.add(convertElement(e, withNullability: withNullability)); | 
|  | } | 
|  | return path; | 
|  | } | 
|  |  | 
|  | engine.CompilationUnitElement _getUnitElement(engine.Element element) { | 
|  | if (element is engine.CompilationUnitElement) { | 
|  | return element; | 
|  | } | 
|  |  | 
|  | var enclosingElement = element.enclosingElement; | 
|  | if (enclosingElement is engine.LibraryElement) { | 
|  | element = enclosingElement; | 
|  | } | 
|  |  | 
|  | if (element is engine.LibraryElement) { | 
|  | return element.definingCompilationUnit; | 
|  | } | 
|  |  | 
|  | for (var e in element.withAncestors) { | 
|  | if (e is engine.CompilationUnitElement) { | 
|  | return e; | 
|  | } | 
|  | } | 
|  |  | 
|  | throw StateError('No unit: $element'); | 
|  | } | 
|  |  | 
|  | /// Creates a new [Location]. | 
|  | Location _locationForArgs( | 
|  | engine.CompilationUnitElement unitElement, engine.SourceRange range) { | 
|  | var lineInfo = unitElement.lineInfo; | 
|  |  | 
|  | var startLocation = lineInfo.getLocation(range.offset); | 
|  | var endLocation = lineInfo.getLocation(range.end); | 
|  |  | 
|  | var startLine = startLocation.lineNumber; | 
|  | var startColumn = startLocation.columnNumber; | 
|  | var endLine = endLocation.lineNumber; | 
|  | var endColumn = endLocation.columnNumber; | 
|  |  | 
|  | return Location(unitElement.source.fullName, range.offset, range.length, | 
|  | startLine, startColumn, | 
|  | endLine: endLine, endColumn: endColumn); | 
|  | } |