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

[WIP] Support for @deprecated() decorator #15330

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
228 changes: 228 additions & 0 deletions src/Bicep.Core.IntegrationTests/DeprecationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Bicep.Core.Diagnostics;
using Bicep.Core.UnitTests;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Bicep.Core.IntegrationTests;

[TestClass]
public class DeprecationTests
{
[NotNull] public TestContext? TestContext { get; set; }

[TestMethod]
public void Deprecation_is_represented_in_metadata()
{
var result = CompilationHelper.Compile("""
@deprecated('deprecated param')
param fooParam string?

@export()
@deprecated('deprecated var')
var fooVar = ''

@export()
@deprecated('deprecated func')
func fooFunc() string => ':('

@export()
@deprecated('deprecated type')
type fooType = {
bar: string
}

@deprecated('deprecated output')
output fooOutput string = ':('
""");

result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
result.Template.Should().HaveValueAtPath($"$.parameters.fooParam.metadata.__bicep_deprecated!", "deprecated param");
result.Template.Should().HaveValueAtPath($"$.metadata.__bicep_exported_variables![?(@.name == 'fooVar')].metadata.__bicep_deprecated!", "deprecated var");
result.Template.Should().HaveValueAtPath($"$.functions[0].members.fooFunc.metadata.__bicep_deprecated!", "deprecated func");
result.Template.Should().HaveValueAtPath($"$.definitions.fooType.metadata.__bicep_deprecated!", "deprecated type");
result.Template.Should().HaveValueAtPath($"$.outputs.fooOutput.metadata.__bicep_deprecated!", "deprecated output");
}

[TestMethod]
public void Type_properties_cannot_be_deprecated()
{
// This is a limitation of the current implementation - something we should try and fix longer-term
var result = CompilationHelper.Compile("""
@export()
type fooType = {
@deprecated('deprecated type property')
bar: string
}
""");

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics([
("BCP410", DiagnosticLevel.Error, $"""Function "deprecated" cannot be used as a type property decorator."""),
]);
}

[TestMethod]
public void Deprecating_unexported_types_vars_and_funcs_is_blocked()
{
var result = CompilationHelper.Compile("""
@deprecated('deprecated var')
var fooVar = ''

@deprecated('deprecated func')
func fooFunc() string => ':('

@deprecated('deprecated type')
type fooType = {
bar: string
}
""");

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics([
("BCP411", DiagnosticLevel.Error, $"""This declaration cannot be marked as deprecated, because it has not been exported."""),
("BCP411", DiagnosticLevel.Error, $"""This declaration cannot be marked as deprecated, because it has not been exported."""),
("BCP411", DiagnosticLevel.Error, $"""This declaration cannot be marked as deprecated, because it has not been exported."""),
]);
}

[TestMethod]
public void Deprecating_required_params_is_blocked()
{
var result = CompilationHelper.Compile("""
@deprecated('deprecated required')
param required string
""");

result.ExcludingLinterDiagnostics().Should().HaveDiagnostics([
("BCP412", DiagnosticLevel.Error, $"""Parameters must either be nullable or have a default value to be marked as deprecated."""),
]);
}

[TestMethod]
public void Deprecation_reason_is_optional()
{
var result = CompilationHelper.Compile("""
@deprecated()
param optional string = ''
""");

result.ExcludingLinterDiagnostics().Should().NotHaveAnyDiagnostics();
}

[TestMethod]
public void Importing_deprecated_types_vars_and_funcs_is_flagged()
{
var result = CompilationHelper.Compile(("main.bicep", """
import { fooVar, fooFunc, fooType } from 'module.bicep'
"""), ("module.bicep", """
@export()
@deprecated('deprecated var')
var fooVar = ''

@export()
@deprecated('deprecated func')
func fooFunc() string => ':('

@export()
@deprecated('deprecated type')
type fooType = {
bar: string
}
"""));

result.Should().HaveDiagnostics([
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooVar' has been marked as deprecated, and should not be used. Reason: 'deprecated var'."""),
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooFunc' has been marked as deprecated, and should not be used. Reason: 'deprecated func'."""),
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooType' has been marked as deprecated, and should not be used. Reason: 'deprecated type'."""),
]);
}

[TestMethod]
public void Importing_deprecated_types_vars_and_funcs_is_flagged_from_json()
{
var json = CompilationHelper.Compile("""
@export()
@deprecated('deprecated var')
var fooVar = ''

@export()
@deprecated('deprecated func')
func fooFunc() string => ':('

@export()
@deprecated('deprecated type')
type fooType = {
bar: string
}
""").Template!.ToString();


var result = CompilationHelper.Compile(("main.bicep", """
import { fooVar, fooFunc, fooType } from 'module.json'
"""), ("module.json", json));

result.Should().HaveDiagnostics([
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooVar' has been marked as deprecated, and should not be used. Reason: 'deprecated var'."""),
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooFunc' has been marked as deprecated, and should not be used. Reason: 'deprecated func'."""),
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooType' has been marked as deprecated, and should not be used. Reason: 'deprecated type'."""),
]);
}

[TestMethod]
public void Setting_params_and_accessing_outputs_is_flagged_if_deprecated()
{
var result = CompilationHelper.Compile(("main.bicep", """
module mod 'module.bicep' = {
name: 'mod'
params: {
fooParam: ''
}
}

var test = mod.outputs.fooOutput
"""), ("module.bicep", """
@deprecated('deprecated param')
param fooParam string?

@deprecated('deprecated output')
output fooOutput string = ':('
"""));

result.ExcludingDiagnostics("no-unused-vars").Should().HaveDiagnostics([
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooParam' has been marked as deprecated, and should not be used. Reason: 'deprecated param'."""),
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooOutput' has been marked as deprecated, and should not be used. Reason: 'deprecated output'."""),
]);
}

[TestMethod]
public void Setting_params_and_accessing_outputs_is_flagged_if_deprecated_from_json()
{
var json = CompilationHelper.Compile("""
@deprecated('deprecated param')
param fooParam string?

@deprecated('deprecated output')
output fooOutput string = ':('
""").Template!.ToString();

var result = CompilationHelper.Compile(("main.bicep", """
module mod 'module.json' = {
name: 'mod'
params: {
fooParam: ''
}
}

var test = mod.outputs.fooOutput
"""), ("module.json", json));

result.ExcludingDiagnostics("no-unused-vars").Should().HaveDiagnostics([
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooParam' has been marked as deprecated, and should not be used. Reason: 'deprecated param'."""),
("no-deprecated-dependencies", DiagnosticLevel.Warning, $"""Symbol 'fooOutput' has been marked as deprecated, and should not be used. Reason: 'deprecated output'."""),
]);
}
}
2 changes: 2 additions & 0 deletions src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
refersToCopyVariable
} from 'modules/mod.json'

