PropertyName { get; private set; }",
+ statement.Content);
+ }
+
+ private RazorCodeDocument CreateDocument(string content)
+ {
+ using (var stream = new MemoryStream())
+ {
+ var bytes = Encoding.UTF8.GetBytes(content);
+ stream.Write(bytes, 0, bytes.Length);
+ stream.Seek(0L, SeekOrigin.Begin);
+
+ var source = RazorSourceDocument.ReadFrom(stream, "test.cshtml");
+ return RazorCodeDocument.Create(source);
+ }
+ }
+
+ private ClassDeclarationIRNode FindClassNode(RazorIRNode node)
+ {
+ var visitor = new ClassNodeVisitor();
+ visitor.Visit(node);
+ return visitor.Node;
+ }
+
+ private RazorEngine CreateEngine()
+ {
+ return RazorEngine.Create(b =>
+ {
+ // Notice we're not registering the InjectDirective.Pass here so we can run it on demand.
+ b.AddDirective(InjectDirective.Directive);
+ b.AddDirective(ModelDirective.Directive);
+ });
+ }
+
+ private DocumentIRNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
+ {
+ for (var i = 0; i < engine.Phases.Count; i++)
+ {
+ var phase = engine.Phases[i];
+ phase.Execute(codeDocument);
+
+ if (phase is IRazorIRPhase)
+ {
+ break;
+ }
+ }
+
+ return codeDocument.GetIRDocument();
+ }
+
+ private string GetCSharpContent(RazorIRNode node)
+ {
+ var builder = new StringBuilder();
+ for (var i = 0; i < node.Children.Count; i++)
+ {
+ var child = node.Children[i] as CSharpTokenIRNode;
+ builder.Append(child.Content);
+ }
+
+ return builder.ToString();
+ }
+
+ private class ClassNodeVisitor : RazorIRNodeWalker
+ {
+ public ClassDeclarationIRNode Node { get; set; }
+
+ public override void VisitClass(ClassDeclarationIRNode node)
+ {
+ Node = node;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/TagHelperChunkDecoratorTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/TagHelperChunkDecoratorTest.cs
index 03bbea9b86..d6edfdd2d9 100644
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/TagHelperChunkDecoratorTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/TagHelperChunkDecoratorTest.cs
@@ -3,7 +3,6 @@
using System;
using System.Linq;
-using Microsoft.AspNetCore.Mvc.Razor.Host.Internal;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Moq;
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/ViewComponentTagHelperChunkVisitorTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/ViewComponentTagHelperChunkVisitorTest.cs
index bf3257abd5..f926e1e8b9 100644
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/ViewComponentTagHelperChunkVisitorTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/Internal/ViewComponentTagHelperChunkVisitorTest.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Reflection;
-using Microsoft.AspNetCore.Mvc.Razor.Host.Internal;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.CodeGenerators;
using Xunit;
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/ModelExpressionPassTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/ModelExpressionPassTest.cs
new file mode 100644
index 0000000000..8662152a64
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/ModelExpressionPassTest.cs
@@ -0,0 +1,258 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Microsoft.AspNetCore.Razor.Evolution;
+using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+using Xunit;
+using ErrorSink = Microsoft.AspNetCore.Razor.Evolution.Legacy.ErrorSink;
+
+namespace Microsoft.AspNetCore.Mvc.Razor.Host
+{
+ public class MvcTagHelperAttributeValueCodeRendererTest
+ {
+ [Fact]
+ public void ModelExpressionPass_NonModelExpressionProperty_Ignored()
+ {
+ // Arrange
+ var codeDocument = CreateDocument(@"
+@addTagHelper TestTagHelper, TestAssembly
+");
+
+ var tagHelpers = new[]
+ {
+ new TagHelperDescriptor()
+ {
+ AssemblyName = "TestAssembly",
+ TypeName = "TestTagHelper",
+ TagName = "p",
+ Attributes = new TagHelperAttributeDescriptor[]
+ {
+ new TagHelperAttributeDescriptor()
+ {
+ TypeName = "System.Int32",
+ Name = "Foo",
+ }
+
+ }
+ }
+ };
+
+ var engine = CreateEngine(tagHelpers);
+ var pass = new ModelExpressionPass()
+ {
+ Engine = engine,
+ };
+
+ var irDocument = CreateIRDocument(engine, codeDocument);
+
+ // Act
+ pass.Execute(codeDocument, irDocument);
+
+ // Assert
+ var tagHelper = FindTagHelperNode(irDocument);
+ var setProperty = tagHelper.Children.OfType().Single();
+
+ var child = Assert.IsType(Assert.Single(setProperty.Children));
+ Assert.Equal("17", child.Content);
+ }
+
+ [Fact]
+ public void ModelExpressionPass_ModelExpressionProperty_SimpleExpression()
+ {
+ // Arrange
+ var codeDocument = CreateDocument(@"
+@addTagHelper TestTagHelper, TestAssembly
+");
+
+ var tagHelpers = new[]
+ {
+ new TagHelperDescriptor()
+ {
+ AssemblyName = "TestAssembly",
+ TypeName = "TestTagHelper",
+ TagName = "p",
+ Attributes = new TagHelperAttributeDescriptor[]
+ {
+ new TagHelperAttributeDescriptor()
+ {
+ TypeName = typeof(ModelExpression).FullName,
+ Name = "Foo",
+ }
+
+ }
+ }
+ };
+
+ var engine = CreateEngine(tagHelpers);
+ var pass = new ModelExpressionPass()
+ {
+ Engine = engine,
+ };
+
+ var irDocument = CreateIRDocument(engine, codeDocument);
+
+ // Act
+ pass.Execute(codeDocument, irDocument);
+
+ // Assert
+ var tagHelper = FindTagHelperNode(irDocument);
+ var setProperty = tagHelper.Children.OfType().Single();
+
+ var expression = Assert.IsType(Assert.Single(setProperty.Children));
+ Assert.Equal("ModelExpressionProvider.CreateModelExpression(ViewData, __model => __model.Bar)", GetCSharpContent(expression));
+
+ var originalNode = Assert.IsType(expression.Children[2]);
+ Assert.Equal("Bar", originalNode.Content);
+ Assert.Equal(new SourceSpan("test.cshtml", 53, 2, 8, 3), originalNode.Source.Value);
+ }
+
+ [Fact]
+ public void ModelExpressionPass_ModelExpressionProperty_ComplexExpression()
+ {
+ // Arrange
+ var codeDocument = CreateDocument(@"
+@addTagHelper TestTagHelper, TestAssembly
+");
+
+ var tagHelpers = new[]
+ {
+ new TagHelperDescriptor()
+ {
+ AssemblyName = "TestAssembly",
+ TypeName = "TestTagHelper",
+ TagName = "p",
+ Attributes = new TagHelperAttributeDescriptor[]
+ {
+ new TagHelperAttributeDescriptor()
+ {
+ TypeName = typeof(ModelExpression).FullName,
+ Name = "Foo",
+ }
+
+ }
+ }
+ };
+
+ var engine = CreateEngine(tagHelpers);
+ var pass = new ModelExpressionPass()
+ {
+ Engine = engine,
+ };
+
+ var irDocument = CreateIRDocument(engine, codeDocument);
+
+ // Act
+ pass.Execute(codeDocument, irDocument);
+
+ // Assert
+ var tagHelper = FindTagHelperNode(irDocument);
+ var setProperty = tagHelper.Children.OfType().Single();
+
+ var expression = Assert.IsType(Assert.Single(setProperty.Children));
+ Assert.Equal("ModelExpressionProvider.CreateModelExpression(ViewData, __model => Bar)", GetCSharpContent(expression));
+
+ var originalNode = Assert.IsType(expression.Children[1]);
+ Assert.Equal("Bar", originalNode.Content);
+ Assert.Equal(new SourceSpan("test.cshtml", 54, 2, 9, 3), originalNode.Source.Value);
+ }
+
+ private RazorCodeDocument CreateDocument(string content)
+ {
+ using (var stream = new MemoryStream())
+ {
+ var bytes = Encoding.UTF8.GetBytes(content);
+ stream.Write(bytes, 0, bytes.Length);
+ stream.Seek(0L, SeekOrigin.Begin);
+
+ var source = RazorSourceDocument.ReadFrom(stream, "test.cshtml");
+ return RazorCodeDocument.Create(source);
+ }
+ }
+
+ private RazorEngine CreateEngine(params TagHelperDescriptor[] tagHelpers)
+ {
+ return RazorEngine.Create(b =>
+ {
+ b.Features.Add(new TagHelperFeature(tagHelpers));
+ });
+ }
+
+ private DocumentIRNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
+ {
+ for (var i = 0; i < engine.Phases.Count; i++)
+ {
+ var phase = engine.Phases[i];
+ phase.Execute(codeDocument);
+
+ if (phase is IRazorIRPhase)
+ {
+ break;
+ }
+ }
+
+ return codeDocument.GetIRDocument();
+ }
+
+ private TagHelperIRNode FindTagHelperNode(RazorIRNode node)
+ {
+ var visitor = new TagHelperNodeVisitor();
+ visitor.Visit(node);
+ return visitor.Node;
+ }
+
+ private string GetCSharpContent(RazorIRNode node)
+ {
+ var builder = new StringBuilder();
+ for (var i = 0; i < node.Children.Count; i++)
+ {
+ var child = node.Children[i] as CSharpTokenIRNode;
+ builder.Append(child.Content);
+ }
+
+ return builder.ToString();
+ }
+
+ private class TagHelperNodeVisitor : RazorIRNodeWalker
+ {
+ public TagHelperIRNode Node { get; set; }
+
+ public override void VisitTagHelper(TagHelperIRNode node)
+ {
+ Node = node;
+ }
+ }
+
+ private class TagHelperFeature : ITagHelperFeature
+ {
+ public TagHelperFeature(TagHelperDescriptor[] tagHelpers)
+ {
+ Resolver = new TagHelperDescriptorResolver(tagHelpers);
+ }
+
+ public RazorEngine Engine { get; set; }
+
+ public ITagHelperDescriptorResolver Resolver { get; }
+ }
+
+ private class TagHelperDescriptorResolver : ITagHelperDescriptorResolver
+ {
+ public TagHelperDescriptorResolver(TagHelperDescriptor[] tagHelpers)
+ {
+ TagHelpers = tagHelpers;
+ }
+
+ public TagHelperDescriptor[] TagHelpers { get; }
+
+ public IEnumerable Resolve(ErrorSink errorSink)
+ {
+ return TagHelpers;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/MvcTagHelperAttributeValueCodeRendererTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/MvcTagHelperAttributeValueCodeRendererTest.cs
deleted file mode 100644
index 7d873eba3e..0000000000
--- a/test/Microsoft.AspNetCore.Mvc.Razor.Host.Test/MvcTagHelperAttributeValueCodeRendererTest.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.AspNetCore.Razor;
-using Microsoft.AspNetCore.Razor.Chunks.Generators;
-using Microsoft.AspNetCore.Razor.CodeGenerators;
-using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Mvc.Razor
-{
- public class MvcTagHelperAttributeValueCodeRendererTest
- {
- #if OLD_RAZOR
- [Theory]
- [InlineData("SomeType", "SomeType", "Provider.SomeMethod(ViewData, __model => __model.MyValue)")]
- [InlineData("SomeType", "SomeType2", "MyValue")]
- public void RenderAttributeValue_RendersModelExpressionsCorrectly(
- string modelExpressionType,
- string propertyType,
- string expectedValue)
- {
- // Arrange
- var renderer = new MvcTagHelperAttributeValueCodeRenderer(
- new GeneratedTagHelperAttributeContext
- {
- ModelExpressionTypeName = modelExpressionType,
- CreateModelExpressionMethodName = "SomeMethod",
- ModelExpressionProviderPropertyName = "Provider",
- ViewDataPropertyName = "ViewData"
- });
- var attributeDescriptor = new TagHelperAttributeDescriptor
- {
- Name = "MyAttribute",
- PropertyName = "SomeProperty",
- TypeName = propertyType,
- };
- var writer = new CSharpCodeWriter();
- var generatorContext = new ChunkGeneratorContext(
- host: null,
- className: string.Empty,
- rootNamespace: string.Empty,
- sourceFile: string.Empty,
- shouldGenerateLinePragmas: true);
- var errorSink = new ErrorSink();
- var context = new CodeGeneratorContext(generatorContext, errorSink);
-
- // Act
- renderer.RenderAttributeValue(attributeDescriptor, writer, context,
- (codeWriter) =>
- {
- codeWriter.Write("MyValue");
- },
- complexValue: false);
-
- // Assert
- Assert.Equal(expectedValue, writer.GenerateCode());
- }
-#endif
- }
-}
\ No newline at end of file