import type { TSESTree , TSESLint } from '@typescript-eslint/experimental-utils' ; import { isIdentifier , isLiteral } from '../utils/nodes' ; import { areEquivalent } from '../utils/equivalence' ; import { report , issueLocation , IssueLocation } from '../utils/locations' ; import docsUrl from '../utils/docs-url' ; const EQUALITY_OPERATOR_TOKEN_KINDS = new Set ( [ '==' , '===' , '!=' , '!==' ] ) ; const RELEVANT_OPERATOR_TOKEN_KINDS = new Set ( [ '&&' , '||' , '/' , '-' , '<<' , '>>' , '<' , '<=' , '>' , '>=' , ] ) ; function hasRelevantOperator ( node ) { return ( RELEVANT_OPERATOR_TOKEN_KINDS . has ( node . operator ) || ( EQUALITY_OPERATOR_TOKEN_KINDS . has ( node . operator ) && ! hasIdentifierOperands ( node ) ) ) ; } function hasIdentifierOperands ( node ) { return isIdentifier ( node . left ) && isIdentifier ( node . right ) ; } function isOneOntoOneShifting ( node ) { return node . operator === '<<' && isLiteral ( node . left ) && node . left . value === 1 ; } const message = 'Correct one of the identical sub-expressions on both sides of operator "{{operator}}"' ; const rule = { meta : { messages : { correctIdenticalSubExpressions : message , sonarRuntime : '{{sonarRuntimeData}}' , } , type : 'problem' , docs : { description : 'Identical expressions should not be used on both sides of a binary operator' , recommended : 'error' , url : docsUrl ( __filename ) , } , schema : [ { enum : [ 'sonar-runtime' ] , } , ] , } , create ( context ) { return { LogicalExpression ( node ) { check ( node as TSESTree . LogicalExpression ) ; } , BinaryExpression ( node ) { check ( node as TSESTree . BinaryExpression ) ; } , } ; function check ( expr ) { if ( hasRelevantOperator ( expr ) && ! isOneOntoOneShifting ( expr ) && areEquivalent ( expr . left , expr . right , context . getSourceCode ( ) ) ) { const secondaryLocations = [ ] ; if ( expr . left . loc ) { secondaryLocations . push ( issueLocation ( expr . left . loc ) ) ; } report ( context , { messageId : 'correctIdenticalSubExpressions' , data : { operator : expr . operator , } , node : isSonarRuntime ( ) ? expr . right : expr , } , secondaryLocations , message ) ; } } function isSonarRuntime ( ) { return context . options [ context . options . length - 1 ] === 'sonar-runtime' ; } } , } ; export = rule ;