diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs index dacb264d3ab7a91b94ab65a38939d87398f0a53c..d27dc382dbccc4a235538762b95f4bd6bf7892f8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; @@ -1262,6 +1263,47 @@ public void M0() .VerifyAnalyzerDiagnostics(new DiagnosticAnalyzer[] { new NoneOperationTestAnalyzer() }, null, null, false); } + // This test can't reliablely trigger stack overflow on Linux + [ClrOnlyFact, WorkItem(9025, "https://github.com/dotnet/roslyn/issues/9025")] + public void LongArithmeticExpressionCSharp() + { + Func buildSequenceOfBinaryExpressions = + (count) => + { + var builder = new System.Text.StringBuilder(); + int i; + for (i = 0; i < count; i++) + { + builder.Append(i + 1); + builder.Append(" * "); + builder.Append("f["); + builder.Append(i); + builder.Append("] + "); + } + + builder.Append(i + 1); + + return builder.ToString(); + }; + // This code will cause OperationWalker to throw `InsufficientExecutionStackException` + var source = @" +class Test +{ + public static long Calculate1(long[] f) + { + long x; +" + $" x = { buildSequenceOfBinaryExpressions(8192) };" + @" + return x; + } +}"; + + CreateCompilationWithMscorlib45(source) + .VerifyDiagnostics() + .VerifyAnalyzerDiagnostics(new DiagnosticAnalyzer[] { new AssignmentOperationSyntaxTestAnalyzer() }, null, null, true, + Diagnostic("AD0002").WithArguments("System.InsufficientExecutionStackException", "Insufficient stack to continue executing the program safely. This can happen from having too many functions on the call stack or function on the stack using too much stack space.").WithLocation(1, 1), + Diagnostic(AssignmentOperationSyntaxTestAnalyzer.AssignmentSyntaxDescriptor.Id, $"x = { buildSequenceOfBinaryExpressions(8192) }").WithLocation(7, 9)); + } + [WorkItem(9020, "https://github.com/dotnet/roslyn/issues/9020")] [Fact] public void AddressOfExpressionCSharp() diff --git a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTestAnalyzer.cs b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTestAnalyzer.cs index 10d455683cd136c32f5b197c23997a48cf395f7b..f19c5a24f0ecf5708ffa3003cca9d61e49ef9747 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTestAnalyzer.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Diagnostics/OperationTestAnalyzer.cs @@ -1948,6 +1948,50 @@ public sealed override void Initialize(AnalysisContext context) } } + public class AssignmentOperationSyntaxTestAnalyzer : DiagnosticAnalyzer + { + private const string ReliabilityCategory = "Reliability"; + + public static readonly DiagnosticDescriptor AssignmentOperationDescriptor = new DiagnosticDescriptor( + "AssignmentOperation", + "An assignment operation is found", + "An assignment operation is found", + ReliabilityCategory, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor AssignmentSyntaxDescriptor = new DiagnosticDescriptor( + "AssignmentSyntax", + "An assignment syntax is found", + "An assignment syntax is found", + ReliabilityCategory, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public sealed override ImmutableArray SupportedDiagnostics + { + get { return ImmutableArray.Create(AssignmentOperationDescriptor, AssignmentSyntaxDescriptor); } + } + + public sealed override void Initialize(AnalysisContext context) + { + context.RegisterOperationAction( + (operationContext) => + { + operationContext.ReportDiagnostic(Diagnostic.Create(AssignmentOperationDescriptor, operationContext.Operation.Syntax.GetLocation())); + }, + OperationKind.AssignmentExpression); + + context.RegisterSyntaxNodeAction( + (syntaxContext) => + { + + syntaxContext.ReportDiagnostic(Diagnostic.Create(AssignmentSyntaxDescriptor, syntaxContext.Node.GetLocation())); + }, + CSharp.SyntaxKind.SimpleAssignmentExpression); + } + } + public class LiteralTestAnalyzer : DiagnosticAnalyzer { private const string ReliabilityCategory = "Reliability"; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 85778919058499973fcb6aa34872019ac8c02fda..e7af89eeee6efc8edd621cc678dc2cdf63e794a7 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -1548,7 +1548,20 @@ private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymb if (shouldExecuteOperationActions || shouldExecuteOperationBlockActions) { var operationBlocksToAnalyze = GetOperationBlocksToAnalyze(executableCodeBlocks, semanticModel, cancellationToken); - var operationsToAnalyze = GetOperationsToAnalyze(operationBlocksToAnalyze); + var operationsToAnalyze = ImmutableArray.Empty; + try + { + operationsToAnalyze = GetOperationsToAnalyze(operationBlocksToAnalyze); + } + catch (Exception ex) when (StackGuard.IsInsufficientExecutionStackException(ex) || FatalError.ReportWithoutCrashUnlessCanceled(ex)) + { + // the exception filter will short-circuit if `ex` is `InsufficientExecutionStackException` (from OperationWalker) + // and no non-fatal-watson will be logged as a result. + var diagnostic = AnalyzerExecutor.CreateDriverExceptionDiagnostic(ex); + var analyzer = this.analyzers[0]; + + analyzerExecutor.OnAnalyzerException(ex, analyzer, diagnostic); + } if (!operationsToAnalyze.IsEmpty) {