Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/rules/no-undocumented-throws.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,23 @@ module.exports = createRule({
const calleeDeclaration = getCalleeDeclaration(services, node);
if (!calleeDeclaration) return;

const signature = getCallSignature(
services,
services.tsNodeToESTreeNodeMap.get(calleeDeclaration)
);
switch (node.type) {
case AST_NODE_TYPES.CallExpression:
case AST_NODE_TYPES.NewExpression: {
if (services.tsNodeToESTreeNodeMap.has(calleeDeclaration)) {
const signature = getCallSignature(
services,
services.tsNodeToESTreeNodeMap.get(calleeDeclaration)
);

const returnType = signature?.getReturnType();
if (returnType && isGeneratorLike(returnType)) return;
const returnType = signature?.getReturnType();
Comment on lines +149 to +155
Copy link

Copilot AI Jun 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Generator-like checks only run when the declaration is in tsNodeToESTreeNodeMap. If the signature isn’t mapped there, generator return types won’t be skipped. Consider obtaining the signature unconditionally or adjusting the guard.

Suggested change
if (services.tsNodeToESTreeNodeMap.has(calleeDeclaration)) {
const signature = getCallSignature(
services,
services.tsNodeToESTreeNodeMap.get(calleeDeclaration)
);
const returnType = signature?.getReturnType();
if (returnType && isGeneratorLike(returnType)) return;
const returnType = signature?.getReturnType();
const signature = checker.getResolvedSignature(calleeDeclaration);
if (signature) {
const returnType = signature.getReturnType();

Copilot uses AI. Check for mistakes.

if (returnType && isGeneratorLike(returnType)) return;
}
break;
}
default:
break;
}

/** @type {import('typescript').JSDocThrowsTag[]} */
const comments = [];
Expand Down
13 changes: 7 additions & 6 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,11 +350,14 @@ const getCalleeDeclaration = (services, node) => {
const calleeNode = getCallee(node);
if (!calleeNode) return null;

const symbol = services
.getTypeAtLocation(calleeNode)
.symbol;
const type = services.getTypeAtLocation(calleeNode);

if (!symbol || !symbol.valueDeclaration) {
if (type.symbol?.valueDeclaration) {
declaration = type.symbol.valueDeclaration;
} else if (type.symbol?.declarations?.length) {
// If there are multiple declarations, we take the first one.
declaration = type.symbol.declarations[0];
Comment on lines +358 to +359
Copy link

Copilot AI Jun 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Although you guard for declarations?.length above, consider capturing const decls = type.symbol.declarations to avoid reaccessing the optional chain—this also makes the assignment clearer and prevents any future undefined access.

Suggested change
// If there are multiple declarations, we take the first one.
declaration = type.symbol.declarations[0];
const decls = type.symbol.declarations;
// If there are multiple declarations, we take the first one.
declaration = decls[0];

Copilot uses AI. Check for mistakes.

} else {
const declarations = services
.getSymbolAtLocation(calleeNode)
?.declarations;
Expand All @@ -363,8 +366,6 @@ const getCalleeDeclaration = (services, node) => {

// If there are multiple declarations, we take the first one.
declaration = declarations[0];
} else {
declaration = symbol.valueDeclaration;
}
}
if (!declaration) return null;
Expand Down
Loading