Skip to content

Commit

Permalink
fix(no-missing-syntax, no-restricted-syntax): support "any" with …
Browse files Browse the repository at this point in the history
…comment including global comments
  • Loading branch information
brettz9 committed May 30, 2021
1 parent bb51f15 commit dc641cc
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 19 deletions.
64 changes: 64 additions & 0 deletions README.md
Expand Up @@ -8185,6 +8185,21 @@ function quux () {
}
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock[postDelimiter=\"\"]:has(JsdocTypeUnion > JsdocTypeName[value=\"Bar\"]:nth-child(1))","context":"FunctionDeclaration","minimum":2}]}]
// Message: Syntax is required: FunctionDeclaration with JsdocBlock[postDelimiter=""]:has(JsdocTypeUnion > JsdocTypeName[value="Bar"]:nth-child(1))

/**
* @param ab
* @param cd
*/
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
// Message: Require names matching `/^opt_/i`.

/**
* @param ab
* @param cd
*/
function quux () {}
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
// Message: Require names matching `/^opt_/i`.
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -8219,6 +8234,19 @@ function baz () {

}
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock[postDelimiter=\"\"]:has(JsdocTypeUnion > JsdocTypeName[value=\"Bar\"]:nth-child(1))","context":"FunctionDeclaration","minimum":2}]}]

/**
* @param opt_a
* @param opt_b
*/
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]

/**
* @param opt_a
* @param opt_b
*/
function quux () {}
// "jsdoc/no-missing-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Require names matching `/^opt_/i`."}]}]
````


Expand Down Expand Up @@ -8513,6 +8541,29 @@ function quux () {
function a () {}
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"FunctionDeclaration","message":"Only allowing names not matching `/^opt_/i`."}]}]
// Message: Only allowing names not matching `/^opt_/i`.

/**
* @param opt_a
* @param opt_b
*/
function a () {}
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
// Message: Only allowing names not matching `/^opt_/i`.

/**
* @param opt_a
* @param opt_b
*/
function a () {}
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/not-this/])","context":"any","message":"Only allowing names not matching `/^not-this/i`."},{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
// Message: Only allowing names not matching `/^opt_/i`.

/**
* @param opt_a
* @param opt_b
*/
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
// Message: Only allowing names not matching `/^opt_/i`.
````

The following patterns are not considered problems:
Expand All @@ -8533,6 +8584,19 @@ function quux () {

}
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock[postDelimiter=\"\"]:has(JsdocTypeUnion > JsdocTypeName[value=\"Foo\"]:nth-child(1))","context":"FunctionDeclaration"}]}]

/**
* @param ab
* @param cd
*/
function a () {}
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]

/**
* @param ab
* @param cd
*/
// "jsdoc/no-restricted-syntax": ["error"|"warn", {"contexts":[{"comment":"JsdocBlock:has(JsdocTag[name=/opt_/])","context":"any","message":"Only allowing names not matching `/^opt_/i`."}]}]
````


