Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix debug assert with operation blocks in invalid code #74354

Merged
merged 2 commits into from
Jul 13, 2024
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 @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
Expand All @@ -16,15 +17,28 @@
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseSystemHashCode;

[Trait(Traits.Feature, Traits.Features.CodeActionsUseSystemHashCode)]
public partial class UseSystemHashCodeTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor
public partial class UseSystemHashCodeTests(ITestOutputHelper logger)
: AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger)
{
public UseSystemHashCodeTests(ITestOutputHelper logger)
: base(logger)
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new UseSystemHashCodeDiagnosticAnalyzer(), new UseSystemHashCodeCodeFixProvider());

private new Task TestInRegularAndScript1Async(
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initialMarkup,
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expectedMarkup,
int index = 0,
TestParameters? parameters = null)
{
return base.TestInRegularAndScript1Async(initialMarkup, expectedMarkup, index, parameters);
}

internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new UseSystemHashCodeDiagnosticAnalyzer(), new UseSystemHashCodeCodeFixProvider());
private new Task TestMissingAsync(
[StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string initialMarkup,
TestParameters? parameters = null,
int codeActionIndex = 0)
{
return base.TestMissingAsync(initialMarkup, parameters, codeActionIndex);
}

[Fact]
public async Task TestDerivedClassWithFieldWithBase()
Expand Down Expand Up @@ -1447,4 +1461,16 @@ class C
}
""");
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74315")]
public async Task TestMissingBaseType()
{
await TestMissingAsync("""
using System;

namespace System { public struct HashCode { } }

record $$B(int I) : A(I);
""");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace Microsoft.CodeAnalysis.UseSystemHashCode;

[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
internal class UseSystemHashCodeDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
internal sealed class UseSystemHashCodeDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
{
public UseSystemHashCodeDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseSystemHashCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4360,5 +4360,40 @@ internal async Task TestMinimumReportedSeverity(SeverityFilter severityFilter, D
Assert.True(analyzer.AnalyzerInvoked);
Assert.Equal(expectedMinimumReportedSeverity, analyzer.MinimumReportedSeverity);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74315")]
public async Task TestOperationConstructorBlockCallbackOnInvalidBaseCall()
{
// lang=C#-Test
Copy link
Member

Choose a reason for hiding this comment

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

lang=C# turns on syntax highlighting in the string, right? but what does lang=C#-Test do?

Copy link
Member Author

Choose a reason for hiding this comment

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

marks it as a C# test file, with supported C# test file syntax. Note; we don't have lang=c# support (yet). but it's something we could consider.

string source = """
record B(int I) : A(I);
""";

var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CreateCompilationWithCSharp(new[] { tree, CSharpSyntaxTree.ParseText(IsExternalInitTypeDefinition) });
compilation.VerifyDiagnostics(
// (1,19): error CS0246: The type or namespace name 'A' could not be found (are you missing a using directive or an assembly reference?)
// record B(int I) : A(I);
Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "A").WithArguments("A").WithLocation(1, 19),
// (1,20): error CS1729: 'A' does not contain a constructor that takes 1 arguments
// record B(int I) : A(I);
Diagnostic(ErrorCode.ERR_BadCtorArgCount, "(I)").WithArguments("A", "1").WithLocation(1, 20));

// Verify analyzer execution from command line
// 'VerifyAnalyzerDiagnostics' helper executes the analyzers on the entire compilation without any state-based analysis.
var analyzer = new RegisterOperationBlockAndOperationActionAnalyzer();
compilation.VerifyAnalyzerDiagnostics([analyzer],
expected: Diagnostic("ID0001", "B").WithLocation(1, 8));

// Now verify analyzer execution for a single file.
// 'GetAnalyzerSemanticDiagnosticsAsync' executes the analyzers on the given file with state-based analysis.
var model = compilation.GetSemanticModel(tree);
var compWithAnalyzers = new CompilationWithAnalyzers(
compilation,
[analyzer],
new AnalyzerOptions([]));
var diagnostics = await compWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, filterSpan: null, CancellationToken.None);
diagnostics.Verify(Diagnostic("ID0001", "B").WithLocation(1, 8));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2959,8 +2959,9 @@ private static ImmutableArray<IOperation> GetOperationsToAnalyze(
break;

case OperationKind.ExpressionStatement:
// For constructor initializer, we generate an IInvocationOperation with an implicit IExpressionStatementOperation parent.
Debug.Assert(operationBlock.Kind == OperationKind.Invocation);
// For constructor initializer, we generate an IInvocationOperation (or invalid
// operation in the case of an error) with an implicit IExpressionStatementOperation parent.
Debug.Assert(operationBlock.Kind is OperationKind.Invocation or OperationKind.Invalid);
Debug.Assert(operationBlock.Parent.IsImplicit);
Debug.Assert(operationBlock.Parent.Parent is IConstructorBodyOperation ctorBody &&
ctorBody.Initializer == operationBlock.Parent);
Expand Down
Loading