Skip to content

Commit 731bd44

Browse files
committed
Don't produce name conflict errors due to imports
Closes #888
1 parent aa76c8a commit 731bd44

File tree

6 files changed

+293
-70
lines changed

6 files changed

+293
-70
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
* **Potentially breaking bug fix:** Members loaded through a nested `@import`
44
are no longer ever accessible outside that nested context.
55

6+
* Don't throw an error when importing two modules that both forward members with
7+
the same name. The latter name now takes precedence over the former, as per
8+
the specification.
9+
610
## 1.23.7
711

812
* No user-visible changes.

lib/src/async_environment.dart

Lines changed: 97 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import 'exception.dart';
1717
import 'extend/extender.dart';
1818
import 'module.dart';
1919
import 'module/forwarded_view.dart';
20+
import 'module/shadowed_view.dart';
2021
import 'util/merged_map_view.dart';
2122
import 'util/public_member_map_view.dart';
2223
import 'utils.dart';
@@ -42,6 +43,13 @@ class AsyncEnvironment {
4243
/// This is `null` if there are no forwarded modules.
4344
List<Module> _forwardedModules;
4445

46+
/// Modules forwarded by nested imports at each lexical scope level *beneath
47+
/// the global scope*.
48+
///
49+
/// This is `null` until it's needed, since most environments won't ever use
50+
/// this.
51+
List<List<Module>> _nestedForwardedModules;
52+
4553
/// Modules from [_modules], [_globalModules], and [_forwardedModules], in the
4654
/// order in which they were `@use`d.
4755
final List<Module> _allModules;
@@ -128,6 +136,7 @@ class AsyncEnvironment {
128136
: _modules = {},
129137
_globalModules = null,
130138
_forwardedModules = null,
139+
_nestedForwardedModules = null,
131140
_allModules = [],
132141
_variables = [{}],
133142
_variableNodes = sourceMap ? [{}] : null,
@@ -141,6 +150,7 @@ class AsyncEnvironment {
141150
this._modules,
142151
this._globalModules,
143152
this._forwardedModules,
153+
this._nestedForwardedModules,
144154
this._allModules,
145155
this._variables,
146156
this._variableNodes,
@@ -164,6 +174,7 @@ class AsyncEnvironment {
164174
_modules,
165175
_globalModules,
166176
_forwardedModules,
177+
_nestedForwardedModules,
167178
_allModules,
168179
_variables.toList(),
169180
_variableNodes?.toList(),
@@ -179,6 +190,7 @@ class AsyncEnvironment {
179190
{},
180191
null,
181192
null,
193+
null,
182194
[],
183195
_variables.toList(),
184196
_variableNodes?.toList(),
@@ -270,30 +282,63 @@ class AsyncEnvironment {
270282
/// This is called when [module] is `@import`ed.
271283
void importForwards(Module module) {
272284
if (module is _EnvironmentModule) {
273-
for (var forwarded
274-
in module._environment._forwardedModules ?? const <Module>[]) {
275-
_globalModules ??= {};
276-
_globalModules.add(forwarded);
277-
278-
// Remove existing definitions that the forwarded members are now
279-
// shadowing.
280-
for (var variable in forwarded.variables.keys) {
281-
var index =
282-
_variableIndices.remove(variable) ?? _variableIndex(variable);
283-
if (index != null) {
284-
_variables[index].remove(variable);
285-
if (_variableNodes != null) _variableNodes[index].remove(variable);
285+
var forwarded = module._environment._forwardedModules;
286+
if (forwarded == null) return;
287+
288+
_globalModules ??= {};
289+
_forwardedModules ??= [];
290+
291+
var forwardedVariableNames =
292+
forwarded.expand((module) => module.variables.keys).toSet();
293+
var forwardedFunctionNames =
294+
forwarded.expand((module) => module.functions.keys).toSet();
295+
var forwardedMixinNames =
296+
forwarded.expand((module) => module.mixins.keys).toSet();
297+
298+
if (atRoot) {
299+
// Hide members from modules that have already been imported or
300+
// forwarded that would otherwise conflict with the @imported members.
301+
for (var module in _globalModules.toList()) {
302+
var shadowed = ShadowedModuleView.ifNecessary(module,
303+
variables: forwardedVariableNames,
304+
mixins: forwardedMixinNames,
305+
functions: forwardedFunctionNames);
306+
if (shadowed != null) {
307+
_globalModules.remove(module);
308+
_globalModules.add(shadowed);
286309
}
287310
}
288-
for (var function in forwarded.functions.keys) {
289-
var index =
290-
_functionIndices.remove(function) ?? _functionIndex(function);
291-
if (index != null) _functions[index].remove(function);
292-
}
293-
for (var mixin in forwarded.mixins.keys) {
294-
var index = _mixinIndices.remove(mixin) ?? _mixinIndex(mixin);
295-
if (index != null) _mixins[index].remove(mixin);
311+
for (var i = 0; i < _forwardedModules.length; i++) {
312+
var module = _forwardedModules[i];
313+
var shadowed = ShadowedModuleView.ifNecessary(module,
314+
variables: forwardedVariableNames,
315+
mixins: forwardedMixinNames,
316+
functions: forwardedFunctionNames);
317+
if (shadowed != null) _forwardedModules[i] = shadowed;
296318
}
319+
320+
_globalModules.addAll(forwarded);
321+
_forwardedModules.addAll(forwarded);
322+
} else {
323+
_nestedForwardedModules ??=
324+
List.generate(_variables.length - 1, (_) => []);
325+
_nestedForwardedModules.last.addAll(forwarded);
326+
}
327+
328+
// Remove existing member definitions that are now shadowed by the
329+
// forwarded modules.
330+
for (var variable in forwardedVariableNames) {
331+
_variableIndices.remove(variable);
332+
_variables.last.remove(variable);
333+
if (_variableNodes != null) _variableNodes.last.remove(variable);
334+
}
335+
for (var function in forwardedFunctionNames) {
336+
_functionIndices.remove(function);
337+
_functions.last.remove(function);
338+
}
339+
for (var mixin in forwardedMixinNames) {
340+
_mixinIndices.remove(mixin);
341+
_mixins.last.remove(mixin);
297342
}
298343
}
299344
}
@@ -467,6 +512,19 @@ class AsyncEnvironment {
467512
return;
468513
}
469514

515+
if (_nestedForwardedModules != null &&
516+
!_variableIndices.containsKey(name) &&
517+
_variableIndex(name) == null) {
518+
for (var modules in _nestedForwardedModules.reversed) {
519+
for (var module in modules.reversed) {
520+
if (module.variables.containsKey(name)) {
521+
module.setVariable(name, value, nodeWithSpan);
522+
return;
523+
}
524+
}
525+
}
526+
}
527+
470528
var index = _lastVariableName == name
471529
? _lastVariableIndex
472530
: _variableIndices.putIfAbsent(
@@ -652,6 +710,7 @@ class AsyncEnvironment {
652710
_variableNodes?.add({});
653711
_functions.add({});
654712
_mixins.add({});
713+
_nestedForwardedModules?.add([]);
655714
try {
656715
return await callback();
657716
} finally {
@@ -667,14 +726,18 @@ class AsyncEnvironment {
667726
for (var name in _mixins.removeLast().keys) {
668727
_mixinIndices.remove(name);
669728
}
729+
_nestedForwardedModules?.removeLast();
670730
}
671731
}
672732

673733
/// Returns a module that represents the top-level members defined in [this],
674734
/// that contains [css] as its CSS tree, which can be extended using
675735
/// [extender].
676-
Module toModule(CssStylesheet css, Extender extender) =>
677-
_EnvironmentModule(this, css, extender, forwarded: _forwardedModules);
736+
Module toModule(CssStylesheet css, Extender extender) {
737+
assert(atRoot);
738+
return _EnvironmentModule(this, css, extender,
739+
forwarded: _forwardedModules);
740+
}
678741

679742
/// Returns the module with the given [namespace], or throws a
680743
/// [SassScriptException] if none exists.
@@ -687,14 +750,24 @@ class AsyncEnvironment {
687750
}
688751

689752
/// Returns the result of [callback] if it returns non-`null` for exactly one
690-
/// module in [_globalModules].
753+
/// module in [_globalModules] *or* for any module in
754+
/// [_nestedForwardedModules].
691755
///
692756
/// Returns `null` if [callback] returns `null` for all modules. Throws an
693757
/// error if [callback] returns non-`null` for more than one module.
694758
///
695759
/// The [type] should be the singular name of the value type being returned.
696760
/// It's used to format an appropriate error message.
697761
T _fromOneModule<T>(String type, T callback(Module module)) {
762+
if (_nestedForwardedModules != null) {
763+
for (var modules in _nestedForwardedModules.reversed) {
764+
for (var module in modules.reversed) {
765+
var value = callback(module);
766+
if (value != null) return value;
767+
}
768+
}
769+
}
770+
698771
if (_globalModules == null) return null;
699772

700773
T value;

0 commit comments

Comments
 (0)