Skip to content
Open
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
23 changes: 20 additions & 3 deletions src/Analyzer/ReferenceTrimmerAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class ReferenceTrimmerAnalyzer : DiagnosticAnalyzer
private static readonly DiagnosticDescriptor RT0003Descriptor = new(
"RT0003",
"Unnecessary package reference",
"PackageReference {0} can be removed",
"PackageReference {0} can be removed{1}",
"ReferenceTrimmer",
DiagnosticSeverity.Warning,
isEnabledByDefault: true);
Expand Down Expand Up @@ -109,6 +109,7 @@ private static void DumpUsedReferences(CompilationAnalysisContext context)
}

Dictionary<string, List<string>> packageAssembliesDict = new(StringComparer.OrdinalIgnoreCase);
Dictionary<string, List<string>> topLevelPackageAssembliesDict = new(StringComparer.OrdinalIgnoreCase);
foreach (DeclaredReference declaredReference in declaredReferences.References)
{
switch (declaredReference.Kind)
Expand All @@ -135,11 +136,23 @@ private static void DumpUsedReferences(CompilationAnalysisContext context)
{
if (!packageAssembliesDict.TryGetValue(declaredReference.Spec, out List<string> packageAssemblies))
{
packageAssemblies = new List<string>();
packageAssemblies = [];
packageAssembliesDict.Add(declaredReference.Spec, packageAssemblies);
}

packageAssemblies.Add(declaredReference.AssemblyPath);

bool isTopLevelPackageAssembly = string.Equals(declaredReference.Spec, declaredReference.AdditionalSpec, StringComparison.OrdinalIgnoreCase);
if (isTopLevelPackageAssembly)
{
if (!topLevelPackageAssembliesDict.TryGetValue(declaredReference.Spec, out List<string> topLevelPackageAssemblies))
{
topLevelPackageAssemblies = [];
topLevelPackageAssembliesDict.Add(declaredReference.Spec, topLevelPackageAssemblies);
}

topLevelPackageAssemblies.Add(declaredReference.AssemblyPath);
}
break;
}
}
Expand All @@ -152,7 +165,11 @@ private static void DumpUsedReferences(CompilationAnalysisContext context)
List<string> packageAssemblies = kvp.Value;
if (!packageAssemblies.Any(usedReferences.Contains))
{
context.ReportDiagnostic(Diagnostic.Create(RT0003Descriptor, Location.None, packageName));
context.ReportDiagnostic(Diagnostic.Create(RT0003Descriptor, Location.None, packageName, string.Empty));
}
else if (!topLevelPackageAssembliesDict[packageName].Any(usedReferences.Contains))
{
context.ReportDiagnostic(Diagnostic.Create(RT0003Descriptor, Location.None, packageName, " (though some of its transitive dependent packages may be used)"));
}
}
}
Expand Down
11 changes: 7 additions & 4 deletions src/Shared/DeclaredReferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public void SaveToFile(string filePath)
writer.Append(KindEnumToString[reference.Kind]);
writer.Append(FieldDelimiter);
writer.Append(reference.Spec);
writer.Append(FieldDelimiter);
writer.Append(reference.AdditionalSpec);
writer.AppendLine();
}

Expand All @@ -58,23 +60,24 @@ public static DeclaredReferences ReadFromFile(string filePath)
string? line;
while ((line = reader.ReadLine()) != null)
{
string[] parts = line.Split(FieldDelimiters, 3);
if (parts.Length != 3)
string[] parts = line.Split(FieldDelimiters, 4);
if (parts.Length != 4)
{
throw new InvalidDataException($"File '{filePath}' is invalid. Line: {references.Count + 1}");
}

string assemblyName = parts[0];
DeclaredReferenceKind kind = KindStringToEnum[parts[1]];
string spec = parts[2];
DeclaredReference reference = new(assemblyName, kind, spec);
string additionalSpec = parts[3];
DeclaredReference reference = new(assemblyName, kind, spec, additionalSpec);
references.Add(reference);
}

return new DeclaredReferences(references);
}
}

internal record DeclaredReference(string AssemblyPath, DeclaredReferenceKind Kind, string Spec);
internal record DeclaredReference(string AssemblyPath, DeclaredReferenceKind Kind, string Spec, string AdditionalSpec);

internal enum DeclaredReferenceKind { Reference, ProjectReference, PackageReference }
22 changes: 11 additions & 11 deletions src/Tasks/CollectDeclaredReferencesTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public override bool Execute()

