8000 feat: Add suggestions to no-console (#17680) · eslint/eslint@1452dc9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1452dc9

Browse files
authored
feat: Add suggestions to no-console (#17680)
* feat: Add suggestions to no-console Fixes #17493 * Fix method of when to show safe suggestions and fix tests. * improved formatting of tests with suggestions by breaking them into multiple lines * Updated function name to canProvideSuggestions. Added better description for the function. * Fixed code that fails when AST is not deep enough. * Added suggestions:null for test cases that will not provide a suggestion. * test to make sure console statement with semicolon is removed via suggestion * dont provide suggestions if removing console.log() will lead to ASI breaking * missing period * renamed regexps variable names for better understanding * updated passing in expressionstatement node instead of memberexpression node to maybeAsiHazard * ++ or -- in the token before is not always safe.
1 parent 6fb8805 commit 1452dc9

File tree

2 files changed

+451
-15
lines changed

2 files changed

+451
-15
lines changed

lib/rules/no-console.js

Lines changed: 74 additions & 2 deletions
< AA31 /tr>
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ module.exports = {
4343
}
4444
],
4545

46+
hasSuggestions: true,
47+
4648
messages: {
47-
unexpected: "Unexpected console statement."
49+
unexpected: "Unexpected console statement.",
50+
removeConsole: "Remove the console.{{ propertyName }}()."
4851
}
4952
},
5053

@@ -94,6 +97,64 @@ module.exports = {
9497
);
9598
}
9699

100+
/**
101+
* Checks if removing the ExpressionStatement node will cause ASI to
102+
* break.
103+
* eg.
104+
* foo()
105+
* console.log();
106+
* [1, 2, 3].forEach(a => doSomething(a))
107+
*
108+
* Removing the console.log(); statement should leave two statements, but
109+
* here the two statements will become one because [ causes continuation after
110+
* foo().
111+
* @param {ASTNode} node The ExpressionStatement node to check.
112+
* @returns {boolean} `true` if ASI will break after removing the ExpressionStatement
113+
* node.
114+
*/
115+
function maybeAsiHazard(node) {
116+
const SAFE_TOKENS_BEFORE = /^[:;{]$/u; // One of :;{
117+
const UNSAFE_CHARS_AFTER = /^[-[(/+`]/u; // One of [(/+-`
118+
119+
const tokenBefore = sourceCode.getTokenBefore(node);
120+
const tokenAfter = sourceCode.getTokenAfter(node);
121+
122+
return (
123+
Boolean(tokenAfter) &&
124+
UNSAFE_CHARS_AFTER.test(tokenAfter.value) &&
125+
tokenAfter.value !== "++" &&
126+
tokenAfter.value !== "--" &&
127+
Boolean(tokenBefore) &&
128+
!SAFE_TOKENS_BEFORE.test(tokenBefore.value)
129+
);
130+
}
131+
132+
/**
133+
* Checks if the MemberExpression node's parent.parent.parent is a
134+
* Program, BlockStatement, StaticBlock, or SwitchCase node. This check
135+
* is necessary to avoid providing a suggestion that might cause a syntax error.
136+
*
137+
* eg. if (a) console.log(b), removing console.log() here will lead to a
138+
* syntax error.
139+
* if (a) { console.log(b) }, removing console.log() here is acceptable.
140+
*
141+
* Additionally, it checks if the callee of the CallExpression node is
142+
* the node itself.
143+
*
144+
* eg. foo(console.log), cannot provide a suggestion here.
145+
* @param {ASTNode} node The MemberExpression node to check.
146+
* @returns {boolean} `true` if a suggestion can be provided for a node.
147+
*/
148+
function canProvideSuggestions(node) {
149+
return (
150+
node.parent.type === "CallExpression" &&
151+
node.parent.callee === node &&
152+
node.parent.parent.type === "ExpressionStatement" &&
153+
astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type) &&
154+
!maybeAsiHazard(node.parent.parent)
155+
);
156+
}
157+
97158
/**
98159
* Reports the given reference as a violation.
99160
* @param {eslint-scope.Reference} reference The reference to report.
@@ -102,10 +163,21 @@ module.exports = {
102163
function report(reference) {
103164
const node = reference.identifier.parent;
104165

166+
const propertyName = astUtils.getStaticPropertyName(node);
167+
105168
context.report({
106169
node,
107170
loc: node.loc,
108-
messageId: "unexpected"
171+
messageId: "unexpected",
172+
suggest: canProvideSuggestions(node)
173+
? [{
174+
messageId: "removeConsole",
175+
data: { propertyName },
176+
fix(fixer) {
177+
return fixer.remove(node.parent.parent);
178+
}
179+
}]
180+
: []
109181
});
110182
}
111183

0 commit comments

Comments
 (0)
0