eslint-plugin-reliability 4.1.4 → 4.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-reliability",
3
- "version": "4.1.4",
3
+ "version": "4.1.5",
4
4
  "description": "ESLint plugin for runtime reliability — enforces error handling, network timeouts, null checks, and safe type narrowing.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
package/src/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample},get"error-handling/no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"error-handling/no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"error-handling/no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-handling/error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"reliability/no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"reliability/no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"reliability/require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"reliability/no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"reliability/no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-reliability",exports.rules);exports.plugin={meta:{name:"eslint-plugin-reliability",version:"4.1.4"},rules:exports.rules};exports.configs={recommended:{plugins:{reliability:exports.plugin},rules:{"reliability/no-silent-errors":"error","reliability/no-missing-null-checks":"warn","reliability/require-network-timeout":"error"}}};exports.default=exports.plugin;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample},get"error-handling/no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"error-handling/no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"error-handling/no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-handling/error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"reliability/no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"reliability/no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"reliability/require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"reliability/no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"reliability/no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-reliability",exports.rules);exports.plugin={meta:{name:"eslint-plugin-reliability",version:"4.1.5"},rules:exports.rules};exports.configs={recommended:{plugins:{reliability:exports.plugin},rules:{"reliability/no-silent-errors":"error","reliability/no-missing-null-checks":"warn","reliability/require-network-timeout":"error"}}};exports.default=exports.plugin;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnhandledPromise=void 0;exports.hasPromiseEvidence=hasPromiseEvidence;exports.isLikelyPromiseExpression=isLikelyPromiseExpression;exports.isPromiseHandled=isPromiseHandled;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const NEVER_RETURNS_PROMISE_FUNCTIONS=new Set(["setTimeout","clearTimeout","setInterval","clearInterval","setImmediate","clearImmediate","requestAnimationFrame","cancelAnimationFrame","queueMicrotask","String","Number","Boolean","Symbol","BigInt","parseInt","parseFloat","isNaN","isFinite","Array","Object"]);const NEVER_RETURNS_PROMISE_METHODS=new Set(["log","error","warn","info","debug","trace","group","groupEnd","time","timeEnd","assert","floor","ceil","round","abs","min","max","pow","sqrt","random","sin","cos","tan","log2","log10","slice","split","join","concat","includes","indexOf","lastIndexOf","startsWith","endsWith","replace","replaceAll","trim","toLowerCase","toUpperCase","repeat","padStart","padEnd","charAt","charCodeAt","codePointAt","push","pop","shift","unshift","splice","reverse","sort","map","filter","reduce","reduceRight","forEach","every","some","find","findIndex","flat","flatMap","fill","copyWithin","entries","keys","values","parse","stringify","abort","addEventListener","removeEventListener","dispatchEvent","now","parse","UTC","from","of","isArray","isBuffer","isInteger","isFinite","isNaN","isSafeInteger","fromCharCode","fromCodePoint","raw","assign","freeze","isFrozen","create","defineProperty","defineProperties","getOwnPropertyDescriptor","getOwnPropertyNames","getPrototypeOf","setPrototypeOf","preventExtensions","isExtensible","seal","isSealed","fromEntries"]);const SYNC_NAMESPACE_OBJECTS=new Set(["Math","JSON","Date","Buffer","Array","Object","Number","String","Boolean","Symbol","BigInt","Reflect","console","process"]);const PROMISE_STATICS=new Set(["all","allSettled","any","race","resolve","reject"]);function isAsyncFunctionNode(node){if(!node)return false;return(node.type==="FunctionDeclaration"||node.type==="FunctionExpression"||node.type==="ArrowFunctionExpression")&&node.async===true}function hasPromiseEvidence(node,promiseReturning,resolveBinding){if(isAsyncFunctionNode(node.callee))return true;if(node.callee.type==="MemberExpression"){const{object}=node.callee;if(object.type==="Identifier"&&object.name==="Promise"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(node.callee),PROMISE_STATICS)){return true}const member=(0,eslint_devkit_1.propertyName)(node.callee);if(member==="then")return true;return object.type==="Identifier"&&promiseReturning.has(object.name)}if(node.callee.type==="Identifier"){if(promiseReturning.has(node.callee.name))return true;return isAsyncFunctionNode(resolveBinding(node.callee.name))}return false}function isLikelyPromiseExpression(node){if(node.type!=="CallExpression")return false;const callee=node.callee;if(callee.type==="Identifier"){const name=callee.name;if(NEVER_RETURNS_PROMISE_FUNCTIONS.has(name))return false;return true}if(callee.type==="MemberExpression"){const prop=callee.property;if(prop.type==="Identifier"){if(NEVER_RETURNS_PROMISE_METHODS.has(prop.name))return false}const obj=callee.object;if(obj.type==="Identifier"&&SYNC_NAMESPACE_OBJECTS.has(obj.name)){return false}return true}return true}function isPromiseDelegatedToCaller(node){const parent=node.parent;if(!parent)return false;if(parent.type==="ReturnStatement")return true;if(parent.type==="ArrowFunctionExpression"&&parent.body===node)return true;return false}function isInsidePromiseCallback(node){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(!parent)break;if(parent.type==="ArrowFunctionExpression"||parent.type==="FunctionExpression"){const funcParent=parent.parent;if(funcParent&&funcParent.type==="CallExpression"&&funcParent.callee&&funcParent.callee.type==="MemberExpression"){const memberExpr=funcParent.callee;const settled=(0,eslint_devkit_1.propertyName)(memberExpr);if(settled!==null){const methodName=settled;if(methodName==="then"||methodName==="catch"||methodName==="finally"){return true}}}}current=parent;depth++}return false}function isPromiseHandled(node){if(node.type==="Identifier"){const parent=node.parent;if(parent&&parent.type==="MemberExpression"&&parent.object===node){if(parent.property.type==="Identifier"){const methodName=parent.property.name;if(methodName==="catch"||methodName==="then"||methodName==="finally"){const memberParent=parent.parent;if(memberParent&&memberParent.type==="CallExpression"&&memberParent.callee===parent){return true}}}}}let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(!parent)break;if(parent.type==="MemberExpression"&&parent.object===current){if(parent.property.type==="Identifier"){const methodName=parent.property.name;if(methodName==="catch"||methodName==="then"||methodName==="finally"){const memberParent=parent.parent;if(memberParent&&memberParent.type==="CallExpression"&&memberParent.callee===parent){return true}}}}if(parent.type==="TryStatement"){return true}if(parent.type==="AwaitExpression"){return true}current=parent;depth++}return false}exports.noUnhandledPromise=(0,eslint_devkit_2.createRule)({name:"no-unhandled-promise",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-unhandled-promise.md",description:"Detects unhandled Promise rejections",cwe:"CWE-1024",cvss:7.5},hasSuggestions:true,messages:{unhandledPromise:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Unhandled promise",cwe:"CWE-1024",description:"Unhandled Promise rejection detected",severity:"HIGH",fix:"Add .catch() handler or use try/catch with await",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-4635/"}),addCatch:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add catch handler",description:"Add .catch() handler to promise",severity:"LOW",fix:"promise.catch(error => console.error(error))",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch"}),useTryCatch:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use try/catch",description:"Use try/catch with await",severity:"LOW",fix:"try { await promise; } catch (error) { handle(error); }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch"}),useAwait:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use await",description:"Use await to handle promise",severity:"LOW",fix:"await promise;",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore promises in test files"},requirePromiseEvidence:{type:"boolean",default:true,description:"Report only calls the file itself shows to be promise-producing."},promiseReturning:{type:"array",items:{type:"string"},default:["fetch"],description:"Names that return a promise and cannot be resolved from the file."},ignoreVoidExpressions:{type:"boolean",default:false,description:"Ignore promises in void expressions"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,ignoreVoidExpressions:false}],create(context,[options={}]){const{ignoreInTests=true,ignoreVoidExpressions=false,requirePromiseEvidence=true,promiseReturning=["fetch"]}=options||{};const promiseNames=new Set(promiseReturning);function resolveBinding(name,from){let scope=context.sourceCode.getScope(from);while(scope){const variable=scope.variables.find(v=>v.name===name);if(variable){const def=variable.defs[0];if(!def)return null;if(def.node.type==="FunctionDeclaration")return def.node;if(def.node.type==="VariableDeclarator"){return def.node.init??null}return null}scope=scope.upper}return null}const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}function checkCallExpression(node){if(isInsidePromiseCallback(node)){return}if(isPromiseDelegatedToCaller(node)){return}if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"){const methodName=node.callee.property.name;if(methodName==="catch"||methodName==="finally"){if(node.arguments.length>0&&node.arguments[0].type==="ArrowFunctionExpression"){const callback=node.arguments[0];if(callback.body.type==="BlockStatement"&&callback.body.body.length===0){}else{return}}else{return}}}if(!isLikelyPromiseExpression(node)){return}if(requirePromiseEvidence&&!hasPromiseEvidence(node,promiseNames,name=>resolveBinding(name,node))){return}if(isPromiseHandled(node)){return}if(ignoreVoidExpressions){const parent2=node.parent;if(parent2&&parent2.type==="UnaryExpression"&&parent2.operator==="void"){return}}const parent=node.parent;if(parent&&parent.type==="CallExpression"){const grandParent=parent.parent;const chainOn=grandParent?.type==="MemberExpression"&&grandParent.object===parent?grandParent:null;const outerChainMethod=chainOn===null?null:(0,eslint_devkit_1.propertyName)(chainOn);const inPromiseChain=chainOn!==null&&chainOn.property.type==="Identifier"&&(outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally");const isArgument=parent.arguments.some(a=>a===node);const outerIsPromise=outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally"||hasPromiseEvidence(parent,promiseNames,name=>resolveBinding(name,parent));if(!inPromiseChain&&(!isArgument||outerIsPromise)){return}}context.report({node,messageId:"unhandledPromise",suggest:[{messageId:"addCatch",fix:()=>null},{messageId:"useTryCatch",fix:()=>null},{messageId:"useAwait",fix:()=>null}]})}function checkStatementPromise(node){const parent=node.parent;if(parent?.type!=="ExpressionStatement")return;context.report({node,messageId:"unhandledPromise"})}return{CallExpression:checkCallExpression,ImportExpression:checkStatementPromise,NewExpression(node){if(node.callee.type==="Identifier"&&node.callee.name==="Promise"){checkStatementPromise(node)}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnhandledPromise=void 0;exports.hasPromiseEvidence=hasPromiseEvidence;exports.isLikelyPromiseExpression=isLikelyPromiseExpression;exports.isPromiseHandled=isPromiseHandled;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const NEVER_RETURNS_PROMISE_FUNCTIONS=new Set(["setTimeout","clearTimeout","setInterval","clearInterval","setImmediate","clearImmediate","requestAnimationFrame","cancelAnimationFrame","queueMicrotask","String","Number","Boolean","Symbol","BigInt","parseInt","parseFloat","isNaN","isFinite","Array","Object"]);const NEVER_RETURNS_PROMISE_METHODS=new Set(["log","error","warn","info","debug","trace","group","groupEnd","time","timeEnd","assert","floor","ceil","round","abs","min","max","pow","sqrt","random","sin","cos","tan","log2","log10","slice","split","join","concat","includes","indexOf","lastIndexOf","startsWith","endsWith","replace","replaceAll","trim","toLowerCase","toUpperCase","repeat","padStart","padEnd","charAt","charCodeAt","codePointAt","push","pop","shift","unshift","splice","reverse","sort","map","filter","reduce","reduceRight","forEach","every","some","find","findIndex","flat","flatMap","fill","copyWithin","entries","keys","values","parse","stringify","abort","addEventListener","removeEventListener","dispatchEvent","now","parse","UTC","from","of","isArray","isBuffer","isInteger","isFinite","isNaN","isSafeInteger","fromCharCode","fromCodePoint","raw","assign","freeze","isFrozen","create","defineProperty","defineProperties","getOwnPropertyDescriptor","getOwnPropertyNames","getPrototypeOf","setPrototypeOf","preventExtensions","isExtensible","seal","isSealed","fromEntries"]);const SYNC_NAMESPACE_OBJECTS=new Set(["Math","JSON","Date","Buffer","Array","Object","Number","String","Boolean","Symbol","BigInt","Reflect","console","process"]);const PROMISE_STATICS=new Set(["all","allSettled","any","race","resolve","reject"]);function isAsyncFunctionNode(node){if(!node)return false;return(node.type==="FunctionDeclaration"||node.type==="FunctionExpression"||node.type==="ArrowFunctionExpression")&&node.async===true}function hasPromiseEvidence(node,promiseReturning,resolveBinding){if(isAsyncFunctionNode(node.callee))return true;if(node.callee.type==="MemberExpression"){const{object}=node.callee;if(object.type==="Identifier"&&object.name==="Promise"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(node.callee),PROMISE_STATICS)){return true}const member=(0,eslint_devkit_1.propertyName)(node.callee);if(member==="then")return true;return object.type==="Identifier"&&promiseReturning.has(object.name)}if(node.callee.type==="Identifier"){if(promiseReturning.has(node.callee.name))return true;const bound=resolveBinding(node.callee.name);return isAsyncFunctionNode(bound)||isPromiseReturningAnnotation(bound)}return false}function isPromiseReturningAnnotation(node){if(node?.type!=="Identifier")return false;const annotation=node.typeAnnotation?.typeAnnotation;if(annotation?.type!=="TSFunctionType")return false;const returned=annotation.returnType?.typeAnnotation;return returned?.type==="TSTypeReference"&&returned.typeName.type==="Identifier"&&returned.typeName.name==="Promise"}function isLikelyPromiseExpression(node){if(node.type!=="CallExpression")return false;const callee=node.callee;if(callee.type==="Identifier"){const name=callee.name;if(NEVER_RETURNS_PROMISE_FUNCTIONS.has(name))return false;return true}if(callee.type==="MemberExpression"){const prop=callee.property;if(prop.type==="Identifier"){if(NEVER_RETURNS_PROMISE_METHODS.has(prop.name))return false}const obj=callee.object;if(obj.type==="Identifier"&&SYNC_NAMESPACE_OBJECTS.has(obj.name)){return false}return true}return true}function isPromiseDelegatedToCaller(node){const parent=node.parent;if(!parent)return false;if(parent.type==="ReturnStatement")return true;if(parent.type==="ArrowFunctionExpression"&&parent.body===node)return true;return false}function isInsidePromiseCallback(node){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(!parent)break;if(parent.type==="ArrowFunctionExpression"||parent.type==="FunctionExpression"){const funcParent=parent.parent;if(funcParent&&funcParent.type==="CallExpression"&&funcParent.callee&&funcParent.callee.type==="MemberExpression"){const memberExpr=funcParent.callee;const settled=(0,eslint_devkit_1.propertyName)(memberExpr);if(settled!==null){const methodName=settled;if(methodName==="then"||methodName==="catch"||methodName==="finally"){return true}}}}current=parent;depth++}return false}function isPromiseHandled(node){if(node.type==="Identifier"){const parent=node.parent;if(parent&&parent.type==="MemberExpression"&&parent.object===node){if(parent.property.type==="Identifier"){const methodName=parent.property.name;if(methodName==="catch"||methodName==="then"||methodName==="finally"){const memberParent=parent.parent;if(memberParent&&memberParent.type==="CallExpression"&&memberParent.callee===parent){return true}}}}}let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(!parent)break;if(parent.type==="MemberExpression"&&parent.object===current){if(parent.property.type==="Identifier"){const methodName=parent.property.name;if(methodName==="catch"||methodName==="then"||methodName==="finally"){const memberParent=parent.parent;if(memberParent&&memberParent.type==="CallExpression"&&memberParent.callee===parent){return true}}}}if(parent.type==="TryStatement"){return true}if(parent.type==="AwaitExpression"){return true}current=parent;depth++}return false}exports.noUnhandledPromise=(0,eslint_devkit_2.createRule)({name:"no-unhandled-promise",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-unhandled-promise.md",description:"Detects unhandled Promise rejections",cwe:"CWE-1024",cvss:7.5},hasSuggestions:true,messages:{unhandledPromise:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Unhandled promise",cwe:"CWE-1024",description:"Unhandled Promise rejection detected",severity:"HIGH",fix:"Add .catch() handler or use try/catch with await",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-4635/"}),addCatch:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add catch handler",description:"Add .catch() handler to promise",severity:"LOW",fix:"promise.catch(error => console.error(error))",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch"}),useTryCatch:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use try/catch",description:"Use try/catch with await",severity:"LOW",fix:"try { await promise; } catch (error) { handle(error); }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch"}),useAwait:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use await",description:"Use await to handle promise",severity:"LOW",fix:"await promise;",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore promises in test files"},requirePromiseEvidence:{type:"boolean",default:true,description:"Report only calls the file itself shows to be promise-producing."},promiseReturning:{type:"array",items:{type:"string"},default:["fetch"],description:"Names that return a promise and cannot be resolved from the file."},ignoreVoidExpressions:{type:"boolean",default:false,description:"Ignore promises in void expressions"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,ignoreVoidExpressions:false}],create(context,[options={}]){const{ignoreInTests=true,ignoreVoidExpressions=false,requirePromiseEvidence=true,promiseReturning=["fetch"]}=options||{};const promiseNames=new Set(promiseReturning);function resolveBinding(name,from){let scope=context.sourceCode.getScope(from);while(scope){const variable=scope.variables.find(v=>v.name===name);if(variable){const def=variable.defs[0];if(!def)return null;if(def.type==="Parameter"){const pattern=def.name.parent;if(pattern?.type==="AssignmentPattern"&&pattern.left===def.name){return pattern.right}return def.name}if(def.node.type==="FunctionDeclaration")return def.node;if(def.node.type==="VariableDeclarator"){return def.node.init??null}return null}scope=scope.upper}return null}const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}function checkCallExpression(node){if(isInsidePromiseCallback(node)){return}if(isPromiseDelegatedToCaller(node)){return}if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"){const methodName=node.callee.property.name;if(methodName==="catch"||methodName==="finally"){if(node.arguments.length>0&&node.arguments[0].type==="ArrowFunctionExpression"){const callback=node.arguments[0];if(callback.body.type==="BlockStatement"&&callback.body.body.length===0){}else{return}}else{return}}}if(!isLikelyPromiseExpression(node)){return}if(requirePromiseEvidence&&!hasPromiseEvidence(node,promiseNames,name=>resolveBinding(name,node))){return}if(isPromiseHandled(node)){return}if(ignoreVoidExpressions){const parent2=node.parent;if(parent2&&parent2.type==="UnaryExpression"&&parent2.operator==="void"){return}}const parent=node.parent;if(parent&&parent.type==="CallExpression"){const grandParent=parent.parent;const chainOn=grandParent?.type==="MemberExpression"&&grandParent.object===parent?grandParent:null;const outerChainMethod=chainOn===null?null:(0,eslint_devkit_1.propertyName)(chainOn);const inPromiseChain=chainOn!==null&&chainOn.property.type==="Identifier"&&(outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally");const isArgument=parent.arguments.some(a=>a===node);const outerIsPromise=outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally"||hasPromiseEvidence(parent,promiseNames,name=>resolveBinding(name,parent));if(!inPromiseChain&&(!isArgument||outerIsPromise)){return}}context.report({node,messageId:"unhandledPromise",suggest:[{messageId:"addCatch",fix:()=>null},{messageId:"useTryCatch",fix:()=>null},{messageId:"useAwait",fix:()=>null}]})}function checkStatementPromise(node){const parent=node.parent;if(parent?.type!=="ExpressionStatement")return;context.report({node,messageId:"unhandledPromise"})}return{CallExpression:checkCallExpression,ImportExpression:checkStatementPromise,NewExpression(node){if(node.callee.type==="Identifier"&&node.callee.name==="Promise"){checkStatementPromise(node)}}}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMissingNullChecks=void 0;exports.hasNullCheck=hasNullCheck;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function assignedValue(init){let current=init;while(current?.type==="AssignmentExpression"&&current.operator==="="){current=current.right}return current}const NULLABLE_RETURNS=new Set(["find","findLast","pop","shift","match","exec","getElementById","querySelector","closest","getAttribute","getNamedItem"]);function isShadowedBinding(scope,name){for(let s=scope;s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable.defs.length>0}return false}function nullabilityEvidence(ident,scope,seen=new Set){const readAt=ident.range?.[0]??Number.POSITIVE_INFINITY;if(seen.has(ident.name))return null;seen.add(ident.name);let s=scope;while(s){const variable=s.variables.find(v=>v.name===ident.name);if(!variable){s=s.upper;continue}if(variable.defs.length!==1)return null;const[def]=variable.defs;if(def.type!=="Variable"||def.node?.type!=="VariableDeclarator")return null;const declarator=def.node;const declaration=def.parent;if(declaration?.type==="VariableDeclaration"&&declaration.declare){return null}const init=assignedValue(declarator.init);if(init===null){const declParent=declarator.parent;const loopParent=declParent?.parent;if(loopParent?.type==="ForOfStatement"||loopParent?.type==="ForInStatement"){return null}const written=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(written)return null;return"declared-without-initializer"}const reassigned=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(reassigned)return null;if(init.type==="Literal"&&init.value===null)return"assigned-null";if(init.type==="Identifier"&&init.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(init.type==="Identifier"){return nullabilityEvidence(init,s,seen)}if(init.type==="ConditionalExpression"){for(const arm of[init.consequent,init.alternate]){if(arm.type==="Literal"&&arm.value===null)return"assigned-null";if(arm.type==="Identifier"&&arm.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(arm.type==="Identifier"){const viaArm=nullabilityEvidence(arm,s,seen);if(viaArm)return viaArm}if(arm.type==="CallExpression"&&arm.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(arm.callee),NULLABLE_RETURNS)){return"nullable-return"}}return null}if(init.type==="CallExpression"&&init.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(init.callee),NULLABLE_RETURNS)){return"nullable-return"}return null}return null}function baseHasNullabilityEvidence(objectNode,scope){let base=objectNode;while(base.type==="MemberExpression"){base=base.object}if(base.type!=="Identifier")return false;return nullabilityEvidence(base,scope)!==null}function hasNullCheck(node,sourceCode){if(node.optional){return true}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return true}if(usesNullishCoalescing(node)){return true}const objectText=sourceCode.getText(node.object);const immediateParent=parent;const nodeOrCall=immediateParent?.type==="CallExpression"&&immediateParent.callee===node?immediateParent:node;const andParent=nodeOrCall.parent;if(andParent?.type==="LogicalExpression"&&andParent.operator==="&&"&&andParent.right===nodeOrCall){const leftText=sourceCode.getText(andParent.left);if(leftText===objectText)return true}let cur=node;for(let depth=0;depth<8;depth++){const p=cur.parent;if(!p)break;if(p.type==="ConditionalExpression"&&p.consequent===cur){if(sourceCode.getText(p.test)===objectText){return true}}cur=p}if(hasExplicitNullCheck(node,sourceCode)){return true}if(isGuardedByEarlyExit(node,sourceCode)){return true}return false}function endsInExit(stmt){switch(stmt.type){case"ReturnStatement":case"ThrowStatement":case"ContinueStatement":case"BreakStatement":return true;case"BlockStatement":{const last=stmt.body[stmt.body.length-1];return last!==void 0&&endsInExit(last)}default:return false}}function falsyGuardTargets(test){if(test.type==="UnaryExpression"&&test.operator==="!"){return[test.argument]}if(test.type==="BinaryExpression"&&(test.operator==="=="||test.operator==="===")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right))return[test.left];if(isNil(test.left))return[test.right];return[]}if(test.type==="LogicalExpression"&&test.operator==="||"){return[...falsyGuardTargets(test.left),...falsyGuardTargets(test.right)]}return[]}function rootBinding(expr,sourceCode){let base=expr;while(base.type==="MemberExpression")base=base.object;const name=base.name;for(let s=sourceCode.getScope(base);s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable}return null}function guardsSameValue(guarded,object,sourceCode){const binding=rootBinding(guarded,sourceCode);return binding!==null&&binding===rootBinding(object,sourceCode)&&sourceCode.getText(guarded)===sourceCode.getText(object)}function statementListOf(node){switch(node.type){case"Program":case"BlockStatement":return node.body;case"SwitchCase":return node.consequent;default:return null}}function isGuardedByEarlyExit(node,sourceCode){let child=node;let parent=child.parent;while(parent){const list=statementListOf(parent);if(list){const index=list.indexOf(child);for(let i=0;i<index;i++){const stmt=list[i];if(stmt.type==="IfStatement"&&endsInExit(stmt.consequent)&&falsyGuardTargets(stmt.test).some(guarded=>guardsSameValue(guarded,node.object,sourceCode))){return true}}}child=parent;parent=child.parent}return false}function hasExplicitNullCheck(node,sourceCode){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="IfStatement"){const test=parent.test;if(isNullCheckForObject(test,node.object,sourceCode)){return true}}current=parent;depth++}return false}function isNullCheckForObject(test,object,sourceCode){const objectText=sourceCode.getText(object);if(test.type==="Identifier"||test.type==="MemberExpression"){const testText=sourceCode.getText(test);if(testText===objectText||objectText.startsWith(testText+".")){return true}}if(test.type==="BinaryExpression"){const{left,right,operator}=test;if(operator==="!=="||operator==="!="||operator==="==="||operator==="=="){const leftText=sourceCode.getText(left);const rightText=sourceCode.getText(right);if(leftText===objectText&&(rightText==="null"||rightText==="undefined")||rightText===objectText&&(leftText==="null"||leftText==="undefined")){return true}}}if(test.type==="LogicalExpression"){return isNullCheckForObject(test.left,object,sourceCode)||isNullCheckForObject(test.right,object,sourceCode)}return false}function usesNullishCoalescing(node){let current=node;let depth=0;const maxDepth=5;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="LogicalExpression"&&parent.operator==="??"){return true}current=parent;depth++}return false}exports.noMissingNullChecks=(0,eslint_devkit_2.createRule)({name:"no-missing-null-checks",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-missing-null-checks.md",description:"Detects potential null pointer dereferences",cwe:"CWE-476",cvss:7.5},hasSuggestions:true,messages:{missingNullCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Missing null check",cwe:"CWE-476",description:"Potential null/undefined dereference detected",severity:"HIGH",fix:"Use optional chaining (?.) or add explicit null check",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-2259/"}),useOptionalChaining:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Optional Chaining",description:"Use optional chaining operator",severity:"LOW",fix:"obj?.property?.method()",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining"}),useNullishCoalescing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Nullish Coalescing",description:"Use nullish coalescing operator",severity:"LOW",fix:"value ?? defaultValue",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing"}),addExplicitCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Explicit Check",description:"Add explicit null check",severity:"LOW",fix:"if (obj !== null) { obj.property }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"},requireExplicitChecks:{type:"boolean",default:false,description:"Require explicit null checks"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,requireExplicitChecks:false}],create(context,[options={}]){const{ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;const services=(0,eslint_devkit_1.hasParserServices)(context)?(0,eslint_devkit_1.getParserServices)(context):null;function typeSaysNonNullable(objectNode){if(!services)return false;const type=(0,eslint_devkit_1.getTypeOfNode)(objectNode,services);if((0,eslint_devkit_1.isAnyType)(type)||(0,eslint_devkit_1.isUnknownType)(type))return false;return!(0,eslint_devkit_1.isNullableType)(type)}const reportedMemberExpressions=new Set;function getMemberExpressionKey(node){if(node.range&&Array.isArray(node.range)&&node.range.length>=2){return`me-${node.range[0]}-${node.range[1]}`}const loc=node.loc;if(loc&&loc.start){return`me-${loc.start.line}-${loc.start.column}-${loc.end?.line||loc.start.line}-${loc.end?.column||loc.start.column}`}return`me-${JSON.stringify(node).slice(0,50)}`}function checkMemberExpression(node){if(node.optional){return}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return}if(parent&&parent.type==="MemberExpression"&&parent.object===node){return}const objectNode=node.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(node))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(node,sourceCode)){const nodeKey=getMemberExpressionKey(node);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}function checkCallExpression(node){if(node.type!=="CallExpression"){return}if(node.callee.type==="MemberExpression"){const memberExpr=node.callee;if(memberExpr.optional){return}const parent=memberExpr.parent;if(parent&&parent.type==="ChainExpression"){return}const objectNode=memberExpr.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(memberExpr))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(memberExpr,sourceCode)){const nodeKey=getMemberExpressionKey(memberExpr);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node:memberExpr,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}}return{MemberExpression:checkMemberExpression,CallExpression:checkCallExpression}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMissingNullChecks=void 0;exports.hasNullCheck=hasNullCheck;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function assignedValue(init){let current=init;while(current?.type==="AssignmentExpression"&&current.operator==="="){current=current.right}return current}const NULLABLE_RETURNS=new Set(["find","findLast","pop","shift","match","exec","getElementById","querySelector","closest","getAttribute","getNamedItem"]);function isShadowedBinding(scope,name){for(let s=scope;s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable.defs.length>0}return false}function nullabilityEvidence(ident,scope,seen=new Set){const readAt=ident.range?.[0]??Number.POSITIVE_INFINITY;if(seen.has(ident.name))return null;seen.add(ident.name);let s=scope;while(s){const variable=s.variables.find(v=>v.name===ident.name);if(!variable){s=s.upper;continue}if(variable.defs.length!==1)return null;const[def]=variable.defs;if(def.type!=="Variable"||def.node?.type!=="VariableDeclarator")return null;const declarator=def.node;const declaration=def.parent;if(declaration?.type==="VariableDeclaration"&&declaration.declare){return null}const init=assignedValue(declarator.init);if(init===null){const declParent=declarator.parent;const loopParent=declParent?.parent;if(loopParent?.type==="ForOfStatement"||loopParent?.type==="ForInStatement"){return null}const written=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(written)return null;return"declared-without-initializer"}const reassigned=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(reassigned)return null;if(init.type==="Literal"&&init.value===null)return"assigned-null";if(init.type==="Identifier"&&init.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(init.type==="Identifier"){return nullabilityEvidence(init,s,seen)}if(init.type==="ConditionalExpression"){for(const arm of[init.consequent,init.alternate]){if(arm.type==="Literal"&&arm.value===null)return"assigned-null";if(arm.type==="Identifier"&&arm.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(arm.type==="Identifier"){const viaArm=nullabilityEvidence(arm,s,seen);if(viaArm)return viaArm}if(arm.type==="CallExpression"&&arm.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(arm.callee),NULLABLE_RETURNS)){return"nullable-return"}}return null}if(init.type==="CallExpression"&&init.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(init.callee),NULLABLE_RETURNS)){return"nullable-return"}return null}return null}function baseHasNullabilityEvidence(objectNode,scope){let base=objectNode;while(base.type==="MemberExpression"){base=base.object}if(base.type!=="Identifier")return false;return nullabilityEvidence(base,scope)!==null}function hasNullCheck(node,sourceCode){if(node.optional){return true}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return true}if(usesNullishCoalescing(node)){return true}const objectText=sourceCode.getText(node.object);const immediateParent=parent;const nodeOrCall=immediateParent?.type==="CallExpression"&&immediateParent.callee===node?immediateParent:node;const andParent=nodeOrCall.parent;if(andParent?.type==="LogicalExpression"&&andParent.operator==="&&"&&andParent.right===nodeOrCall){const leftText=sourceCode.getText(andParent.left);if(leftText===objectText)return true}let cur=node;for(let depth=0;depth<8;depth++){const p=cur.parent;if(!p)break;if(p.type==="ConditionalExpression"&&p.consequent===cur){const test=p.test;if(sourceCode.getText(test)===objectText||narrowsToObject(test,node.object,sourceCode)){return true}}cur=p}if(hasExplicitNullCheck(node,sourceCode)){return true}if(isGuardedByEarlyExit(node,sourceCode)){return true}return false}function endsInExit(stmt){switch(stmt.type){case"ReturnStatement":case"ThrowStatement":case"ContinueStatement":case"BreakStatement":return true;case"BlockStatement":{const last=stmt.body[stmt.body.length-1];return last!==void 0&&endsInExit(last)}default:return false}}function falsyGuardTargets(test){if(test.type==="UnaryExpression"&&test.operator==="!"){return[test.argument]}if(test.type==="BinaryExpression"&&(test.operator==="=="||test.operator==="===")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right))return[test.left];if(isNil(test.left))return[test.right];return[]}if(test.type==="LogicalExpression"&&test.operator==="||"){return[...falsyGuardTargets(test.left),...falsyGuardTargets(test.right)]}return[]}function rootBinding(expr,sourceCode){let base=expr;while(base.type==="MemberExpression")base=base.object;const name=base.name;for(let s=sourceCode.getScope(base);s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable}return null}function guardsSameValue(guarded,object,sourceCode){const binding=rootBinding(guarded,sourceCode);return binding!==null&&binding===rootBinding(object,sourceCode)&&sourceCode.getText(guarded)===sourceCode.getText(object)}function statementListOf(node){switch(node.type){case"Program":case"BlockStatement":return node.body;case"SwitchCase":return node.consequent;default:return null}}function isGuardedByEarlyExit(node,sourceCode){let child=node;let parent=child.parent;while(parent){const list=statementListOf(parent);if(list){const index=list.indexOf(child);for(let i=0;i<index;i++){const stmt=list[i];if(stmt.type==="IfStatement"&&endsInExit(stmt.consequent)&&falsyGuardTargets(stmt.test).some(guarded=>guardsSameValue(guarded,node.object,sourceCode))){return true}}}child=parent;parent=child.parent}return false}function hasExplicitNullCheck(node,sourceCode){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="IfStatement"){const test=parent.test;if(isNullCheckForObject(test,node.object,sourceCode)){return true}}current=parent;depth++}return false}function rootText(expr,sourceCode){let base=expr;for(;;){if(base.type==="MemberExpression")base=base.object;else if(base.type==="CallExpression")base=base.callee;else break}return sourceCode.getText(base)}function rootGuards(root,object,sourceCode){const objectText=sourceCode.getText(object);return root===objectText||objectText.startsWith(`${root}.`)}function isOptionalChainRootedAt(expr,object,sourceCode){return expr.type==="ChainExpression"&&rootGuards(rootText(expr.expression,sourceCode),object,sourceCode)}function narrowsToObject(test,object,sourceCode){if(test.type==="BinaryExpression"&&test.operator==="in"){return rootGuards(sourceCode.getText(test.right),object,sourceCode)}if(isOptionalChainRootedAt(test,object,sourceCode)){return true}if(test.type==="BinaryExpression"&&(test.operator==="!=="||test.operator==="!=")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right)){return isOptionalChainRootedAt(test.left,object,sourceCode)}if(isNil(test.left)){return isOptionalChainRootedAt(test.right,object,sourceCode)}}return false}function isNullCheckForObject(test,object,sourceCode){const objectText=sourceCode.getText(object);if(narrowsToObject(test,object,sourceCode)){return true}if(test.type==="Identifier"||test.type==="MemberExpression"){const testText=sourceCode.getText(test);if(testText===objectText||objectText.startsWith(testText+".")){return true}}if(test.type==="BinaryExpression"){const{left,right,operator}=test;if(operator==="!=="||operator==="!="||operator==="==="||operator==="=="){const leftText=sourceCode.getText(left);const rightText=sourceCode.getText(right);if(leftText===objectText&&(rightText==="null"||rightText==="undefined")||rightText===objectText&&(leftText==="null"||leftText==="undefined")){return true}}}if(test.type==="LogicalExpression"){return isNullCheckForObject(test.left,object,sourceCode)||isNullCheckForObject(test.right,object,sourceCode)}return false}function usesNullishCoalescing(node){let current=node;let depth=0;const maxDepth=5;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="LogicalExpression"&&parent.operator==="??"){return true}current=parent;depth++}return false}exports.noMissingNullChecks=(0,eslint_devkit_2.createRule)({name:"no-missing-null-checks",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-missing-null-checks.md",description:"Detects potential null pointer dereferences",cwe:"CWE-476",cvss:7.5},hasSuggestions:true,messages:{missingNullCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Missing null check",cwe:"CWE-476",description:"Potential null/undefined dereference detected",severity:"HIGH",fix:"Use optional chaining (?.) or add explicit null check",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-2259/"}),useOptionalChaining:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Optional Chaining",description:"Use optional chaining operator",severity:"LOW",fix:"obj?.property?.method()",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining"}),useNullishCoalescing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Nullish Coalescing",description:"Use nullish coalescing operator",severity:"LOW",fix:"value ?? defaultValue",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing"}),addExplicitCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Explicit Check",description:"Add explicit null check",severity:"LOW",fix:"if (obj !== null) { obj.property }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"},requireExplicitChecks:{type:"boolean",default:false,description:"Require explicit null checks"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,requireExplicitChecks:false}],create(context,[options={}]){const{ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;const services=(0,eslint_devkit_1.hasParserServices)(context)?(0,eslint_devkit_1.getParserServices)(context):null;function typeSaysNonNullable(objectNode){if(!services)return false;const type=(0,eslint_devkit_1.getTypeOfNode)(objectNode,services);if((0,eslint_devkit_1.isAnyType)(type)||(0,eslint_devkit_1.isUnknownType)(type))return false;return!(0,eslint_devkit_1.isNullableType)(type)}const reportedMemberExpressions=new Set;function getMemberExpressionKey(node){if(node.range&&Array.isArray(node.range)&&node.range.length>=2){return`me-${node.range[0]}-${node.range[1]}`}const loc=node.loc;if(loc&&loc.start){return`me-${loc.start.line}-${loc.start.column}-${loc.end?.line||loc.start.line}-${loc.end?.column||loc.start.column}`}return`me-${JSON.stringify(node).slice(0,50)}`}function checkMemberExpression(node){if(node.optional){return}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return}if(parent&&parent.type==="MemberExpression"&&parent.object===node){return}const objectNode=node.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(node))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(node,sourceCode)){const nodeKey=getMemberExpressionKey(node);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}function checkCallExpression(node){if(node.type!=="CallExpression"){return}if(node.callee.type==="MemberExpression"){const memberExpr=node.callee;if(memberExpr.optional){return}const parent=memberExpr.parent;if(parent&&parent.type==="ChainExpression"){return}const objectNode=memberExpr.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(memberExpr))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(memberExpr,sourceCode)){const nodeKey=getMemberExpressionKey(memberExpr);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node:memberExpr,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}}return{MemberExpression:checkMemberExpression,CallExpression:checkCallExpression}}});