Skip to content

Commit 056b62b

Browse files
Planeshifterkgryte
andauthored
feat: add ESLint rule disallowing string concatenation in benchmark descriptions
PR-URL: #9031 Closes: stdlib-js/metr-issue-tracker#129 Co-authored-by: Athan Reines <kgryte@gmail.com> Reviewed-by: Athan Reines <kgryte@gmail.com>
1 parent ec526fd commit 056b62b

File tree

10 files changed

+611
-0
lines changed

10 files changed

+611
-0
lines changed

etc/eslint/.eslintrc.benchmarks.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ eslint.rules[ 'stdlib/jsdoc-doctest' ] = 'off';
126126
*/
127127
eslint.rules[ 'stdlib/no-unnecessary-nested-functions' ] = 'off';
128128

129+
/**
130+
* Warn when using string concatenation in benchmark descriptions.
131+
*
132+
* @private
133+
*/
134+
eslint.rules[ 'stdlib/no-bench-string-concat' ] = 'warn';
135+
129136

130137
// EXPORTS //
131138

lib/node_modules/@stdlib/_tools/eslint/rules/lib/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,15 @@ setReadOnly( rules, 'new-cap-error', require( '@stdlib/_tools/eslint/rules/new-c
846846
*/
847847
setReadOnly( rules, 'new-cap-regexp', require( '@stdlib/_tools/eslint/rules/new-cap-regexp' ) );
848848

849+
/**
850+
* @name no-bench-string-concat
851+
* @memberof rules
852+
* @readonly
853+
* @type {Function}
854+
* @see {@link module:@stdlib/_tools/eslint/rules/no-bench-string-concat}
855+
*/
856+
setReadOnly( rules, 'no-bench-string-concat', require( '@stdlib/_tools/eslint/rules/no-bench-string-concat' ) );
857+
849858
/**
850859
* @name no-builtin-big-int
851860
* @memberof rules
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<!--
2+
3+
@license Apache-2.0
4+
5+
Copyright (c) 2025 The Stdlib Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
19+
-->
20+
21+
# no-bench-string-concat
22+
23+
> [ESLint rule][eslint-rules] enforcing that `@stdlib/string/format` is used instead of string concatenation in benchmark descriptions.
24+
25+
<section class="intro">
26+
27+
</section>
28+
29+
<!-- /.intro -->
30+
31+
<section class="usage">
32+
33+
## Usage
34+
35+
```javascript
36+
var rule = require( '@stdlib/_tools/eslint/rules/no-bench-string-concat' );
37+
```
38+
39+
#### rule
40+
41+
[ESLint rule][eslint-rules] enforcing that `@stdlib/string/format` is used instead of string concatenation in benchmark descriptions.
42+
43+
**Bad**:
44+
45+
<!-- run-disable -->
46+
47+
<!-- eslint-disable stdlib/no-bench-string-concat, no-restricted-syntax -->
48+
49+
```javascript
50+
bench( pkg+':len='+len, function benchmark( b ) {
51+
// ...
52+
});
53+
```
54+
55+
**Good**:
56+
57+
<!-- run-disable -->
58+
59+
<!-- eslint-disable no-restricted-syntax -->
60+
61+
```javascript
62+
var format = require( '@stdlib/string/format' );
63+
64+
bench( format( '%s:len=%d', pkg, len ), function benchmark( b ) {
65+
// ...
66+
});
67+
```
68+
69+
</section>
70+
71+
<!-- /.usage -->
72+
73+
<section class="examples">
74+
75+
## Examples
76+
77+
<!-- eslint no-undef: "error" -->
78+
79+
```javascript
80+
var Linter = require( 'eslint' ).Linter;
81+
var rule = require( '@stdlib/_tools/eslint/rules/no-bench-string-concat' );
82+
83+
var linter = new Linter();
84+
linter.defineRule( 'no-bench-string-concat', rule );
85+
86+
var code = 'bench( pkg+\':len=\'+len, f );';
87+
var result = linter.verify( code, {
88+
'rules': {
89+
'no-bench-string-concat': 'error'
90+
}
91+
});
92+
/* returns
93+
[
94+
{
95+
'ruleId': 'no-bench-string-concat',
96+
'severity': 2,
97+
'message': 'Use `@stdlib/string/format` instead of string concatenation for benchmark descriptions.',
98+
'line': 1,
99+
'column': 8,
100+
'nodeType': 'BinaryExpression',
101+
'endLine': 1,
102+
'endColumn': 23
103+
}
104+
]
105+
*/
106+
```
107+
108+
</section>
109+
110+
<!-- /.examples -->
111+
112+
<!-- Section for related `stdlib` packages. Do not manually edit this section, as it is automatically populated. -->
113+
114+
<section class="related">
115+
116+
</section>
117+
118+
<!-- /.related -->
119+
120+
<!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
121+
122+
<section class="links">
123+
124+
[eslint-rules]: https://eslint.org/docs/developer-guide/working-with-rules
125+
126+
</section>
127+
128+
<!-- /.links -->
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
var Linter = require( 'eslint' ).Linter;
22+
var rule = require( './../lib' );
23+
24+
var linter = new Linter();
25+
linter.defineRule( 'no-bench-string-concat', rule );
26+
27+
var code = 'bench( pkg+\':len=\'+len, f );';
28+
var result = linter.verify( code, {
29+
'rules': {
30+
'no-bench-string-concat': 'error'
31+
}
32+
});
33+
console.log( result );
34+
/* =>
35+
[
36+
{
37+
'ruleId': 'no-bench-string-concat',
38+
'severity': 2,
39+
'message': 'Use `@stdlib/string/format` instead of string concatenation for benchmark descriptions.',
40+
'line': 1,
41+
'column': 8,
42+
'nodeType': 'BinaryExpression',
43+
'endLine': 1,
44+
'endColumn': 23
45+
}
46+
]
47+
*/
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
/**
22+
* ESLint rule enforcing that string concatenation is not used in benchmark descriptions.
23+
*
24+
* @module @stdlib/_tools/eslint/rules/no-bench-string-concat
25+
*
26+
* @example
27+
* var rule = require( '@stdlib/_tools/eslint/rules/no-bench-string-concat' );
28+
*
29+
* console.log( rule );
30+
*/
31+
32+
// MODULES //
33+
34+
var main = require( './main.js' );
35+
36+
37+
// EXPORTS //
38+
39+
module.exports = main;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// VARIABLES //
22+
23+
var rule;
24+
25+
26+
// FUNCTIONS //
27+
28+
/**
29+
* Checks whether a node is a BinaryExpression with a '+' operator.
30+
*
31+
* @private
32+
* @param {ASTNode} node - AST node
33+
* @returns {boolean} boolean indicating whether the node is a string concatenation
34+
*/
35+
function isStringConcatenation( node ) {
36+
if ( node.type !== 'BinaryExpression' ) {
37+
return false;
38+
}
39+
if ( node.operator !== '+' ) {
40+
return false;
41+
}
42+
return true;
43+
}
44+
45+
/**
46+
* Rule for validating that `@stdlib/string/format` is used instead of string concatenation in benchmark descriptions.
47+
*
48+
* @param {Object} context - ESLint context
49+
* @returns {Object} validators
50+
*/
51+
function main( context ) {
52+
/**
53+
* Reports the error message.
54+
*
55+
* @private
56+
* @param {ASTNode} node - node to report
57+
*/
58+
function report( node ) {
59+
context.report({
60+
'node': node,
61+
'message': 'Use `@stdlib/string/format` instead of string concatenation for benchmark descriptions.'
62+
});
63+
}
64+
65+
/**
66+
* Checks whether a `bench()` call uses string concatenation in its first argument.
67+
*
68+
* @private
69+
* @param {ASTNode} node - CallExpression node to examine
70+
*/
71+
function validate( node ) {
72+
var firstArg;
73+
var callee;
74+
75+
callee = node.callee;
76+
77+
// Check if this is a call to `bench`:
78+
if ( callee.type !== 'Identifier' || callee.name !== 'bench' ) {
79+
return;
80+
}
81+
82+
// Check if there is at least one argument:
83+
if ( node.arguments.length === 0 ) {
84+
return;
85+
}
86+
87+
firstArg = node.arguments[ 0 ];
88+
89+
// Check if the first argument is string concatenation:
90+
if ( isStringConcatenation( firstArg ) ) {
91+
report( firstArg );
92+
}
93+
}
94+
95+
return {
96+
'CallExpression': validate
97+
};
98+
}
99+
100+
101+
// MAIN //
102+
103+
rule = {
104+
'meta': {
105+
'docs': {
106+
'description': 'enforce that `@stdlib/string/format` is used instead of string concatenation in benchmark descriptions'
107+
},
108+
'schema': []
109+
},
110+
'create': main
111+
};
112+
113+
114+
// EXPORTS //
115+
116+
module.exports = rule;

0 commit comments

Comments
 (0)