Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace __GeneratedComponent
#line default
#line hidden
#nullable restore
public partial class AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable
public partial class BasicComponent : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable
#nullable disable
{
#pragma warning disable 219
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
UsingDirective - (67:2,1 [25] ) - global::System.Linq
UsingDirective - (95:3,1 [36] ) - global::System.Threading.Tasks
UsingDirective - (134:4,1 [45] ) - global::Microsoft.AspNetCore.Components
ClassDeclaration - - public partial - AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable
ClassDeclaration - - public partial - BasicComponent - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable
DesignTimeDirective -
DirectiveToken - (12:0,12 [11] BasicComponent.cshtml) - IDisposable
CSharpCode -
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
Source Location: (12:0,12 [11] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|IDisposable|
Generated Location: (813:21,0 [11] )
Generated Location: (752:21,0 [11] )
|IDisposable|

Source Location: (38:1,13 [15] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|this.ToString()|
Generated Location: (1413:39,13 [15] )
Generated Location: (1352:39,13 [15] )
|this.ToString()|

Source Location: (79:3,5 [29] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|string.Format("{0}", "Hello")|
Generated Location: (1610:47,6 [29] )
Generated Location: (1549:47,6 [29] )
|string.Format("{0}", "Hello")|

Source Location: (132:6,12 [37] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
void IDisposable.Dispose(){ }
|
Generated Location: (1862:56,12 [37] )
Generated Location: (1801:56,12 [37] )
|
void IDisposable.Dispose(){ }
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace __GeneratedComponent
#line default
#line hidden
#nullable restore
public partial class AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable
public partial class BasicComponent : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable
#nullable disable
{
#pragma warning disable 1998
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
UsingDirective - (67:2,1 [27] ) - global::System.Linq
UsingDirective - (95:3,1 [38] ) - global::System.Threading.Tasks
UsingDirective - (134:4,1 [45] ) - global::Microsoft.AspNetCore.Components
ClassDeclaration - - public partial - AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable
ClassDeclaration - - public partial - BasicComponent - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable
MethodDeclaration - - protected override - void - BuildRenderTree
MarkupElement - (25:1,0 [91] BasicComponent.cshtml) - div
HtmlAttribute - (29:1,4 [25] BasicComponent.cshtml) - class=" - "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,20 @@ public void Execute_NoNamespaceSet_Noops()
pass.Execute(codeDocument, irDocument);

// Assert
SingleChild<NamespaceDeclarationIntermediateNode>(irDocument);
Assert.Equal(2, irDocument.Children.Count);

var item = Assert.IsType<RazorCompiledItemAttributeIntermediateNode>(irDocument.Children[0]);
Assert.Equal("/test.cshtml", item.Identifier);
Assert.Equal("test", item.Kind);
Assert.Equal("Test", item.TypeName);

Assert.Equal(2, @namespace.Children.Count);
var checksum = Assert.IsType<RazorSourceChecksumAttributeIntermediateNode>(@namespace.Children[0]);
Assert.Equal(CodeAnalysis.Text.SourceHashAlgorithm.Sha256, checksum.ChecksumAlgorithm);
Assert.Equal("/test.cshtml", checksum.Identifier);

var foundClass = Assert.IsType<ClassDeclarationIntermediateNode>(@namespace.Children[1]);
Assert.Equal("Test", foundClass.ClassName);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ private ref struct DisplayNameHelper(INamedTypeSymbol type)
private (string Type, string Namespace)? _names;

public (string Type, string Namespace) GetNames()
=> _names ??= (_type.ToDisplayString(), _type.ContainingNamespace.ToDisplayString());
=> _names ??= (_type.ToDisplayString(),
_type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_type.ContainingNamespace.ToDisplayString() would be "<global namespace>"; with SymbolExtensions.FullNameTypeDisplayFormat we get an empty string instead (this format was already used in other places, just not everywhere)

}

private static TagHelperDescriptor CreateElementBindTagHelper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,10 @@ private static TagHelperDescriptor CreateNameMatchingDescriptor(

if (fullyQualified)
{
var containingNamespace = type.ContainingNamespace.ToDisplayString();
var fullName = $"{containingNamespace}.{type.Name}";
var containingNamespace = type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat);
var fullName = string.IsNullOrEmpty(containingNamespace)
? type.Name
: $"{containingNamespace}.{type.Name}";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer if we were more direct with the condition here:

Suggested change
var containingNamespace = type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat);
var fullName = string.IsNullOrEmpty(containingNamespace)
? type.Name
: $"{containingNamespace}.{type.Name}";
var fullName = type.ContainingNamespace.IsGlobalNamespace
? type.Name
: $"{type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat)}.{type.Name}";

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is it possible to have a component in a nested type? If so, will this handle that correctly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, is it possible to have a component in a nested type? If so, will this handle that correctly?

I don't think it's possible. (You've already asked me this previously in #9689 (comment) :D)

In any case, the previous code did "{namespace}.{typeName}" and I've just changed it to omit the "{namespace}." prefix when needed, so nothing should change at this front, right?


builder.TagMatchingRule(r =>
{
Expand All @@ -125,7 +127,8 @@ private static TagHelperDescriptor CreateNameMatchingDescriptor(

metadata.Add(ComponentMetadata.Component.NameMatchKey, ComponentMetadata.Component.FullyQualifiedNameMatch);
}
else
// If the component is in the global namespace, skip adding this rule which is the same as the fully qualified one.
else if (!type.ContainingNamespace.IsGlobalNamespace)
{
builder.TagMatchingRule(r =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ private ref struct DisplayNameHelper(INamedTypeSymbol type)

public (string Type, string Namespace) GetNames()
{
_names ??= (_type.ToDisplayString(), _type.ContainingNamespace.ToDisplayString());
_names ??= (_type.ToDisplayString(),
_type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat));

return _names.GetValueOrDefault();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public static CodeWriter WriteEnhancedLineNumberDirective(this CodeWriter writer
.Write(characterEndAsString)
.Write(") ");

// an offset of zero is indicated by its absence.
// an offset of zero is indicated by its absence.
if (characterOffset != 0)
{
var characterOffsetAsString = characterOffset.ToString(CultureInfo.InvariantCulture);
Expand Down Expand Up @@ -423,8 +423,14 @@ private static CSharpCodeWritingScope BuildLambda(CodeWriter writer, bool async,
return scope;
}

public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, string name, SourceSpan? span, CodeRenderingContext context)
#nullable enable
public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, string? name, SourceSpan? span, CodeRenderingContext context)
{
if (name.IsNullOrEmpty())
{
return new CSharpCodeWritingScope(writer, writeBraces: false);
}

writer.Write("namespace ");
if (context.Options.DesignTime || span is null)
{
Expand All @@ -440,6 +446,7 @@ public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, stri
}
return new CSharpCodeWritingScope(writer);
}
#nullable disable

public static CSharpCodeWritingScope BuildClassDeclaration(
this CodeWriter writer,
Expand Down Expand Up @@ -711,13 +718,15 @@ public struct CSharpCodeWritingScope : IDisposable
{
private readonly CodeWriter _writer;
private readonly bool _autoSpace;
private readonly bool _writeBraces;
private readonly int _tabSize;
private int _startIndent;

public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace = true)
public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace = true, bool writeBraces = true)
{
_writer = writer;
_autoSpace = autoSpace;
_writeBraces = writeBraces;
_tabSize = writer.TabSize;
_startIndent = -1; // Set in WriteStartScope

Expand All @@ -733,7 +742,15 @@ private void WriteStartScope()
{
TryAutoSpace(" ");

_writer.WriteLine("{");
if (_writeBraces)
{
_writer.WriteLine("{");
}
else
{
_writer.WriteLine();
}

_writer.CurrentIndent += _tabSize;
_startIndent = _writer.CurrentIndent;
}
Expand All @@ -748,7 +765,14 @@ private void WriteEndScope()
_writer.CurrentIndent -= _tabSize;
}

_writer.WriteLine("}");
if (_writeBraces)
{
_writer.WriteLine("}");
}
else
{
_writer.WriteLine();
}
}

private void TryAutoSpace(string spaceCharacter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ protected override void OnDocumentStructureCreated(
ClassDeclarationIntermediateNode @class,
MethodDeclarationIntermediateNode method)
{
if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var computedNamespace, out var computedNamespaceSpan) ||
!TryComputeClassName(codeDocument, out var computedClass))
if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, allowEmptyRootNamespace: true, out var computedNamespace, out var computedNamespaceSpan))
{
// If we can't compute a nice namespace (no relative path) then just generate something
// mangled.
computedNamespace = FallbackRootNamespace;
}

if (!TryComputeClassName(codeDocument, out var computedClass))
{
var checksum = ChecksumUtilities.BytesToString(codeDocument.Source.Text.GetChecksum());
computedClass = $"AspNetCore_{checksum}";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ child is not DirectiveTokenIntermediateNode directiveToken

// generate the attribute usage on top of the class
var attributeNode = new CSharpCodeIntermediateNode();
var namespaceSeparator = string.IsNullOrEmpty(@namespace.Content) ? string.Empty : ".";
attributeNode.Children.Add(new IntermediateToken()
{
Kind = TokenKind.CSharp,
Content = $"[global::{@namespace.Content}.{@class.ClassName}.{GeneratedRenderModeAttributeName}]",
Content = $"[global::{@namespace.Content}{namespaceSeparator}{@class.ClassName}.{GeneratedRenderModeAttributeName}]",
});

// Insert the new attribute on top of the class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
//
// If we can't use [RazorCompiledItem] then we don't care about the rest of the attributes.
var @namespace = documentNode.FindPrimaryNamespace();
if (@namespace == null || string.IsNullOrEmpty(@namespace.Content))
if (@namespace == null)
{
// No namespace node or it's incomplete. Skip.
// No namespace node. Skip.
return;
}

Expand All @@ -70,7 +70,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
// [RazorCompiledItem] is an [assembly: ... ] attribute, so it needs to be applied at the global scope.
documentNode.Children.Insert(0, new RazorCompiledItemAttributeIntermediateNode()
{
TypeName = @namespace.Content + "." + @class.ClassName,
TypeName = string.IsNullOrEmpty(@namespace.Content)
? @class.ClassName
: @namespace.Content + "." + @class.ClassName,
Kind = documentNode.DocumentKind,
Identifier = identifier,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,14 @@ public static bool TryComputeClassName(this RazorCodeDocument codeDocument, [Not
#nullable disable

public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace)
=> TryComputeNamespace(document, fallbackToRootNamespace, out @namespace, out _);
=> TryComputeNamespace(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: false, out @namespace, out _);

// In general documents will have a relative path (relative to the project root).
// We can only really compute a nice namespace when we know a relative path.
//
// However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't
// set up correctly.
public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace, out SourceSpan? namespaceSpan)
internal static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, bool allowEmptyRootNamespace, out string @namespace, out SourceSpan? namespaceSpan)
{
if (document == null)
{
Expand All @@ -325,7 +325,7 @@ public static bool TryComputeNamespace(this RazorCodeDocument document, bool fal
}
else
{
var result = TryComputeNamespaceCore(document, fallbackToRootNamespace, out @namespace, out namespaceSpan);
var result = TryComputeNamespaceCore(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: allowEmptyRootNamespace, out @namespace, out namespaceSpan);
if (result)
{
document.Items[NamespaceKey] = (@namespace, namespaceSpan);
Expand All @@ -336,14 +336,14 @@ public static bool TryComputeNamespace(this RazorCodeDocument document, bool fal
#if DEBUG
// In debug mode, even if we're cached, lets take the hit to run this again and make sure the cached value is correct.
// This is to help us find issues with caching logic during development.
var validateResult = TryComputeNamespaceCore(document, fallbackToRootNamespace, out var validateNamespace, out _);
var validateResult = TryComputeNamespaceCore(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: allowEmptyRootNamespace, out var validateNamespace, out _);
Debug.Assert(validateResult, "We couldn't compute the namespace, but have a cached value, so something has gone wrong");
Debug.Assert(validateNamespace == @namespace, $"We cached a namespace of {@namespace} but calculated that it should be {validateNamespace}");
#endif

return true;

bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace, out SourceSpan? namespaceSpan)
bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootNamespace, bool allowEmptyRootNamespace, out string @namespace, out SourceSpan? namespaceSpan)
{
var filePath = document.Source.FilePath;
if (filePath == null || document.Source.RelativePath == null || filePath.Length < document.Source.RelativePath.Length)
Expand Down Expand Up @@ -408,11 +408,18 @@ bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootName
var options = document.GetCodeGenerationOptions() ?? document.GetDocumentIntermediateNode()?.Options;
baseNamespace = options?.RootNamespace;
appendSuffix = true;
}

if (string.IsNullOrEmpty(baseNamespace))
// null means RootNamespace wasn't set yet, so we fail for now, tooling seems to rely on this
if (baseNamespace is null ||
(!allowEmptyRootNamespace && string.IsNullOrEmpty(baseNamespace)))
{
@namespace = null;
return false;
}
}
else
{
// There was no valid @namespace directive and we couldn't compute the RootNamespace.
// There was no valid @namespace directive.
@namespace = null;
return false;
}
Expand Down Expand Up @@ -455,7 +462,11 @@ bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootName

previousLength = builder.Length;

builder.Append('.');
if (previousLength != 0)
{
builder.Append('.');
}

CSharpIdentifier.AppendSanitized(builder, token);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ public bool Add(TagHelperDescriptor tagHelper)
}

var className = $"__Generated__{tagHelper.GetViewComponentName()}ViewComponentTagHelper";
var fullyQualifiedName = $"{Namespace.Content}.{Class.ClassName}.{className}";
var namespaceSeparator = string.IsNullOrEmpty(Namespace.Content) ? string.Empty : ".";
var fullyQualifiedName = $"{Namespace.Content}{namespaceSeparator}{Class.ClassName}.{className}";
var fieldName = GenerateFieldName(tagHelper);

_tagHelpers.Add(tagHelper, (className, fullyQualifiedName, fieldName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
}

var @namespace = documentNode.FindPrimaryNamespace();
if (@namespace == null || string.IsNullOrEmpty(@namespace.Content))
if (@namespace == null)
{
// No namespace node or it's incomplete. Skip.
// No namespace node. Skip.
return;
}

Expand All @@ -36,7 +36,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte
return;
}

var generatedTypeName = $"{@namespace.Content}.{@class.ClassName}";
var generatedTypeName = string.IsNullOrEmpty(@namespace.Content)
? @class.ClassName
: $"{@namespace.Content}.{@class.ClassName}";

// The MVC attributes require a relative path to be specified so that we can make a view engine path.
// We can't use a rooted path because we don't know what the project root is.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ public bool Add(TagHelperDescriptor tagHelper)
}

var className = $"__Generated__{tagHelper.GetViewComponentName()}ViewComponentTagHelper";
var fullyQualifiedName = $"{Namespace.Content}.{Class.ClassName}.{className}";
var namespaceSeparator = string.IsNullOrEmpty(Namespace.Content) ? string.Empty : ".";
var fullyQualifiedName = $"{Namespace.Content}{namespaceSeparator}{Class.ClassName}.{className}";
var fieldName = GenerateFieldName(tagHelper);

_tagHelpers.Add(tagHelper, (className, fullyQualifiedName, fieldName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ public bool Add(TagHelperDescriptor tagHelper)
}

var className = $"__Generated__{tagHelper.GetViewComponentName()}ViewComponentTagHelper";
var fullyQualifiedName = $"{Namespace.Content}.{Class.ClassName}.{className}";
var namespaceSeparator = string.IsNullOrEmpty(Namespace.Content) ? string.Empty : ".";
var fullyQualifiedName = $"{Namespace.Content}{namespaceSeparator}{Class.ClassName}.{className}";
var fieldName = GenerateFieldName(tagHelper);

_tagHelpers.Add(tagHelper, (className, fullyQualifiedName, fieldName));
Expand Down
Loading