Skip to content

Commit

Permalink
fix: false positive with no-unused-message-ids from external violatio…
Browse files Browse the repository at this point in the history
…n reporting function (#286)
  • Loading branch information
bmish committed Aug 12, 2022
1 parent 3657287 commit 01d0eef
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 37 deletions.
52 changes: 33 additions & 19 deletions lib/rules/no-unused-message-ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ module.exports = {

const messageIdsUsed = new Set();
let contextIdentifiers;
let shouldPerformUnusedCheck = true;
let hasSeenUnknownMessageId = false;
let hasSeenViolationReport = false;

const messageIdNodes = utils.getMessageIdNodes(info, scopeManager);
if (!messageIdNodes) {
Expand All @@ -44,22 +45,29 @@ module.exports = {
},

'Program:exit'() {
if (shouldPerformUnusedCheck) {
const messageIdNodesUnused = messageIdNodes.filter(
(node) =>
!messageIdsUsed.has(utils.getKeyName(node, context.getScope()))
);

// Report any messageIds that were never used.
for (const messageIdNode of messageIdNodesUnused) {
context.report({
node: messageIdNode,
messageId: 'unusedMessage',
data: {
messageId: utils.getKeyName(messageIdNode, context.getScope()),
},
});
}
if (hasSeenUnknownMessageId || !hasSeenViolationReport) {
/*
Bail out when the rule is likely to have false positives.
- If we saw a dynamic/unknown messageId
- If we couldn't find any violation reporting code, likely because a helper function from an external file is handling this
*/
return;
}

const messageIdNodesUnused = messageIdNodes.filter(
(node) =>
!messageIdsUsed.has(utils.getKeyName(node, context.getScope()))
);

// Report any messageIds that were never used.
for (const messageIdNode of messageIdNodesUnused) {
context.report({
node: messageIdNode,
messageId: 'unusedMessage',
data: {
messageId: utils.getKeyName(messageIdNode, context.getScope()),
},
});
}
},

Expand All @@ -76,6 +84,8 @@ module.exports = {
return;
}

hasSeenViolationReport = true;

const reportMessagesAndDataArray =
utils.collectReportViolationAndSuggestionData(reportInfo);
for (const { messageId } of reportMessagesAndDataArray.filter(
Expand All @@ -90,7 +100,7 @@ module.exports = {
values.some((val) => val.type !== 'Literal')
) {
// When a dynamic messageId is used and we can't detect its value, disable the rule to avoid false positives.
shouldPerformUnusedCheck = false;
hasSeenUnknownMessageId = true;
}
values.forEach((val) => messageIdsUsed.add(val.value));
}
Expand All @@ -101,17 +111,21 @@ module.exports = {
// In order to reduce false positives, we will also check for messageId properties anywhere in the file.
// This is helpful especially in the event that helper functions are used for reporting violations.
if (node.key.type === 'Identifier' && node.key.name === 'messageId') {
hasSeenViolationReport = true;

const values =
node.value.type === 'Literal'
? [node.value]
: utils.findPossibleVariableValues(node.value, scopeManager);

if (
values.length === 0 ||
values.some((val) => val.type !== 'Literal')
) {
// When a dynamic messageId is used and we can't detect its value, disable the rule to avoid false positives.
shouldPerformUnusedCheck = false;
hasSeenUnknownMessageId = true;
}

values.forEach((val) => messageIdsUsed.add(val.value));
}
},
Expand Down
27 changes: 9 additions & 18 deletions tests/lib/rules/no-unused-message-ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ ruleTester.run('no-unused-message-ids', rule, {
}
};
`,
// Ignore when we couldn't find any calls to `context.report()`, likely because an external helper function is in use.
`
module.exports = {
meta: { messages: { foo: 'bar' } },
create(context) {}
};
`,
],

invalid: [
Expand Down Expand Up @@ -313,29 +320,13 @@ ruleTester.run('no-unused-message-ids', rule, {
},
],
},
{
// messageId unused with no reports
code: `
module.exports = {
meta: { messages: { foo: 'hello world' } },
create(context) { }
};
`,
errors: [
{
messageId: 'unusedMessage',
data: { messageId: 'foo' },
type: 'Property',
},
],
},
{
// messageId unused with meta.messages in variable
code: `
const messages = { foo: 'hello world' };
module.exports = {
meta: { messages },
create(context) { }
create(context) { context.report({node, messageId: 'other'}); }
};
`,
errors: [
Expand All @@ -353,7 +344,7 @@ ruleTester.run('no-unused-message-ids', rule, {
const extraMeta = { messages: { ...extraMessages } };
module.exports = {
meta: { ...extraMeta },
create(context) { }
create(context) { context.report({node, messageId: 'other'}); }
};
`,
errors: [
Expand Down

0 comments on commit 01d0eef

Please sign in to comment.