SomMeri / less4j

Less language is an extension of css and less4j compiles it into regular css. Less adds several dynamic features into css: variables, expressions, nested rules, and so on. Less was designed to be compatible with css and any correct css file is also correct less file.
145 stars 47 forks source link

Regression between 1.8.4 and 1.16: parametric namespaces (?) are not supported #333

Closed yrodiere closed 8 years ago

yrodiere commented 8 years ago

Hi,

It seems the following snippet (extracted from Bootstrap 2.3.2) won't compile with Less4J 1.16. It used to compile just fine up to Less4J 1.8.4 (at least).

// Extracted from Bootstrap 2.3.2 variables.less, line 274
@gridColumns:             12;
@gridColumnWidth:         60px;
@gridGutterWidth:         20px;

// Extracted from Bootstrap 2.3.2 mixins.less, line 593
#grid {
    .core (@gridColumnWidth, @gridGutterWidth) {
        .span(@columns) {
            width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14;
        }
    }
}

// Extracted from Bootstrap 2.3.2 navbar.less, line 196
.navbar-static-top .container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
    #grid > .core > .span(@gridColumns);
}

Expected output:

.navbar-static-top .container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
  width: 926px;
}

... but errors are reported instead (this stack trace was generated using your test framework):

org.junit.ComparisonFailure: src/test/resources/compile-basic-features/namespaces/namespace-bootstrap2.less expected:<[]> but was:<[Errors produced by compilation of testCase
ERROR 16:2 The namespace "#grid > .core > .span(...)" was not declared.
 15: .navbar-fixed-bottom .container {
 16:    #grid > .core > .span(@gridColumns);
 17: }

ERROR 16:18 Could not find mixin named ".span".
 15: .navbar-fixed-bottom .container {
 16:    #grid > .core > .span(@gridColumns);
 17: }]>
    at org.junit.Assert.assertEquals(Assert.java:115)
    at com.github.sommeri.less4j.AbstractFileBasedTest.assertCorrectErrors(AbstractFileBasedTest.java:94)
    at com.github.sommeri.less4j.AbstractFileBasedTest.compileAndCompare(AbstractFileBasedTest.java:64)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.junit.runners.Suite.runChild(Suite.java:127)
    at org.junit.runners.Suite.runChild(Suite.java:26)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

This code will compile just fine, so I guess it's somewhat related to "parametric" namespaces:

@gridColumns:             12;
@gridColumnWidth:         60px;
@gridGutterWidth:         20px;

#grid {
    .core {
        .span(@columns) {
            width: ((@gridColumnWidth) * @columns) + (@gridGutterWidth * (@columns - 1)) - 14;
        }
    }
}

.navbar-static-top .container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
    #grid > .core > .span(@gridColumns);
}

I'm going to submit a pull request that includes the test, so that your can witness it first-hand.

SomMeri commented 8 years ago

This seems to be bug in bootstrap, the same code fails in latest less.js too.

There was a bug in both less.js and less4j that allowed you to use mixins with parameters as namespaces, but it got fixed in both. Unfortunately, it looks like bootstrap was using that bug. I cant fix this here, because less4j is supposed to behave as closely as possible to latest less.js.