Skip to content

Commit

Permalink
Merge pull request #10 from valtzu/fn-arg-type-linting
Browse files Browse the repository at this point in the history
Lint types of call arguments
  • Loading branch information
valtzu authored Dec 30, 2024
2 parents 81d41ac + 4cf3292 commit 7e234ae
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 14 deletions.
28 changes: 23 additions & 5 deletions src/linter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { EditorState } from "@codemirror/state";
import { Diagnostic, linter } from "@codemirror/lint";
import { syntaxTree } from "@codemirror/language";
import { getExpressionLanguageConfig, resolveFunctionDefinition, resolveIdentifier, resolveTypes } from "./utils";
import { ELScalar } from "./types";

/**
* @internal
Expand Down Expand Up @@ -36,14 +37,23 @@ export const expressionLanguageLinterSource = (state: EditorState) => {
return;
}

let i = 0;
let n = node.node.firstChild;
while (n) {
if (n.name !== 'BlockComment' && ++i > args.length) {
for (let n = node.node.firstChild, i = 0; n != null; n = n.nextSibling) {
if (n.name === 'BlockComment') {
continue;
}

if (i > args.length - 1) {
diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `Unexpected argument` });
continue;
}

n = n.nextSibling;
const typesUsed = Array.from(resolveTypes(state, n, config, true));
const typesExpected = args[i].type;

if (typesExpected && !typesExpected.includes(ELScalar.Any) && !typesUsed.some(x => typesExpected.includes(x))) {
diagnostics.push({ from: n.from, to: n.to, severity: 'error', message: `<code>${typesExpected.join('|')}</code> expected, got <code>${typesUsed.join('|')}</code>` });
}
i++;
}

break;
Expand Down Expand Up @@ -74,6 +84,14 @@ export const expressionLanguageLinterSource = (state: EditorState) => {
}
});

diagnostics.forEach(d => {
d.renderMessage = () => {
const span = document.createElement('span');
span.innerHTML = d.message;
return span;
};
});

return diagnostics;
};

Expand Down
4 changes: 2 additions & 2 deletions src/syntax.grammar
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ list {
(expression ("," expression)*)?
}

Object {
Object[t="object"] {
"{" (expression ":" expression ("," expression ":" expression)*)? "}"
}

ArrayAccess {
expression !left "[" expression "]"
}

Array {
Array[t="array"] {
"[" list "]"
}

Expand Down
5 changes: 5 additions & 0 deletions src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ export const baseTheme = EditorView.baseTheme({
opacity: 0.5,
fontStyle: "inherit !important",
},
"code": {
fontSize: ".8em",
fontStyle: "monospace",
backgroundColor: "rgba(127, 127, 127, .3)",
},
});
4 changes: 2 additions & 2 deletions test/test-complete.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ function get(doc, conf = {}) {
types: {
"custom44": {
identifiers: [
{name: "property11", type: ["mixed"]},
{name: "property22", type: ["mixed"]},
{name: "property11", type: ["any"]},
{name: "property22", type: ["any"]},
],
functions: [
{name: "firstMethod", args: [], returnType: ["custom44"]},
Expand Down
31 changes: 28 additions & 3 deletions test/test-linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const config = {
types: {
"custom44": {
identifiers: [
{name: "property11", type: ["mixed"]},
{name: "property22", type: ["mixed"]},
{name: "property11", type: ["any"]},
{name: "property22", type: ["any"]},
],
functions: [
{name: "firstMethod", args: [], returnType: ["custom44"]},
Expand All @@ -23,7 +23,8 @@ const config = {
],
functions: [
{name: "smh", args: [], returnType: ["string"]},
{name: "smash_my_head", args: [{name: "object"}]},
{name: "any_fn", args: [{name: "anything", type: ["any"]}], returnType: ["any"]},
{name: "smash_my_head", args: [{name: "object", type: ["object"]}]},
{name: "getObject", returnType: ["custom44"]},
],
};
Expand Down Expand Up @@ -94,6 +95,30 @@ describe("Expression language linting", () => {
ist(diagnostics[0].message, "Unexpected argument");
});

it("complains about wrong argument type using constant", () => {
const diagnostics = get("smash_my_head(5)");

ist(diagnostics.length, 1);
ist(diagnostics[0].from, 14);
ist(diagnostics[0].to, 15);
ist(diagnostics[0].message, "<code>object</code> expected, got <code>number</code>");
});

it("complains about wrong argument type using resolved type", () => {
const diagnostics = get("smash_my_head(smh())");

ist(diagnostics.length, 1);
ist(diagnostics[0].from, 14);
ist(diagnostics[0].to, 19);
ist(diagnostics[0].message, "<code>object</code> expected, got <code>string</code>");
});

it('does not complain about putting "wrong" argument type to any', () => {
const diagnostics = get("any_fn(smh())");

ist(diagnostics.length, 0);
});

it("comments ignored in arguments", () => {
const diagnostics = get("smh(/* comment */)");

Expand Down
4 changes: 2 additions & 2 deletions test/test-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const config = {
types: {
"custom44": {
identifiers: [
{name: "property11", type: ["mixed"]},
{name: "property22", type: ["mixed"]},
{name: "property11", type: ["any"]},
{name: "property22", type: ["any"]},
],
functions: [
{name: "firstMethod", args: [], returnType: ["custom44"]},
Expand Down

0 comments on commit 7e234ae

Please sign in to comment.