@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
106 lines • 5.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ROXYGEN_ARGS = void 0;
const linter_format_1 = require("../linter-format");
const range_1 = require("../../util/range");
const flowr_search_builder_1 = require("../../search/flowr-search-builder");
const linter_tags_1 = require("../linter-tags");
const assert_1 = require("../../util/assert");
const vertex_1 = require("../../dataflow/graph/vertex");
const roxygen_ast_1 = require("../../r-bridge/roxygen2/roxygen-ast");
const r_function_definition_1 = require("../../r-bridge/lang-4.x/ast/model/nodes/r-function-definition");
const search_enrichers_1 = require("../../search/search-executor/search-enrichers");
function calculateArgumentDiff(inheritedParams, functionParam, roxygenParam) {
//match documented against existing params
let underDocumented = new Set(functionParam);
const notOverDocumented = underDocumented.has('...');
let overDocumented = new Set(roxygenParam);
const commonParams = underDocumented.intersection(overDocumented);
underDocumented = underDocumented.difference(commonParams);
overDocumented = overDocumented.difference(commonParams);
//case: '...', overdocumentation not possible
if (notOverDocumented) {
//if still remaining overdocumented parameters, "..." doesn't need to be documented
if (overDocumented.size > 0) {
underDocumented.delete('...');
}
//can't be overdocumented
overDocumented.clear();
}
//inherited params removed from list of overdocumented params
overDocumented = overDocumented.difference(new Set(inheritedParams));
return underDocumented.size === 0 && overDocumented.size === 0 ? false : { under: Array.from(underDocumented), over: Array.from(overDocumented) };
}
exports.ROXYGEN_ARGS = {
createSearch: () => flowr_search_builder_1.Q.all()
.filter(vertex_1.VertexType.FunctionDefinition)
.with(search_enrichers_1.Enrichment.Roxygen),
processSearchResult: (elements, _config) => {
return {
results: elements.getElements()
.map(element => ({
element,
underDocumented: [],
overDocumented: []
}))
.filter(({ element, underDocumented, overDocumented }) => {
const roxygen = (0, search_enrichers_1.enrichmentContent)(element, search_enrichers_1.Enrichment.Roxygen);
if (!roxygen.documentation.length) {
return false;
}
//get parameter names
const params = roxygen.tags[roxygen_ast_1.KnownRoxygenTags.Param] ?? [];
const functionParamNames = getParameters(element.node).map(p => p.name.content.toString());
const inheritedParams = params.filter(tag => tag.inherited).map(tag => tag.value.name);
const roxygenParamNames = params.map(tag => tag.value.name);
const result = calculateArgumentDiff(inheritedParams ?? [], functionParamNames, roxygenParamNames);
if (result === false) {
return false;
}
underDocumented.push(...result.under);
overDocumented.push(...result.over);
return true;
})
.map(({ element, overDocumented, underDocumented }) => ({
certainty: linter_format_1.LintingResultCertainty.Uncertain,
involvedId: element.node.info.id,
loc: range_1.SourceLocation.fromNode(element.node),
underDocumented: underDocumented,
overDocumented: overDocumented
}))
.filter(element => (0, assert_1.isNotUndefined)(element.loc)),
'.meta': {}
};
},
prettyPrint: {
[linter_format_1.LintingPrettyPrintContext.Query]: result => `Function at ${range_1.SourceLocation.format(result.loc)} has ${describeArgumentDiff(result)}`,
[linter_format_1.LintingPrettyPrintContext.Full]: result => `Function at ${range_1.SourceLocation.format(result.loc)} has ${describeArgumentDiff(result)}`
},
info: {
name: 'Roxygen Arguments',
tags: [linter_tags_1.LintingRuleTag.Smell, linter_tags_1.LintingRuleTag.Documentation, linter_tags_1.LintingRuleTag.Style],
certainty: linter_format_1.LintingRuleCertainty.BestEffort,
description: 'Checks whether a function has undocumented or overdocumented parameters',
defaultConfig: {}
}
};
function getParameters(node) {
return r_function_definition_1.RFunctionDefinition.is(node) ? node.parameters : [];
}
function quoteAll(names) {
return names.map(name => `'${name}'`).join(', ');
}
/** Describes the concrete mismatch between roxygen `@param` documentation and the actual function arguments. */
function describeArgumentDiff(result) {
const parts = [];
const under = result.underDocumented ?? [];
const over = result.overDocumented ?? [];
if (under.length > 0) {
parts.push(`undocumented argument${under.length > 1 ? 's' : ''} ${quoteAll(under)}`);
}
if (over.length > 0) {
parts.push(`documented @param${over.length > 1 ? 's' : ''} with no matching argument: ${quoteAll(over)}`);
}
return parts.join(', and ');
}
//# sourceMappingURL=roxygen-arguments.js.map