Expand Down
51 changes: 36 additions & 15 deletions src/iterateJsdoc.js
Expand Up @@ -681,7 +681,7 @@ const iterate = (
info,
indent, jsdoc,
ruleConfig, context, lines, jsdocNode, node, settings,
sourceCode, iterator, state, iteratingAll,
sourceCode, iterator, state, lastComment, iteratingAll,
) => {
const report = makeReport(context, jsdocNode);

Expand Down Expand Up @@ -745,10 +745,12 @@ const getIndentAndJSDoc = function (lines, jsdocNode) {
*
* @param {JsdocVisitor} iterator
* @param {{meta: any}} ruleConfig
* @param contexts
*/
const iterateAllJsdocs = (iterator, ruleConfig) => {
const iterateAllJsdocs = (iterator, ruleConfig, contexts) => {
const trackedJsdocs = [];

let handler;
let settings;
const callIterator = (context, node, jsdocNodes, state, lastCall) => {
const sourceCode = context.getSourceCode();
Expand All @@ -764,12 +766,21 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
lines, jsdocNode,
);

let lastComment;
if (contexts && contexts.every(({comment}) => {
lastComment = comment;

return handler(comment, jsdoc) === false;
})) {
return;
}

iterate(
null,
lastComment ? {comment: lastComment, selector: node?.type} : null,
indent, jsdoc,
ruleConfig, context, lines, jsdocNode, node,
settings, sourceCode, iterator,
state, true,
state, lastComment, true,
);
});
if (lastCall && ruleConfig.exit) {
Expand All @@ -788,6 +799,9 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
if (!settings) {
return {};
}
if (contexts) {
handler = commentHandler(settings);
}

const state = {};

Expand All @@ -799,11 +813,11 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
return;
}

const comment = getJSDocComment(sourceCode, node, settings);
if (trackedJsdocs.includes(comment)) {
const commentNode = getJSDocComment(sourceCode, node, settings);
if (trackedJsdocs.includes(commentNode)) {
return;
}
if (!comment) {
if (!commentNode) {
if (ruleConfig.nonComment) {
ruleConfig.nonComment({
node,
Expand All @@ -814,8 +828,8 @@ const iterateAllJsdocs = (iterator, ruleConfig) => {
return;
}

trackedJsdocs.push(comment);
callIterator(context, node, [comment], state);
trackedJsdocs.push(commentNode);
callIterator(context, node, [commentNode], state);
},
'Program:exit' () {
const allComments = sourceCode.getAllComments();
Expand Down Expand Up @@ -911,18 +925,25 @@ export default function iterateJsdoc (iterator, ruleConfig) {
* a list with parser callback function.
*/
create (context) {
const settings = getSettings(context);
if (!settings) {
return {};
}

let contexts;
if (ruleConfig.contextDefaults || ruleConfig.contextSelected) {
contexts = jsdocUtils.enforcedContexts(context, ruleConfig.contextDefaults);
if (contexts?.includes('any')) {
return iterateAllJsdocs(iterator, ruleConfig).create(context);
const hasPlainAny = contexts?.includes('any');
const hasObjectAny = !hasPlainAny && contexts?.find((ctxt) => {
return ctxt?.context === 'any';
});
if (hasPlainAny || hasObjectAny) {
return iterateAllJsdocs(
iterator, ruleConfig, hasObjectAny ? contexts : null,
).create(context);
}
}
const sourceCode = context.getSourceCode();
const settings = getSettings(context);
if (!settings) {
return {};
}
const {lines} = sourceCode;

const state = {};
Expand Down
12 changes: 9 additions & 3 deletions src/rules/noMissingSyntax.js
Expand Up @@ -52,9 +52,15 @@ export default iterateJsdoc(({
const contextStr = typeof cntxt === 'object' ? cntxt.context : cntxt;
const comment = cntxt?.comment ?? '';

if (!state.selectorMap[contextStr] ||
!state.selectorMap[contextStr][comment] ||
state.selectorMap[contextStr][comment] < (cntxt?.minimum ?? 1)
const contextKey = contextStr === 'any' ? undefined : contextStr;

if (
(!state.selectorMap[contextKey] ||
!state.selectorMap[contextKey][comment] ||
state.selectorMap[contextKey][comment] < (cntxt?.minimum ?? 1)) &&
(contextStr !== 'any' || Object.values(state.selectorMap).every((cmmnt) => {
return !cmmnt[comment] || cmmnt[comment] < (cntxt?.minimum ?? 1);
}))
) {
const message = cntxt?.message ?? 'Syntax is required: {{context}}' +
(comment ? ' with {{comment}}' : '');
Expand Down
3 changes: 2 additions & 1 deletion src/rules/noRestrictedSyntax.js
Expand Up @@ -14,7 +14,8 @@ export default iterateJsdoc(({

const foundContext = contexts.find((cntxt) => {
return cntxt === selector ||
typeof cntxt === 'object' && selector === cntxt.context &&
typeof cntxt === 'object' &&
(cntxt.context === 'any' || selector === cntxt.context) &&
comment === cntxt.comment;
});

Expand Down
78 changes: 78 additions & 0 deletions test/rules/assertions/noMissingSyntax.js
Expand Up @@ -115,6 +115,49 @@ export default {
],
}],
},
{
code: `
/**
* @param ab
* @param cd
*/
`,
errors: [{
line: 1,
message: 'Require names matching `/^opt_/i`.',
}],
options: [{
contexts: [
{
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
context: 'any',
message: 'Require names matching `/^opt_/i`.',
},
],
}],
},
{
code: `
/**
* @param ab
* @param cd
*/
function quux () {}
`,
errors: [{
line: 1,
message: 'Require names matching `/^opt_/i`.',
}],
options: [{
contexts: [
{
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
context: 'any',
message: 'Require names matching `/^opt_/i`.',
},
],
}],
},
],
valid: [
{
Expand Down Expand Up @@ -168,5 +211,40 @@ export default {
],
}],
},
{
code: `
/**
* @param opt_a
* @param opt_b
*/
`,
options: [{
contexts: [
{
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
context: 'any',
message: 'Require names matching `/^opt_/i`.',
},
],
}],
},
{
code: `
/**
* @param opt_a
* @param opt_b
*/
function quux () {}
`,
options: [{
contexts: [
{
comment: 'JsdocBlock:has(JsdocTag[name=/opt_/])',
context: 'any',
message: 'Require names matching `/^opt_/i`.',
},
],
}],
},
],
};

0 comments on commit dc641cc

Please sign in to comment.