import { fooFunc, fooVar, fooType } from 'modules/deprecations.bicep'

var aliasedFoo = foo
var aliasedBar = mod2.foo

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import {
refersToCopyVariable
} from 'modules/mod.json'

import { fooFunc, fooVar, fooType } from 'modules/deprecations.bicep'
//@[09:16) [no-deprecated-dependencies (Warning)] Symbol 'fooFunc' has been marked as deprecated, and should not be used. Reason: 'deprecated func'. (bicep core linter https://aka.ms/bicep/linter/no-deprecated-dependencies) |fooFunc|
//@[18:24) [no-deprecated-dependencies (Warning)] Symbol 'fooVar' has been marked as deprecated, and should not be used. Reason: 'deprecated var'. (bicep core linter https://aka.ms/bicep/linter/no-deprecated-dependencies) |fooVar|
//@[26:33) [no-deprecated-dependencies (Warning)] Symbol 'fooType' has been marked as deprecated, and should not be used. Reason: 'deprecated type'. (bicep core linter https://aka.ms/bicep/linter/no-deprecated-dependencies) |fooType|

var aliasedFoo = foo
//@[4:14) [no-unused-vars (Warning)] Variable "aliasedFoo" is declared but never used. (bicep core linter https://aka.ms/bicep/linter/no-unused-vars) |aliasedFoo|
//@[04:14) [no-unused-vars (Warning)] Variable "aliasedFoo" is declared but never used. (bicep core linter https://aka.ms/bicep/linter/no-unused-vars) |aliasedFoo|
var aliasedBar = mod2.foo
//@[4:14) [no-unused-vars (Warning)] Variable "aliasedBar" is declared but never used. (bicep core linter https://aka.ms/bicep/linter/no-unused-vars) |aliasedBar|
//@[04:14) [no-unused-vars (Warning)] Variable "aliasedBar" is declared but never used. (bicep core linter https://aka.ms/bicep/linter/no-unused-vars) |aliasedBar|

