From 608e4708f28768f0efa9285160440c62e83bf991 Mon Sep 17 00:00:00 2001 From: Lars Reimann Date: Mon, 9 Oct 2023 21:06:15 +0200 Subject: [PATCH] feat: handle backticks surrounding IDs (#622) Closes #579 ### Summary of Changes Backticks allow using keywords as IDs. This now removes them properly from IDs. --------- Co-authored-by: megalinter-bot <129584137+megalinter-bot@users.noreply.github.com> --- .../grammar/safe-ds-value-converter.ts | 2 + tests/helpers/nodeFinder.test.ts | 4 +- tests/helpers/nodeFinder.ts | 4 +- .../grammar/safe-ds-value-converter.test.ts | 99 +++++++++++++++++++ .../names/casing/annotations.sdstest | 2 + .../names/casing/attributes.sdstest | 2 + .../names/casing/block lambda results.sdstest | 2 + .../validation/names/casing/classes.sdstest | 2 + .../names/casing/enum variants.sdstest | 2 + .../validation/names/casing/enums.sdstest | 2 + .../validation/names/casing/functions.sdstest | 2 + .../package name lowercase escaped.sdstest | 2 + .../names/casing/parameters.sdstest | 2 + .../validation/names/casing/pipelines.sdstest | 2 + .../names/casing/placeholders.sdstest | 2 + .../validation/names/casing/results.sdstest | 2 + .../validation/names/casing/schemas.sdstest | 2 + .../validation/names/casing/segments.sdstest | 2 + .../names/casing/type parameters.sdstest | 2 + vitest.config.ts | 2 +- 20 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 tests/language/grammar/safe-ds-value-converter.test.ts create mode 100644 tests/resources/validation/names/casing/package name lowercase escaped.sdstest diff --git a/src/language/grammar/safe-ds-value-converter.ts b/src/language/grammar/safe-ds-value-converter.ts index b34fc6c0e..bc61170e6 100644 --- a/src/language/grammar/safe-ds-value-converter.ts +++ b/src/language/grammar/safe-ds-value-converter.ts @@ -3,6 +3,8 @@ import { convertString, CstNode, DefaultValueConverter, GrammarAST, ValueType } export class SafeDsValueConverter extends DefaultValueConverter { protected override runConverter(rule: GrammarAST.AbstractRule, input: string, cstNode: CstNode): ValueType { switch (rule.name.toUpperCase()) { + case 'ID': + return input.replaceAll('`', ''); case 'TEMPLATE_STRING_START': return convertString(input.substring(0, input.length - 1)); case 'TEMPLATE_STRING_INNER': diff --git a/tests/helpers/nodeFinder.test.ts b/tests/helpers/nodeFinder.test.ts index 0600c1170..8ab06cd9b 100644 --- a/tests/helpers/nodeFinder.test.ts +++ b/tests/helpers/nodeFinder.test.ts @@ -85,10 +85,12 @@ describe('getNodeOfType', () => { it('should return the nth matching node if an index is set', async () => { const code = ` + package p + class C enum D `; - const node = await getNodeOfType(services, code, isSdsDeclaration, 1); + const node = await getNodeOfType(services, code, isSdsDeclaration, 2); expect(node).to.satisfy(isSdsEnum); }); }); diff --git a/tests/helpers/nodeFinder.ts b/tests/helpers/nodeFinder.ts index 576dbb582..21ec8a29d 100644 --- a/tests/helpers/nodeFinder.ts +++ b/tests/helpers/nodeFinder.ts @@ -1,7 +1,7 @@ import { Location, Range } from 'vscode-languageserver'; import { isRangeEqual, parseHelper } from 'langium/test'; import { SafeDsServices } from '../../src/language/safe-ds-module.js'; -import { AstNode, streamAllContents, URI } from 'langium'; +import { AstNode, streamAllContents, streamAst, URI } from 'langium'; import { SdsModule } from '../../src/language/generated/ast.js'; import { AssertionError } from 'assert'; import { locationToString } from './location.js'; @@ -67,7 +67,7 @@ export const getNodeOfType = async ( ): Promise => { const document = await parseHelper(services)(code); const module = document.parseResult.value as SdsModule; - const candidates = streamAllContents(module).filter(predicate).toArray(); + const candidates = streamAst(module).filter(predicate).toArray(); if (candidates.length === 0) { throw new AssertionError({ message: `Expected to find a matching node but found none.` }); diff --git a/tests/language/grammar/safe-ds-value-converter.test.ts b/tests/language/grammar/safe-ds-value-converter.test.ts new file mode 100644 index 000000000..4b3955175 --- /dev/null +++ b/tests/language/grammar/safe-ds-value-converter.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from 'vitest'; +import { createSafeDsServices } from '../../../src/language/safe-ds-module.js'; +import { EmptyFileSystem } from 'langium'; +import { getNodeOfType } from '../../helpers/nodeFinder.js'; +import { + isSdsClass, + isSdsModule, + isSdsTemplateStringEnd, + isSdsTemplateStringInner, + isSdsTemplateStringStart, +} from '../../../src/language/generated/ast.js'; + +const services = createSafeDsServices(EmptyFileSystem).SafeDs; + +describe('runConverter', () => { + it('should remove backticks from IDs (package)', async () => { + const code = ` + package \`foo\`.bar + `; + + const module = await getNodeOfType(services, code, isSdsModule); + expect(module.name).toBe('foo.bar'); + }); + + it('should remove backticks from IDs (declaration)', async () => { + const code = ` + class \`MyClass\` + `; + + const firstClass = await getNodeOfType(services, code, isSdsClass); + expect(firstClass.name).toBe('MyClass'); + }); + + it('should remove delimiters from TEMPLATE_STRING_STARTs', async () => { + const code = ` + pipeline myPipeline { + "start{{ 1 }}inner{{ 2 }}end"; + } + `; + + const firstTemplateStringStart = await getNodeOfType(services, code, isSdsTemplateStringStart); + expect(firstTemplateStringStart.value).toBe('start'); + }); + + it('should handle escape sequences in TEMPLATE_STRING_STARTs', async () => { + const code = ` + pipeline myPipeline { + "\\tstart{{ 1 }}inner{{ 2 }}end"; + } + `; + + const firstTemplateStringStart = await getNodeOfType(services, code, isSdsTemplateStringStart); + expect(firstTemplateStringStart.value).toBe('\tstart'); + }); + + it('should remove delimiters from TEMPLATE_STRING_INNERs', async () => { + const code = ` + pipeline myPipeline { + "start{{ 1 }}inner{{ 2 }}end"; + } + `; + + const firstTemplateStringInner = await getNodeOfType(services, code, isSdsTemplateStringInner); + expect(firstTemplateStringInner.value).toBe('inner'); + }); + + it('should handle escape sequences in TEMPLATE_STRING_INNERs', async () => { + const code = ` + pipeline myPipeline { + "start{{ 1 }}\\tinner{{ 2 }}end"; + } + `; + + const firstTemplateStringInner = await getNodeOfType(services, code, isSdsTemplateStringInner); + expect(firstTemplateStringInner.value).toBe('\tinner'); + }); + + it('should remove delimiters from TEMPLATE_STRING_ENDs', async () => { + const code = ` + pipeline myPipeline { + "start{{ 1 }}inner{{ 2 }}end"; + } + `; + + const firstTemplateStringEnd = await getNodeOfType(services, code, isSdsTemplateStringEnd); + expect(firstTemplateStringEnd.value).toBe('end'); + }); + + it('should handle escape sequences in TEMPLATE_STRING_ENDs', async () => { + const code = ` + pipeline myPipeline { + "start{{ 1 }}inner{{ 2 }}\\tend"; + } + `; + + const firstTemplateStringEnd = await getNodeOfType(services, code, isSdsTemplateStringEnd); + expect(firstTemplateStringEnd.value).toBe('\tend'); + }); +}); diff --git a/tests/resources/validation/names/casing/annotations.sdstest b/tests/resources/validation/names/casing/annotations.sdstest index dcea9ac1f..d1b1be50e 100644 --- a/tests/resources/validation/names/casing/annotations.sdstest +++ b/tests/resources/validation/names/casing/annotations.sdstest @@ -2,6 +2,8 @@ package tests.validation.names.casing // $TEST$ no warning "Names of annotations should be UpperCamelCase." annotation »AnnotationUppercase1« +// $TEST$ no warning "Names of annotations should be UpperCamelCase." +annotation »`AnnotationUppercase2`« // $TEST$ warning "Names of annotations should be UpperCamelCase." annotation »annotationLowercase« // $TEST$ warning "Names of annotations should be UpperCamelCase." diff --git a/tests/resources/validation/names/casing/attributes.sdstest b/tests/resources/validation/names/casing/attributes.sdstest index 20a166b4a..66099ce62 100644 --- a/tests/resources/validation/names/casing/attributes.sdstest +++ b/tests/resources/validation/names/casing/attributes.sdstest @@ -5,6 +5,8 @@ class MyClass { attr »AttributeUppercase«: Int // $TEST$ no warning "Names of attributes should be lowerCamelCase." attr »attributeLowercase1«: Int + // $TEST$ no warning "Names of attributes should be lowerCamelCase." + attr »`attributeLowercase2`«: Int // $TEST$ warning "Names of attributes should be lowerCamelCase." attr »_attributeUnderscore«: Int // $TEST$ warning "Names of attributes should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/block lambda results.sdstest b/tests/resources/validation/names/casing/block lambda results.sdstest index 94eb791c5..63ca47c34 100644 --- a/tests/resources/validation/names/casing/block lambda results.sdstest +++ b/tests/resources/validation/names/casing/block lambda results.sdstest @@ -6,6 +6,8 @@ pipeline myPipeline1 { yield »LambdaResultUppercase« = 1; // $TEST$ no warning "Names of block lambda results should be lowerCamelCase." yield »lambdaResultLowercase1« = 1; + // $TEST$ no warning "Names of block lambda results should be lowerCamelCase." + yield »`lambdaResultLowercase2`« = 1; // $TEST$ warning "Names of block lambda results should be lowerCamelCase." yield »_lambdaResultUnderscore« = 1; // $TEST$ warning "Names of block lambda results should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/classes.sdstest b/tests/resources/validation/names/casing/classes.sdstest index 76974656c..8c571f723 100644 --- a/tests/resources/validation/names/casing/classes.sdstest +++ b/tests/resources/validation/names/casing/classes.sdstest @@ -2,6 +2,8 @@ package tests.validation.names.casing // $TEST$ no warning "Names of classes should be UpperCamelCase." class »ClassUppercase1« +// $TEST$ no warning "Names of classes should be UpperCamelCase." +class »`ClassUppercase2`« // $TEST$ warning "Names of classes should be UpperCamelCase." class »classLowercase« // $TEST$ warning "Names of classes should be UpperCamelCase." diff --git a/tests/resources/validation/names/casing/enum variants.sdstest b/tests/resources/validation/names/casing/enum variants.sdstest index 3742ecda0..11fbe086d 100644 --- a/tests/resources/validation/names/casing/enum variants.sdstest +++ b/tests/resources/validation/names/casing/enum variants.sdstest @@ -3,6 +3,8 @@ package tests.validation.names.casing enum MyEnum { // $TEST$ no warning "Names of enum variants should be UpperCamelCase." »EnumVariantUppercase1« + // $TEST$ no warning "Names of enum variants should be UpperCamelCase." + »`EnumVariantUppercase2`« // $TEST$ warning "Names of enum variants should be UpperCamelCase." »enumVariantLowercase« // $TEST$ warning "Names of enum variants should be UpperCamelCase." diff --git a/tests/resources/validation/names/casing/enums.sdstest b/tests/resources/validation/names/casing/enums.sdstest index 095b2e130..90bf4cf02 100644 --- a/tests/resources/validation/names/casing/enums.sdstest +++ b/tests/resources/validation/names/casing/enums.sdstest @@ -2,6 +2,8 @@ package tests.validation.names.casing // $TEST$ no warning "Names of enums should be UpperCamelCase." enum »EnumUppercase1« +// $TEST$ no warning "Names of enums should be UpperCamelCase." +enum »`EnumUppercase2`« // $TEST$ warning "Names of enums should be UpperCamelCase." enum »enumLowercase« // $TEST$ warning "Names of enums should be UpperCamelCase." diff --git a/tests/resources/validation/names/casing/functions.sdstest b/tests/resources/validation/names/casing/functions.sdstest index b4a7d2da9..41d16b3dd 100644 --- a/tests/resources/validation/names/casing/functions.sdstest +++ b/tests/resources/validation/names/casing/functions.sdstest @@ -4,6 +4,8 @@ package tests.validation.names.casing fun »FunctionUppercase«() // $TEST$ no warning "Names of functions should be lowerCamelCase." fun »functionLowercase1«() +// $TEST$ no warning "Names of functions should be lowerCamelCase." +fun »`functionLowercase2`«() // $TEST$ warning "Names of functions should be lowerCamelCase." fun »_functionUnderscore«() // $TEST$ warning "Names of functions should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/package name lowercase escaped.sdstest b/tests/resources/validation/names/casing/package name lowercase escaped.sdstest new file mode 100644 index 000000000..c5bf85806 --- /dev/null +++ b/tests/resources/validation/names/casing/package name lowercase escaped.sdstest @@ -0,0 +1,2 @@ +// $TEST$ no warning "All segments of the qualified name of a package should be lowerCamelCase." +package »tests.validation.declarations.`lowercase1`« diff --git a/tests/resources/validation/names/casing/parameters.sdstest b/tests/resources/validation/names/casing/parameters.sdstest index 837a3b9e5..59b575730 100644 --- a/tests/resources/validation/names/casing/parameters.sdstest +++ b/tests/resources/validation/names/casing/parameters.sdstest @@ -5,6 +5,8 @@ fun myFunction1( »ParameterUppercase«: Int, // $TEST$ no warning "Names of parameters should be lowerCamelCase." »parameterLowercase1«: Int, + // $TEST$ no warning "Names of parameters should be lowerCamelCase." + »`parameterLowercase2`«: Int, // $TEST$ warning "Names of parameters should be lowerCamelCase." »_parameterUnderscore«: Int, // $TEST$ warning "Names of parameters should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/pipelines.sdstest b/tests/resources/validation/names/casing/pipelines.sdstest index e277b4e20..c9a46ef9b 100644 --- a/tests/resources/validation/names/casing/pipelines.sdstest +++ b/tests/resources/validation/names/casing/pipelines.sdstest @@ -4,6 +4,8 @@ package tests.validation.names.casing pipeline »PipelineUppercase« {} // $TEST$ no warning "Names of pipelines should be lowerCamelCase." pipeline »pipelineLowercase1« {} +// $TEST$ no warning "Names of pipelines should be lowerCamelCase." +pipeline »`pipelineLowercase2`« {} // $TEST$ warning "Names of pipelines should be lowerCamelCase." pipeline »_pipelineUnderscore« {} // $TEST$ warning "Names of pipelines should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/placeholders.sdstest b/tests/resources/validation/names/casing/placeholders.sdstest index 5ecc7fb76..1cb0658b7 100644 --- a/tests/resources/validation/names/casing/placeholders.sdstest +++ b/tests/resources/validation/names/casing/placeholders.sdstest @@ -5,6 +5,8 @@ pipeline myPipeline2 { val »PlaceholderUppercase« = 1; // $TEST$ no warning "Names of placeholders should be lowerCamelCase." val »placeholderLowercase1« = 1; + // $TEST$ no warning "Names of placeholders should be lowerCamelCase." + val »`placeholderLowercase2`« = 1; // $TEST$ warning "Names of placeholders should be lowerCamelCase." val »_placeholderUnderscore« = 1; // $TEST$ warning "Names of placeholders should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/results.sdstest b/tests/resources/validation/names/casing/results.sdstest index 1eb9d87b4..f0d2a0483 100644 --- a/tests/resources/validation/names/casing/results.sdstest +++ b/tests/resources/validation/names/casing/results.sdstest @@ -5,6 +5,8 @@ fun myFunction2() -> ( »ResultUppercase«: Int, // $TEST$ no warning "Names of results should be lowerCamelCase." »resultLowercase1«: Int, + // $TEST$ no warning "Names of results should be lowerCamelCase." + »`resultLowercase2`«: Int, // $TEST$ warning "Names of results should be lowerCamelCase." »_resultUnderscore«: Int, // $TEST$ warning "Names of results should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/schemas.sdstest b/tests/resources/validation/names/casing/schemas.sdstest index 8cc8d550b..a456b7646 100644 --- a/tests/resources/validation/names/casing/schemas.sdstest +++ b/tests/resources/validation/names/casing/schemas.sdstest @@ -2,6 +2,8 @@ package tests.validation.names.casing // $TEST$ no warning "Names of schemas should be UpperCamelCase." schema »SchemaUppercase1« {} +// $TEST$ no warning "Names of schemas should be UpperCamelCase." +schema »`SchemaUppercase2`« {} // $TEST$ warning "Names of schemas should be UpperCamelCase." schema »schemaLowercase« {} // $TEST$ warning "Names of schemas should be UpperCamelCase." diff --git a/tests/resources/validation/names/casing/segments.sdstest b/tests/resources/validation/names/casing/segments.sdstest index ba257bce5..b06585eb8 100644 --- a/tests/resources/validation/names/casing/segments.sdstest +++ b/tests/resources/validation/names/casing/segments.sdstest @@ -4,6 +4,8 @@ package tests.validation.names.casing segment »SegmentUppercase«() {} // $TEST$ no warning "Names of segments should be lowerCamelCase." segment »segmentLowercase1«() {} +// $TEST$ no warning "Names of segments should be lowerCamelCase." +segment »`segmentLowercase2`«() {} // $TEST$ warning "Names of segments should be lowerCamelCase." segment »_segmentUnderscore«() {} // $TEST$ warning "Names of segments should be lowerCamelCase." diff --git a/tests/resources/validation/names/casing/type parameters.sdstest b/tests/resources/validation/names/casing/type parameters.sdstest index b9fede2b4..267bec768 100644 --- a/tests/resources/validation/names/casing/type parameters.sdstest +++ b/tests/resources/validation/names/casing/type parameters.sdstest @@ -3,6 +3,8 @@ package tests.validation.names.casing fun myFunction3< // $TEST$ no warning "Names of type parameters should be UpperCamelCase." »TypeParameterUppercase1«, + // $TEST$ no warning "Names of type parameters should be UpperCamelCase." + »`TypeParameterUppercase2`«, // $TEST$ warning "Names of type parameters should be UpperCamelCase." »typeParameterLowercase«, // $TEST$ warning "Names of type parameters should be UpperCamelCase." diff --git a/vitest.config.ts b/vitest.config.ts index 27b081a10..aec91105e 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { chaiConfig: { - truncateThreshold: 0, + truncateThreshold: 200, }, coverage: { provider: 'v8',