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 'value.dart';
import 'visitor/clone_css.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. /// The lexical environment in which Sass is executed.
/// ///
/// This tracks lexically-scoped information, such as variables, functions, and /// 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. /// 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 /// 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 /// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span. /// 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]. /// A map of variable names to their indices in [_variables].
/// ///
@ -145,7 +148,7 @@ class AsyncEnvironment {
/// Creates an [AsyncEnvironment]. /// Creates an [AsyncEnvironment].
/// ///
/// If [sourceMap] is `true`, this tracks variables' source locations /// If [sourceMap] is `true`, this tracks variables' source locations
AsyncEnvironment({bool sourceMap = false}) AsyncEnvironment()
: _modules = {}, : _modules = {},
_namespaceNodes = {}, _namespaceNodes = {},
_globalModules = {}, _globalModules = {},
@ -155,7 +158,7 @@ class AsyncEnvironment {
_nestedForwardedModules = null, _nestedForwardedModules = null,
_allModules = [], _allModules = [],
_variables = [{}], _variables = [{}],
_variableNodes = sourceMap ? [{}] : null, _variableNodes = [{}],
_variableIndices = {}, _variableIndices = {},
_functions = [{}], _functions = [{}],
_functionIndices = {}, _functionIndices = {},
@ -199,7 +202,7 @@ class AsyncEnvironment {
_nestedForwardedModules, _nestedForwardedModules,
_allModules, _allModules,
_variables.toList(), _variables.toList(),
_variableNodes?.toList(), _variableNodes.toList(),
_functions.toList(), _functions.toList(),
_mixins.toList(), _mixins.toList(),
_content); _content);
@ -218,7 +221,7 @@ class AsyncEnvironment {
null, null,
[], [],
_variables.toList(), _variables.toList(),
_variableNodes?.toList(), _variableNodes.toList(),
_functions.toList(), _functions.toList(),
_mixins.toList(), _mixins.toList(),
_content); _content);
@ -406,7 +409,7 @@ class AsyncEnvironment {
for (var variable in forwardedVariableNames) { for (var variable in forwardedVariableNames) {
_variableIndices.remove(variable); _variableIndices.remove(variable);
_variables.last.remove(variable); _variables.last.remove(variable);
_variableNodes?.last.remove(variable); _variableNodes.last.remove(variable);
} }
for (var function in forwardedFunctionNames) { for (var function in forwardedFunctionNames) {
_functionIndices.remove(function); _functionIndices.remove(function);
@ -468,17 +471,10 @@ class AsyncEnvironment {
/// required, since some nodes need to do real work to manufacture a source /// required, since some nodes need to do real work to manufacture a source
/// span. /// span.
AstNode? getVariableNode(String name, {String? namespace}) { AstNode? getVariableNode(String name, {String? namespace}) {
var variableNodes = _variableNodes; if (namespace != null) return _getModule(namespace).variableNodes[name];
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 (_lastVariableName == name) { if (_lastVariableName == name) {
return variableNodes[_lastVariableIndex!][name] ?? return _variableNodes[_lastVariableIndex!][name] ??
_getVariableNodeFromGlobalModule(name); _getVariableNodeFromGlobalModule(name);
} }
@ -486,7 +482,7 @@ class AsyncEnvironment {
if (index != null) { if (index != null) {
_lastVariableName = name; _lastVariableName = name;
_lastVariableIndex = index; _lastVariableIndex = index;
return variableNodes[index][name] ?? return _variableNodes[index][name] ??
_getVariableNodeFromGlobalModule(name); _getVariableNodeFromGlobalModule(name);
} }
@ -496,7 +492,8 @@ class AsyncEnvironment {
_lastVariableName = name; _lastVariableName = name;
_lastVariableIndex = index; _lastVariableIndex = index;
_variableIndices[name] = 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 /// 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, // We don't need to worry about multiple modules defining the same variable,
// because that's already been checked by [getVariable]. // because that's already been checked by [getVariable].
for (var module in _globalModules) { for (var module in _globalModules) {
var value = module.variableNodes![name]; var value = module.variableNodes[name];
if (value != null) return value; if (value != null) return value;
} }
return null; return null;
@ -559,7 +556,7 @@ class AsyncEnvironment {
/// defined with the given namespace, if no variable with the given [name] is /// 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 /// defined in module with the given namespace, or if no [namespace] is passed
/// and multiple global modules define variables named [name]. /// 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}) { {String? namespace, bool global = false}) {
if (namespace != null) { if (namespace != null) {
_getModule(namespace).setVariable(name, value, nodeWithSpan); _getModule(namespace).setVariable(name, value, nodeWithSpan);
@ -587,7 +584,7 @@ class AsyncEnvironment {
} }
_variables.first[name] = value; _variables.first[name] = value;
if (nodeWithSpan != null) _variableNodes?.first[name] = nodeWithSpan; _variableNodes.first[name] = nodeWithSpan;
return; return;
} }
@ -617,7 +614,7 @@ class AsyncEnvironment {
_lastVariableName = name; _lastVariableName = name;
_lastVariableIndex = index; _lastVariableIndex = index;
_variables[index][name] = value; _variables[index][name] = value;
_variableNodes?[index][name] = nodeWithSpan!; _variableNodes[index][name] = nodeWithSpan;
} }
/// Sets the variable named [name] to [value], associated with /// 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 /// 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 /// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span. /// 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; var index = _variables.length - 1;
_lastVariableName = name; _lastVariableName = name;
_lastVariableIndex = index; _lastVariableIndex = index;
_variableIndices[name] = index; _variableIndices[name] = index;
_variables[index][name] = value; _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 /// Returns the value of the function named [name], optionally with the given
@ -789,7 +784,7 @@ class AsyncEnvironment {
_inSemiGlobalScope = semiGlobal; _inSemiGlobalScope = semiGlobal;
_variables.add({}); _variables.add({});
_variableNodes?.add({}); _variableNodes.add({});
_functions.add({}); _functions.add({});
_mixins.add({}); _mixins.add({});
_nestedForwardedModules?.add([]); _nestedForwardedModules?.add([]);
@ -818,12 +813,12 @@ class AsyncEnvironment {
var configuration = <String, ConfiguredValue>{}; var configuration = <String, ConfiguredValue>{};
for (var i = 0; i < _variables.length; i++) { for (var i = 0; i < _variables.length; i++) {
var values = _variables[i]; var values = _variables[i];
var nodes = _variableNodes?[i] ?? <String, AstNode>{}; var nodes = _variableNodes[i];
for (var entry in values.entries) { for (var entry in values.entries) {
// Implicit configurations are never invalid, making [configurationSpan] // Implicit configurations are never invalid, making [configurationSpan]
// unnecessary, so we pass null here to avoid having to compute it. // unnecessary, so we pass null here to avoid having to compute it.
configuration[entry.key] = configuration[entry.key] =
ConfiguredValue.implicit(entry.value, nodes[entry.key]); ConfiguredValue.implicit(entry.value, nodes[entry.key]!);
} }
} }
return Configuration.implicit(configuration); return Configuration.implicit(configuration);
@ -921,7 +916,7 @@ class _EnvironmentModule implements Module {
final List<Module> upstream; final List<Module> upstream;
final Map<String, Value> variables; final Map<String, Value> variables;
final Map<String, AstNode>? variableNodes; final Map<String, AstNode> variableNodes;
final Map<String, AsyncCallable> functions; final Map<String, AsyncCallable> functions;
final Map<String, AsyncCallable> mixins; final Map<String, AsyncCallable> mixins;
final ExtensionStore extensionStore; final ExtensionStore extensionStore;
@ -951,10 +946,8 @@ class _EnvironmentModule implements Module {
_makeModulesByVariable(forwarded), _makeModulesByVariable(forwarded),
_memberMap(environment._variables.first, _memberMap(environment._variables.first,
forwarded.map((module) => module.variables)), forwarded.map((module) => module.variables)),
environment._variableNodes.andThen((nodes) => _memberMap( _memberMap(environment._variableNodes.first,
nodes.first, forwarded.map((module) => module.variableNodes)),
// dart-lang/sdk#45348
forwarded!.map((module) => module.variableNodes!))),
_memberMap(environment._functions.first, _memberMap(environment._functions.first,
forwarded.map((module) => module.functions)), forwarded.map((module) => module.functions)),
_memberMap(environment._mixins.first, _memberMap(environment._mixins.first,
@ -1017,7 +1010,7 @@ class _EnvironmentModule implements Module {
required this.transitivelyContainsExtensions}) required this.transitivelyContainsExtensions})
: upstream = _environment._allModules; : upstream = _environment._allModules;
void setVariable(String name, Value value, AstNode? nodeWithSpan) { void setVariable(String name, Value value, AstNode nodeWithSpan) {
var module = _modulesByVariable[name]; var module = _modulesByVariable[name];
if (module != null) { if (module != null) {
module.setVariable(name, value, nodeWithSpan); module.setVariable(name, value, nodeWithSpan);
@ -1029,9 +1022,7 @@ class _EnvironmentModule implements Module {
} }
_environment._variables.first[name] = value; _environment._variables.first[name] = value;
if (nodeWithSpan != null) { _environment._variableNodes.first[name] = nodeWithSpan;
_environment._variableNodes?.first[name] = nodeWithSpan;
}
return; return;
} }

View File

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

View File

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

View File

@ -26,15 +26,13 @@ abstract class Module<T extends AsyncCallable> {
/// The nodes where each variable in [_variables] was defined. /// 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 /// 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 /// [AstNode.span] if the span isn't required, since some nodes need to do
/// real work to manufacture a source span. /// real work to manufacture a source span.
/// ///
/// Implementations must ensure that this has the same keys as [variables] if /// Implementations must ensure that this has the same keys as [variables] if
/// it's not `null`. /// it's not `null`.
Map<String, AstNode>? get variableNodes; Map<String, AstNode> get variableNodes;
/// The module's functions. /// 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 /// Throws a [SassScriptException] if this module doesn't define a variable
/// named [name]. /// 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 /// Returns an opaque object that will be equal to another
/// `variableIdentity()` return value for the same name in another module if /// `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( : UnmodifiableMapView(
{for (var callable in callables) callable.name: callable})); {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)) { if (!variables.containsKey(name)) {
throw SassScriptException("Undefined variable."); throw SassScriptException("Undefined variable.");
} }

View File

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

View File

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

View File

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

View File

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