From 8de3fcf5f4222a7d1b217f36b731b25d845dbc5d Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Thu, 26 Dec 2024 16:26:13 -0500 Subject: [PATCH 1/3] Hang ImportClosureInfo off the model --- src/Bicep.Core/Emit/EmitterContext.cs | 6 ------ src/Bicep.Core/Emit/ExpressionConverter.cs | 8 ++++---- .../Emit/ParameterAssignmentEvaluator.cs | 6 +++--- src/Bicep.Core/Emit/TemplateWriter.cs | 16 ++++++++-------- src/Bicep.Core/Semantics/SemanticModel.cs | 5 +++++ 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/Bicep.Core/Emit/EmitterContext.cs b/src/Bicep.Core/Emit/EmitterContext.cs index 3ad961c3917..8663ef6a62f 100644 --- a/src/Bicep.Core/Emit/EmitterContext.cs +++ b/src/Bicep.Core/Emit/EmitterContext.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Collections.Immutable; using Bicep.Core.DataFlow; -using Bicep.Core.Emit.CompileTimeImports; using Bicep.Core.Semantics; using Bicep.Core.Semantics.Metadata; using Bicep.Core.Syntax; @@ -12,8 +11,6 @@ namespace Bicep.Core.Emit { public class EmitterContext { - private readonly Lazy importClosureInfoLazy; - public EmitterContext(SemanticModel semanticModel) { Settings = semanticModel.EmitterSettings; @@ -22,7 +19,6 @@ public EmitterContext(SemanticModel semanticModel) VariablesToInline = InlineDependencyVisitor.GetVariablesToInline(semanticModel); ResourceDependencies = ResourceDependencyVisitor.GetResourceDependencies(semanticModel); FunctionVariables = FunctionVariableGeneratorVisitor.GetFunctionVariables(semanticModel); - importClosureInfoLazy = new(() => ImportClosureInfo.Calculate(semanticModel), LazyThreadSafetyMode.PublicationOnly); } public EmitterSettings Settings { get; } @@ -40,7 +36,5 @@ public EmitterContext(SemanticModel semanticModel) public ImmutableDictionary ModuleScopeData => SemanticModel.EmitLimitationInfo.ModuleScopeData; public ImmutableDictionary ResourceScopeData => SemanticModel.EmitLimitationInfo.ResourceScopeData; - - public ImportClosureInfo ImportClosureInfo => importClosureInfoLazy.Value; } } diff --git a/src/Bicep.Core/Emit/ExpressionConverter.cs b/src/Bicep.Core/Emit/ExpressionConverter.cs index 6630d8e4a26..98e074a17b8 100644 --- a/src/Bicep.Core/Emit/ExpressionConverter.cs +++ b/src/Bicep.Core/Emit/ExpressionConverter.cs @@ -110,7 +110,7 @@ public LanguageExpression ConvertExpression(Expression expression) case ImportedUserDefinedFunctionCallExpression importedFunction: { - var (namespaceName, functionName) = GetFunctionName(context.ImportClosureInfo.ImportedSymbolNames[importedFunction.Symbol]); + var (namespaceName, functionName) = GetFunctionName(context.SemanticModel.ImportClosureInfo.ImportedSymbolNames[importedFunction.Symbol]); return CreateFunction( $"{namespaceName}.{functionName}", importedFunction.Parameters.Select(ConvertExpression)); @@ -119,7 +119,7 @@ public LanguageExpression ConvertExpression(Expression expression) case WildcardImportInstanceFunctionCallExpression importedFunction: { var (namespaceName, functionName) = GetFunctionName( - context.ImportClosureInfo.WildcardImportPropertyNames[new(importedFunction.ImportSymbol, importedFunction.MethodName)]); + context.SemanticModel.ImportClosureInfo.WildcardImportPropertyNames[new(importedFunction.ImportSymbol, importedFunction.MethodName)]); return CreateFunction( $"{namespaceName}.{functionName}", importedFunction.Parameters.Select(ConvertExpression)); @@ -167,11 +167,11 @@ public LanguageExpression ConvertExpression(Expression expression) return CreateFunction("variables", new JTokenExpression(exp.Name)); case ImportedVariableReferenceExpression exp: - return CreateFunction("variables", new JTokenExpression(context.ImportClosureInfo.ImportedSymbolNames[exp.Variable])); + return CreateFunction("variables", new JTokenExpression(context.SemanticModel.ImportClosureInfo.ImportedSymbolNames[exp.Variable])); case WildcardImportVariablePropertyReferenceExpression exp: return CreateFunction("variables", - new JTokenExpression(context.ImportClosureInfo.WildcardImportPropertyNames[new(exp.ImportSymbol, exp.PropertyName)])); + new JTokenExpression(context.SemanticModel.ImportClosureInfo.WildcardImportPropertyNames[new(exp.ImportSymbol, exp.PropertyName)])); case ParametersReferenceExpression exp: return CreateFunction("parameters", new JTokenExpression(exp.Parameter.Name)); diff --git a/src/Bicep.Core/Emit/ParameterAssignmentEvaluator.cs b/src/Bicep.Core/Emit/ParameterAssignmentEvaluator.cs index b211000173c..b61d6ad4cc7 100644 --- a/src/Bicep.Core/Emit/ParameterAssignmentEvaluator.cs +++ b/src/Bicep.Core/Emit/ParameterAssignmentEvaluator.cs @@ -155,11 +155,11 @@ public ParameterAssignmentEvaluator(SemanticModel model) EmitterContext context = new(model); this.converter = new(context); - this.importsByName = context.ImportClosureInfo.ImportedSymbolNames.Keys - .Select(importedVariable => (context.ImportClosureInfo.ImportedSymbolNames[importedVariable], importedVariable)) + this.importsByName = context.SemanticModel.ImportClosureInfo.ImportedSymbolNames.Keys + .Select(importedVariable => (context.SemanticModel.ImportClosureInfo.ImportedSymbolNames[importedVariable], importedVariable)) .GroupBy(x => x.Item1, LanguageConstants.IdentifierComparer) .ToImmutableDictionary(x => x.Key, x => x.First().importedVariable, LanguageConstants.IdentifierComparer); - this.wildcardImportPropertiesByName = context.ImportClosureInfo.WildcardImportPropertyNames + this.wildcardImportPropertiesByName = context.SemanticModel.ImportClosureInfo.WildcardImportPropertyNames .GroupBy(x => x.Value, LanguageConstants.IdentifierComparer) .ToImmutableDictionary(x => x.Key, x => x.First().Key, LanguageConstants.IdentifierComparer); this.synthesizedVariableValuesByName = context.FunctionVariables.Values diff --git a/src/Bicep.Core/Emit/TemplateWriter.cs b/src/Bicep.Core/Emit/TemplateWriter.cs index 75005157ed3..77eb2cd7ac5 100644 --- a/src/Bicep.Core/Emit/TemplateWriter.cs +++ b/src/Bicep.Core/Emit/TemplateWriter.cs @@ -98,7 +98,7 @@ public void Write(SourceAwareJsonTextWriter writer) var program = (ProgramExpression)ExpressionBuilder.Convert(Context.SemanticModel.Root.Syntax); - var programTypes = program.Types.Concat(Context.ImportClosureInfo.ImportedTypesInClosure); + var programTypes = program.Types.Concat(Context.SemanticModel.ImportClosureInfo.ImportedTypesInClosure); declaredTypesByName = programTypes.ToImmutableDictionary(t => t.Name); jsonWriter.WriteStartObject(); @@ -128,11 +128,11 @@ public void Write(SourceAwareJsonTextWriter writer) this.EmitTypeDefinitionsIfPresent(emitter, programTypes); - this.EmitUserDefinedFunctions(emitter, program.Functions.Concat(Context.ImportClosureInfo.ImportedFunctionsInClosure)); + this.EmitUserDefinedFunctions(emitter, program.Functions.Concat(Context.SemanticModel.ImportClosureInfo.ImportedFunctionsInClosure)); this.EmitParametersIfPresent(emitter, program.Parameters); - this.EmitVariablesIfPresent(emitter, program.Variables.Concat(Context.ImportClosureInfo.ImportedVariablesInClosure)); + this.EmitVariablesIfPresent(emitter, program.Variables.Concat(Context.SemanticModel.ImportClosureInfo.ImportedVariablesInClosure)); this.EmitExtensionsIfPresent(emitter, program.Extensions); @@ -315,7 +315,7 @@ private void EmitUserDefinedFunction(ExpressionEmitter emitter, DeclaredFunction ? function.Name : $"{function.Namespace}.{function.Name}"; - if (function.Description is not null || function.Exported is not null || Context.ImportClosureInfo.ImportedSymbolOriginMetadata.ContainsKey(originMetadataLookupKey)) + if (function.Description is not null || function.Exported is not null || Context.SemanticModel.ImportClosureInfo.ImportedSymbolOriginMetadata.ContainsKey(originMetadataLookupKey)) { emitter.EmitObjectProperty(LanguageConstants.ParameterMetadataPropertyName, () => { @@ -329,7 +329,7 @@ private void EmitUserDefinedFunction(ExpressionEmitter emitter, DeclaredFunction emitter.EmitProperty(LanguageConstants.MetadataExportedPropertyName, ExpressionFactory.CreateBooleanLiteral(true, function.Exported.SourceSyntax)); } - if (Context.ImportClosureInfo.ImportedSymbolOriginMetadata.TryGetValue(originMetadataLookupKey, out var originMetadata)) + if (Context.SemanticModel.ImportClosureInfo.ImportedSymbolOriginMetadata.TryGetValue(originMetadataLookupKey, out var originMetadata)) { emitter.EmitObjectProperty(LanguageConstants.MetadataImportedFromPropertyName, () => { @@ -363,7 +363,7 @@ private void EmitTypeDeclaration(ExpressionEmitter emitter, DeclaredTypeExpressi () => { var declaredTypeObject = ApplyTypeModifiers(declaredType, TypePropertiesForTypeExpression(declaredType.Value)); - if (Context.ImportClosureInfo.ImportedSymbolOriginMetadata.TryGetValue(declaredType.Name, out var originMetadata)) + if (Context.SemanticModel.ImportClosureInfo.ImportedSymbolOriginMetadata.TryGetValue(declaredType.Name, out var originMetadata)) { var importedFromProperties = ExpressionFactory.CreateObjectProperty(LanguageConstants.ImportMetadataSourceTemplatePropertyName, ExpressionFactory.CreateStringLiteral(originMetadata.SourceTemplateIdentifier)).AsEnumerable(); @@ -542,9 +542,9 @@ private ITypeReferenceExpressionResolution ResolveTypeReferenceExpression(TypeEx TypeAliasReferenceExpression typeAliasReference => ForNamedRoot(typeAliasReference.Symbol.Name), SynthesizedTypeAliasReferenceExpression typeAliasReference => ForNamedRoot(typeAliasReference.Name), ImportedTypeReferenceExpression importedTypeReference => ForNamedRoot( - Context.ImportClosureInfo.ImportedSymbolNames[importedTypeReference.Symbol]), + Context.SemanticModel.ImportClosureInfo.ImportedSymbolNames[importedTypeReference.Symbol]), WildcardImportTypePropertyReferenceExpression importedTypeReference => ForNamedRoot( - Context.ImportClosureInfo.WildcardImportPropertyNames[new(importedTypeReference.ImportSymbol, importedTypeReference.PropertyName)]), + Context.SemanticModel.ImportClosureInfo.WildcardImportPropertyNames[new(importedTypeReference.ImportSymbol, importedTypeReference.PropertyName)]), ResourceDerivedTypeExpression resourceDerived => new ResourceDerivedTypeResolution(resourceDerived), _ => throw new ArgumentException($"Cannot resolve type reference access expression with a root of type '{root.GetType().Name}'."), }; diff --git a/src/Bicep.Core/Semantics/SemanticModel.cs b/src/Bicep.Core/Semantics/SemanticModel.cs index 91715623097..9160d5e727e 100644 --- a/src/Bicep.Core/Semantics/SemanticModel.cs +++ b/src/Bicep.Core/Semantics/SemanticModel.cs @@ -12,6 +12,7 @@ using Bicep.Core.Configuration; using Bicep.Core.Diagnostics; using Bicep.Core.Emit; +using Bicep.Core.Emit.CompileTimeImports; using Bicep.Core.Extensions; using Bicep.Core.Features; using Bicep.Core.Registry; @@ -37,6 +38,7 @@ public class SemanticModel : ISemanticModel private readonly Lazy> outputsLazy; private readonly Lazy apiVersionProviderLazy; private readonly Lazy emitterSettingsLazy; + private readonly Lazy importClosureInfoLazy; // needed to support param file go to def private readonly Lazy> declarationsByAssignment; @@ -89,6 +91,7 @@ public SemanticModel(IBicepAnalyzer linterAnalyzer, INamespaceProvider namespace this.emitterSettingsLazy = new(() => new(this)); this.emitLimitationInfoLazy = new(() => EmitLimitationCalculator.Calculate(this)); + this.importClosureInfoLazy = new(() => ImportClosureInfo.Calculate(this)); this.symbolHierarchyLazy = new(() => { var hierarchy = new SymbolHierarchy(); @@ -242,6 +245,8 @@ private static void TraceBuildOperation(BicepSourceFile sourceFile, IFeatureProv public EmitLimitationInfo EmitLimitationInfo => emitLimitationInfoLazy.Value; + public ImportClosureInfo ImportClosureInfo => importClosureInfoLazy.Value; + public ResourceAncestorGraph ResourceAncestors => resourceAncestorsLazy.Value; public ResourceMetadataCache ResourceMetadata { get; } From 908caa076737aa589dae4b6ea89afc582c9f0549 Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Fri, 27 Dec 2024 11:14:42 -0500 Subject: [PATCH 2/3] Include imported variables in max variables check --- .../MaxNumberVariablesRuleTests.cs | 25 +++++++++++++++++++ .../Linter/Rules/MaxNumberVariablesRule.cs | 4 ++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/MaxNumberVariablesRuleTests.cs b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/MaxNumberVariablesRuleTests.cs index 00a98e211e5..3d4bd0a64b3 100644 --- a/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/MaxNumberVariablesRuleTests.cs +++ b/src/Bicep.Core.UnitTests/Diagnostics/LinterRuleTests/MaxNumberVariablesRuleTests.cs @@ -40,5 +40,30 @@ public void TooManyVariables(int i, int j, string pattern, string[] expectedMess { CompileAndTest(GenerateText(i, j, pattern), MaxNumberVariablesRule.Code, DiagnosticLevel.Error, expectedMessages); } + + [TestMethod] + public void TooManyVariablesAfterImport() + { + var withoutImport = GenerateText(1, MaxNumberVariablesRule.MaxNumber, "var v% = %"); + CompileAndTest(withoutImport, MaxNumberVariablesRule.Code, DiagnosticLevel.Off, []); + + var importTarget = """ + @export() + var imported1 = 1 + """; + CompileAndTest( + string.Join('\n', "import {imported1} from 'imported.bicep'", withoutImport), + MaxNumberVariablesRule.Code, + DiagnosticLevel.Error, + [$"Too many variables. Number of variables is limited to {MaxNumberVariablesRule.MaxNumber}."], + new(AdditionalFiles: [ ("imported.bicep", importTarget) ])); + + CompileAndTest( + string.Join('\n', "import * as imported from 'imported.bicep'", withoutImport), + MaxNumberVariablesRule.Code, + DiagnosticLevel.Error, + [$"Too many variables. Number of variables is limited to {MaxNumberVariablesRule.MaxNumber}."], + new(AdditionalFiles: [("imported.bicep", importTarget)])); + } } } diff --git a/src/Bicep.Core/Analyzers/Linter/Rules/MaxNumberVariablesRule.cs b/src/Bicep.Core/Analyzers/Linter/Rules/MaxNumberVariablesRule.cs index c442d5eebf2..b49db334f81 100644 --- a/src/Bicep.Core/Analyzers/Linter/Rules/MaxNumberVariablesRule.cs +++ b/src/Bicep.Core/Analyzers/Linter/Rules/MaxNumberVariablesRule.cs @@ -26,7 +26,9 @@ public override string FormatMessage(params object[] values) override public IEnumerable AnalyzeInternal(SemanticModel model, DiagnosticLevel diagnosticLevel) { - if (model.Root.VariableDeclarations.Count() > MaxNumber) + var variablesInCompiledTemplate = model.Root.VariableDeclarations.Count() + + model.ImportClosureInfo.ImportedVariablesInClosure.Count(); + if (variablesInCompiledTemplate > MaxNumber) { var firstItem = model.Root.VariableDeclarations.First(); return new IDiagnostic[] { CreateDiagnosticForSpan(diagnosticLevel, firstItem.NameSource.Span, MaxNumber) }; From 54adda817d8bbf6e57ebe842e19c30b1b9747967 Mon Sep 17 00:00:00 2001 From: Jonathan Eskew Date: Fri, 27 Dec 2024 11:42:02 -0500 Subject: [PATCH 3/3] Fix failing test --- .../Emit/CompileTimeImports/ImportClosureInfo.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Bicep.Core/Emit/CompileTimeImports/ImportClosureInfo.cs b/src/Bicep.Core/Emit/CompileTimeImports/ImportClosureInfo.cs index 14ed007311d..9a72b2a49c1 100644 --- a/src/Bicep.Core/Emit/CompileTimeImports/ImportClosureInfo.cs +++ b/src/Bicep.Core/Emit/CompileTimeImports/ImportClosureInfo.cs @@ -358,8 +358,10 @@ IntraTemplateSymbolicReferenceFactory referenceFactory ArmTemplateFile templateFile, BicepWildcardImportSymbolicReference referrer, IntraTemplateSymbolicReferenceFactory referenceFactory - ) => model.Exports.Values.Select( - md => (md.Name, ReferenceForArmTarget(md, templateFile, model, referrer, referenceFactory))); + ) => model.Exports.Values + .Where(export => export.Kind != ExportMetadataKind.Error) + .Select( + md => (md.Name, ReferenceForArmTarget(md, templateFile, model, referrer, referenceFactory))); private static DeclaredSymbol FindExportedSymbol(ExportMetadata target, SemanticModel model) { @@ -379,9 +381,7 @@ private static IntraTemplateSymbolicReference ReferenceForArmTarget( ArmTemplateFile sourceTemplateFile, ISemanticModel sourceModel, InterTemplateSymbolicReference referrer, - IntraTemplateSymbolicReferenceFactory referenceFactory - ) - => targetMetadata switch + IntraTemplateSymbolicReferenceFactory referenceFactory) => targetMetadata switch { ExportedTypeMetadata => referenceFactory.SymbolFor( ArmSymbolType.Type,