Always track variables' source spans

This is necessary for generating useful error messages for
/-as-division.
This commit is contained in:
Natalie Weizenbaum 2021-05-10 14:28:59 -07:00
parent be2d3e848a
commit 742023a877
10 changed files with 155 additions and 193 deletions

View File

@ -25,6 +25,11 @@ import 'utils.dart';
import 'value.dart';
import 'visitor/clone_css.dart';
// TODO(nweiz): This used to avoid tracking source spans for variables if source
// map generation was disabled. We always have to track them now to produce
// better warnings for /-as-division, but once those warnings are gone we should
// go back to tracking conditionally.
/// The lexical environment in which Sass is executed.
///
/// This tracks lexically-scoped information, such as variables, functions, and
@ -77,12 +82,10 @@ class AsyncEnvironment {
/// The nodes where each variable in [_variables] was defined.
///
/// This is `null` if source mapping is disabled.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
final List<Map<String, AstNode>>? _variableNodes;
final List<Map<String, AstNode>> _variableNodes;
/// A map of variable names to their indices in [_variables].
///
@ -145,7 +148,7 @@ class AsyncEnvironment {
/// Creates an [AsyncEnvironment].
///
/// If [sourceMap] is `true`, this tracks variables' source locations
AsyncEnvironment({bool sourceMap = false})
AsyncEnvironment()
: _modules = {},
_namespaceNodes = {},
_globalModules = {},
@ -155,7 +158,7 @@ class AsyncEnvironment {
_nestedForwardedModules = null,
_allModules = [],
_variables = [{}],
_variableNodes = sourceMap ? [{}] : null,
_variableNodes = [{}],
_variableIndices = {},
_functions = [{}],
_functionIndices = {},
@ -199,7 +202,7 @@ class AsyncEnvironment {
_nestedForwardedModules,
_allModules,
_variables.toList(),
_variableNodes?.toList(),
_variableNodes.toList(),
_functions.toList(),
_mixins.toList(),
_content);
@ -218,7 +221,7 @@ class AsyncEnvironment {
null,
[],
_variables.toList(),
_variableNodes?.toList(),
_variableNodes.toList(),
_functions.toList(),
_mixins.toList(),
_content);
@ -406,7 +409,7 @@ class AsyncEnvironment {
for (var variable in forwardedVariableNames) {
_variableIndices.remove(variable);
_variables.last.remove(variable);
_variableNodes?.last.remove(variable);
_variableNodes.last.remove(variable);
}
for (var function in forwardedFunctionNames) {
_functionIndices.remove(function);
@ -468,17 +471,10 @@ class AsyncEnvironment {
/// required, since some nodes need to do real work to manufacture a source
/// span.
AstNode? getVariableNode(String name, {String? namespace}) {
var variableNodes = _variableNodes;
if (variableNodes == null) {
throw StateError(
"getVariableNodes() should only be called if sourceMap = true was "
"passed in.");
}
if (namespace != null) return _getModule(namespace).variableNodes![name];
if (namespace != null) return _getModule(namespace).variableNodes[name];
if (_lastVariableName == name) {
return variableNodes[_lastVariableIndex!][name] ??
return _variableNodes[_lastVariableIndex!][name] ??
_getVariableNodeFromGlobalModule(name);
}
@ -486,7 +482,7 @@ class AsyncEnvironment {
if (index != null) {
_lastVariableName = name;
_lastVariableIndex = index;
return variableNodes[index][name] ??
return _variableNodes[index][name] ??
_getVariableNodeFromGlobalModule(name);
}
@ -496,7 +492,8 @@ class AsyncEnvironment {
_lastVariableName = name;
_lastVariableIndex = index;
_variableIndices[name] = index;
return variableNodes[index][name] ?? _getVariableNodeFromGlobalModule(name);
return _variableNodes[index][name] ??
_getVariableNodeFromGlobalModule(name);
}
/// Returns the node for the variable named [name] from a namespaceless
@ -511,7 +508,7 @@ class AsyncEnvironment {
// We don't need to worry about multiple modules defining the same variable,
// because that's already been checked by [getVariable].
for (var module in _globalModules) {
var value = module.variableNodes![name];
var value = module.variableNodes[name];
if (value != null) return value;
}
return null;
@ -559,7 +556,7 @@ class AsyncEnvironment {
/// defined with the given namespace, if no variable with the given [name] is
/// defined in module with the given namespace, or if no [namespace] is passed
/// and multiple global modules define variables named [name].
void setVariable(String name, Value value, AstNode? nodeWithSpan,
void setVariable(String name, Value value, AstNode nodeWithSpan,
{String? namespace, bool global = false}) {
if (namespace != null) {
_getModule(namespace).setVariable(name, value, nodeWithSpan);
@ -587,7 +584,7 @@ class AsyncEnvironment {
}
_variables.first[name] = value;
if (nodeWithSpan != null) _variableNodes?.first[name] = nodeWithSpan;
_variableNodes.first[name] = nodeWithSpan;
return;
}
@ -617,7 +614,7 @@ class AsyncEnvironment {
_lastVariableName = name;
_lastVariableIndex = index;
_variables[index][name] = value;
_variableNodes?[index][name] = nodeWithSpan!;
_variableNodes[index][name] = nodeWithSpan;
}
/// Sets the variable named [name] to [value], associated with
@ -629,15 +626,13 @@ class AsyncEnvironment {
/// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
void setLocalVariable(String name, Value value, AstNode? nodeWithSpan) {
void setLocalVariable(String name, Value value, AstNode nodeWithSpan) {
var index = _variables.length - 1;
_lastVariableName = name;
_lastVariableIndex = index;
_variableIndices[name] = index;
_variables[index][name] = value;
if (nodeWithSpan != null) {
_variableNodes?[index][name] = nodeWithSpan;
}
_variableNodes[index][name] = nodeWithSpan;
}
/// Returns the value of the function named [name], optionally with the given
@ -789,7 +784,7 @@ class AsyncEnvironment {
_inSemiGlobalScope = semiGlobal;
_variables.add({});
_variableNodes?.add({});
_variableNodes.add({});
_functions.add({});
_mixins.add({});
_nestedForwardedModules?.add([]);
@ -818,12 +813,12 @@ class AsyncEnvironment {
var configuration = <String, ConfiguredValue>{};
for (var i = 0; i < _variables.length; i++) {
var values = _variables[i];
var nodes = _variableNodes?[i] ?? <String, AstNode>{};
var nodes = _variableNodes[i];
for (var entry in values.entries) {
// Implicit configurations are never invalid, making [configurationSpan]
// unnecessary, so we pass null here to avoid having to compute it.
configuration[entry.key] =
ConfiguredValue.implicit(entry.value, nodes[entry.key]);
ConfiguredValue.implicit(entry.value, nodes[entry.key]!);
}
}
return Configuration.implicit(configuration);
@ -921,7 +916,7 @@ class _EnvironmentModule implements Module {
final List<Module> upstream;
final Map<String, Value> variables;
final Map<String, AstNode>? variableNodes;
final Map<String, AstNode> variableNodes;
final Map<String, AsyncCallable> functions;
final Map<String, AsyncCallable> mixins;
final ExtensionStore extensionStore;
@ -951,10 +946,8 @@ class _EnvironmentModule implements Module {
_makeModulesByVariable(forwarded),
_memberMap(environment._variables.first,
forwarded.map((module) => module.variables)),
environment._variableNodes.andThen((nodes) => _memberMap(
nodes.first,
// dart-lang/sdk#45348
forwarded!.map((module) => module.variableNodes!))),
_memberMap(environment._variableNodes.first,
forwarded.map((module) => module.variableNodes)),
_memberMap(environment._functions.first,
forwarded.map((module) => module.functions)),
_memberMap(environment._mixins.first,
@ -1017,7 +1010,7 @@ class _EnvironmentModule implements Module {
required this.transitivelyContainsExtensions})
: upstream = _environment._allModules;
void setVariable(String name, Value value, AstNode? nodeWithSpan) {
void setVariable(String name, Value value, AstNode nodeWithSpan) {
var module = _modulesByVariable[name];
if (module != null) {
module.setVariable(name, value, nodeWithSpan);
@ -1029,9 +1022,7 @@ class _EnvironmentModule implements Module {
}
_environment._variables.first[name] = value;
if (nodeWithSpan != null) {
_environment._variableNodes?.first[name] = nodeWithSpan;
}
_environment._variableNodes.first[name] = nodeWithSpan;
return;
}

View File

@ -17,9 +17,7 @@ class ConfiguredValue {
final FileSpan? configurationSpan;
/// The [AstNode] where the variable's value originated.
///
/// This is used to generate source maps.
final AstNode? assignmentNode;
final AstNode assignmentNode;
/// Creates a variable value that's been configured explicitly with a `with`
/// clause.

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_environment.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: bb0b47fc04e32f36a0f87dc73bdfe3f89dc51aa4
// Checksum: 588f0864bb1f889586178c799d91696341ecf218
//
// ignore_for_file: unused_import
@ -32,6 +32,11 @@ import 'utils.dart';
import 'value.dart';
import 'visitor/clone_css.dart';
// TODO(nweiz): This used to avoid tracking source spans for variables if source
// map generation was disabled. We always have to track them now to produce
// better warnings for /-as-division, but once those warnings are gone we should
// go back to tracking conditionally.
/// The lexical environment in which Sass is executed.
///
/// This tracks lexically-scoped information, such as variables, functions, and
@ -84,12 +89,10 @@ class Environment {
/// The nodes where each variable in [_variables] was defined.
///
/// This is `null` if source mapping is disabled.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
final List<Map<String, AstNode>>? _variableNodes;
final List<Map<String, AstNode>> _variableNodes;
/// A map of variable names to their indices in [_variables].
///
@ -152,7 +155,7 @@ class Environment {
/// Creates an [Environment].
///
/// If [sourceMap] is `true`, this tracks variables' source locations
Environment({bool sourceMap = false})
Environment()
: _modules = {},
_namespaceNodes = {},
_globalModules = {},
@ -162,7 +165,7 @@ class Environment {
_nestedForwardedModules = null,
_allModules = [],
_variables = [{}],
_variableNodes = sourceMap ? [{}] : null,
_variableNodes = [{}],
_variableIndices = {},
_functions = [{}],
_functionIndices = {},
@ -206,7 +209,7 @@ class Environment {
_nestedForwardedModules,
_allModules,
_variables.toList(),
_variableNodes?.toList(),
_variableNodes.toList(),
_functions.toList(),
_mixins.toList(),
_content);
@ -225,7 +228,7 @@ class Environment {
null,
[],
_variables.toList(),
_variableNodes?.toList(),
_variableNodes.toList(),
_functions.toList(),
_mixins.toList(),
_content);
@ -414,7 +417,7 @@ class Environment {
for (var variable in forwardedVariableNames) {
_variableIndices.remove(variable);
_variables.last.remove(variable);
_variableNodes?.last.remove(variable);
_variableNodes.last.remove(variable);
}
for (var function in forwardedFunctionNames) {
_functionIndices.remove(function);
@ -476,17 +479,10 @@ class Environment {
/// required, since some nodes need to do real work to manufacture a source
/// span.
AstNode? getVariableNode(String name, {String? namespace}) {
var variableNodes = _variableNodes;
if (variableNodes == null) {
throw StateError(
"getVariableNodes() should only be called if sourceMap = true was "
"passed in.");
}
if (namespace != null) return _getModule(namespace).variableNodes![name];
if (namespace != null) return _getModule(namespace).variableNodes[name];
if (_lastVariableName == name) {
return variableNodes[_lastVariableIndex!][name] ??
return _variableNodes[_lastVariableIndex!][name] ??
_getVariableNodeFromGlobalModule(name);
}
@ -494,7 +490,7 @@ class Environment {
if (index != null) {
_lastVariableName = name;
_lastVariableIndex = index;
return variableNodes[index][name] ??
return _variableNodes[index][name] ??
_getVariableNodeFromGlobalModule(name);
}
@ -504,7 +500,8 @@ class Environment {
_lastVariableName = name;
_lastVariableIndex = index;
_variableIndices[name] = index;
return variableNodes[index][name] ?? _getVariableNodeFromGlobalModule(name);
return _variableNodes[index][name] ??
_getVariableNodeFromGlobalModule(name);
}
/// Returns the node for the variable named [name] from a namespaceless
@ -519,7 +516,7 @@ class Environment {
// We don't need to worry about multiple modules defining the same variable,
// because that's already been checked by [getVariable].
for (var module in _globalModules) {
var value = module.variableNodes![name];
var value = module.variableNodes[name];
if (value != null) return value;
}
return null;
@ -567,7 +564,7 @@ class Environment {
/// defined with the given namespace, if no variable with the given [name] is
/// defined in module with the given namespace, or if no [namespace] is passed
/// and multiple global modules define variables named [name].
void setVariable(String name, Value value, AstNode? nodeWithSpan,
void setVariable(String name, Value value, AstNode nodeWithSpan,
{String? namespace, bool global = false}) {
if (namespace != null) {
_getModule(namespace).setVariable(name, value, nodeWithSpan);
@ -595,7 +592,7 @@ class Environment {
}
_variables.first[name] = value;
if (nodeWithSpan != null) _variableNodes?.first[name] = nodeWithSpan;
_variableNodes.first[name] = nodeWithSpan;
return;
}
@ -625,7 +622,7 @@ class Environment {
_lastVariableName = name;
_lastVariableIndex = index;
_variables[index][name] = value;
_variableNodes?[index][name] = nodeWithSpan!;
_variableNodes[index][name] = nodeWithSpan;
}
/// Sets the variable named [name] to [value], associated with
@ -637,15 +634,13 @@ class Environment {
/// This takes an [AstNode] rather than a [FileSpan] so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
void setLocalVariable(String name, Value value, AstNode? nodeWithSpan) {
void setLocalVariable(String name, Value value, AstNode nodeWithSpan) {
var index = _variables.length - 1;
_lastVariableName = name;
_lastVariableIndex = index;
_variableIndices[name] = index;
_variables[index][name] = value;
if (nodeWithSpan != null) {
_variableNodes?[index][name] = nodeWithSpan;
}
_variableNodes[index][name] = nodeWithSpan;
}
/// Returns the value of the function named [name], optionally with the given
@ -795,7 +790,7 @@ class Environment {
_inSemiGlobalScope = semiGlobal;
_variables.add({});
_variableNodes?.add({});
_variableNodes.add({});
_functions.add({});
_mixins.add({});
_nestedForwardedModules?.add([]);
@ -824,12 +819,12 @@ class Environment {
var configuration = <String, ConfiguredValue>{};
for (var i = 0; i < _variables.length; i++) {
var values = _variables[i];
var nodes = _variableNodes?[i] ?? <String, AstNode>{};
var nodes = _variableNodes[i];
for (var entry in values.entries) {
// Implicit configurations are never invalid, making [configurationSpan]
// unnecessary, so we pass null here to avoid having to compute it.
configuration[entry.key] =
ConfiguredValue.implicit(entry.value, nodes[entry.key]);
ConfiguredValue.implicit(entry.value, nodes[entry.key]!);
}
}
return Configuration.implicit(configuration);
@ -928,7 +923,7 @@ class _EnvironmentModule implements Module<Callable> {
final List<Module<Callable>> upstream;
final Map<String, Value> variables;
final Map<String, AstNode>? variableNodes;
final Map<String, AstNode> variableNodes;
final Map<String, Callable> functions;
final Map<String, Callable> mixins;
final ExtensionStore extensionStore;
@ -958,10 +953,8 @@ class _EnvironmentModule implements Module<Callable> {
_makeModulesByVariable(forwarded),
_memberMap(environment._variables.first,
forwarded.map((module) => module.variables)),
environment._variableNodes.andThen((nodes) => _memberMap(
nodes.first,
// dart-lang/sdk#45348
forwarded!.map((module) => module.variableNodes!))),
_memberMap(environment._variableNodes.first,
forwarded.map((module) => module.variableNodes)),
_memberMap(environment._functions.first,
forwarded.map((module) => module.functions)),
_memberMap(environment._mixins.first,
@ -1025,7 +1018,7 @@ class _EnvironmentModule implements Module<Callable> {
required this.transitivelyContainsExtensions})
: upstream = _environment._allModules;
void setVariable(String name, Value value, AstNode? nodeWithSpan) {
void setVariable(String name, Value value, AstNode nodeWithSpan) {
var module = _modulesByVariable[name];
if (module != null) {
module.setVariable(name, value, nodeWithSpan);
@ -1037,9 +1030,7 @@ class _EnvironmentModule implements Module<Callable> {
}
_environment._variables.first[name] = value;
if (nodeWithSpan != null) {
_environment._variableNodes?.first[name] = nodeWithSpan;
}
_environment._variableNodes.first[name] = nodeWithSpan;
return;
}

View File

@ -26,15 +26,13 @@ abstract class Module<T extends AsyncCallable> {
/// The nodes where each variable in [_variables] was defined.
///
/// This is `null` if source mapping is disabled.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
///
/// Implementations must ensure that this has the same keys as [variables] if
/// it's not `null`.
Map<String, AstNode>? get variableNodes;
Map<String, AstNode> get variableNodes;
/// The module's functions.
///
@ -71,7 +69,7 @@ abstract class Module<T extends AsyncCallable> {
///
/// Throws a [SassScriptException] if this module doesn't define a variable
/// named [name].
void setVariable(String name, Value value, AstNode? nodeWithSpan);
void setVariable(String name, Value value, AstNode nodeWithSpan);
/// Returns an opaque object that will be equal to another
/// `variableIdentity()` return value for the same name in another module if

View File

@ -44,7 +44,7 @@ class BuiltInModule<T extends AsyncCallable> implements Module<T> {
: UnmodifiableMapView(
{for (var callable in callables) callable.name: callable}));
void setVariable(String name, Value value, AstNode? nodeWithSpan) {
void setVariable(String name, Value value, AstNode nodeWithSpan) {
if (!variables.containsKey(name)) {
throw SassScriptException("Undefined variable.");
}

View File

@ -10,7 +10,6 @@ import '../exception.dart';
import '../extend/extension_store.dart';
import '../module.dart';
import '../util/limited_map_view.dart';
import '../util/nullable.dart';
import '../util/prefixed_map_view.dart';
import '../value.dart';
@ -31,7 +30,7 @@ class ForwardedModuleView<T extends AsyncCallable> implements Module<T> {
_inner.transitivelyContainsExtensions;
final Map<String, Value> variables;
final Map<String, AstNode>? variableNodes;
final Map<String, AstNode> variableNodes;
final Map<String, T> functions;
final Map<String, T> mixins;
@ -53,8 +52,8 @@ class ForwardedModuleView<T extends AsyncCallable> implements Module<T> {
ForwardedModuleView(this._inner, this._rule)
: variables = _forwardedMap(_inner.variables, _rule.prefix,
_rule.shownVariables, _rule.hiddenVariables),
variableNodes = _inner.variableNodes.andThen((inner) => _forwardedMap(
inner, _rule.prefix, _rule.shownVariables, _rule.hiddenVariables)),
variableNodes = _forwardedMap(_inner.variableNodes, _rule.prefix,
_rule.shownVariables, _rule.hiddenVariables),
functions = _forwardedMap(_inner.functions, _rule.prefix,
_rule.shownMixinsAndFunctions, _rule.hiddenMixinsAndFunctions),
mixins = _forwardedMap(_inner.mixins, _rule.prefix,
@ -86,7 +85,7 @@ class ForwardedModuleView<T extends AsyncCallable> implements Module<T> {
return map;
}
void setVariable(String name, Value value, AstNode? nodeWithSpan) {
void setVariable(String name, Value value, AstNode nodeWithSpan) {
var shownVariables = _rule.shownVariables;
var hiddenVariables = _rule.hiddenVariables;
if (shownVariables != null && !shownVariables.contains(name)) {

View File

@ -9,7 +9,6 @@ import '../exception.dart';
import '../extend/extension_store.dart';
import '../module.dart';
import '../util/limited_map_view.dart';
import '../util/nullable.dart';
import '../utils.dart';
import '../value.dart';
@ -28,7 +27,7 @@ class ShadowedModuleView<T extends AsyncCallable> implements Module<T> {
_inner.transitivelyContainsExtensions;
final Map<String, Value> variables;
final Map<String, AstNode>? variableNodes;
final Map<String, AstNode> variableNodes;
final Map<String, T> functions;
final Map<String, T> mixins;
@ -57,8 +56,7 @@ class ShadowedModuleView<T extends AsyncCallable> implements Module<T> {
ShadowedModuleView(this._inner,
{Set<String>? variables, Set<String>? functions, Set<String>? mixins})
: variables = _shadowedMap(_inner.variables, variables),
variableNodes =
_inner.variableNodes.andThen((map) => _shadowedMap(map, variables)),
variableNodes = _shadowedMap(_inner.variableNodes, variables),
functions = _shadowedMap(_inner.functions, functions),
mixins = _shadowedMap(_inner.mixins, mixins);
@ -77,7 +75,7 @@ class ShadowedModuleView<T extends AsyncCallable> implements Module<T> {
Map<String, Object?> map, Set<String>? blocklist) =>
blocklist != null && map.isNotEmpty && blocklist.any(map.containsKey);
void setVariable(String name, Value value, AstNode? nodeWithSpan) {
void setVariable(String name, Value value, AstNode nodeWithSpan) {
if (!variables.containsKey(name)) {
throw SassScriptException("Undefined variable.");
} else {

View File

@ -298,7 +298,7 @@ class _EvaluateVisitor
_sourceMap = sourceMap,
// The default environment is overridden in [_execute] for full
// stylesheets, but for [AsyncEvaluator] this environment is used.
_environment = AsyncEnvironment(sourceMap: sourceMap) {
_environment = AsyncEnvironment() {
var metaFunctions = [
// These functions are defined in the context of the evaluator because
// they need access to the [_environment] or other local state.
@ -677,7 +677,7 @@ class _EvaluateVisitor
return alreadyLoaded;
}
var environment = AsyncEnvironment(sourceMap: _sourceMap);
var environment = AsyncEnvironment();
late CssStylesheet css;
var extensionStore = ExtensionStore();
await _withEnvironment(environment, () async {
@ -2219,7 +2219,7 @@ class _EvaluateVisitor
_environment.setLocalVariable(
declaredArguments[i].name,
evaluated.positional[i].withoutSlash(),
evaluated.positionalNodes?[i]);
evaluated.positionalNodes[i]);
}
for (var i = evaluated.positional.length;
@ -2231,8 +2231,8 @@ class _EvaluateVisitor
_environment.setLocalVariable(
argument.name,
value.withoutSlash(),
evaluated.namedNodes?[argument.name] ??
argument.defaultValue.andThen(_expressionNode));
evaluated.namedNodes[argument.name] ??
_expressionNode(argument.defaultValue!));
}
SassArgumentList? argumentList;
@ -2325,7 +2325,7 @@ class _EvaluateVisitor
/// body.
Future<Value> _runBuiltInCallable(ArgumentInvocation arguments,
AsyncBuiltInCallable callable, AstNode nodeWithSpan) async {
var evaluated = await _evaluateArguments(arguments, trackSpans: false);
var evaluated = await _evaluateArguments(arguments);
var oldCallableNode = _callableNode;
_callableNode = nodeWithSpan;
@ -2405,12 +2405,13 @@ class _EvaluateVisitor
}
/// Returns the evaluated values of the given [arguments].
///
/// If [trackSpans] is `true`, this tracks the source spans of the arguments
/// being passed in. It defaults to [_sourceMap].
Future<_ArgumentResults> _evaluateArguments(ArgumentInvocation arguments,
{bool? trackSpans}) async {
trackSpans ??= _sourceMap;
Future<_ArgumentResults> _evaluateArguments(
ArgumentInvocation arguments) async {
// TODO(nweiz): This used to avoid tracking source spans for arguments if
// [_sourceMap]s was false or it was being called from
// [_runBuiltInCallable]. We always have to track them now to produce better
// warnings for /-as-division, but once those warnings are gone we should go
// back to tracking conditionally.
var positional = [
for (var expression in arguments.positional) await expression.accept(this)
@ -2420,23 +2421,18 @@ class _EvaluateVisitor
entry.key: await entry.value.accept(this)
};
var positionalNodes = trackSpans
? [
for (var expression in arguments.positional)
_expressionNode(expression)
]
: null;
var namedNodes = trackSpans
? {
var positionalNodes = [
for (var expression in arguments.positional) _expressionNode(expression)
];
var namedNodes = {
for (var entry in arguments.named.entries)
entry.key: _expressionNode(entry.value)
}
: null;
};
var restArgs = arguments.rest;
if (restArgs == null) {
return _ArgumentResults(positional, named, ListSeparator.undecided,
positionalNodes: positionalNodes, namedNodes: namedNodes);
return _ArgumentResults(positional, positionalNodes, named, namedNodes,
ListSeparator.undecided);
}
var rest = await restArgs.accept(this);
@ -2444,42 +2440,42 @@ class _EvaluateVisitor
var separator = ListSeparator.undecided;
if (rest is SassMap) {
_addRestMap(named, rest, restArgs, (value) => value);
namedNodes?.addAll({
namedNodes.addAll({
for (var key in rest.contents.keys)
(key as SassString).text: restNodeForSpan
});
} else if (rest is SassList) {
positional.addAll(rest.asList);
positionalNodes?.addAll(List.filled(rest.lengthAsList, restNodeForSpan));
positionalNodes.addAll(List.filled(rest.lengthAsList, restNodeForSpan));
separator = rest.separator;
if (rest is SassArgumentList) {
rest.keywords.forEach((key, value) {
named[key] = value;
if (namedNodes != null) namedNodes[key] = restNodeForSpan;
namedNodes[key] = restNodeForSpan;
});
}
} else {
positional.add(rest);
positionalNodes?.add(restNodeForSpan);
positionalNodes.add(restNodeForSpan);
}
var keywordRestArgs = arguments.keywordRest;
if (keywordRestArgs == null) {
return _ArgumentResults(positional, named, separator,
positionalNodes: positionalNodes, namedNodes: namedNodes);
return _ArgumentResults(
positional, positionalNodes, named, namedNodes, separator);
}
var keywordRest = await keywordRestArgs.accept(this);
var keywordRestNodeForSpan = _expressionNode(keywordRestArgs);
if (keywordRest is SassMap) {
_addRestMap(named, keywordRest, keywordRestArgs, (value) => value);
namedNodes?.addAll({
namedNodes.addAll({
for (var key in keywordRest.contents.keys)
(key as SassString).text: keywordRestNodeForSpan
});
return _ArgumentResults(positional, named, separator,
positionalNodes: positionalNodes, namedNodes: namedNodes);
return _ArgumentResults(
positional, positionalNodes, named, namedNodes, separator);
} else {
throw _exception(
"Variable keyword arguments must be a map (was $keywordRest).",
@ -2892,10 +2888,10 @@ class _EvaluateVisitor
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
AstNode _expressionNode(Expression expression) {
// If we aren't making a source map this doesn't matter, but we still return
// the expression so we don't have to make the type (and everything
// downstream of it) nullable.
if (!_sourceMap) return expression;
// TODO(nweiz): This used to return [expression] as-is if source map
// generation was disabled. We always have to track the original location
// now to produce better warnings for /-as-division, but once those warnings
// are gone we should go back to short-circuiting.
if (expression is VariableExpression) {
return _environment.getVariableNode(expression.name,
@ -3205,28 +3201,26 @@ class _ArgumentResults {
/// Arguments passed by position.
final List<Value> positional;
/// The [AstNode]s that hold the spans for each [positional] argument, or
/// `null` if source span tracking is disabled.
/// The [AstNode]s that hold the spans for each [positional] argument.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
final List<AstNode>? positionalNodes;
final List<AstNode> positionalNodes;
/// Arguments passed by name.
final Map<String, Value> named;
/// The [AstNode]s that hold the spans for each [named] argument, or `null` if
/// source span tracking is disabled.
/// The [AstNode]s that hold the spans for each [named] argument.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
final Map<String, AstNode>? namedNodes;
final Map<String, AstNode> namedNodes;
/// The separator used for the rest argument list, if any.
final ListSeparator separator;
_ArgumentResults(this.positional, this.named, this.separator,
{this.positionalNodes, this.namedNodes});
_ArgumentResults(this.positional, this.positionalNodes, this.named,
this.namedNodes, this.separator);
}

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: acaafcfe17e8cb582fb01ea8b95afaf871af4eed
// Checksum: 6351ed2d303a58943ce6be39dc794fb46286fd64
//
// ignore_for_file: unused_import
@ -306,7 +306,7 @@ class _EvaluateVisitor
_sourceMap = sourceMap,
// The default environment is overridden in [_execute] for full
// stylesheets, but for [AsyncEvaluator] this environment is used.
_environment = Environment(sourceMap: sourceMap) {
_environment = Environment() {
var metaFunctions = [
// These functions are defined in the context of the evaluator because
// they need access to the [_environment] or other local state.
@ -682,7 +682,7 @@ class _EvaluateVisitor
return alreadyLoaded;
}
var environment = Environment(sourceMap: _sourceMap);
var environment = Environment();
late CssStylesheet css;
var extensionStore = ExtensionStore();
_withEnvironment(environment, () {
@ -2206,7 +2206,7 @@ class _EvaluateVisitor
_environment.setLocalVariable(
declaredArguments[i].name,
evaluated.positional[i].withoutSlash(),
evaluated.positionalNodes?[i]);
evaluated.positionalNodes[i]);
}
for (var i = evaluated.positional.length;
@ -2218,8 +2218,8 @@ class _EvaluateVisitor
_environment.setLocalVariable(
argument.name,
value.withoutSlash(),
evaluated.namedNodes?[argument.name] ??
argument.defaultValue.andThen(_expressionNode));
evaluated.namedNodes[argument.name] ??
_expressionNode(argument.defaultValue!));
}
SassArgumentList? argumentList;
@ -2310,7 +2310,7 @@ class _EvaluateVisitor
/// body.
Value _runBuiltInCallable(ArgumentInvocation arguments,
BuiltInCallable callable, AstNode nodeWithSpan) {
var evaluated = _evaluateArguments(arguments, trackSpans: false);
var evaluated = _evaluateArguments(arguments);
var oldCallableNode = _callableNode;
_callableNode = nodeWithSpan;
@ -2390,12 +2390,12 @@ class _EvaluateVisitor
}
/// Returns the evaluated values of the given [arguments].
///
/// If [trackSpans] is `true`, this tracks the source spans of the arguments
/// being passed in. It defaults to [_sourceMap].
_ArgumentResults _evaluateArguments(ArgumentInvocation arguments,
{bool? trackSpans}) {
trackSpans ??= _sourceMap;
_ArgumentResults _evaluateArguments(ArgumentInvocation arguments) {
// TODO(nweiz): This used to avoid tracking source spans for arguments if
// [_sourceMap]s was false or it was being called from
// [_runBuiltInCallable]. We always have to track them now to produce better
// warnings for /-as-division, but once those warnings are gone we should go
// back to tracking conditionally.
var positional = [
for (var expression in arguments.positional) expression.accept(this)
@ -2405,23 +2405,18 @@ class _EvaluateVisitor
entry.key: entry.value.accept(this)
};
var positionalNodes = trackSpans
? [
for (var expression in arguments.positional)
_expressionNode(expression)
]
: null;
var namedNodes = trackSpans
? {
var positionalNodes = [
for (var expression in arguments.positional) _expressionNode(expression)
];
var namedNodes = {
for (var entry in arguments.named.entries)
entry.key: _expressionNode(entry.value)
}
: null;
};
var restArgs = arguments.rest;
if (restArgs == null) {
return _ArgumentResults(positional, named, ListSeparator.undecided,
positionalNodes: positionalNodes, namedNodes: namedNodes);
return _ArgumentResults(positional, positionalNodes, named, namedNodes,
ListSeparator.undecided);
}
var rest = restArgs.accept(this);
@ -2429,42 +2424,42 @@ class _EvaluateVisitor
var separator = ListSeparator.undecided;
if (rest is SassMap) {
_addRestMap(named, rest, restArgs, (value) => value);
namedNodes?.addAll({
namedNodes.addAll({
for (var key in rest.contents.keys)
(key as SassString).text: restNodeForSpan
});
} else if (rest is SassList) {
positional.addAll(rest.asList);
positionalNodes?.addAll(List.filled(rest.lengthAsList, restNodeForSpan));
positionalNodes.addAll(List.filled(rest.lengthAsList, restNodeForSpan));
separator = rest.separator;
if (rest is SassArgumentList) {
rest.keywords.forEach((key, value) {
named[key] = value;
if (namedNodes != null) namedNodes[key] = restNodeForSpan;
namedNodes[key] = restNodeForSpan;
});
}
} else {
positional.add(rest);
positionalNodes?.add(restNodeForSpan);
positionalNodes.add(restNodeForSpan);
}
var keywordRestArgs = arguments.keywordRest;
if (keywordRestArgs == null) {
return _ArgumentResults(positional, named, separator,
positionalNodes: positionalNodes, namedNodes: namedNodes);
return _ArgumentResults(
positional, positionalNodes, named, namedNodes, separator);
}
var keywordRest = keywordRestArgs.accept(this);
var keywordRestNodeForSpan = _expressionNode(keywordRestArgs);
if (keywordRest is SassMap) {
_addRestMap(named, keywordRest, keywordRestArgs, (value) => value);
namedNodes?.addAll({
namedNodes.addAll({
for (var key in keywordRest.contents.keys)
(key as SassString).text: keywordRestNodeForSpan
});
return _ArgumentResults(positional, named, separator,
positionalNodes: positionalNodes, namedNodes: namedNodes);
return _ArgumentResults(
positional, positionalNodes, named, namedNodes, separator);
} else {
throw _exception(
"Variable keyword arguments must be a map (was $keywordRest).",
@ -2870,10 +2865,10 @@ class _EvaluateVisitor
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
AstNode _expressionNode(Expression expression) {
// If we aren't making a source map this doesn't matter, but we still return
// the expression so we don't have to make the type (and everything
// downstream of it) nullable.
if (!_sourceMap) return expression;
// TODO(nweiz): This used to return [expression] as-is if source map
// generation was disabled. We always have to track the original location
// now to produce better warnings for /-as-division, but once those warnings
// are gone we should go back to short-circuiting.
if (expression is VariableExpression) {
return _environment.getVariableNode(expression.name,
@ -3146,28 +3141,26 @@ class _ArgumentResults {
/// Arguments passed by position.
final List<Value> positional;
/// The [AstNode]s that hold the spans for each [positional] argument, or
/// `null` if source span tracking is disabled.
/// The [AstNode]s that hold the spans for each [positional] argument.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
final List<AstNode>? positionalNodes;
final List<AstNode> positionalNodes;
/// Arguments passed by name.
final Map<String, Value> named;
/// The [AstNode]s that hold the spans for each [named] argument, or `null` if
/// source span tracking is disabled.
/// The [AstNode]s that hold the spans for each [named] argument.
///
/// This stores [AstNode]s rather than [FileSpan]s so it can avoid calling
/// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span.
final Map<String, AstNode>? namedNodes;
final Map<String, AstNode> namedNodes;
/// The separator used for the rest argument list, if any.
final ListSeparator separator;
_ArgumentResults(this.positional, this.named, this.separator,
{this.positionalNodes, this.namedNodes});
_ArgumentResults(this.positional, this.positionalNodes, this.named,
this.namedNodes, this.separator);
}