type fizzes = fizz[]

param fizzParam mod2.fizz
//@[6:15) [no-unused-params (Warning)] Parameter "fizzParam" is declared but never used. (bicep core linter https://aka.ms/bicep/linter/no-unused-params) |fizzParam|
//@[06:15) [no-unused-params (Warning)] Parameter "fizzParam" is declared but never used. (bicep core linter https://aka.ms/bicep/linter/no-unused-params) |fizzParam|
output magicWord pop = refersToCopyVariable[3].value

output greeting string = greet('friend')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
refersToCopyVariable
} from 'modules/mod.json'

import { fooFunc, fooVar, fooType } from 'modules/deprecations.bicep'

var aliasedFoo = foo
var aliasedBar = mod2.foo

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {foo, fizz, pop, greet} from 'modules/mod.bicep'
//@[00:407) ProgramExpression
//@[00:478) ProgramExpression
import * as mod2 from 'modules/mod2.bicep'
import {
'not-a-valid-bicep-identifier' as withInvalidIdentifier
refersToCopyVariable
} from 'modules/mod.json'

import { fooFunc, fooVar, fooType } from 'modules/deprecations.bicep'

var aliasedFoo = foo
//@[00:020) ├─DeclaredVariableExpression { Name = aliasedFoo }
//@[17:020) | └─ImportedVariableReferenceExpression { Variable = foo }
Expand Down
34 changes: 31 additions & 3 deletions src/Bicep.Core.Samples/Files/baselines/Imports_LF/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"_generator": {
"name": "bicep",
"version": "dev",
"templateHash": "18183633158511667674"
"templateHash": "16966837921391267388"
}
},
"definitions": {
Expand Down Expand Up @@ -52,6 +52,20 @@
}
}
},
"fooType": {
"type": "object",
"properties": {
"bar": {
"type": "string"
}
},
"metadata": {
"__bicep_deprecated!": "deprecated type",
"__bicep_imported_from!": {
"sourceTemplate": "modules/deprecations.bicep"
}
}
},
"pop": {
"type": "string",
"minLength": 3,
Expand All @@ -66,6 +80,19 @@
{
"namespace": "__bicep",
"members": {
"fooFunc": {
"parameters": [],
"output": {
"type": "string",
"value": ":("
},
"metadata": {
"__bicep_imported_from!": {
"sourceTemplate": "modules/deprecations.bicep"
},
"__bicep_deprecated!": "deprecated func"
}
},
"greet": {
"parameters": [
{
Expand All @@ -78,10 +105,10 @@
"value": "[format('Hi, {0}!', parameters('name'))]"
},
"metadata": {
"description": "Say hi to someone!",
"__bicep_imported_from!": {
"sourceTemplate": "modules/mod.bicep"
}
},
"description": "Say hi to someone!"
}
}
}
Expand Down Expand Up @@ -114,6 +141,7 @@
],
"_3.foo": "bar",
"foo": "[variables('_1.bar')]",
"fooVar": "",
"refersToCopyVariable": "[variables('_2.copyVariable')]",
"withInvalidIdentifier": "value"
},
Expand Down
Loading
Loading