@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
103 lines • 5.74 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FILE_PATH_VALIDITY = void 0;
const linter_format_1 = require("../linter-format");
const flowr_search_builder_1 = require("../../search/flowr-search-builder");
const dfg_1 = require("../../util/mermaid/dfg");
const dependencies_query_format_1 = require("../../queries/catalog/dependencies-query/dependencies-query-format");
const built_in_source_1 = require("../../dataflow/internal/process/functions/call/built-in/built-in-source");
const logic_1 = require("../../util/logic");
const retriever_1 = require("../../r-bridge/retriever");
const read_functions_1 = require("../../queries/catalog/dependencies-query/function-info/read-functions");
const write_functions_1 = require("../../queries/catalog/dependencies-query/function-info/write-functions");
const happens_before_1 = require("../../control-flow/happens-before");
const linter_tags_1 = require("../linter-tags");
const search_enrichers_1 = require("../../search/search-executor/search-enrichers");
exports.FILE_PATH_VALIDITY = {
createSearch: (config) => flowr_search_builder_1.Q.fromQuery({
type: 'dependencies',
// we only want to check read and write functions, so we explicitly clear all others
ignoreDefaultFunctions: true,
readFunctions: read_functions_1.ReadFunctions.concat(config.additionalReadFunctions),
writeFunctions: write_functions_1.WriteFunctions.concat(config.additionalWriteFunctions)
}).with(search_enrichers_1.Enrichment.CfgInformation),
processSearchResult: (elements, config, data) => {
const cfg = elements.enrichmentContent(search_enrichers_1.Enrichment.CfgInformation).cfg.graph;
const metadata = {
totalReads: 0,
totalUnknown: 0,
totalWritesBeforeAlways: 0,
totalValid: 0
};
return {
results: elements.getElements().flatMap(element => {
const results = elements.enrichmentContent(search_enrichers_1.Enrichment.QueryData).queries['dependencies'];
const matchingRead = results.readData.find(r => r.nodeId == element.node.info.id);
if (!matchingRead) {
return [];
}
metadata.totalReads++;
const range = element.node.info.fullRange;
// check if we can't parse the file path statically
if (matchingRead.source === dependencies_query_format_1.Unknown) {
metadata.totalUnknown++;
if (config.includeUnknown) {
return [{
range,
filePath: dependencies_query_format_1.Unknown,
certainty: linter_format_1.LintingResultCertainty.Uncertain
}];
}
else {
return [];
}
}
// check if any write to the same file happens before the read, and exclude this case if so
const writesToFile = results.writtenData.filter(r => samePath(r.destination, matchingRead.source, data.config.solver.resolveSource?.ignoreCapitalization));
const writesBefore = writesToFile.map(w => (0, happens_before_1.happensBefore)(cfg, w.nodeId, element.node.info.id));
if (writesBefore.some(w => w === logic_1.Ternary.Always)) {
metadata.totalWritesBeforeAlways++;
return [];
}
// check if the file exists!
const paths = (0, built_in_source_1.findSource)(data.config.solver.resolveSource, matchingRead.source, {
referenceChain: element.node.info.file ? [(0, retriever_1.requestFromInput)(`file://${element.node.info.file}`)] : []
});
if (paths && paths.length) {
metadata.totalValid++;
return [];
}
return [{
range,
filePath: matchingRead.source,
certainty: writesBefore && writesBefore.length && writesBefore.every(w => w === logic_1.Ternary.Maybe) ? linter_format_1.LintingResultCertainty.Uncertain : linter_format_1.LintingResultCertainty.Certain
}];
}),
'.meta': metadata
};
},
info: {
name: 'File Path Validity',
description: 'Checks whether file paths used in read and write operations are valid and point to existing files.',
// checks all found paths for whether they're valid to ensure correctness, but doesn't handle non-constant paths so not all will be returned
certainty: linter_format_1.LintingRuleCertainty.BestEffort,
tags: [linter_tags_1.LintingRuleTag.Robustness, linter_tags_1.LintingRuleTag.Reproducibility, linter_tags_1.LintingRuleTag.Bug],
defaultConfig: {
additionalReadFunctions: [],
additionalWriteFunctions: [],
includeUnknown: false
}
},
prettyPrint: {
[linter_format_1.LintingPrettyPrintContext.Query]: result => `Path \`${result.filePath}\` at ${(0, dfg_1.formatRange)(result.range)}`,
[linter_format_1.LintingPrettyPrintContext.Full]: result => `Path \`${result.filePath}\` at ${(0, dfg_1.formatRange)(result.range)} does not point to a valid file`
}
};
function samePath(a, b, ignoreCapitalization) {
if (ignoreCapitalization === true) {
a = a.toLowerCase();
b = b.toLowerCase();
}
return a === b;
}
//# sourceMappingURL=file-path-validity.js.map