SERVER-101533: Add eslint rule that catches attempts of calling print(tojson(...)) in jstests (#32938)

Co-authored-by: Serhii Lysenko <serhiilysenko@mongodb.com>
GitOrigin-RevId: 555e589a77dee7d42607bb101f439c595fe0f77c
This commit is contained in:
Serhii Lysenko 2025-03-03 15:06:27 +00:00 committed by MongoDB Bot
parent bec96d789c
commit e0db31fa1d
5 changed files with 77 additions and 39 deletions

View File

@ -6,7 +6,7 @@ npm_package(
"package.json",
"plugin.js",
"rules/no-print-fn.js",
"rules/no-tojson-fn.js",
"rules/no-printing-tojson.js",
],
package = "eslint-plugin-mongodb",
visibility = ["//visibility:public"],

View File

@ -1,11 +1,11 @@
// plugin.js
import {default as no_print} from "./rules/no-print-fn.js";
import {default as no_tojson} from "./rules/no-tojson-fn.js";
import {default as no_printing_tojson} from "./rules/no-printing-tojson.js";
export default {
rules: {
"no-print-fn": 0,
"no-tojson-fn": 0,
"no-print-fn": no_print,
"no-printing-tojson": no_printing_tojson,
},
};

View File

@ -0,0 +1,72 @@
const print_fns = [
'jsTestLog',
'jsTest.log',
'jsTest.log.info',
'jsTest.log.debug',
'jsTest.log.warning',
'jsTest.log.error',
'print',
];
function flattenMemberExpressionName(expr) {
if (expr.object.type == "MemberExpression") {
return flattenMemberExpressionName(expr.object) + "." + expr.property.name;
} else if (expr.object.type == "Identifier") {
return expr.object.name + "." + expr.property.name;
} else {
return "";
}
}
export default {
meta: {
type: "problem",
docs: {
description: "Ensure no calls like print(tojson(x))",
},
fixable: "code",
},
create(context) {
return {
CallExpression: function(node) {
if (node.callee.type == "MemberExpression") {
node.callee.name = flattenMemberExpressionName(node.callee);
} else if (node.callee.type != "Identifier")
return;
if (print_fns.every((name) => name != node.callee.name))
return;
node.arguments.forEach(arg => {
if (arg.type != "CallExpression")
return;
if (arg.callee.type != "Identifier")
return;
if (arg.callee.name != "tojson" && arg.callee.name != "tojsononeline")
return;
context.report(
{
node,
message: `Calling ${arg.callee.name}() as a parameter of ${
node.callee
.name}(). Consider using toJsonForLog() instead or disable this rule by adding '// eslint-disable-next-line mongodb/no-printing-tojson'`,
fix(fixer) {
return fixer.replaceTextRange(
[
arg.callee.start,
arg.callee.end,
],
"toJsonForLog");
}
});
});
}
};
}
};

View File

@ -1,34 +0,0 @@
const stopList = [
"tojson",
"tojsononeline",
];
export default {
meta: {
type: "problem",
docs: {
description: "Ensure no direct calls to tojson* functions",
},
fixable: "code",
},
create(context) {
return {
CallExpression: function(node) {
if (node.callee.type == "Identifier" &&
stopList.some(fn => fn == node.callee.name)) {
context.report(
{
node,
message: `Direct use of '${
node.callee
.name}()'. Consider using jsTest.log.info() instead or disable mongodb/no-tojson-fn rule when necessary, e.g., '// eslint-disable-next-line mongodb/no-print-fn'
More about rules configuration: https://eslint.org/docs/latest/use/configure/rules`,
});
}
}
};
}
};

View File

@ -252,7 +252,7 @@ export default [
rules: {
// TODO SERVER-99571 : enable mongodb/* rules.
"mongodb/no-print-fn": 0,
"mongodb/no-tojson-fn": 1,
"mongodb/no-printing-tojson": 0,
"no-prototype-builtins": 0,
"no-useless-escape": 0,