-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
dotnet/sdk
#51456Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.IOcode-analyzerMarks an issue that suggests a Roslyn analyzerMarks an issue that suggests a Roslyn analyzercode-fixerMarks an issue that suggests a Roslyn code fixerMarks an issue that suggests a Roslyn code fixerhelp wanted[up-for-grabs] Good issue for external contributors[up-for-grabs] Good issue for external contributorsin-prThere is an active PR which will close this issue when it is mergedThere is an active PR which will close this issue when it is merged
Milestone
Description
Suggested severity: Info
Suggested category: Reliability
When generating multiple combined/joined path segments in a row, where each depends on the previous one (excluding the first one of course), and only the last resulting segment is the one being properly consumed somewhere else, then they can be collapsed into a single invocation of Combine or Join.
The maximum number of segments in a row that can be fixed are 3, since the largest Combine/Join overloads take 4 parameters.
These are the APIs that can benefit from the analyzer:
static class Path
{
// Unlimited segment collapse
static string Combine (params string[] paths);
static string Combine (string path1, string path2);
static string Combine (string path1, string path2, string path3);
static string Combine (string path1, string path2, string path3, string path4);
static string Join (params string?[] paths);
static string Join (string? path1, string? path2, string? path3, string? path4);
static string Join (string? path1, string? path2, string? path3);
static string Join (string? path1, string? path2);
// Limited to up to 3 path segments
static bool TryJoin (ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, Span<char> destination, out int charsWritten);
// Not flagged, but used by fixer for max allowed
// static bool TryJoin (ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, Span<char> destination, out int charsWritten);
// Limited to up to 4 path segments
static string Join (ReadOnlySpan<char> path1, ReadOnlySpan<char> path2);
static string Join (ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3);
// Not flagged, but used by fixer for max allowed
// static string Join (ReadOnlySpan<char> path1, ReadOnlySpan<char> path2, ReadOnlySpan<char> path3, ReadOnlySpan<char> path4);
}An additional improvement: whenever possible, switch to using the ReadOnlySpan<char> overload instead of the string overload, if the number of arguments allows it.
Flag
- Simplest case: Use a non-
paramsoverload
// Before
string first = Path.Join("folder", "another"); // first is consumed only as argument of second
string second = Path.Join(first, "onemore");
// After
string second = Path.Join("folder", "another", "onemore");
- Special case: Use the `params` overload
// Before
string first = Path.Join("folder", "another"); // first is consumed only as argument of second
string second = Path.Join(first, "onemore"); // second is consumed only as argument of third
string third = Path.Join(second, "anotherone"); // third is consumed only as argument of fourth
string fourth = Path.Join(third, "file.txt");
// After
string fourth= Path.Join("folder", "another", "onemore", "anotherone", "file.txt"); // passed as params- For
JoinandTryJoinoverloads that takeReadOnlySpan<char>parameters,Joincan only collapse 4,TryJoinonly 3
string one = "one";
string two = "two";
string three = "three";
string four = "four";
string file = "file.txt";
// Before
string first = Path.Join(one.AsSpan(), two.AsSpan());
string second = Path.Join(first.AsSpan(), three.AsSpan()); // first is consumed only as argument of second
string third = Path.Join(second.AsSpan(), four.AsSpan()); // second is consumed only as argument of third
string fourth = Path.Join(third.AsSpan(), file.AsSpan()); // third is consumed only as argument of fourth
// After: The first segment will have to be skipped if more than 4 parameters in total
string first = Path.Join(one.AsSpan(), two.AsSpan());
string fourth = Path.Join(first, three.AsSpan(), four.AsSpan(), file.AsSpan());Do not flag
- If one of the joined/combined strings is being consumed somewhere else after the final collapse, do not flag
string first = Path.Join("one", "two");
string second = Path.Join(first, "three");
string third = Path.Join(second, "file.txt"); // Can't collapse: first is consumed in the WriteLine
Console.WriteLine(first);- For
Combine, the analyzer should not flag cases where there are potentialnullarguments.
string? MyNullStringMethod()
{
// ...
}
string? one = null;
string two = "path";
Path.Combine(one, two, MyNullStringMethod());cc @buyaa-n
adamsitnik, GrabYourPitchforks, giladfrid009 and AraHaanCopilot
Metadata
Metadata
Assignees
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.IOcode-analyzerMarks an issue that suggests a Roslyn analyzerMarks an issue that suggests a Roslyn analyzercode-fixerMarks an issue that suggests a Roslyn code fixerMarks an issue that suggests a Roslyn code fixerhelp wanted[up-for-grabs] Good issue for external contributors[up-for-grabs] Good issue for external contributorsin-prThere is an active PR which will close this issue when it is mergedThere is an active PR which will close this issue when it is merged