Files
Konstantin Shcheglov 3e73e2fed4 CQ. Fixes to make analyzer/ and analysis_server/ pass with TEST_ANALYZER_WINDOWS_PATHS=true on Mac.
Avoid constructing file URIs and paths directly from the host platform
when analyzer code is operating on an injected ResourceProvider. Those
direct conversions use the process path context, which is wrong when the
provider is using a Windows path context on another host platform.

Route file URI conversions through ResourceProvider, FileSource, File,
and PathContext APIs instead. This keeps file names, document links,
analysis options includes, navigation targets, and relative paths
aligned with the active provider.

Update affected tests and shared test utilities to use provider-backed
URIs and paths, so they exercise the same path semantics as production
code.

Change-Id: Iaf7751d028273a5fa72a04d8ca60e51aee700594
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/499481
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
2026-04-30 08:20:28 -07:00

278 lines
8.0 KiB
Dart

// 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.
/// Utilities for converting Dart entities into analysis server's protocol
/// entities.
library;
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analyzer/dart/element/element.dart' as engine;
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/src/dart/element/element.dart';
Element convertElement(engine.Element element) {
var kind = convertElementToElementKind(element);
var name = getElementDisplayName(element);
var elementTypeParameters = _getTypeParametersString(element);
var aliasedType = getAliasedTypeString(element);
var elementParameters = getParametersString(element);
var elementReturnType = getReturnTypeString(element);
var extendedType = getExtendedTypeString(element);
return Element(
kind,
name,
Element.makeFlags(
isPrivate: element.isPrivate,
isDeprecated: element.isDeprecatedWithKind('use'),
isAbstract: _isAbstract(element),
isConst: _isConst(element),
isFinal: _isFinal(element),
isStatic: _isStatic(element),
),
location: newLocation_fromElement(element),
typeParameters: elementTypeParameters,
aliasedType: aliasedType,
parameters: elementParameters,
returnType: elementReturnType,
extendedType: extendedType,
);
}
/// Return a protocol [ElementKind] corresponding to the given
/// [engine.ElementKind].
///
/// This does not take into account that an instance of [engine.ClassElement]
/// can be an enum and an instance of [engine.FieldElement] can be an enum
/// constant. Use [convertElementToElementKind] where possible.
ElementKind convertElementKind(engine.ElementKind kind) {
if (kind == engine.ElementKind.CLASS) {
return ElementKind.CLASS;
}
if (kind == engine.ElementKind.COMPILATION_UNIT) {
return ElementKind.COMPILATION_UNIT;
}
if (kind == engine.ElementKind.CONSTRUCTOR) {
return ElementKind.CONSTRUCTOR;
}
if (kind == engine.ElementKind.ENUM) {
return ElementKind.ENUM;
}
if (kind == engine.ElementKind.EXTENSION) {
return ElementKind.EXTENSION;
}
if (kind == engine.ElementKind.EXTENSION_TYPE) {
return ElementKind.EXTENSION_TYPE;
}
if (kind == engine.ElementKind.FIELD) {
return ElementKind.FIELD;
}
if (kind == engine.ElementKind.FUNCTION) {
return ElementKind.FUNCTION;
}
if (kind == engine.ElementKind.FUNCTION_TYPE_ALIAS) {
return ElementKind.FUNCTION_TYPE_ALIAS;
}
if (kind == engine.ElementKind.GENERIC_FUNCTION_TYPE) {
return ElementKind.FUNCTION_TYPE_ALIAS;
}
if (kind == engine.ElementKind.GETTER) {
return ElementKind.GETTER;
}
if (kind == engine.ElementKind.LABEL) {
return ElementKind.LABEL;
}
if (kind == engine.ElementKind.LIBRARY) {
return ElementKind.LIBRARY;
}
if (kind == engine.ElementKind.LOCAL_VARIABLE) {
return ElementKind.LOCAL_VARIABLE;
}
if (kind == engine.ElementKind.METHOD) {
return ElementKind.METHOD;
}
if (kind == engine.ElementKind.MIXIN) {
return ElementKind.MIXIN;
}
if (kind == engine.ElementKind.PARAMETER) {
return ElementKind.PARAMETER;
}
if (kind == engine.ElementKind.PART) {
return ElementKind.COMPILATION_UNIT;
}
if (kind == engine.ElementKind.PREFIX) {
return ElementKind.PREFIX;
}
if (kind == engine.ElementKind.SETTER) {
return ElementKind.SETTER;
}
if (kind == engine.ElementKind.TOP_LEVEL_VARIABLE) {
return ElementKind.TOP_LEVEL_VARIABLE;
}
if (kind == engine.ElementKind.TYPE_ALIAS) {
return ElementKind.TYPE_ALIAS;
}
if (kind == engine.ElementKind.TYPE_PARAMETER) {
return ElementKind.TYPE_PARAMETER;
}
return ElementKind.UNKNOWN;
}
/// Return an [ElementKind] corresponding to the given [engine.Element].
ElementKind convertElementToElementKind(engine.Element element) {
if (element is engine.EnumElement) {
return ElementKind.ENUM;
} else if (element is engine.MixinElement) {
return ElementKind.MIXIN;
}
if (element is engine.FieldElement && element.isEnumConstant) {
return ElementKind.ENUM_CONSTANT;
}
return convertElementKind(element.kind);
}
Element convertLibraryFragment(LibraryFragmentImpl fragment) {
return Element(
ElementKind.COMPILATION_UNIT,
(fragment.source as FileSource).file.shortName,
Element.makeFlags(
isPrivate: fragment.isPrivate,
isDeprecated: fragment.library.isDeprecatedWithKind('use'),
),
location: newLocation_fromFragment(fragment),
);
}
String getElementDisplayName(engine.Element element) {
if (element is engine.LibraryFragment) {
var fragment = element as engine.LibraryFragment;
return (fragment.source as FileSource).file.shortName;
} else {
return element.displayName;
}
}
String getParametersListString(List<engine.FormalParameterElement> parameters) {
parameters.sort(_preferRequiredParams);
var sb = StringBuffer();
var closeOptionalString = '';
for (var parameter in parameters) {
if (sb.isNotEmpty) {
sb.write(', ');
}
if (closeOptionalString.isEmpty) {
if (parameter.isNamed) {
sb.write('{');
closeOptionalString = '}';
} else if (parameter.isOptionalPositional) {
sb.write('[');
closeOptionalString = ']';
}
}
if (parameter.isRequiredNamed) {
sb.write('required ');
} else if (parameter.metadata.hasDeprecated) {
sb.write('@required ');
}
parameter.appendToWithoutDelimiters(sb);
}
sb.write(closeOptionalString);
return '($sb)';
}
String? getParametersString(engine.Element element) {
// TODO(scheglov): expose the corresponding feature from ExecutableElement
List<engine.FormalParameterElement> parameters;
if (element is engine.ExecutableElement) {
// valid getters don't have parameters
if (element.kind == engine.ElementKind.GETTER &&
element.formalParameters.isEmpty) {
return null;
}
parameters = element.formalParameters.toList();
} else if (element is engine.TypeAliasElement) {
var aliasedType = element.aliasedType;
if (aliasedType is FunctionType) {
parameters = aliasedType.formalParameters.toList();
} else {
return null;
}
} else {
return null;
}
return getParametersListString(parameters);
}
String? _getTypeParametersString(engine.Element element) {
List<engine.TypeParameterElement>? typeParameters;
if (element is engine.InterfaceElement) {
typeParameters = element.typeParameters;
} else if (element is engine.TypeAliasElement) {
typeParameters = element.typeParameters;
}
if (typeParameters == null || typeParameters.isEmpty) {
return null;
}
return '<${typeParameters.join(', ')}>';
}
bool _isAbstract(engine.Element element) {
if (element is engine.ClassElement) {
return element.isAbstract;
}
if (element is engine.MethodElement) {
return element.isAbstract;
}
if (element is engine.MixinElement) {
return true;
}
return false;
}
bool _isConst(engine.Element element) {
if (element is engine.ConstructorElement) {
return element.isConst;
}
if (element is engine.VariableElement) {
return element.isConst;
}
return false;
}
bool _isFinal(engine.Element element) {
if (element is engine.VariableElement) {
return element.isFinal;
}
return false;
}
bool _isStatic(engine.Element element) {
if (element is engine.ExecutableElement) {
return element.isStatic;
}
if (element is engine.PropertyInducingElement) {
return element.isStatic;
}
return false;
}
/// Sort required named parameters before optional ones.
int _preferRequiredParams(
engine.FormalParameterElement e1,
engine.FormalParameterElement e2,
) {
var rank1 = (e1.isRequiredNamed || e1.metadata.hasRequired)
? 0
: !e1.isNamed
? -1
: 1;
var rank2 = (e2.isRequiredNamed || e2.metadata.hasRequired)
? 0
: !e2.isNamed
? -1
: 1;
return rank1 - rank2;
}