/** * When two values may be compared, and what to say when they may not: the * intersection `==` requires, the runtime order `<` requires, the enum wire * domains a comparison meets in, the NaN warning, and the membership probes * every collection reads back through. * * D115 §三: this was twenty-six private methods of `Analyzer`, reached from * `inferBinary`, from a comparison chain, from `is`, from a collection probe * and from `equals`. They answer one question in several spellings — can these * two domains ever meet — so one file owns the answer and every caller reads it * through `EqualityRules`. */ import { type Expression } from "../../ast.ts"; import { type DiagnosticFix } from "../../diagnostic.ts"; import { type Span } from "../../source.ts"; import { type TypeParameterBound, type ValueType } from "../../types.ts"; import { LoweringRecorder } from "../lowering-recorder.ts"; import { type Binding, type MemberNarrowing } from "../scopes.ts"; /** What the equality, ordering and enum-domain rules asks of the analyzer that hosts it, and nothing more. */ export interface EqualityRulesHost { boundOf(type: Extract): TypeParameterBound | null; enumTargetOfValidatorObject(object: Expression): Extract | null; enumWireValuesOf(identity: string, name: string): ReadonlyMap | null; expandAliases(type: ValueType, seen?: ReadonlySet): ValueType; fieldsOf(identity: string): ReadonlyMap | null; readonly inferredExpressionTypes: Map; isAssignableHere(actual: ValueType, expected: ValueType): boolean; lookup(name: string): Binding | null; lookupMemberNarrowingEntry(path: string): MemberNarrowing | null; readonly lowering: LoweringRecorder; readonlyDataViewOf(type: ValueType): ValueType; resolveNamedClasses(type: ValueType): ValueType; stableMemberAccessPath(expression: Expression): string | null; readonly testExpectOperands: Map; typeError(message: string, errorSpan: Span, fix?: DiagnosticFix): void; } export declare class EqualityRules { private readonly host; constructor(host: EqualityRulesHost); requireIntersectingEquality(leftType: ValueType, rightType: ValueType, operator: string, leftExpression: Expression, rightExpression: Expression, operationSpan: Span): void; rejectFreshCollectionEquality(left: Expression, right: Expression, operator: string): boolean; private freshCollectionOperand; requireMembershipIntersection(probe: ValueType, domain: ValueType, span: Span, operation: string): boolean; rejectFreshCollectionProbe(probe: Expression, operation: string, probes: "element" | "key"): boolean; rejectDisjointEnumTest(subjectSource: ValueType, checked: ValueType, operator: "is" | "is not", span: Span): void; /** The enum/null arms of a type, or null when any arm falls outside that domain. */ private pureEnumDomainArms; rejectDisjointEnumValidatorProbe(calleeExpression: Expression, arguments_: readonly Expression[]): void; /** * D59 rule 141 settled that `toBe` *is* `==` ("toBe 必须用语言自己的 `==`") * and rule 141.1 settled that `toContain` *is* `values.has(item)`. The * runtime half of both landed; the compile-time half did not travel with * them, so `expect([1]).toBe([1])` compiled and failed at run time with * both operands rendering byte-identically, while `[1] == [1]` is refused * where it is written. This runs the operator's own two gates on the * matcher: D42 item 64's intersection requirement, and COL-I3's rejection * of a freshly built literal in an identity comparison. * * `toBe` and `toEqual` deliberately part company on the fresh-literal gate. * `toBe` asks the `==` question, where a new object can never be identical * to anything, so the literal proves the answer. `toEqual` asks the * `equals(a, b)` question, where a fresh literal is the normal and correct * spelling of the expected value — rejecting it there would refuse the very * repair the `toBe` message teaches. The intersection gate has no such * split: two types with no values in common never deeply equal either. * * `toHaveLength` and `toMatch` are left alone. Neither takes a comparand: * `toHaveLength` takes a count, and `toMatch` takes a regular-expression * pattern whose relation to the subject is matching, not equality. */ checkTestMatcherComparand(calleeExpression: Expression, arguments_: readonly Expression[]): void; rejectCollidingKeyDomain(keySource: ValueType, span: Span, position: string): void; /** * The declared domain behind an assignment-established fact: what a test * (`== null`, `??`) judges, and what an unannotated alias declares. Returns * the inferred type unchanged when the expression's narrowing came from a * check (or from nothing). */ assignedFactDomain(expression: Expression, inferred: ValueType): ValueType; equalityTypesIntersect(leftSource: ValueType, rightSource: ValueType): boolean; typesIntersect(leftSource: ValueType, rightSource: ValueType, enumStringVeto: boolean): boolean; equalityGuidance(leftSource: ValueType, rightSource: ValueType): string; private valueLevelEnum; /** * D102 ruling 1: the boundary the veto names is the one the wire value * crosses — a string-backed member meets raw strings, an integer-pinned one * meets raw numbers. The wording follows the value, so the report never * sends an author looking for a string in a line that holds a number. */ enumMeetDomain(left: ValueType, right: ValueType): "string" | "number"; /** * D102 ruling 1: the scalar kinds an enum's wire values exit to. A member * answers for itself; the whole enum answers with every kind its members * declare, so a mixed enum vetoes both domains and the author narrows to a * member before comparing. An enum this analyzer cannot see keeps the * pre-D102 answer, which is the right one for every string-backed enum. */ private enumWireScalarKinds; private hasValueLevelScalar; equalityOperandMayBeNaN(expression: Expression, type: ValueType): boolean; equalityMayCompareNaN(type: ValueType): boolean; requireOrderedComparison(leftType: ValueType, rightType: ValueType, leftExpression: Expression, rightExpression: Expression, operationSpan: Span): void; unorderedTypeGuidance(...types: readonly ValueType[]): string; private mentionsEnumType; orderedTypeCategory(source: ValueType): "number" | "string" | "comparable" | "dynamic" | null; /** The reason a type cannot participate in equals(a, b), or null when it is pure data. */ equalsDomainViolation(source: ValueType, seen?: Set): string | null; } //# sourceMappingURL=equality.d.ts.map