Repository: lesshint/lesshint (js)

Issue:
stringQuotes doesn't check at-rules
It looks like `stringQuotes` doesn't check at-rules, so with this config:
```json
{
    "stringQuotes": {
        "enabled": true,
        "style": "single"
    }
}
```

The following won't be reported:
```less
@import "foo.less";
```

I think this holds true for other at-rules too, for example `@charset`.

Pull request description:
Ensure that stringQuotes checks at-rules
Closes #224.

Patch:
diff --git a/lib/linters/space_between_parens.js b/lib/linters/space_between_parens.js
index dfb5167..7dd5591 100644
--- a/lib/linters/space_between_parens.js
+++ b/lib/linters/space_between_parens.js
@@ -55,7 +55,7 @@ module.exports = {
                     if (child.value === '(' && (next.raws && next.raws.before)) {
                         results.push({
                             column: column + child.source.start.column,
-                            line: child.source.start.line,
+                            line: node.source.start.line,
                             message: util.format(self.message.opening, ' not', 'followed', 'any')
                         });
                     }
@@ -63,7 +63,7 @@ module.exports = {
                     if (child.value === ')' && (prev.raws && prev.raws.after)) {
                         results.push({
                             column: column + prev.source.end.column,
-                            line: prev.source.end.line,
+                            line: node.source.start.line,
                             message: util.format(self.message.closing, ' not', 'preceded', 'any')
                         });
                     }
@@ -73,7 +73,7 @@ module.exports = {
                     if (child.value === '(' && (next.raws && next.raws.before !== ' ')) {
                         results.push({
                             column: column + child.source.start.column,
-                            line: child.source.start.line,
+                            line: node.source.start.line,
                             message: util.format(self.message.opening, '', 'followed', 'one')
                         });
                     }
@@ -81,7 +81,7 @@ module.exports = {
                     if (child.value === ')' && (prev.raws && prev.raws.after !== ' ')) {
                         results.push({
                             column: column + prev.source.end.column,
-                            line: prev.source.end.line,
+                            line: node.source.start.line,
                             message: util.format(self.message.closing, '', 'preceded', 'one')
                         });
                     }
diff --git a/lib/linters/string_quotes.js b/lib/linters/string_quotes.js
index 1a10182..1d15a19 100644
--- a/lib/linters/string_quotes.js
+++ b/lib/linters/string_quotes.js
@@ -6,12 +6,13 @@ var util = require('util');
 
 module.exports = {
     name: 'stringQuotes',
-    nodeTypes: ['decl', 'rule'],
+    nodeTypes: ['atrule', 'decl', 'rule'],
     message: 'Strings should use %s quotes.',
 
     lint: function stringQuotesLinter (config, node) {
         var results = [];
         var self = this;
+        var column;
         var ast;
         var quotes = {
             double: /"/,
@@ -22,11 +23,29 @@ module.exports = {
             throw new Error('Invalid setting value for stringQuotes: ' + config.style);
         }
 
-        if (node.type === 'decl') {
+        if (node.type === 'atrule') {
+            /**
+             * Hopefully node.ruleWithoutBody will be implemented on AtRule nodes
+             * in the future: https://github.com/webschik/postcss-less/issues/55
+             */
+            if (node.nodes) {
+                return;
+            }
+
+            if (!quotes[config.style].test(node.params)) {
+                column = (node.raws.afterName ? node.raws.afterName.length : 0) +
+                         node.source.start.column +
+                         node.name.length + 1;
+
+                results.push({
+                    column: column,
+                    line: node.source.start.line,
+                    message: util.format(self.message, config.style)
+                });
+            }
+        } else if (node.type === 'decl') {
             ast = valuesParser(node.value).parse();
             ast.first.walk(function (decl) {
-                var column;
-
                 if (decl.type !== 'string') {
                     return;
                 }
@@ -48,8 +67,6 @@ module.exports = {
         } else {
             selectorParser(function (selectors) {
                 selectors.walkAttributes(function (selector) {
-                    var column;
-
                     if (selector.quoted && !quotes[config.style].test(selector.value)) {
                         column = node.source.start.column + selector.source.start.column
                                  + selector.attribute.length + selector.operator.length;


Test patch:
diff --git a/test/specs/linters/string_quotes.js b/test/specs/linters/string_quotes.js
index 33a5cf2..cf3233e 100644
--- a/test/specs/linters/string_quotes.js
+++ b/test/specs/linters/string_quotes.js
@@ -249,6 +249,83 @@ describe('lesshint', function () {
             });
         });
 
+        it('should not allow double quotes in @-rules when "style" is "single"', function () {
+            var source = '@import "foo.less";';
+            var expected = [{
+                column: 9,
+                line: 1,
+                message: 'Strings should use single quotes.'
+            }];
+
+            var options = {
+                style: 'single'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
+        it('should not allow single quotes in @-rules when "style" is "double"', function () {
+            var source = "@import 'foo.less';";
+            var expected = [{
+                column: 9,
+                line: 1,
+                message: 'Strings should use double quotes.'
+            }];
+
+            var options = {
+                style: 'double'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.equal(expected);
+            });
+        });
+
+        it('should allow single quotes in @-rules when "style" is "single"', function () {
+            var source = "@import 'foo.less';";
+            var options = {
+                style: 'single'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.undefined;
+            });
+        });
+
+        it('should allow double quotes in @-rules when "style" is "double"', function () {
+            var source = '@import "foo.less";';
+            var options = {
+                style: 'double'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.undefined;
+            });
+        });
+
+        it('should not check irrelevant @-rules', function () {
+            var source = '@media (screen) {}';
+            var options = {
+                style: 'double'
+            };
+
+            return spec.parse(source, function (ast) {
+                var result = spec.linter.lint(options, ast.root.first);
+
+                expect(result).to.deep.undefined;
+            });
+        });
+
         it('should throw on invalid "style" value', function () {
             var source = ".foo { content: 'Hello world' }";
             var options = {