diff --git a/docs/pipeline-language/statements/assignments.md b/docs/pipeline-language/statements/assignments.md index ee8acf29d..e4112e395 100644 --- a/docs/pipeline-language/statements/assignments.md +++ b/docs/pipeline-language/statements/assignments.md @@ -32,7 +32,9 @@ This assignment to a placeholder has the following syntactic elements: ??? info "Name convention" - Use `#!sds lowerCamelCase` for the name of the placeholder. + Use `#!sds lowerCamelCase` for the name of the placeholder. You may prefix the name of an unused placeholder with an + underscore (`_`) to indicate that it is intentionally unused, e.g. to + [inspect its value](#inspecting-placeholder-values-in-vs-code). This disables the "unused" warning. ### References to Placeholder diff --git a/packages/safe-ds-lang/src/language/validation/names.ts b/packages/safe-ds-lang/src/language/validation/names.ts index e8a1005de..d0ed8d55d 100644 --- a/packages/safe-ds-lang/src/language/validation/names.ts +++ b/packages/safe-ds-lang/src/language/validation/names.ts @@ -144,7 +144,12 @@ export const nameShouldHaveCorrectCasing = (services: SafeDsServices) => { case SdsPipeline: return nameShouldBeLowerCamelCase(node, 'pipelines', accept); case SdsPlaceholder: - return nameShouldBeLowerCamelCase(node, 'placeholders', accept); + const usages = services.helpers.NodeMapper.placeholderToReferences(node as SdsPlaceholder); + if (usages.isEmpty()) { + return nameShouldBeLowerCamelCaseWithOptionalLeadingUnderscore(node, 'unused placeholders', accept); + } else { + return nameShouldBeLowerCamelCase(node, 'used placeholders', accept); + } case SdsResult: return nameShouldBeLowerCamelCase(node, 'results', accept); case SdsSchema: @@ -169,6 +174,21 @@ const isLowerCamelCase = (name: string): boolean => { return /^[a-z][a-zA-Z0-9]*$/gu.test(name); }; +const nameShouldBeLowerCamelCaseWithOptionalLeadingUnderscore = ( + node: SdsDeclaration, + nodeName: string, + accept: ValidationAcceptor, +): void => { + const name = node.name ?? ''; + if (!isLowerCamelCaseWithOptionalLeadingUnderscore(name)) { + acceptCasingWarning(node, nodeName, 'lowerCamelCase with an optional leading underscore', accept); + } +}; + +const isLowerCamelCaseWithOptionalLeadingUnderscore = (name: string): boolean => { + return /^_?[a-z][a-zA-Z0-9]*$/gu.test(name); +}; + const nameShouldBeUpperCamelCase = (node: SdsDeclaration, nodeName: string, accept: ValidationAcceptor): void => { const name = node.name ?? ''; if (!isUpperCamelCase(name)) { diff --git a/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts b/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts index 10f16f36f..9479b1b8d 100644 --- a/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts +++ b/packages/safe-ds-lang/src/language/validation/other/declarations/placeholders.ts @@ -39,6 +39,12 @@ export const placeholdersMustNotBeAnAlias = (node: SdsPlaceholder, accept: Valid export const placeholderShouldBeUsed = (services: SafeDsServices) => (node: SdsPlaceholder, accept: ValidationAcceptor) => { + // Don't a warning if the placeholder's name starts with an underscore + if (node.name.startsWith('_')) { + return; + } + + // Check if the placeholder is used const usages = services.helpers.NodeMapper.placeholderToReferences(node); if (!usages.isEmpty()) { return; @@ -52,10 +58,14 @@ export const placeholderShouldBeUsed = return; } - accept('warning', 'This placeholder is unused and can be removed.', { - node, - property: 'name', - code: CODE_PLACEHOLDER_UNUSED, - tags: [DiagnosticTag.Unnecessary], - }); + accept( + 'warning', + 'This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning.', + { + node, + property: 'name', + code: CODE_PLACEHOLDER_UNUSED, + tags: [DiagnosticTag.Unnecessary], + }, + ); }; diff --git a/packages/safe-ds-lang/tests/resources/validation/names/casing/placeholders.sdsdev b/packages/safe-ds-lang/tests/resources/validation/names/casing/placeholders.sdsdev index 1cb0658b7..b7662a4d2 100644 --- a/packages/safe-ds-lang/tests/resources/validation/names/casing/placeholders.sdsdev +++ b/packages/safe-ds-lang/tests/resources/validation/names/casing/placeholders.sdsdev @@ -1,14 +1,25 @@ package tests.validation.names.casing pipeline myPipeline2 { - // $TEST$ warning "Names of placeholders should be lowerCamelCase." - 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." - val »placeholder_snake_case« = 1; + // $TEST$ warning "Names of used placeholders should be lowerCamelCase." + val »UsedPlaceholderUppercase« = 1; + // $TEST$ no warning "Names of used placeholders should be lowerCamelCase."" + val »usedPlaceholderLowercase1« = 1; + // $TEST$ no warning "Names of used placeholders should be lowerCamelCase."" + val »`usedPlaceholderLowercase2`« = 1; + // $TEST$ warning "Names of used placeholders should be lowerCamelCase." + val »_usedPlaceholderUnderscore« = 1; + // $TEST$ warning "Names of used placeholders should be lowerCamelCase." + val »used_placeholder_snake_case« = 1; + + // $TEST$ warning "Names of unused placeholders should be lowerCamelCase with an optional leading underscore." + val »UnusedPlaceholderUppercase« = UsedPlaceholderUppercase + 1; + // $TEST$ no warning "Names of unused placeholders should be lowerCamelCase. with an optional leading underscore." + val »unusedPlaceholderLowercase1« = usedPlaceholderLowercase1 + 1; + // $TEST$ no warning "Names of unused placeholders should be lowerCamelCase. with an optional leading underscore." + val »`unusedPlaceholderLowercase2`« = usedPlaceholderLowercase2 + 1; + // $TEST$ no warning "Names of unused placeholders should be lowerCamelCase with an optional leading underscore." + val »_unusedPlaceholderUnderscore« = _usedPlaceholderUnderscore + 1; + // $TEST$ warning "Names of unused placeholders should be lowerCamelCase with an optional leading underscore." + val »unused_placeholder_snake_case« = used_placeholder_snake_case + 1; } diff --git a/packages/safe-ds-lang/tests/resources/validation/other/declarations/placeholders/unused/main.sdsdev b/packages/safe-ds-lang/tests/resources/validation/other/declarations/placeholders/unused/main.sdsdev index 1527cb7ac..0feaec622 100644 --- a/packages/safe-ds-lang/tests/resources/validation/other/declarations/placeholders/unused/main.sdsdev +++ b/packages/safe-ds-lang/tests/resources/validation/other/declarations/placeholders/unused/main.sdsdev @@ -3,42 +3,51 @@ package tests.validation.other.declarations.placeholders.unused fun f() -> (r1: Int, r2: Int) segment mySegment() { - // $TEST$ warning "This placeholder is unused and can be removed." + // $TEST$ warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »unused« = 1; - // $TEST$ no warning "This placeholder is unused and can be removed." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." + val »_unused« = 1; + + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »used« = 1; used; - // $TEST$ no warning "This placeholder is unused and can be removed." - // $TEST$ no warning "This placeholder is unused and can be removed." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »last1«, val »last2« = f(); } pipeline myPipeline1 { - // $TEST$ warning "This placeholder is unused and can be removed." + // $TEST$ warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »unused« = 1; - // $TEST$ no warning "This placeholder is unused and can be removed." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." + val »_unused« = 1; + + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »used« = 1; used; - // $TEST$ no warning "This placeholder is unused and can be removed." - // $TEST$ no warning "This placeholder is unused and can be removed." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »last1«, val »last2« = f(); } pipeline myPipeline2 { () { - // $TEST$ warning "This placeholder is unused and can be removed." + // $TEST$ warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »unused« = 1; - // $TEST$ no warning "This placeholder is unused and can be removed." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." + val »_unused« = 1; + + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »used« = 1; used; - // $TEST$ no warning "This placeholder is unused and can be removed." - // $TEST$ no warning "This placeholder is unused and can be removed." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." + // $TEST$ no warning "This placeholder is unused and can be removed. Prefix its name with an underscore to disable this warning." val »last1«, val »last2« = f(); }; }