Page MenuHomePhabricator

Regex replacements are broken, insert duplicate replacements
Closed, ResolvedPublicBUG REPORT

Description

Steps to replicate the issue (include links if applicable):

  • Create test.less with the following contents:
@inputList: 'foo bar baz';
@transformed1: replace( @inputList, '(^| )([^ ]+)', '($2)', 'g');
@transformed2: replace( @inputList, '(^| )([^ ]+)', 'begin $2 end ', 'g');
@selector: e( replace( @inputList, '(^| )([^ ]+)', ':not( :lang( $2 ) )', 'g' ) );

.foo {
	content: '@{transformed1}';
	content: '@{transformed2}';

	&@{selector} {
		filter: invert(1);
	}
}
  • Run ./bin/lessc test.less

What happens?:
The following output is produced:

.foo {
  content: '((foo))(bar)(baz)';
  content: 'begin begin end begin foo end begin end end  begin bar end begin baz end ';
}
.foo:not( :lang( :not( ) ):not( :lang( :lang( ) ):not( :lang( foo ) ):not( :lang( ) ) ):not( :lang( ) ) ):not( :lang( bar ) ):not( :lang( baz ) ) {
  filter: invert(1);
}

Note that the replacement around "foo" introduces excessive output in all three cases, but the nature of the excessive output differs: the first line starts with ((foo)), but the second line starts with begin begin end begin foo end begin end end which is equivalent to (()(foo)())

What should have happened instead?:
Version 4.1.1 produces the correct output, which is:

.foo {
  content: '(foo)(bar)(baz)';
  content: 'begin foo end begin bar end begin baz end ';
}
.foo:not( :lang( foo ) ):not( :lang( bar ) ):not( :lang( baz ) ) {
  filter: invert(1);
}

Software version: Less.php 4.2.1 has the bug, but 4.1.1 behaves correctly

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript

This breaks Codex's CSS-only icons mixin. The following code:

@import 'mediawiki.skin.variables.less'; /* Outside MediaWiki, import codex-icons/codex-icon-paths.less and codex/mixins/css-icon.less */

.foo {
    .cdx-mixin-css-icon( @cdx-icon-help );
}

produces the following output with Less 4.1.1 (partial output with unaffected selectors omitted):

.fooo[dir='rtl']:not(:lang(he)):not(:lang(yi)),
html[dir='rtl'] .fooo:not([dir='ltr']):not(:lang(he)):not(:lang(yi)) {
    transform: scaleX(-1)
}

but the following broken output with Less.php 4.2.1:

.foo[dir='rtl']:not(:lang(:not()):not(:lang(:lang()):not(:lang(he)):not(:lang())):not(:lang())):not(:lang(yi)),
html[dir='rtl'] .foo:not([dir='ltr']):not(:lang(:not()):not(:lang(:lang()):not(:lang(he)):not(:lang())):not(:lang())):not(:lang(yi)) {
    transform: scaleX(-1)
}

Change 1007019 had a related patch set uploaded (by Catrope; author: Catrope):

[mediawiki/libs/less.php@master] Fix multiple replacements

https://gerrit.wikimedia.org/r/1007019

Change 1007019 merged by jenkins-bot:

[mediawiki/libs/less.php@master] Less_Functions: Fix `replace()` when passed multiple replacements

https://gerrit.wikimedia.org/r/1007019