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
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/linting-rule.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ body:
- Naming Convention
- Network Functions
- Seeded Randomness
- Stop without call.=False argument
- Unused Definitions
- Useless Loops
default: 0
Expand Down
39 changes: 26 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,21 @@ It offers a wide variety of features, for example:
╰ Absolute Paths (absolute-file-paths):
╰ certain:
╰ Path `/root/x.txt` at 1.1-23
╰ Metadata: totalConsidered: 1, totalUnknown: 0, searchTimeMs: 0, processTimeMs: 0
╰ Metadata: totalConsidered: 1, totalUnknown: 0, searchTimeMs: 0, processTimeMs: 1
╰ Unused Definitions (unused-definitions):
╰ Metadata: totalConsidered: 0, searchTimeMs: 0, processTimeMs: 0
╰ Naming Convention (naming-convention):
╰ Metadata: numMatches: 0, numBreak: 0, searchTimeMs: 0, processTimeMs: 0
╰ Network Functions (network-functions):
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 1
╰ Metadata: totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0
╰ Dataframe Access Validation (dataframe-access-validation):
╰ Metadata: numOperations: 0, numAccesses: 0, totalAccessed: 0, searchTimeMs: 0, processTimeMs: 0
╰ Dead Code (dead-code):
╰ Metadata: consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0
╰ Useless Loops (useless-loop):
╰ Metadata: numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0
╰ Stop without call.=False argument (stop-call):
╰ Metadata: consideredNodes: 0, searchTimeMs: 0, processTimeMs: 0
All queries together required ≈2 ms (1ms accuracy, total 2 ms)
```

Expand All @@ -82,13 +84,13 @@ It offers a wide variety of features, for example:

Query: **linter** (2 ms)\
   ╰ **Deprecated Functions** (deprecated-functions):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 0, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalCalls: 0, totalFunctionDefinitions: 0, searchTimeMs: 1, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;╰ **File Path Validity** (file-path-validity):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 1</code>\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>totalReads: 1, totalUnknown: 0, totalWritesBeforeAlways: 0, totalValid: 0, searchTimeMs: 0, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;╰ **Seeded Randomness** (seeded-randomness):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 0, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consumerCalls: 0, callsWithFunctionProducers: 0, callsWithAssignmentProducers: 0, callsWithNonConstantProducers: 0, callsWithOtherBranchProducers: 0, searchTimeMs: 1, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;╰ **Absolute Paths** (absolute-file-paths):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ certain:\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ Path `/root/x.txt` at 1.1-23\
Expand All @@ -104,12 +106,14 @@ It offers a wide variety of features, for example:
&nbsp;&nbsp;&nbsp;╰ **Dead Code** (dead-code):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 5, searchTimeMs: 0, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;╰ **Useless Loops** (useless-loop):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 1, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>numOfUselessLoops: 0, searchTimeMs: 0, processTimeMs: 0</code>\
&nbsp;&nbsp;&nbsp;╰ **Stop without call.=False argument** (stop-call):\
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;╰ _Metadata_: <code>consideredNodes: 0, searchTimeMs: 0, processTimeMs: 0</code>\
_All queries together required ≈2 ms (1ms accuracy, total 2 ms)_

<details> <summary style="color:gray">Show Detailed Results as Json</summary>

The analysis required _2.0 ms_ (including parsing and normalization and the query) within the generation environment.
The analysis required _2.5 ms_ (including parsing and normalization and the query) within the generation environment.

In general, the JSON contains the Ids of the nodes in question as they are present in the normalized AST or the dataflow graph of flowR.
Please consult the [Interface](https://github.com/flowr-analysis/flowr/wiki/Interface) wiki page for more information on how to get those.
Expand All @@ -126,7 +130,7 @@ It offers a wide variety of features, for example:
".meta": {
"totalCalls": 0,
"totalFunctionDefinitions": 0,
"searchTimeMs": 0,
"searchTimeMs": 1,
"processTimeMs": 0
}
},
Expand All @@ -150,7 +154,7 @@ It offers a wide variety of features, for example:
"totalWritesBeforeAlways": 0,
"totalValid": 0,
"searchTimeMs": 0,
"processTimeMs": 1
"processTimeMs": 0
}
},
"seeded-randomness": {
Expand All @@ -161,7 +165,7 @@ It offers a wide variety of features, for example:
"callsWithAssignmentProducers": 0,
"callsWithNonConstantProducers": 0,
"callsWithOtherBranchProducers": 0,
"searchTimeMs": 0,
"searchTimeMs": 1,
"processTimeMs": 0
}
},
Expand Down Expand Up @@ -233,7 +237,15 @@ It offers a wide variety of features, for example:
"results": [],
".meta": {
"numOfUselessLoops": 0,
"searchTimeMs": 1,
"searchTimeMs": 0,
"processTimeMs": 0
}
},
"stop-call": {
"results": [],
".meta": {
"consideredNodes": 0,
"searchTimeMs": 0,
"processTimeMs": 0
}
}
Expand Down Expand Up @@ -322,7 +334,7 @@ It offers a wide variety of features, for example:
N <- 10
for(i in 1:(N-1)) sum <- sum + i + w
sum
All queries together required ≈1 ms (1ms accuracy, total 2 ms)
All queries together required ≈3 ms (1ms accuracy, total 3 ms)
```


