Don't discover tag helpers if some references are missing
A common cause for runtime view compilation failure in MVC is when an application is published without reference assemblies. MVC usually handles this at compilation, by looking for specific error codes. More recently, TagHelper discovery fails with an not-so-helpful error message in this scenario. This change attempts to add a little more error checking to cover the most common cases
This commit is contained in:
parent
d6f3a1bd22
commit
a57b7c4d7f
|
|
@ -31,8 +31,19 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
return;
|
||||
}
|
||||
|
||||
var @interface = compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper);
|
||||
var @string = compilation.GetSpecialType(SpecialType.System_String);
|
||||
|
||||
// Ensure ITagHelper and System.String are available. They may be missing or
|
||||
// errored if we're missing references.
|
||||
if (@interface == null || @interface.TypeKind == TypeKind.Error ||
|
||||
@string == null || @string.TypeKind == TypeKind.Error)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var types = new List<INamedTypeSymbol>();
|
||||
var visitor = TagHelperTypeVisitor.Create(compilation, types);
|
||||
var visitor = new TagHelperTypeVisitor(@interface, types);
|
||||
|
||||
// We always visit the global namespace.
|
||||
visitor.Visit(compilation.Assembly.GlobalNamespace);
|
||||
|
|
|
|||
|
|
@ -8,14 +8,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
// Visits top-level types and finds interface implementations.
|
||||
internal class TagHelperTypeVisitor : SymbolVisitor
|
||||
{
|
||||
private INamedTypeSymbol _interface;
|
||||
private List<INamedTypeSymbol> _results;
|
||||
|
||||
public static TagHelperTypeVisitor Create(Compilation compilation, List<INamedTypeSymbol> results)
|
||||
{
|
||||
var @interface = compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper);
|
||||
return new TagHelperTypeVisitor(@interface, results);
|
||||
}
|
||||
private readonly INamedTypeSymbol _interface;
|
||||
private readonly List<INamedTypeSymbol> _results;
|
||||
|
||||
public TagHelperTypeVisitor(INamedTypeSymbol @interface, List<INamedTypeSymbol> results)
|
||||
{
|
||||
|
|
@ -41,12 +35,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
internal bool IsTagHelper(INamedTypeSymbol symbol)
|
||||
{
|
||||
if (_interface == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return
|
||||
symbol.TypeKind != TypeKind.Error &&
|
||||
symbol.DeclaredAccessibility == Accessibility.Public &&
|
||||
!symbol.IsAbstract &&
|
||||
!symbol.IsGenericType &&
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
|
|
@ -21,6 +24,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
var descriptorProvider = new DefaultTagHelperDescriptorProvider();
|
||||
|
||||
var context = TagHelperDescriptorProviderContext.Create();
|
||||
context.SetCompilation(compilation);
|
||||
context.ExcludeHidden = true;
|
||||
|
||||
// Act
|
||||
|
|
@ -33,5 +37,93 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
var editorBrowsableDescriptor = context.Results.Where(descriptor => descriptor.GetTypeName() == editorBrowsableTypeName);
|
||||
Assert.Empty(editorBrowsableDescriptor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoOpsIfCompilationSymbolIsNotSet()
|
||||
{
|
||||
// Arrange
|
||||
var descriptorProvider = new DefaultTagHelperDescriptorProvider();
|
||||
|
||||
var context = TagHelperDescriptorProviderContext.Create();
|
||||
|
||||
// Act
|
||||
descriptorProvider.Execute(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.Results);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoOpsIfTagHelperInterfaceCannotBeFound()
|
||||
{
|
||||
// Arrange
|
||||
var references = new[]
|
||||
{
|
||||
MetadataReference.CreateFromFile(typeof(string).Assembly.Location),
|
||||
};
|
||||
var compilation = CSharpCompilation.Create("Test", references: references);
|
||||
|
||||
var descriptorProvider = new DefaultTagHelperDescriptorProvider();
|
||||
|
||||
var context = TagHelperDescriptorProviderContext.Create();
|
||||
context.SetCompilation(compilation);
|
||||
|
||||
// Act
|
||||
descriptorProvider.Execute(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.Results);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoOpsIfStringCannotBeFound()
|
||||
{
|
||||
// Arrange
|
||||
var references = new[]
|
||||
{
|
||||
MetadataReference.CreateFromFile(typeof(ITagHelper).Assembly.Location),
|
||||
};
|
||||
var compilation = CSharpCompilation.Create("Test", references: references);
|
||||
|
||||
var descriptorProvider = new DefaultTagHelperDescriptorProvider();
|
||||
|
||||
var context = TagHelperDescriptorProviderContext.Create();
|
||||
context.SetCompilation(compilation);
|
||||
|
||||
// Act
|
||||
descriptorProvider.Execute(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.Results);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_DiscoversTagHelpersFromCompilation()
|
||||
{
|
||||
// Arrange
|
||||
var references = new[]
|
||||
{
|
||||
MetadataReference.CreateFromFile(typeof(string).Assembly.Location),
|
||||
MetadataReference.CreateFromFile(typeof(ITagHelper).Assembly.Location),
|
||||
};
|
||||
var projectDirectory = TestProject.GetProjectDirectory(GetType());
|
||||
var tagHelperContent = File.ReadAllText(Path.Combine(projectDirectory, "TagHelperTypes.cs"));
|
||||
var syntaxTree = CSharpSyntaxTree.ParseText(tagHelperContent);
|
||||
var compilation = CSharpCompilation.Create("Test", references: references, syntaxTrees: new[] { syntaxTree });
|
||||
|
||||
var descriptorProvider = new DefaultTagHelperDescriptorProvider();
|
||||
|
||||
var context = TagHelperDescriptorProviderContext.Create();
|
||||
context.SetCompilation(compilation);
|
||||
|
||||
// Act
|
||||
descriptorProvider.Execute(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Results.OrderBy(r => r.Name),
|
||||
tagHelper => Assert.Equal(typeof(Valid_InheritedTagHelper).FullName, tagHelper.Name),
|
||||
tagHelper => Assert.Equal(typeof(Valid_PlainTagHelper).FullName, tagHelper.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,24 +98,4 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
|||
public string Invoke(string foo) => null;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class Invalid_AbstractTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class Invalid_GenericTagHelper<T> : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
internal class Invalid_InternalTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class Valid_PlainTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class Valid_InheritedTagHelper : Valid_PlainTagHelper
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// 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.TagHelpers;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class Invalid_AbstractTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class Invalid_GenericTagHelper<T> : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
internal class Invalid_InternalTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class Valid_PlainTagHelper : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
public class Valid_InheritedTagHelper : Valid_PlainTagHelper
|
||||
{
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue