import { CleanRegexRule , createRuleListener , getDocUrl } from "../rules-util" ; import { mention } from "../format" ; import { Assertion , CapturingGroup , Group , Node , Quantifier } from "regexpp/ast" ; import { assertNever } from "../util" ; import { isZeroLength } from "../ast-util" ; /**
 * Returns the closest ascendant quantifier with a minimum of 0.
 */ function getZeroQuantifier ( node ) { if ( node . type === "Quantifier" && node . min === 0 ) { return node as Quantifier & { min : 0 ; } ; } else if ( node . parent ) { return getZeroQuantifier ( node . parent ) ; } else { return null ; } } /**
 * Returns whether the given assertion is optional in regard to the given quantifier with a minimum of 0.
 *
 * Optional means that all paths in the element if the quantifier which contain the given assertion also have do not
 * consume characters. For more information and examples on optional assertions, see the documentation page of this
 * rule.
 */ function isOptional ( assertion , quantifier ) { /**
     * This will implement a bottom-up approach.
     */ function isOptionalImpl ( element ) { if ( element . parent === quantifier ) { return true ; } const parent = element . parent ; if ( parent . type === "Alternative" ) { for ( const e of parent . elements ) { if ( e === element ) { continue ; } if ( ! isZeroLength ( e ) ) { return false ; } } if ( parent . parent . type === "Pattern" ) { throw new Error ( "The given assertion is not a descendant of the given quantifier." ) ; } else { return isOptionalImpl ( parent . parent ) ; } } else if ( parent . type === "Quantifier" ) { if ( parent . max > 1 && ! isZeroLength ( parent ) ) { return false ; } return isOptionalImpl ( parent ) ; } else { throw assertNever ( parent ) ; } } return isOptionalImpl ( assertion ) ; } export default { meta : { type : "problem" , docs : { description : "Disallow optional assertions." , url : getDocUrl ( "no-optional-assertion" ) , } , } , create ( context ) { return createRuleListener ( ( { visitAST , reportElement } ) => { visitAST ( { onAssertionEnter ( node ) { const q = getZeroQuantifier ( node ) ; if ( q && isOptional ( node , q ) ) { context . report ( { message : "This assertion effectively optional and does not change the pattern." + ` Either remove the assertion or change the parent quantifier ${ mention ( q . raw . substr ( q . element . raw . length ) ) }.` , ... reportElement ( node ) , } ) ; } } , } ) ; } ) ; } , } as CleanRegexRule ;