Expand Down Expand Up @@ -402,6 +414,7 @@ It offers a wide variety of features, for example:

```text
https://mermaid.live/view#base64:eyJjb2RlIjoiZmxvd2NoYXJ0IEJUXG4gICAgMChbXCJgIzkxO1JTeW1ib2wjOTM7IHRlc3RcbiAgICAgICgwKVxuICAgICAgKjEuMS00KmBcIl0pXG4gICAlJSBObyBlZGdlcyBmb3VuZCBmb3IgMFxuICAgIDEoW1wiYCM5MTtSU3ltYm9sIzkzOyB0ZXN0ZmlsZXNcbiAgICAgICgxKVxuICAgICAgKjEuNi0xNCpgXCJdKVxuICAgJSUgTm8gZWRnZXMgZm91bmQgZm9yIDFcbiAgICAyW1tcImAjOTE7UkJpbmFyeU9wIzkzOyAvXG4gICAgICAoMilcbiAgICAgICoxLjEtMTQqXG4gICAgKDAsIDEpYFwiXV1cbiAgICBidWlsdC1pbjpfW1wiYEJ1aWx0LUluOlxuL2BcIl1cbiAgICBzdHlsZSBidWlsdC1pbjpfIHN0cm9rZTpncmF5LGZpbGw6Z3JheSxzdHJva2Utd2lkdGg6MnB4LG9wYWNpdHk6Ljg7XG4gICAgMyhbXCJgIzkxO1JTeW1ib2wjOTM7IGV4YW1wbGUuUlxuICAgICAgKDMpXG4gICAgICAqMS4xNi0yNCpgXCJdKVxuICAgJSUgTm8gZWRnZXMgZm91bmQgZm9yIDNcbiAgICA0W1tcImAjOTE7UkJpbmFyeU9wIzkzOyAvXG4gICAgICAoNClcbiAgICAgICoxLjEtMjQqXG4gICAgKDIsIDMpYFwiXV1cbiAgICAyIC0tPnxcInJlYWRzLCBhcmd1bWVudFwifCAwXG4gICAgMiAtLT58XCJyZWFkcywgYXJndW1lbnRcInwgMVxuICAgIDIgLS4tPnxcInJlYWRzLCBjYWxsc1wifCBidWlsdC1pbjpfXG4gICAgbGlua1N0eWxlIDIgc3Ryb2tlOmdyYXk7XG4gICAgNCAtLT58XCJyZWFkcywgYXJndW1lbnRcInwgMlxuICAgIDQgLS0+fFwicmVhZHMsIGFyZ3VtZW50XCJ8IDNcbiAgICA0IC0uLT58XCJyZWFkcywgY2FsbHNcInwgYnVpbHQtaW46X1xuICAgIGxpbmtTdHlsZSA1IHN0cm9rZTpncmF5OyIsIm1lcm1haWQiOnsiYXV0b1N5bmMiOnRydWV9fQ==
Copied mermaid url to clipboard (dataflow: 1ms).
```


