eslint-plugin-no-mistakes 0.96.0 → 0.97.0

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-no-mistakes",
3
- "version": "0.96.0",
3
+ "version": "0.97.0",
4
4
  "description": "ESLint and Oxlint rules that keep code static enough for no-mistakes analyzers and coding agents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+
3
+ const { resolveVariable } = require("./test-no-shared-state-aliases");
4
+ const { literalString } = require("./module-mock-helpers");
5
+
6
+ function writesNamespaceMember(variable) {
7
+ return variable.references.some((reference) => {
8
+ const member = reference.identifier.parent;
9
+ if (member?.type !== "MemberExpression" || member.object !== reference.identifier) return false;
10
+ const parent = member.parent;
11
+ return (
12
+ (parent.type === "AssignmentExpression" && parent.left === member) ||
13
+ (parent.type === "UpdateExpression" && parent.argument === member) ||
14
+ (parent.type === "UnaryExpression" && parent.operator === "delete")
15
+ );
16
+ });
17
+ }
18
+
19
+ function aliasUsesStayLocal(variable, context, seen = new Set()) {
20
+ if (
21
+ seen.has(variable) ||
22
+ variable.defs[0].node?.parent?.parent?.type === "ExportNamedDeclaration" ||
23
+ writesNamespaceMember(variable)
24
+ )
25
+ return false;
26
+ seen.add(variable);
27
+ return variable.references.every((reference) => {
28
+ if (reference.init) return true;
29
+ if (reference.isWrite() || crossesWith(reference.identifier, variable.scope, context))
30
+ return false;
31
+ const parent = reference.identifier.parent;
32
+ if (parent?.type === "MemberExpression" && parent.object === reference.identifier) {
33
+ const name = parent.computed ? literalString(parent.property) : parent.property.name;
34
+ return (
35
+ name === "resolve" &&
36
+ parent.parent?.type === "CallExpression" &&
37
+ parent.parent.callee === parent
38
+ );
39
+ }
40
+ if (
41
+ parent?.type !== "VariableDeclarator" ||
42
+ parent.init !== reference.identifier ||
43
+ parent.parent?.kind !== "const" ||
44
+ parent.id.type !== "Identifier" ||
45
+ parent.parent.parent?.type === "ExportNamedDeclaration" ||
46
+ variable.defs[0].node.init.range[1] > parent.init.range[0]
47
+ )
48
+ return false;
49
+ const child = resolveVariable(parent.id, context);
50
+ return child?.defs.length === 1 && aliasUsesStayLocal(child, context, seen);
51
+ });
52
+ }
53
+
54
+ function globalUsesStayLocal(variable, context) {
55
+ return variable.references.every((reference) => {
56
+ if (reference.isTypeReference && !reference.isValueReference) return true;
57
+ if (reference.isWrite()) return false;
58
+ const parent = reference.identifier.parent;
59
+ if (parent?.type === "MemberExpression" && parent.object === reference.identifier) {
60
+ const name = parent.computed ? literalString(parent.property) : parent.property.name;
61
+ return (
62
+ ["resolve", "reject", "all", "allSettled", "any", "race"].includes(name) &&
63
+ parent.parent?.type === "CallExpression" &&
64
+ parent.parent.callee === parent
65
+ );
66
+ }
67
+ if (parent?.type === "NewExpression" && parent.callee === reference.identifier) return true;
68
+ if (
69
+ parent?.type !== "VariableDeclarator" ||
70
+ parent.init !== reference.identifier ||
71
+ parent.parent?.kind !== "const" ||
72
+ parent.id.type !== "Identifier"
73
+ )
74
+ return false;
75
+ const child = resolveVariable(parent.id, context);
76
+ return child?.defs.length === 1 && aliasUsesStayLocal(child, context);
77
+ });
78
+ }
79
+
80
+ function hasGlobalObjectPromiseAccess(context) {
81
+ const globalScope = context.sourceCode.scopeManager.globalScope;
82
+ for (const name of ["globalThis", "window", "self", "global"]) {
83
+ const variable = globalScope.set.get(name);
84
+ const references =
85
+ variable && variable.defs.length === 0
86
+ ? variable.references
87
+ : globalScope.through.filter(
88
+ (reference) => reference.identifier.name === name && !reference.resolved,
89
+ );
90
+ if (
91
+ references.some((reference) => {
92
+ const member = reference.identifier.parent;
93
+ if (member?.type !== "MemberExpression" || member.object !== reference.identifier)
94
+ return true; // The global object escaped or was used as a value.
95
+ const property = member.computed ? literalString(member.property) : member.property.name;
96
+ if (property === null || property === "Promise") return true;
97
+ const parent = member.parent;
98
+ return (
99
+ (parent.type === "CallExpression" && parent.callee === member) ||
100
+ (parent.type === "AssignmentExpression" && parent.left === member) ||
101
+ (parent.type === "UpdateExpression" && parent.argument === member) ||
102
+ (parent.type === "UnaryExpression" && parent.operator === "delete")
103
+ );
104
+ })
105
+ )
106
+ return true;
107
+ }
108
+ return false;
109
+ }
110
+
111
+ function crossesWith(identifier, bindingScope, context) {
112
+ for (
113
+ let scope = context.sourceCode.getScope(identifier);
114
+ scope && scope !== bindingScope;
115
+ scope = scope.upper
116
+ ) {
117
+ if (scope.type === "with") return true;
118
+ }
119
+ return false;
120
+ }
121
+
122
+ function immutableAlias(identifier, context) {
123
+ const variable = resolveVariable(identifier, context);
124
+ if (variable?.defs.length !== 1) return null;
125
+ const [definition] = variable.defs;
126
+ const declarator = definition.node;
127
+ if (
128
+ definition.type !== "Variable" ||
129
+ declarator?.parent?.kind !== "const" ||
130
+ declarator.id.type !== "Identifier" ||
131
+ declarator.id.name !== identifier.name ||
132
+ declarator.init?.type !== "Identifier" ||
133
+ declarator.parent.parent?.type === "ExportNamedDeclaration" ||
134
+ variable.references.some((reference) => reference.isWrite() && !reference.init) ||
135
+ !aliasUsesStayLocal(variable, context) ||
136
+ declarator.init.range[1] > identifier.range[0] ||
137
+ crossesWith(identifier, variable.scope, context)
138
+ )
139
+ return null;
140
+ return declarator;
141
+ }
142
+
143
+ function isGlobalPromiseAlias(identifier, context) {
144
+ let current = identifier;
145
+ while (true) {
146
+ const declarator = immutableAlias(current, context);
147
+ if (!declarator) return false;
148
+ const next = declarator.init;
149
+ const binding = resolveVariable(next, context);
150
+ // A later const initializer is still in its TDZ at this read. This
151
+ // decreasing source-order invariant also rules out alias cycles.
152
+ if (binding?.defs.length && binding.defs[0].node?.init?.range[1] > next.range[0]) return false;
153
+ if (next.name === "Promise") {
154
+ return (
155
+ binding?.defs.length === 0 &&
156
+ binding.scope.type === "global" &&
157
+ !writesNamespaceMember(binding) &&
158
+ globalUsesStayLocal(binding, context) &&
159
+ !hasGlobalObjectPromiseAccess(context) &&
160
+ !crossesWith(next, binding?.scope, context)
161
+ );
162
+ }
163
+ if (crossesWith(next, binding?.scope, context)) return false;
164
+ current = next;
165
+ }
166
+ }
167
+
168
+ module.exports = { isGlobalPromiseAlias };
@@ -4,6 +4,7 @@ const { rule } = require("../helpers");
4
4
  const { ruleSuppression } = require("../rule-suppression");
