Repository: dotnet/roslyn
File: src/Workspaces/Core/Portable/Workspace/Solution/ProjectChanges.cs (C#)

Code before:
    public IEnumerable<DocumentId> GetRemovedDocuments()
        => NewProject.State.DocumentStates.GetRemovedStateIds(OldProject.State.DocumentStates);

    /// <summary>
    /// Get <see cref="DocumentId"/>s of removed additional documents in the order they appear in <see cref="Project.DocumentIds"/> of <see cref="OldProject"/>.
    /// </summary>
    public IEnumerable<DocumentId> GetRemovedAdditionalDocuments()
        => NewProject.State.AdditionalDocumentStates.GetRemovedStateIds(OldProject.State.AdditionalDocumentStates);

    /// <summary>
    /// Get <see cref="DocumentId"/>s of removed analyzer config documents in the order they appear in <see cref="Project.DocumentIds"/> of <see cref="OldProject"/>.
    /// </summary>
    public IEnumerable<DocumentId> GetRemovedAnalyzerConfigDocuments()
        => NewProject.State.AnalyzerConfigDocumentStates.GetRemovedStateIds(OldProject.State.AnalyzerConfigDocumentStates);

    private static IEnumerable<T> GetChangedItems<T>(IEnumerable<T> newItems, IEnumerable<T> oldItems)
        => newItems == oldItems ? [] : newItems.Except(oldItems);

    private static IEnumerable<ProjectReference> GetChangedProjectReferences(Project newProject, Project oldProject)
    {
        // AllProjectReferences is equivalent to ProjectReferences only if the set of projects in the solution hasn't changed.
        if (newProject.Solution.ProjectIds == oldProject.Solution.ProjectIds)
            return GetChangedItems(newProject.AllProjectReferences, oldProject.AllProjectReferences);

        // If we can't use AllProjectReferences then there is no point in doing an equality check,
        // because ProjectReferences is a linq query and will always be different.
        return newProject.ProjectReferences.Except(oldProject.ProjectReferences);
    }
}


Review comment (suggestion):
`AllProjectReferences` is not actually equivalent to `ProjectReferences` just because the solution’s `ProjectIds` list instance is the same: `ProjectReferences` filters to projects contained in the solution (`Project.cs:136-142`), while `AllProjectReferences` includes references to projects *not* in the solution. Using `AllProjectReferences` here changes `GetAdded/RemovedProjectReferences()` behavior compared to the previous implementation (which only considered `ProjectReferences`). Consider keeping the diff based on `ProjectReferences` for correctness, and use `newProject.State.ProjectReferences == oldProject.State.ProjectReferences` (plus the `ProjectIds` identity check) only as a fast-path to return an empty result when nothing changed.
```suggestion
        // Fast path: if the set of projects in the solution and the underlying project references
        // collection are identical, then no project references (within the solution) have changed.
        if (newProject.Solution.ProjectIds == oldProject.Solution.ProjectIds &&
            newProject.State.ProjectReferences == oldProject.State.ProjectReferences)
        {
            return [];
        }

        // Compute the diff based on ProjectReferences, which only includes references to projects
        // contained in the solution.
```

Code after:
    public IEnumerable<DocumentId> GetRemovedDocuments()
        => NewProject.State.DocumentStates.GetRemovedStateIds(OldProject.State.DocumentStates);

    /// <summary>
    /// Get <see cref="DocumentId"/>s of removed additional documents in the order they appear in <see cref="Project.DocumentIds"/> of <see cref="OldProject"/>.
    /// </summary>
    public IEnumerable<DocumentId> GetRemovedAdditionalDocuments()
        => NewProject.State.AdditionalDocumentStates.GetRemovedStateIds(OldProject.State.AdditionalDocumentStates);

    /// <summary>
    /// Get <see cref="DocumentId"/>s of removed analyzer config documents in the order they appear in <see cref="Project.DocumentIds"/> of <see cref="OldProject"/>.
    /// </summary>
    public IEnumerable<DocumentId> GetRemovedAnalyzerConfigDocuments()
        => NewProject.State.AnalyzerConfigDocumentStates.GetRemovedStateIds(OldProject.State.AnalyzerConfigDocumentStates);

    private static IEnumerable<T> GetChangedItems<T>(IEnumerable<T> newItems, IEnumerable<T> oldItems)
        => newItems == oldItems ? [] : newItems.Except(oldItems);

    private static IEnumerable<ProjectReference> GetChangedProjectReferences(Project newProject, Project oldProject)
    {
        // Fast path: if the set of projects in the solution and the underlying project references
        // collection are identical, then no project references (within the solution) have changed.
        if (newProject.Solution.ProjectIds == oldProject.Solution.ProjectIds &&
            newProject.State.ProjectReferences == oldProject.State.ProjectReferences)
        {
            return [];
        }

        // Compute the diff based on ProjectReferences, which only includes references to projects
        // contained in the solution.
        return newProject.ProjectReferences.Except(oldProject.ProjectReferences);
    }
}