From 0f68d3ae9a3a015e31f0f5fc8116bbb3b910ea29 Mon Sep 17 00:00:00 2001 From: Gen Lu Date: Tue, 1 Mar 2016 01:07:13 -0800 Subject: [PATCH] Generate an AnalzyerDriver exception if operation collector crashes Log non-fatal-watson if exception is not caused by stack overflow in OperationWalker. --- .../Diagnostics/OperationAnalyzerTests.cs | 42 ++++++++++++++++++ .../Diagnostics/OperationTestAnalyzer.cs | 44 +++++++++++++++++++ .../DiagnosticAnalyzer/AnalyzerDriver.cs | 15 ++++++- 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/OperationAnalyzerTests.cs index dacb264d3ab..d27dc382dbc 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 10d455683cd..f19c5a24f0e 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 85778919058..e7af89eeee6 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) { -- GitLab