5
5
  const { literalString } = require("./module-mock-helpers");
6
6
  const { resolveVariable } = require("./test-no-shared-state-aliases");
7
+ const { isGlobalPromiseAlias } = require("./react-no-use-promise-resolve-alias");
7
8
 
8
9
  function unwrap(node) {
9
10
  while (node?.type === "ChainExpression") node = node.expression;
@@ -106,6 +107,36 @@ function isPromiseResolve(node) {
106
107
  );
107
108
  }
108
109
 
110
+ function isAliasedPromiseResolve(node, context) {
111
+ if (
112
+ node?.type !== "CallExpression" ||
113
+ node.optional ||
114
+ node.arguments.length !== 1 ||
115
+ node.arguments[0].type === "SpreadElement"
116
+ )
117
+ return false;
118
+ const callee = node.callee;
119
+ return (
120
+ callee.type === "MemberExpression" &&
121
+ !callee.optional &&
122
+ callee.object.type === "Identifier" &&
123
+ staticName(callee.property, callee.computed) === "resolve" &&
124
+ isGlobalPromiseAlias(callee.object, context)
125
+ );
126
+ }
127
+
128
+ function ordinaryUseCall(node) {
129
+ if (node.optional || node.arguments.length !== 1 || node.arguments[0].type === "SpreadElement")
130
+ return false;
131
+ const callee = node.callee;
132
+ return (
133
+ callee.type === "Identifier" ||
134
+ (callee.type === "MemberExpression" &&
135
+ !callee.optional &&
136
+ !["call", "apply"].includes(staticName(callee.property, callee.computed)))
137
+ );
138
+ }
139
+
109
140
  module.exports = Object.assign(
110
141
  rule(
111
142
  {
@@ -141,7 +172,8 @@ module.exports = Object.assign(
141
172
  if (
142
173
  suppressed(node) ||
143
174
  !isReactUse(node.callee, context, useNames) ||
144
- !isPromiseResolve(resolvedArgument)
175
+ (!isPromiseResolve(resolvedArgument) &&
176
+ !(ordinaryUseCall(node) && isAliasedPromiseResolve(node.arguments[0], context)))
145
177
  ) {
146
178
  return;
147
179
  }
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ const { unwrapExpression } = require("./async-ast");
4
+
5
+ function findVariable(scope, name) {
6
+ for (let current = scope; current; current = current.upper) {
7
+ const variable =
8
+ current.set?.get?.(name) ?? current.variables?.find((item) => item.name === name);
9
+ if (variable) return variable;
10
+ }
11
+ return null;
12
+ }
13
+
14
+ function crossesWith(identifier, variable, context) {
15
+ for (
16
+ let scope = context.sourceCode.getScope(identifier);
17
+ scope !== variable.scope;
18
+ scope = scope.upper
19
+ ) {
20
+ if (scope.type === "with") return true;
21
+ }
22
+ return false;
23
+ }
24
+
25
+ function immutableDeclarator(identifier, context) {
26
+ const variable = findVariable(context.sourceCode.getScope(identifier), identifier.name);
27
+ if (variable?.defs.length !== 1) return null;
28
+ const [definition] = variable.defs;
29
+ if (definition.type !== "Variable") return null;
30
+ const declarator = definition.node;
31
+ if (
32
+ !declarator?.init ||
33
+ declarator.id.type !== "Identifier" ||
34
+ declarator.id.name !== identifier.name ||
35
+ declarator.parent?.type !== "VariableDeclaration" ||
36
+ declarator.parent.kind !== "const"
37
+ )
38
+ return null;
39
+ return { variable, declarator };
40
+ }
41
+
42
+ function originDeclarator(identifier, context) {
43
+ let reference = identifier;
44
+ while (true) {
45
+ const binding = immutableDeclarator(reference, context);
46
+ if (!binding) return null;
47
+ const { declarator } = binding;
48
+ const value = unwrapExpression(declarator.init);
49
+ if (value.type !== "Identifier") return declarator;
50
+ // A with-object getter can replace an outer lexical value at either read.
51
+ if (crossesWith(reference, binding.variable, context)) return null;
52
+ if (binding.variable.references.some((ref) => ref.isWrite() && !ref.init)) return null;
53
+ // Reading an alias before initialization, or creating it from a binding
54
+ // still in the TDZ, does not establish promise identity.
55
+ if (declarator.init.range[1] > reference.range[0]) return null;
56
+ const target = immutableDeclarator(value, context);
57
+ // Each hop moves to an earlier initializer. That also rules out cycles.
58
+ if (
59
+ !target ||
60
+ crossesWith(value, target.variable, context) ||
61
+ target.variable.references.some((ref) => ref.isWrite() && !ref.init) ||
62
+ target.declarator.init.range[1] > declarator.init.range[0]
63
+ )
64
+ return null;
65
+ reference = value;
66
+ }
67
+ }
68
+
69
+ module.exports = { originDeclarator };
@@ -2,7 +2,8 @@
2
2
 
3
3
  const { rule } = require("../helpers");
4
4
  const { ruleSuppression } = require("../rule-suppression");
5
- const { findContainingRoot, traverse, unwrapExpression } = require("./async-ast");
5
+ const { findContainingRoot, traverse } = require("./async-ast");
6
+ const { originDeclarator } = require("./test-no-delayed-rejects-promise-alias");
6
7
  const { chainIsSafelyObserved, promiseChainBase } = require("./test-no-delayed-rejects-chains");
7
8
  const { canReachMatcher, contains, executesBefore } = require("./test-no-delayed-rejects-flow");
8
9
  const { isImmediateObserver } = require("./test-no-delayed-rejects-observers");
@@ -14,17 +15,6 @@ const {
14
15
 
15
16
  const { expectedIdentifier } = require("./test-no-delayed-rejects-expect");
16
17
 
17
- function findVariable(scope, name) {
18
- let current = scope;
19
- while (current) {
20
- const variable =
21
- current.set?.get?.(name) ?? current.variables?.find((item) => item.name === name);
22
- if (variable) return variable;
23
- current = current.upper;
24
- }
25
- return null;
26
- }
27
-
28
18
  function matcherCall(rejects) {
29
19
  let current = rejects;
30
20
  while (
@@ -38,27 +28,8 @@ function matcherCall(rejects) {
38
28
  : null;
39
29
  }
40
30
 
41
- function constDeclarator(identifier, context) {
42
- const variable = findVariable(context.sourceCode.getScope(identifier), identifier.name);
43
- if (!variable || variable.defs.length !== 1) return null;
44
- const [definition] = variable.defs;
45
- if (definition.type !== "Variable") return null;
46
- const declarator = definition.node;
47
- if (
48
- !declarator?.init ||
49
- declarator.id.type !== "Identifier" ||
50
- declarator.id.name !== identifier.name ||
51
- declarator.parent?.type !== "VariableDeclaration" ||
52
- declarator.parent.kind !== "const"
53
- ) {
54
- return null;
55
- }
56
- if (unwrapExpression(declarator.init).type === "Identifier") return null;
57
- return declarator;
58
- }
59
-
60
31
  function isSameConst(identifier, declarator, context) {
61
- return identifier.type === "Identifier" && constDeclarator(identifier, context) === declarator;
32
+ return identifier.type === "Identifier" && originDeclarator(identifier, context) === declarator;
62
33
  }
63
34
 
64
35
  function isRejectionHandlerCall(node, declarator, context) {
@@ -159,7 +130,7 @@ module.exports = rule(
159
130
  MemberExpression(node) {
160
131
  const identifier = expectedIdentifier(node, context);
161
132
  if (!identifier) return;
162
- const declarator = constDeclarator(identifier, context);
133
+ const declarator = originDeclarator(identifier, context);
163
134
  const call = matcherCall(node);
164
135
  if (
165
136
  !declarator ||
@@ -6,7 +6,13 @@ function unwrapExpression(expression) {
6
6
  current &&
7
7
  (current.type === "AwaitExpression" ||
8
8
  current.type === "ChainExpression" ||
9
- current.type === "TSNonNullExpression")
9
+ [
10
+ "TSAsExpression",
11
+ "TSNonNullExpression",
12
+ "TSSatisfiesExpression",
13
+ "TSTypeAssertion",
14
+ "TSInstantiationExpression",
15
+ ].includes(current.type))
10
16
  ) {
11
17
  current = current.type === "AwaitExpression" ? current.argument : current.expression;
12
18
  }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+
3
+ const { staticMemberName } = require("./ts-no-function-aliases-ast");
4
+ const { resolveVariable } = require("./ts-no-function-aliases-self");
5
+
6
+ function unassertedPath(expression) {
7
+ let current = expression;
8
+ while (["ChainExpression", "MemberExpression", "AwaitExpression"].includes(current.type)) {
9
+ if (current.type === "ChainExpression") current = current.expression;
10
+ else current = current.type === "AwaitExpression" ? current.argument : current.object;
11
+ }
12
+ return !current.type.startsWith("TS");
13
+ }
14
+
15
+ function typeQueryPath(node) {
16
+ if (node.type === "Identifier") return [node.name];
17
+ if (node.type === "TSQualifiedName") return [...typeQueryPath(node.left), node.right.name];
18
+ // A typeof import() query has no static value path in this file.
19
+ return null;
20
+ }
21
+
22
+ function pathRoot(node) {
23
+ let current = node;
24
+ while (current?.type === "MemberExpression" || current?.type === "TSQualifiedName")
25
+ current = current.type === "MemberExpression" ? current.object : current.left;
26
+ return current?.type === "Identifier" ? current : null;
27
+ }
28
+
29
+ function valuePath(node) {
30
+ if (node?.type === "Identifier") return [node.name];
31
+ if (node?.type === "MemberExpression") {
32
+ const left = valuePath(node.object);
33
+ const right = staticMemberName(node);
34
+ return left && right !== null ? [...left, right] : null;
35
+ }
36
+ return null;
37
+ }
38
+
39
+ function samePath(left, right) {
40
+ return (
41
+ left &&
42
+ right &&
43
+ left.length === right.length &&
44
+ left.every((segment, index) => segment === right[index])
45
+ );
46
+ }
47
+
48
+ function assertionType(annotation, sourceCode) {
49
+ if (
50
+ annotation.type !== "TSTypeReference" ||
51
+ annotation.typeName.type !== "Identifier" ||
52
+ annotation.typeArguments
53
+ )
54
+ return annotation;
55
+ const variable = resolveVariable(sourceCode, annotation.typeName);
56
+ const declaration = variable?.defs.length === 1 ? variable.defs[0].node : null;
57
+ if (declaration?.type !== "TSTypeAliasDeclaration" || declaration.typeParameters)
58
+ return annotation;
59
+ // One non-generic, same-file alias is a local proof. Chained aliases and
60
+ // imported type names need type resolution that this rule does not own.
61
+ return declaration.typeAnnotation.type === "TSTypeReference"
62
+ ? annotation
63
+ : declaration.typeAnnotation;
64
+ }
65
+
66
+ function preservedCalleeCast(node, target, sourceCode) {
67
+ if (node.type !== "TSAsExpression" && node.type !== "TSTypeAssertion") return false;
68
+ const annotation = assertionType(node.typeAnnotation, sourceCode);
69
+ return (
70
+ samePath(valuePath(node.expression), valuePath(target)) &&
71
+ annotation.type === "TSTypeQuery" &&
72
+ samePath(typeQueryPath(annotation.exprName), valuePath(target)) &&
73
+ resolveVariable(sourceCode, pathRoot(annotation.exprName)) ===
74
+ resolveVariable(sourceCode, pathRoot(target)) &&
75
+ !annotation.typeArguments
76
+ );
77
+ }
78
+
79
+ function resultCastPreserved(node, target, sourceCode) {
80
+ const castType = assertionType(node.typeAnnotation, sourceCode).type;
81
+ return (
82
+ (node.type === "TSAsExpression" || node.type === "TSTypeAssertion") &&
83
+ target.callable &&
84
+ // The binding annotation can expose a wider contract than the initializer.
85
+ !target.annotation &&
86
+ !wrapperHasOverloads(target.callable, sourceCode) &&
87
+ !target.callable.typeParameters &&
88
+ !target.callable.async &&
89
+ !target.callable.generator &&
90
+ target.callable?.returnType?.typeAnnotation.type === castType &&
91
+ [
92
+ "TSVoidKeyword",
93
+ "TSUndefinedKeyword",
94
+ "TSNullKeyword",
95
+ "TSStringKeyword",
96
+ "TSNumberKeyword",
97
+ "TSBooleanKeyword",
98
+ "TSBigIntKeyword",
99
+ "TSSymbolKeyword",
100
+ ].includes(castType)
101
+ );
102
+ }
103
+
104
+ function assertedContractPreserved(node, call, candidate, target, sourceCode) {
105
+ let result = node.body;
106
+ if (result.type === "BlockStatement") {
107
+ const statement = result.body[0];
108
+ result = statement.argument || statement.expression;
109
+ }
110
+ while (result !== call) {
111
+ if (!result) return false;
112
+ if (["TSAsExpression", "TSTypeAssertion"].includes(result.type)) {
113
+ if (!resultCastPreserved(result, target, sourceCode)) return false;
114
+ result = result.expression;
115
+ } else if (result.type === "ChainExpression" || result.type === "TSNonNullExpression") {
116
+ if (result.type === "TSNonNullExpression") return false;
117
+ result = result.expression;
118
+ } else return false; // Async wrappers were rejected before this walk.
119
+ }
120
+ let callee = call.callee;
121
+ while (callee && callee !== candidate.target) {
122
+ if (["TSAsExpression", "TSTypeAssertion"].includes(callee.type)) {
123
+ if (!preservedCalleeCast(callee, candidate.target, sourceCode)) return false;
124
+ callee = callee.expression;
125
+ } else if (callee.type === "ChainExpression") {
126
+ callee = callee.expression;
127
+ } else if (callee.type === "MemberExpression") {
128
+ callee = callee.object;
129
+ } else return false;
130
+ }
131
+ return callee === candidate.target && unassertedPath(candidate.target);
132
+ }
133
+
134
+ function wrapperHasOverloads(node, sourceCode) {
135
+ if (node.type !== "FunctionDeclaration") return false;
136
+ return sourceCode
137
+ .getDeclaredVariables(node)
138
+ .some((variable) =>
139
+ variable.defs.some(
140
+ (definition) => definition.type === "FunctionName" && definition.node !== node,
141
+ ),
142
+ );
143
+ }
144
+
145
+ module.exports = { unassertedPath, assertedContractPreserved, wrapperHasOverloads };
@@ -2,28 +2,16 @@
2
2
 
3
3
  const { staticMemberName } = require("./ts-no-function-aliases-ast");
4
4
  const { resolveVariable } = require("./ts-no-function-aliases-self");
5
-
6
- function unassertedPath(expression) {
7
- let current = expression;
8
- while (["ChainExpression", "MemberExpression", "AwaitExpression"].includes(current.type)) {
9
- if (current.type === "ChainExpression") current = current.expression;
10
- else current = current.type === "AwaitExpression" ? current.argument : current.object;
11
- }
12
- return !current.type.startsWith("TS");
13
- }
14
-
15
- function unassertedResult(node) {
16
- let result = node.body;
17
- if (result.type === "BlockStatement") {
18
- const statement = result.body[0];
19
- result = statement.argument || statement.expression;
20
- }
21
- return unassertedPath(result);
22
- }
5
+ const {
6
+ unassertedPath,
7
+ assertedContractPreserved,
8
+ wrapperHasOverloads,
9
+ } = require("./ts-no-function-aliases-cast-proof");
23
10
 
24
11
  function unassertedCall(node, call) {
25
12
  if (call.typeArguments || call.typeParameters) return false;
26
- if (!unassertedResult(node) || !unassertedPath(call.callee)) return false;
13
+ // Assertions are checked against the reachable target after candidate
14
+ // selection. This guard still rejects casts on an explicit receiver.
27
15
  const callee = call.callee.type === "ChainExpression" ? call.callee.expression : call.callee;
28
16
  const method = callee.type === "MemberExpression" ? staticMemberName(callee) : null;
29
17
  return (
@@ -31,17 +19,6 @@ function unassertedCall(node, call) {
31
19
  );
32
20
  }
33
21
 
34
- function wrapperHasOverloads(node, sourceCode) {
35
- if (node.type !== "FunctionDeclaration") return false;
36
- return sourceCode
37
- .getDeclaredVariables(node)
38
- .some((variable) =>
39
- variable.defs.some(
40
- (definition) => definition.type === "FunctionName" && definition.node !== node,
41
- ),
42
- );
43
- }
44
-
45
22
  function directIntrinsic(target, sourceCode) {
46
23
  if (target.type !== "MemberExpression") return false;
47
24
  const name = staticMemberName(target);
@@ -86,6 +63,7 @@ function documentedSignature(node, sourceCode) {
86
63
 
87
64
  module.exports = {
88
65
  unassertedCall,
66
+ assertedContractPreserved,
89
67
  wrapperHasOverloads,
90
68
  directIntrinsic,
91
69
  documentedSignature,
@@ -12,6 +12,7 @@ const { legacyIdentifierCall } = require("./ts-no-function-aliases-legacy");
12
12
  const { exportedDeclarations, reachableTarget } = require("./ts-no-function-aliases-reachable");
13
13
  const {
14
14
  unassertedCall,
15
+ assertedContractPreserved,
15
16
  wrapperHasOverloads,
16
17
  directIntrinsic,
17
18
  documentedSignature,
@@ -63,7 +64,12 @@ function reportIfAlias(node, context, suppressed, declarations) {
63
64
  if (!candidate) continue;
64
65
  if (!isSameArgumentList(node.params, candidate.args)) continue;
65
66
  const reachable = reachableTarget(candidate.target, sourceCode, declarations);
66
- if (!reachable || !signaturePreserved(node, reachable)) continue;
67
+ if (
68
+ !reachable ||
69
+ !signaturePreserved(node, reachable) ||
70
+ !assertedContractPreserved(node, call, candidate, reachable, sourceCode)
71
+ )
72
+ continue;
67
73
  context.report({ node, messageId: "alias" });
68
74
  return;
69
75
  }