Check extend usage after the fact.

This means that extensions that match but fail to unify are valid, but
we think that's okay; see sass/sass#2250.
This commit is contained in:
Natalie Weizenbaum 2017-02-24 18:06:36 -08:00
parent 9ded7e9e68
commit 9cfbf79f47
3 changed files with 15 additions and 16 deletions

View File

@ -180,6 +180,9 @@ Sass to update the reference behavior.
14. Universal selector unification is symmetrical. See [issue 2247][].
15. `@extend` doesn't produce an error if it matches but fails to unify. See
[issue 2250][].
[issue 1599]: https://github.com/sass/sass/issues/1599
[issue 1126]: https://github.com/sass/sass/issues/1126
[issue 2120]: https://github.com/sass/sass/issues/2120
@ -194,5 +197,6 @@ Sass to update the reference behavior.
[issue 2245]: https://github.com/sass/sass/issues/2245
[issue 303]: https://github.com/sass/sass/issues/303
[issue 2247]: https://github.com/sass/sass/issues/2247
[issue 2250]: https://github.com/sass/sass/issues/2250
Disclaimer: this is not an official Google product.

View File

@ -173,14 +173,12 @@ class Extender {
if (rules == null) return;
if (newExtenders == null) return;
for (var rule in rules) {
try {
var extended = _extendList(
rule.selector.value, {target: newExtenders}, _mediaContexts[rule]);
// A selector can't extend its own rule.
if (identical(rule.selector.value, extender)) continue;
// A selector can't extend its own rule. We still have to run the extend
// to set [state.isUsed] appropriately, though.
if (identical(rule.selector.value, extender)) continue;
rule.selector.value = extended;
try {
rule.selector.value = _extendList(
rule.selector.value, {target: newExtenders}, _mediaContexts[rule]);
} on SassException catch (error) {
throw new SassException(
"From ${rule.selector.span.message('')}\n"
@ -195,13 +193,15 @@ class Extender {
/// Throws a [SassException] if any (non-optional) extensions failed to match
/// any selectors.
void finalize() {
_extensions.forEach((target, sources) {
sources.forEach((_, state) {
if (state.isOptional || state.isUsed) return;
_extensions.forEach((target, extensions) {
if (_selectors.containsKey(target)) return;
extensions.forEach((_, extension) {
if (extension.isOptional) return;
throw new SassException(
'The target selector was not found.\n'
'Use "@extend $target !optional" to avoid this error.',
state.span);
extension.span);
});
});
}
@ -343,7 +343,6 @@ class Extender {
// need any unification.
if (options.length == 1) {
return options.first.map((state) {
state.isUsed = true;
state.assertCompatibleMediaContext(mediaQueryContext);
return state.extender;
}).toList();
@ -416,7 +415,6 @@ class Extender {
var specificity = _sourceSpecificityFor(compound);
for (var state in path) {
state.assertCompatibleMediaContext(mediaQueryContext);
state.isUsed = true;
lineBreak = lineBreak || state.extender.lineBreak;
specificity = math.max(specificity, state.specificity);
}

View File

@ -29,9 +29,6 @@ class Extension {
/// originally in the document, rather than one defined with `@extend`.
final bool isOriginal;
/// Whether this extension matched a selector.
var isUsed = false;
/// The media query context to which this extend is restricted, or `null` if
/// it can apply within any context.
List<CssMediaQuery> get mediaContext => _mediaContext;