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 @@ -268,6 +268,40 @@ class Test {
void T(Task t) => Foo(t);
void Foo(object o) {}
}
";
await Verify.VerifyAsync(test);
}

[Test]
public async Task NoWarn_When_Type_Has_Implicit_Conversion_To_String()
{
// Issue #317: a type that defines 'public static implicit operator string'
// opts into its own string representation, so EPC20 should not fire.
var test = @"
public class AmazonFile
{
public static implicit operator string(AmazonFile f) => ""x"";
}
class Test {
string Concat(AmazonFile f) => ""x"" + f;
string Interp(AmazonFile f) => $""{f}"";
string Format(AmazonFile f) => string.Format(""{0}"", f);
}
";
await Verify.VerifyAsync(test);
}

[Test]
public async Task Warn_When_Implicit_Conversion_Returns_Something_Other_Than_String()
{
var test = @"
public class AmazonFile
{
public static implicit operator int(AmazonFile f) => 0;
}
class Test {
string Concat(AmazonFile f) => ""x"" + [|f|];
}
";
await Verify.VerifyAsync(test);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ protected override bool TryCreateDiagnostic(Compilation compilation, ITypeSymbol
return false;
}

if (HasImplicitConversionToString(type))
{
// The type opts into a custom string representation via 'public static implicit operator string',
// so a default ToString impl is not what will actually be used. See issue #317.
return false;
}

if (NoToStringOverride(type, out var typeWithNoToString))
{
diagnostic = Diagnostic.Create(Rule, location, typeWithNoToString);
Expand All @@ -58,6 +65,20 @@ protected override bool TryCreateDiagnostic(Compilation compilation, ITypeSymbol
return diagnostic != null;
}

private static bool HasImplicitConversionToString(ITypeSymbol type)
{
foreach (var member in type.GetMembers(WellKnownMemberNames.ImplicitConversionName))
{
if (member is IMethodSymbol { MethodKind: MethodKind.Conversion } method &&
method.ReturnType.SpecialType == SpecialType.System_String)
{
return true;
}
}

return false;
}

private static bool NoToStringOverride(ITypeSymbol type, [NotNullWhen(true)]out ITypeSymbol? typeWithNoToString)
{
typeWithNoToString = null;
Expand Down
Loading