Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes warnings on non declared labels #114

Merged
merged 1 commit into from
Oct 17, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ function warnOnUndeclaredLabels(
if (dbSchema.labels && dbSchema.relationshipTypes) {
const dbLabels = new Set(dbSchema.labels);
const dbRelationshipTypes = new Set(dbSchema.relationshipTypes);
const labelsAndRelTypes = parsingResult.collectedLabelOrRelTypes;

parsingResult.collectedLabelOrRelTypes.forEach((labelOrRelType) => {
labelsAndRelTypes.forEach((labelOrRelType) => {
const warning = detectNonDeclaredLabel(
labelOrRelType,
dbLabels,
Expand Down
18 changes: 7 additions & 11 deletions packages/language-support/src/parserWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@ import CypherLexer from './generated-parser/CypherLexer';

import CypherParser, {
ClauseContext,
CreateClauseContext,
ExpressionContext,
LabelNameContext,
LabelNameIsContext,
LabelOrRelTypeContext,
MergeClauseContext,
StatementsContext,
VariableContext,
} from './generated-parser/CypherParser';
Expand Down Expand Up @@ -53,15 +50,14 @@ function getLabelType(ctx: ParserRuleContext): LabelType {
}

function couldCreateNewLabel(ctx: ParserRuleContext): boolean {
const parent = findParent(
ctx,
(ctx) => ctx instanceof ClauseContext || ctx instanceof ExpressionContext,
);
const parent = findParent(ctx, (ctx) => ctx instanceof ClauseContext);

return (
parent instanceof CreateClauseContext ||
parent instanceof MergeClauseContext
);
if (parent instanceof ClauseContext) {
const clause = parent;
return isDefined(clause.mergeClause()) || isDefined(clause.createClause());
} else {
return false;
}
}

export type LabelOrRelType = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,29 +188,122 @@ describe('Syntactic validation spec', () => {
]);
});

test('Syntax validation warns on missing relationship type when database can be contacted', () => {
const query = `MATCH (n)-[r:Rel3]->(m) RETURN n`;
test('Syntax validation warns on missing rel type when database can be contacted', () => {
const query = `MATCH (n)-[:Rel]->(m) RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Rel3'], relationshipTypes: ['Rel1', 'Rel2'] },
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([
{
message:
"Relationship type Rel is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 17,
start: 13,
end: 15,
start: 12,
},
range: {
end: {
character: 15,
line: 0,
},
start: {
character: 12,
line: 0,
},
},
severity: 2,
},
]);
});

test('Syntax validation warns on missing label in a WHERE node predicate', () => {
const query = `MATCH (n WHERE n IS Person) RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([
{
message:
"Relationship type Rel3 is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
"Label Person is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 26,
start: 20,
},
range: {
end: {
character: 17,
character: 26,
line: 0,
},
start: {
character: 13,
character: 20,
line: 0,
},
},
severity: 2,
},
]);
});

test('Syntax validation warns on missing label in a WHERE clause if no labels or rel types match it', () => {
const query = `MATCH (n) WHERE n IS Person RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Cow'] },
}),
).toEqual([
{
message:
"Label or relationship type Person is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 27,
start: 21,
},
range: {
end: {
character: 27,
line: 0,
},
start: {
character: 21,
line: 0,
},
},
severity: 2,
},
]);
});

test('Syntax validation warns on missing rel type in a WHERE clause if no labels or rel types match it', () => {
const query = `MATCH (n)-[r]-(m) WHERE r IS Rel RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Cow'] },
}),
).toEqual([
{
message:
"Label or relationship type Rel is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 32,
start: 29,
},
range: {
end: {
character: 32,
line: 0,
},
start: {
character: 29,
line: 0,
},
},
Expand All @@ -219,6 +312,76 @@ describe('Syntactic validation spec', () => {
]);
});

test('Syntax validation does not warn on missing label in a WHERE clause if it is a relationship type', () => {
const query = `MATCH (n) WHERE n IS Person RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([]);
});

test('Syntax validation does not warn on missing label in a WHERE clause if it is a label', () => {
const query = `MATCH ()-[r]-() WHERE r IS Dog RETURN r`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([]);
});

test('Syntax validation does not warn on missing label in a CREATE', () => {
const query = `CREATE (n: Person) RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([]);
});

test('Syntax validation does not warn on missing rel type in a CREATE', () => {
const query = `CREATE (n)-[r:Rel]->(m) RETURN n`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([]);
});

test('Syntax validation does not warn on missing label in a MERGE', () => {
const query = `MERGE (n:Label {name: $value})
ON CREATE SET n.created = timestamp()
ON MATCH SET
n.accessTime = timestamp()
`;

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([]);
});

test('Syntax validation does not warn on missing rel type in a MERGE', () => {
const query = 'MERGE (n)-[r:Rel]-(m) RETURN r';

expect(
getDiagnosticsForQuery({
query,
dbSchema: { labels: ['Dog', 'Cat'], relationshipTypes: ['Person'] },
}),
).toEqual([]);
});

test('Syntax validation errors on missing label expression', () => {
const query = `MATCH (n:) RETURN n`;

Expand Down