Expand Down Expand Up @@ -700,7 +713,7 @@ It offers a wide variety of features, for example:
```


(The analysis required _1.6 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)
(The analysis required _1.5 ms_ (including parse and normalize, using the [tree-sitter](https://github.com/flowr-analysis/flowr/wiki/Engines) engine) within the generation environment.)



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import { resolveByName } from '../../../../../environments/resolve-by-name';
import { expensiveTrace } from '../../../../../../util/log';


function getArgsOfName(argMaps: Map<NodeId, string>, name: string): Set<NodeId> {
/**
*
*/
export function getArgsOfName(argMaps: Map<NodeId, string>, name: string): Set<NodeId> {
return new Set(argMaps.entries().filter(([, v]) => v === name).map(([k]) => k));
}

Expand Down
4 changes: 4 additions & 0 deletions src/documentation/wiki-linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ df[6, "value"]
'useless-loop', 'UselessLoopConfig', 'USELESS_LOOP', 'lint-useless-loop',
'for(i in c(1)) { print(i) }', tagTypes);

rule(knownParser,
'stop-call', 'StopWithCallConfig', 'STOP_WITH_CALL_ARG', 'lint-stop-call',
'stop(42)', tagTypes);

function rule(parser: KnownParser, name: LintingRuleNames, configType: string, ruleType: string, testfile: string, example: string, types: TypeElementInSource[]) {
const rule = LintingRules[name];

Expand Down
4 changes: 3 additions & 1 deletion src/linter/linter-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { NAMING_CONVENTION } from './rules/naming-convention';
import { DATA_FRAME_ACCESS_VALIDATION } from './rules/dataframe-access-validation';
import { USELESS_LOOP } from './rules/useless-loop';
import { NETWORK_FUNCTIONS } from './rules/network-functions';
import { STOP_WITH_CALL_ARG } from './rules/stop-with-call-arg';

/**
* The registry of currently supported linting rules.
Expand All @@ -24,7 +25,8 @@ export const LintingRules = {
'network-functions': NETWORK_FUNCTIONS,
'dataframe-access-validation': DATA_FRAME_ACCESS_VALIDATION,
'dead-code': DEAD_CODE,
'useless-loop': USELESS_LOOP
'useless-loop': USELESS_LOOP,
'stop-call': STOP_WITH_CALL_ARG
} as const;

export type LintingRuleNames = keyof typeof LintingRules;
Expand Down
92 changes: 92 additions & 0 deletions src/linter/rules/stop-with-call-arg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {
LintingPrettyPrintContext,
type LintingResult,
LintingResultCertainty,
type LintingRule,
LintingRuleCertainty
} from '../linter-format';
import { SourceLocation } from '../../util/range';
import type { MergeableRecord } from '../../util/objects';
import { Q } from '../../search/flowr-search-builder';
import { LintingRuleTag } from '../linter-tags';
import { isNotUndefined } from '../../util/assert';
import type { Writable } from 'ts-essentials';
import type { DataflowGraphVertexFunctionCall } from '../../dataflow/graph/vertex';
import { VertexType } from '../../dataflow/graph/vertex';
import { resolveIdToValue } from '../../dataflow/eval/resolve/alias-tracking';
import { getOriginInDfg, OriginType } from '../../dataflow/origin/dfg-get-origin';
import { pMatch } from '../../dataflow/internal/linker';
import { getArgsOfName } from '../../dataflow/internal/process/functions/call/built-in/built-in-try-catch';
import { valueSetGuard } from '../../dataflow/eval/values/general';

export interface StopWithCallResult extends LintingResult {
readonly loc: SourceLocation
}

export type StopWithCallConfig = MergeableRecord;

export interface StopWithCallMetadata extends MergeableRecord {
consideredNodes: number
}

export const STOP_WITH_CALL_ARG = {
createSearch: () => Q.var('stop').filter(VertexType.FunctionCall),
processSearchResult: (elements, _config, { dataflow, analyzer }) => {
const meta: StopWithCallMetadata = {
consideredNodes: 0
};
return {
results:
elements.getElements()
.filter(element => {
//only built-in functions
const origins = getOriginInDfg(dataflow.graph, element.node.info.id);
if(isNotUndefined(origins)) {
const builtIn = origins.every(e => e.type === OriginType.BuiltInFunctionOrigin);
if(!builtIn){
return false;
}
}

const fCall = dataflow.graph.getVertex(element.node.info.id) as DataflowGraphVertexFunctionCall;

//filter out function calls with argument "call." set to false
const stopParamMap = {
'...': '...',
'call.': 'call.',
'domain': 'domain'
};
const mapping = pMatch(fCall.args, stopParamMap);
const mappedToStop = getArgsOfName(mapping, 'call.');
for(const argId of mappedToStop) {
const res = resolveIdToValue(argId, { graph: dataflow.graph, environment: fCall.environment, ctx: analyzer.inspectContext() });
const values = valueSetGuard(res);
if(values?.type === 'set' && values.elements.length !== 0){
if(values.elements[0].type === 'logical'){
return values.elements[0].value;
}
}
}
return true;
})
.map(element => ({
certainty: LintingResultCertainty.Uncertain,
involvedId: element.node.info.id,
loc: SourceLocation.fromNode(element.node)
}))
.filter(element => isNotUndefined(element.loc)) as Writable<StopWithCallResult>[],
'.meta': meta
};
},
prettyPrint: {
[LintingPrettyPrintContext.Query]: result => `Code at ${SourceLocation.format(result.loc)}`,
[LintingPrettyPrintContext.Full]: result => `Code at ${SourceLocation.format(result.loc)} does call stop without setting call. to FALSE`,
},
info: {
name: 'Stop without call.=False argument',
tags: [LintingRuleTag.Smell],
certainty: LintingRuleCertainty.BestEffort,
description: 'Checks whether stop calls without call. argument set to FALSE are used.',
defaultConfig: {}
}
} as const satisfies LintingRule<StopWithCallResult, StopWithCallMetadata, StopWithCallConfig>;
26 changes: 26 additions & 0 deletions test/functionality/linter/lint-stop-call.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { describe } from 'vitest';
import { withTreeSitter } from '../_helper/shell';
import { assertLinter } from '../_helper/linter';
import { SourceRange } from '../../../src/util/range';
import { LintingResultCertainty } from '../../../src/linter/linter-format';

describe('flowR linter', withTreeSitter(parser => {
describe('stop with call', () => {
assertLinter('none', parser, 'x <- 1', 'stop-call', []);
assertLinter('none', parser, 'stop(x)', 'stop-call', [
{
certainty: LintingResultCertainty.Uncertain,
loc: SourceRange.from(1, 1, 1, 7)
}
]);
assertLinter('none', parser, 'stop(x, call.=FALSE)', 'stop-call', []);
assertLinter('none', parser, 'stop <- function(x, call.){return 0}\nstop(3, call.=TRUE)', 'stop-call', []);
assertLinter('none', parser, 'stop(y, call.=TRUE)', 'stop-call', [
{
certainty: LintingResultCertainty.Uncertain,
loc: SourceRange.from(1, 1, 1, 19)
}
]);
assertLinter('none', parser, 'x <- FALSE\nstop(y, call.=x)', 'stop-call', []);
});
}));
Loading
Loading