if (referencePath is not null)
{
declaredReferences.Add(new DeclaredReference(referencePath, DeclaredReferenceKind.Reference, referenceSpec));
declaredReferences.Add(new DeclaredReference(referencePath, DeclaredReferenceKind.Reference, referenceSpec, string.Empty));
}
}
}
Expand Down Expand Up @@ -149,7 +149,7 @@ public override bool Execute()
string projectReferenceAssemblyPath = Path.GetFullPath(projectReference.ItemSpec);
string referenceProjectFile = projectReference.GetMetadata("OriginalProjectReferenceItemSpec");

declaredReferences.Add(new DeclaredReference(projectReferenceAssemblyPath, DeclaredReferenceKind.ProjectReference, referenceProjectFile));
declaredReferences.Add(new DeclaredReference(projectReferenceAssemblyPath, DeclaredReferenceKind.ProjectReference, referenceProjectFile, string.Empty));
}
}

Expand All @@ -176,9 +176,9 @@ public override bool Execute()
continue;
}

foreach (string assemblyPath in packageInfo.CompileTimeAssemblies)
foreach (var assemblyPath in packageInfo.CompileTimeAssemblies)
{
declaredReferences.Add(new DeclaredReference(assemblyPath, DeclaredReferenceKind.PackageReference, packageReference.ItemSpec));
declaredReferences.Add(new DeclaredReference(assemblyPath.Item2, DeclaredReferenceKind.PackageReference, packageReference.ItemSpec, assemblyPath.Item1));
}
}
}
Expand Down Expand Up @@ -297,7 +297,7 @@ private Dictionary<string, PackageInfo> GetPackageInfos()
packageInfoBuilders.Add(packageId, packageInfoBuilder);
}

packageInfoBuilder.AddCompileTimeAssemblies(nugetLibraryAssemblies);
packageInfoBuilder.AddCompileTimeAssemblies(nugetLibrary.Name, nugetLibraryAssemblies);
packageInfoBuilder.AddBuildFiles(buildFiles);

// Recurse though dependents
Expand Down Expand Up @@ -421,18 +421,18 @@ private static bool IsSuppressed(ITaskItem item, string warningId)

private sealed class PackageInfoBuilder
{
private List<string>? _compileTimeAssemblies;
private List<Tuple<string, string>>? _compileTimeAssemblies;
private List<string>? _buildFiles;

public void AddCompileTimeAssemblies(List<string> compileTimeAssemblies)
public void AddCompileTimeAssemblies(string packageName, List<string> compileTimeAssemblies)
{
if (compileTimeAssemblies.Count == 0)
{
return;
}

_compileTimeAssemblies ??= new(compileTimeAssemblies.Count);
_compileTimeAssemblies.AddRange(compileTimeAssemblies);
_compileTimeAssemblies.AddRange(compileTimeAssemblies.Select(assemblyPath => Tuple.Create(packageName, assemblyPath)));
}

public void AddBuildFiles(List<string> buildFiles)
Expand All @@ -448,11 +448,11 @@ public void AddBuildFiles(List<string> buildFiles)

public PackageInfo ToPackageInfo()
=> new(
(IReadOnlyCollection<string>?)_compileTimeAssemblies ?? Array.Empty<string>(),
(IReadOnlyCollection<string>?)_buildFiles ?? Array.Empty<string>());
(IReadOnlyCollection<Tuple<string, string>>?)_compileTimeAssemblies ?? [],
(IReadOnlyCollection<string>?)_buildFiles ?? []);
}

private readonly record struct PackageInfo(
IReadOnlyCollection<string> CompileTimeAssemblies,
IReadOnlyCollection<Tuple<string, string>> CompileTimeAssemblies,
IReadOnlyCollection<string> BuildFiles);
}
11 changes: 11 additions & 0 deletions src/Tests/E2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,17 @@ public Task UnusedPackageReference()
});
}

[TestMethod]
public Task UnusedPackageReferenceWithSdk()
{
return RunMSBuildAsync(
projectFile: "Test/Test.csproj",
expectedWarnings:
[
new Warning("RT0003: PackageReference Moq can be removed (though some of its transitive dependent packages may be used)", "Test/Test.csproj")
]);
}

[TestMethod]
public Task UnusedPackageReferenceNoWarn()
{
Expand Down
9 changes: 9 additions & 0 deletions src/Tests/TestData/UnusedPackageReferenceWithSdk/Test/Test.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Castle.Core.Logging;

namespace Test
{
public class Foo
{
public static ILogger Logger() => NullLogger.Instance;
}
}
12 changes: 12 additions & 0 deletions src/Tests/TestData/UnusedPackageReferenceWithSdk/Test/Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="MSTest.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ExplicitUsings>enable</ExplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>

</Project>
Loading