Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,37 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
return true;
}

if (oldSymbol is IAssemblySymbol oldAssembly && newSymbol is IAssemblySymbol newAssembly)
if (oldSymbol is not IAssemblySymbol oldAssembly || newSymbol is not IAssemblySymbol newAssembly)
{
var oldModuleMVIDs = oldAssembly.Modules.Select(GetMVID);
var newModuleMVIDs = newAssembly.Modules.Select(GetMVID);
return oldModuleMVIDs.SequenceEqual(newModuleMVIDs);
return false;
}

// Compare the MVIDs of the modules in each assembly. If they aren't present or don't match we don't consider them equal
var oldModules = oldAssembly.Modules.ToArray();
var newModules = newAssembly.Modules.ToArray();
if (oldModules.Length != newModules.Length)
{
return false;
}
Comment on lines +182 to +187
Copy link
Member

Choose a reason for hiding this comment

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

grumble grumble Why does IAssemblySymbol.Modules return an IEnumerable<IModuleSymbol> when it's always backed by an ImmutableArray<IModuleSymbol>? grumble grumble

(no change requested)


static Guid GetMVID(IModuleSymbol m) => m.GetMetadata()?.GetModuleVersionId() ?? Guid.Empty;
for (int i = 0; i < oldModules.Length; i++)
{
var oldMetadata = oldModules[i].GetMetadata();
var newMetadata = newModules[i].GetMetadata();

if (oldMetadata is null || newMetadata is null)
{
return false;
}

if (oldMetadata.GetModuleVersionId() != newMetadata.GetModuleVersionId())
{
return false;
}
}
Comment on lines +189 to 203
Copy link
Member

Choose a reason for hiding this comment

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

Is the order of modules in an assembly stable? Or, should two assemblies be considered identical if their modules are in a different order? Or, should I be quiet and remember that multi-module assemblies are an uncommon scenario? I'm guessing your answer will be an affirmative to the third question.

Copy link
Member Author

Choose a reason for hiding this comment

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

Heh, AFAIK the modules should be stable, and I would assume if they had moved around, we would need to re-run discovery anyway as there are odd initialization things that can change, I think. If we were re-running when we shouldn't, that's obviously a potential perf hit, but not a correctness one at least.

Generally though it's super rare to actually have multiple modules in an assembly (and I believe it's not even possible in modern .NET)

Copy link
Member

Choose a reason for hiding this comment

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

I gave you an out with my third question BTW. You could have typed way fewer words. 😁


return false;
// All module MVIDs matched.
return true;
})))
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3605,5 +3605,85 @@ public async Task IncrementalCompilation_RerunsGenerator_When_AdditionalFileRena
var newCouNterSource = result.GeneratedSources.FirstOrDefault(s => s.HintName.Contains("NewCouNter"));
Assert.Contains("public partial class NewCouNter", newCouNterSource.SourceText.ToString());
}

[Fact, WorkItem("https://github.com/dotnet/razor/issues/12316")]
public async Task RazorClassLibrary_Change_Updates_DependentProject_WhenReferencedAsCompilation()
{
var rclProject = CreateTestProject(new()
{
["LibComponent.razor"] = "<p>Library component</p>",
});
rclProject = rclProject.WithAssemblyName("RazorClassLibrary");

var rclCompilation = await rclProject.GetCompilationAsync();
var rclDriver = await GetDriverAsync(rclProject);
var rclRun = RunGenerator(rclCompilation!, ref rclDriver, out var rclOutputCompilation);
Assert.Empty(rclRun.Diagnostics);
Assert.Single(rclRun.GeneratedSources); // LibComponent

// Explicitly use a CompilationReference
var rclReference = rclOutputCompilation.ToMetadataReference();

// Create the main project that references the RCL and uses its component.
var mainProject = CreateTestProject(new()
{
["Pages/Index.razor"] = "<LibComponent />",
});
mainProject = mainProject.AddMetadataReference(rclReference);

var mainCompilation = await mainProject.GetCompilationAsync();
var (mainDriver, mainAdditionalTexts) = await GetDriverWithAdditionalTextAsync(mainProject);
var mainRun = RunGenerator(mainCompilation!, ref mainDriver);
Assert.Empty(mainRun.Diagnostics);
Assert.Single(mainRun.GeneratedSources);

// Rename the component in the RCL: LibComponent -> RenamedComponent
rclProject = CreateTestProject(new()
{
["RenamedComponent.razor"] = "<p>Library component</p>",
}).WithAssemblyName("RazorClassLibrary");

rclCompilation = await rclProject.GetCompilationAsync()!;
rclDriver = await GetDriverAsync(rclProject);
rclRun = RunGenerator(rclCompilation!, ref rclDriver, out rclOutputCompilation);
Assert.Empty(rclRun.Diagnostics);
Assert.Single(rclRun.GeneratedSources); // RenamedComponent

var rclReference2 = rclOutputCompilation.ToMetadataReference();

// Update main project to point to the new reference (with renamed component).
mainProject = mainProject.RemoveMetadataReference(rclReference)
.AddMetadataReference(rclReference2);
mainCompilation = await mainProject.GetCompilationAsync();

// Re-run generator: expect missing component diagnostic (RZ10012).
mainRun = RunGenerator(mainCompilation!, ref mainDriver);
var missing = Assert.Single(mainRun.Diagnostics);
Assert.Equal("RZ10012", missing.Id);

// Update main project's Index.razor to use the renamed component.
var updatedIndex = new TestAdditionalText("Pages/Index.razor", SourceText.From("<RenamedComponent />", Encoding.UTF8));
mainDriver = mainDriver.ReplaceAdditionalText(
mainAdditionalTexts.First(t => t.Path.EndsWith("Index.razor", StringComparison.OrdinalIgnoreCase)),
updatedIndex);

// Re-run generator: should compile cleanly again.
mainRun = RunGenerator(mainCompilation!, ref mainDriver);
Assert.Empty(mainRun.Diagnostics);
Assert.Single(mainRun.GeneratedSources);

// Update the compilation, which will cause us to re-run.
RazorEventListener eventListener = new RazorEventListener();

mainCompilation = mainCompilation!.WithOptions(mainCompilation.Options.WithModuleName("newMain"));
mainRun = RunGenerator(mainCompilation!, ref mainDriver);
Assert.Empty(mainRun.Diagnostics);
Assert.Single(mainRun.GeneratedSources);

// Confirm that the tag helpers from metadata refs _didn't_ re-run
Assert.Collection(eventListener.Events,
e => Assert.Equal("DiscoverTagHelpersFromCompilationStart", e.EventName),
e => Assert.Equal("DiscoverTagHelpersFromCompilationStop", e.EventName));
}
}
}
Loading