Skip to content

Commit 5c03d64

Browse files
sophiebitskassens
authored andcommitted
Use .slice() for all substring-ing (#26677)
- substr is Annex B - substring silently flips its arguments if they're in the "wrong order", which is confusing - slice is better than sliced bread (no pun intended) and also it works the same way on Arrays so there's less to remember --- > I'd be down to just lint and enforce a single form just for the potential compression savings by using a repeated string. _Originally posted by @sebmarkbage in #26663 (comment)
1 parent 06fbf92 commit 5c03d64

File tree

35 files changed

+97
-90
lines changed

35 files changed

+97
-90
lines changed

.eslintrc.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,14 @@ module.exports = {
236236
'no-inner-declarations': [ERROR, 'functions'],
237237
'no-multi-spaces': ERROR,
238238
'no-restricted-globals': [ERROR].concat(restrictedGlobals),
239-
'no-restricted-syntax': [ERROR, 'WithStatement'],
239+
'no-restricted-syntax': [
240+
ERROR,
241+
'WithStatement',
242+
{
243+
selector: 'MemberExpression[property.name=/^(?:substring|substr)$/]',
244+
message: 'Prefer string.slice() over .substring() and .substr().',
245+
},
246+
],
240247
'no-shadow': ERROR,
241248
'no-unused-vars': [ERROR, {args: 'none'}],
242249
'no-use-before-define': OFF,

fixtures/concurrent/time-slicing/src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class App extends PureComponent {
2222
}
2323
const multiplier = input.length !== 0 ? input.length : 1;
2424
const complexity =
25-
(parseInt(window.location.search.substring(1), 10) / 100) * 25 || 25;
25+
(parseInt(window.location.search.slice(1), 10) / 100) * 25 || 25;
2626
const data = _.range(5).map(t =>
2727
_.range(complexity * multiplier).map((j, i) => {
2828
return {

fixtures/dom/src/react-loader.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import semver from 'semver';
1111

1212
function parseQuery(qstr) {
1313
var query = {};
14-
var a = qstr.substr(1).split('&');
14+
var a = qstr.slice(1).split('&');
1515

1616
for (var i = 0; i < a.length; i++) {
1717
var b = a[i].split('=');

packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const ReactHooksESLintRule = ReactHooksESLintPlugin.rules['exhaustive-deps'];
1818
function normalizeIndent(strings) {
1919
const codeLines = strings[0].split('\n');
2020
const leftPadding = codeLines[1].match(/\s+/)[0];
21-
return codeLines.map(line => line.substr(leftPadding.length)).join('\n');
21+
return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
2222
}
2323

2424
// ***************************************************

packages/eslint-plugin-react-hooks/__tests__/ESLintRulesOfHooks-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ESLintTester.setDefaultConfig({
2626
function normalizeIndent(strings) {
2727
const codeLines = strings[0].split('\n');
2828
const leftPadding = codeLines[1].match(/\s+/)[0];
29-
return codeLines.map(line => line.substr(leftPadding.length)).join('\n');
29+
return codeLines.map(line => line.slice(leftPadding.length)).join('\n');
3030
}
3131

3232
// ***************************************************

packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,7 @@ export default {
11031103
extraWarning =
11041104
` You can also do a functional update '${
11051105
setStateRecommendation.setter
1106-
}(${setStateRecommendation.missingDep.substring(
1106+
}(${setStateRecommendation.missingDep.slice(
11071107
0,
11081108
1,
11091109
)} => ...)' if you only need '${

packages/react-client/src/ReactFlightClient.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -515,33 +515,33 @@ export function parseModelString(
515515
switch (value[1]) {
516516
case '$': {
517517
// This was an escaped string value.
518-
return value.substring(1);
518+
return value.slice(1);
519519
}
520520
case 'L': {
521521
// Lazy node
522-
const id = parseInt(value.substring(2), 16);
522+
const id = parseInt(value.slice(2), 16);
523523
const chunk = getChunk(response, id);
524524
// We create a React.lazy wrapper around any lazy values.
525525
// When passed into React, we'll know how to suspend on this.
526526
return createLazyChunkWrapper(chunk);
527527
}
528528
case '@': {
529529
// Promise
530-
const id = parseInt(value.substring(2), 16);
530+
const id = parseInt(value.slice(2), 16);
531531
const chunk = getChunk(response, id);
532532
return chunk;
533533
}
534534
case 'S': {
535535
// Symbol
536-
return Symbol.for(value.substring(2));
536+
return Symbol.for(value.slice(2));
537537
}
538538
case 'P': {
539539
// Server Context Provider
540-
return getOrCreateServerContext(value.substring(2)).Provider;
540+
return getOrCreateServerContext(value.slice(2)).Provider;
541541
}
542542
case 'F': {
543543
// Server Reference
544-
const id = parseInt(value.substring(2), 16);
544+
const id = parseInt(value.slice(2), 16);
545545
const chunk = getChunk(response, id);
546546
switch (chunk.status) {
547547
case RESOLVED_MODEL:
@@ -582,15 +582,15 @@ export function parseModelString(
582582
}
583583
case 'D': {
584584
// Date
585-
return new Date(Date.parse(value.substring(2)));
585+
return new Date(Date.parse(value.slice(2)));
586586
}
587587
case 'n': {
588588
// BigInt
589-
return BigInt(value.substring(2));
589+
return BigInt(value.slice(2));
590590
}
591591
default: {
592592
// We assume that anything else is a reference ID.
593-
const id = parseInt(value.substring(1), 16);
593+
const id = parseInt(value.slice(1), 16);
594594
const chunk = getChunk(response, id);
595595
switch (chunk.status) {
596596
case RESOLVED_MODEL:

packages/react-client/src/ReactFlightClientStream.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,19 @@ function processFullRow(response: Response, row: string): void {
3535
return;
3636
}
3737
const colon = row.indexOf(':', 0);
38-
const id = parseInt(row.substring(0, colon), 16);
38+
const id = parseInt(row.slice(0, colon), 16);
3939
const tag = row[colon + 1];
4040
// When tags that are not text are added, check them here before
4141
// parsing the row as text.
4242
// switch (tag) {
4343
// }
4444
switch (tag) {
4545
case 'I': {
46-
resolveModule(response, id, row.substring(colon + 2));
46+
resolveModule(response, id, row.slice(colon + 2));
4747
return;
4848
}
4949
case 'E': {
50-
const errorInfo = JSON.parse(row.substring(colon + 2));
50+
const errorInfo = JSON.parse(row.slice(colon + 2));
5151
if (__DEV__) {
5252
resolveErrorDev(
5353
response,
@@ -63,7 +63,7 @@ function processFullRow(response: Response, row: string): void {
6363
}
6464
default: {
6565
// We assume anything else is JSON.
66-
resolveModel(response, id, row.substring(colon + 1));
66+
resolveModel(response, id, row.slice(colon + 1));
6767
return;
6868
}
6969
}
@@ -76,13 +76,13 @@ export function processStringChunk(
7676
): void {
7777
let linebreak = chunk.indexOf('\n', offset);
7878
while (linebreak > -1) {
79-
const fullrow = response._partialRow + chunk.substring(offset, linebreak);
79+
const fullrow = response._partialRow + chunk.slice(offset, linebreak);
8080
processFullRow(response, fullrow);
8181
response._partialRow = '';
8282
offset = linebreak + 1;
8383
linebreak = chunk.indexOf('\n', offset);
8484
}
85-
response._partialRow += chunk.substring(offset);
85+
response._partialRow += chunk.slice(offset);
8686
}
8787

8888
export function processBinaryChunk(

packages/react-debug-tools/src/ReactDebugHooks.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,10 @@ function parseCustomHookName(functionName: void | string): string {
513513
if (startIndex === -1) {
514514
startIndex = 0;
515515
}
516-
if (functionName.substr(startIndex, 3) === 'use') {
516+
if (functionName.slice(startIndex, startIndex + 3) === 'use') {
517517
startIndex += 3;
518518
}
519-
return functionName.substr(startIndex);
519+
return functionName.slice(startIndex);
520520
}
521521

522522
function buildTree(

packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,7 @@ describe('ReactHooksInspectionIntegration', () => {
869869
const Suspense = React.Suspense;
870870

871871
function Foo(props) {
872-
const [value] = React.useState(props.defaultValue.substr(0, 3));
872+
const [value] = React.useState(props.defaultValue.slice(0, 3));
873873
return <div>{value}</div>;
874874
}
875875
Foo.defaultProps = {

0 commit comments

Comments
 (0)