private static void Analyze(CSharpSyntaxNode declarationBody, ISymbol symbol,
                                    Action <CSharpExplodedGraph, SyntaxNodeAnalysisContext> analyze, SyntaxNodeAnalysisContext context)
        {
            if (declarationBody == null ||
                declarationBody.ContainsDiagnostics)
            {
                return;
            }

            if (!CSharpControlFlowGraph.TryGet(declarationBody, context.SemanticModel, out var cfg))
            {
                return;
            }

            var lva = CSharpLiveVariableAnalysis.Analyze(cfg, symbol, context.SemanticModel);

            try
            {
                var explodedGraph = new CSharpExplodedGraph(cfg, symbol, context.SemanticModel, lva);
                analyze(explodedGraph, context);
            }
            catch (Exception e)
            {
                // Roslyn/MSBuild is currently cutting exception message at the end of the line instead
                // of displaying the full message. As a workaround, we replace the line ending with ' ## '.
                // See https://github.com/dotnet/roslyn/issues/1455 and https://github.com/dotnet/roslyn/issues/24346
                var sb = new StringBuilder();
                sb.AppendLine($"Error processing method: {symbol?.Name ?? "{unknown}"}");
                sb.AppendLine($"Method file: {declarationBody.GetLocation()?.GetLineSpan().Path ?? "{unknown}"}");
                sb.AppendLine($"Method line: {declarationBody.GetLocation()?.GetLineSpan().StartLinePosition.ToString() ?? "{unknown}"}");
                sb.AppendLine($"Inner exception: {e.ToString()}");

                throw new SymbolicExecutionException(sb.ToString().Replace(Environment.NewLine, " ## "), e);
            }
        }
Example #2
0
        private void WriteUCFG <TDeclarationSyntax>(SyntaxNodeAnalysisContext context, Func <TDeclarationSyntax, CSharpSyntaxNode> getBody)
            where TDeclarationSyntax : SyntaxNode
        {
            var declaration = (TDeclarationSyntax)context.Node;

            var symbol = context.SemanticModel.GetDeclaredSymbol(declaration);

            var methodSymbol = (symbol is IPropertySymbol propertySymbol)
                ? propertySymbol.GetMethod // We are in PropertyDeclarationSyntax
                : symbol as IMethodSymbol; // all other are methods

            if (methodSymbol == null ||
                methodSymbol.IsAbstract ||
                methodSymbol.IsExtern ||
                !CSharpControlFlowGraph.TryGet(getBody(declaration), context.SemanticModel, out var cfg))
            {
                return;
            }

            var ucfg = new UniversalControlFlowGraphBuilder()
                       .Build(context.SemanticModel, declaration, methodSymbol, cfg);

            if (IsValid(ucfg))
            {
                var fileName = $"{projectBuildId}_{Interlocked.Increment(ref protobufFileIndex)}";

                WriteProtobuf(ucfg, Path.Combine(protobufDirectory, $"ucfg_{fileName}.pb"));

                if (ShouldGenerateDot)
                {
                    WriteDot(Path.Combine(protobufDirectory, $"ucfg_{fileName}.dot"), writer => UcfgSerializer.Serialize(ucfg, writer));
                    WriteDot(Path.Combine(protobufDirectory, $"cfg_{fileName}.dot"), writer => CfgSerializer.Serialize(ucfg.MethodId, cfg, writer));
                }
            }
        }
        private static void ReportOnDeadParametersAtEntry(MethodContext declaration, IImmutableList <IParameterSymbol> noReportOnParameters)
        {
            var bodyNode = (CSharpSyntaxNode)declaration.Body ?? declaration.ExpressionBody;

            if (bodyNode == null || declaration.Context.Node.IsKind(SyntaxKind.ConstructorDeclaration))
            {
                return;
            }

            var excludedParameters = noReportOnParameters;

            if (declaration.Symbol.IsExtensionMethod)
            {
                excludedParameters = excludedParameters.Add(declaration.Symbol.Parameters.First());
            }
            excludedParameters = excludedParameters.AddRange(declaration.Symbol.Parameters.Where(p => p.RefKind != RefKind.None));

            var candidateParameters = declaration.Symbol.Parameters.Except(excludedParameters);

            if (!candidateParameters.Any())
            {
                return;
            }
            if (CSharpControlFlowGraph.TryGet(bodyNode, declaration.Context.SemanticModel, out var cfg))
            {
                var lva            = CSharpLiveVariableAnalysis.Analyze(cfg, declaration.Symbol, declaration.Context.SemanticModel);
                var liveParameters = lva.GetLiveIn(cfg.EntryBlock).OfType <IParameterSymbol>();
                ReportOnUnusedParameters(declaration, candidateParameters.Except(liveParameters).Except(lva.CapturedVariables), MessageDead, isRemovable: false);
            }
        }
        private void ProcessLocalFunction(SyntaxNode instruction, HashSet <ISymbol> assignedInBlock,
                                          HashSet <ISymbol> usedBeforeAssigned, HashSet <ISymbol> processedLocalFunctions)
        {
            var symbol = semanticModel.GetSymbolInfo(((InvocationExpressionSyntax)instruction).Expression).Symbol;

            // Local function invocation
            if (symbol != null &&
                (processedLocalFunctions == null || !processedLocalFunctions.Contains(symbol)) &&
                symbol.ContainingSymbol is IMethodSymbol &&
                symbol.DeclaringSyntaxReferences.Length == 1 &&
                symbol.DeclaringSyntaxReferences.Single().GetSyntax() is CSharpSyntaxNode node &&
                node.Kind() == SyntaxKindEx.LocalFunctionStatement &&
                (LocalFunctionStatementSyntaxWrapper)node is LocalFunctionStatementSyntaxWrapper function &&
                CSharpControlFlowGraph.TryGet(function.Body ?? function.ExpressionBody as CSharpSyntaxNode, semanticModel, out var cfg))
            {
                processedLocalFunctions = processedLocalFunctions ?? new HashSet <ISymbol>();
                processedLocalFunctions.Add(symbol);
                foreach (var block in cfg.Blocks.Reverse())
                {
                    ProcessBlockInternal(block, processedLocalFunctions, out var subAssigned, out var subUsed);
                    assignedInBlock.UnionWith(subAssigned);
                    usedBeforeAssigned.UnionWith(subUsed);
                }
            }
        }
Example #5
0
        private void WriteUCFG <TDeclarationSyntax>(SyntaxNodeAnalysisContext context, Func <TDeclarationSyntax, CSharpSyntaxNode> getBody)
            where TDeclarationSyntax : SyntaxNode
        {
            var declaration = (TDeclarationSyntax)context.Node;

            var symbol = context.SemanticModel.GetDeclaredSymbol(declaration);

            var methodSymbol = (symbol is IPropertySymbol propertySymbol)
                ? propertySymbol.GetMethod // We are in PropertyDeclarationSyntax
                : symbol as IMethodSymbol; // all other are methods

            if (methodSymbol == null ||
                methodSymbol.IsAbstract ||
                methodSymbol.IsExtern ||
                !CSharpControlFlowGraph.TryGet(getBody(declaration), context.SemanticModel, out var cfg))
            {
                return;
            }

            var ucfg = new UniversalControlFlowGraphBuilder()
                       .Build(context.SemanticModel, declaration, methodSymbol, cfg);

            if (!IsValid(ucfg))
            {
                return;
            }

            var path = Path.Combine(protobufDirectory,
                                    $"ucfg_{projectBuildId}_{Interlocked.Increment(ref protobufFileIndex)}.pb");

            using (var stream = File.Create(path))
            {
                ucfg.WriteTo(stream);
            }
        }
Example #6
0
        private static void CheckForNoExitMethod(SyntaxNodeAnalysisContext c, CSharpSyntaxNode body, SyntaxToken identifier)
        {
            var symbol = c.SemanticModel.GetDeclaredSymbol(c.Node);

            if (symbol != null && body != null && CSharpControlFlowGraph.TryGet(body, c.SemanticModel, out var cfg))
            {
                var walker = new CfgWalkerForMethod(new RecursionAnalysisContext(cfg, symbol, identifier.GetLocation(), c));
                walker.CheckPaths();
                CheckInfiniteJumpLoop(body, cfg, "method", c);
            }
        }
Example #7
0
        private static void CheckForDeadStores(CSharpSyntaxNode node, ISymbol declaration, SyntaxNodeAnalysisContext context)
        {
            if (declaration == null || !CSharpControlFlowGraph.TryGet(node, context.SemanticModel, out var cfg))
            {
                return;
            }

            var lva = CSharpLiveVariableAnalysis.Analyze(cfg, declaration, context.SemanticModel);

            foreach (var block in cfg.Blocks)
            {
                CheckCfgBlockForDeadStores(block, lva.GetLiveOut(block), lva.CapturedVariables, node, declaration, context);
            }
        }
Example #8
0
        private static void CheckForNoExitProperty(SyntaxNodeAnalysisContext c)
        {
            var property       = (PropertyDeclarationSyntax)c.Node;
            var propertySymbol = c.SemanticModel.GetDeclaredSymbol(property);

            if (propertySymbol == null)
            {
                return;
            }

            IControlFlowGraph cfg;

            if (property.ExpressionBody?.Expression != null)
            {
                if (CSharpControlFlowGraph.TryGet(property.ExpressionBody.Expression, c.SemanticModel, out cfg))
                {
                    var walker = new CfgWalkerForProperty(
                        new RecursionAnalysisContext(cfg, propertySymbol, property.Identifier.GetLocation(), c),
                        "property's recursion",
                        isSetAccessor: false);
                    walker.CheckPaths();
                }
                return;
            }

            var accessors = property.AccessorList?.Accessors.Where(a => a.HasBodyOrExpressionBody());

            if (accessors != null)
            {
                foreach (var accessor in accessors)
                {
                    var bodyNode = (CSharpSyntaxNode)accessor.Body ?? accessor.ExpressionBody();
                    if (CSharpControlFlowGraph.TryGet(bodyNode, c.SemanticModel, out cfg))
                    {
                        var walker = new CfgWalkerForProperty(
                            new RecursionAnalysisContext(cfg, propertySymbol, accessor.Keyword.GetLocation(), c),
                            "property accessor's recursion",
                            isSetAccessor: accessor.Keyword.IsKind(SyntaxKind.SetKeyword));
                        walker.CheckPaths();

                        CheckInfiniteJumpLoop(bodyNode, cfg, "property accessor", c);
                    }
                }
            }
        }
        private static bool IsSymbolFirstSetInCtor(ISymbol declaredSymbol, CtorDeclarationTuple ctor)
        {
            if (ctor.SyntaxNode.Initializer != null &&
                ctor.SyntaxNode.Initializer.ThisOrBaseKeyword.IsKind(SyntaxKind.ThisKeyword))
            {
                // Calls another ctor, which is also checked.
                return(true);
            }

            if (!CSharpControlFlowGraph.TryGet(ctor.SyntaxNode.Body, ctor.SemanticModel, out var cfg))
            {
                return(false);
            }

            var checker = new MemberInitializerRedundancyChecker(cfg, declaredSymbol, ctor.SemanticModel);

            return(checker.CheckAllPaths());
        }
Example #10
0
        private static void CheckForRedundantJumps(CSharpSyntaxNode node, SyntaxNodeAnalysisContext context)
        {
            if (!CSharpControlFlowGraph.TryGet(node, context.SemanticModel, out var cfg))
            {
                return;
            }

            var yieldStatementCount = node.DescendantNodes().OfType <YieldStatementSyntax>().Count();

            var removableJumps = cfg.Blocks
                                 .OfType <JumpBlock>()
                                 .Where(jumpBlock => IsJumpRemovable(jumpBlock, yieldStatementCount));

            foreach (var jumpBlock in removableJumps)
            {
                context.ReportDiagnosticWhenActive(Diagnostic.Create(rule, jumpBlock.JumpNode.GetLocation()));
            }
        }
        private void WriteUcfg <TDeclarationSyntax>(SyntaxNodeAnalysisContext context, Func <TDeclarationSyntax, CSharpSyntaxNode> getBody)
            where TDeclarationSyntax : SyntaxNode
        {
            var declaration = (TDeclarationSyntax)context.Node;

            var symbol = context.SemanticModel.GetDeclaredSymbol(declaration);

            var methodSymbol = (symbol is IPropertySymbol propertySymbol)
                ? propertySymbol.GetMethod // We are in PropertyDeclarationSyntax
                : symbol as IMethodSymbol; // all other are methods

            if (methodSymbol == null ||
                methodSymbol.IsAbstract ||
                methodSymbol.IsExtern ||
                !CSharpControlFlowGraph.TryGet(getBody(declaration), context.SemanticModel, out var cfg))
            {
                return;
            }

            try
            {
                var ucfg = new UcfgFactory(context.SemanticModel)
                           .Create(declaration, methodSymbol, cfg);

                if (!IsValid(ucfg))
                {
                    return;
                }

                var fileName = $"{this.projectBuildId}_{Interlocked.Increment(ref this.protobufFileIndex)}";

                WriteProtobuf(ucfg, Path.Combine(this.protobufDirectory, $"ucfg_{fileName}.pb"));

                if (ShouldGenerateDot)
                {
                    WriteDot(Path.Combine(this.protobufDirectory, $"ucfg_{fileName}.dot"), writer => UcfgSerializer.Serialize(ucfg, writer));
                    WriteDot(Path.Combine(this.protobufDirectory, $"cfg_{fileName}.dot"), writer => CfgSerializer.Serialize(ucfg.MethodId, cfg, writer));
                }
            }
            catch (UcfgException) when(!DebugHelper.IsInternalDebuggingContext())
            {
                // Ignore the exception in production
            }
        }
Example #12
0
        private static void Analyze(CSharpSyntaxNode declarationBody, ISymbol symbol,
                                    Action <CSharpExplodedGraph, SyntaxNodeAnalysisContext> analyze, SyntaxNodeAnalysisContext context)
        {
            if (declarationBody == null ||
                declarationBody.ContainsDiagnostics)
            {
                return;
            }

            if (!CSharpControlFlowGraph.TryGet(declarationBody, context.SemanticModel, out var cfg))
            {
                return;
            }

            var lva = CSharpLiveVariableAnalysis.Analyze(cfg, symbol, context.SemanticModel);

            var explodedGraph = new CSharpExplodedGraph(cfg, symbol, context.SemanticModel, lva);

            analyze(explodedGraph, context);
        }
        private static void CheckForNoExitMethod(SyntaxNodeAnalysisContext c)
        {
            var method       = (MethodDeclarationSyntax)c.Node;
            var methodSymbol = c.SemanticModel.GetDeclaredSymbol(method);

            if (methodSymbol == null)
            {
                return;
            }

            if (CSharpControlFlowGraph.TryGet(method.Body, c.SemanticModel, out var cfg) ||
                CSharpControlFlowGraph.TryGet(method.ExpressionBody?.Expression, c.SemanticModel, out cfg))
            {
                var walker = new CfgWalkerForMethod(
                    new RecursionAnalysisContext(cfg, methodSymbol, method.Identifier.GetLocation(), c));
                walker.CheckPaths();

                CheckInfiniteJumpLoop(method.Body, cfg, "method", c);
            }
        }
Example #14
0
        private static void CheckForDeadStores(CSharpSyntaxNode node, ISymbol declaration, SyntaxNodeAnalysisContext context)
        {
            if (declaration == null ||
                node == null ||
                // Currently the tuple expressions are not supported and this is known to cause false positives.
                // Please check:
                // - community feedback: https://github.com/SonarSource/sonar-dotnet/issues/3094
                // - implementation ticket: https://github.com/SonarSource/sonar-dotnet/issues/2933
                node.DescendantNodes().AnyOfKind(SyntaxKindEx.TupleExpression) ||
                !CSharpControlFlowGraph.TryGet(node, context.SemanticModel, out var cfg))
            {
                return;
            }

            var lva = CSharpLiveVariableAnalysis.Analyze(cfg, declaration, context.SemanticModel);

            foreach (var block in cfg.Blocks)
            {
                CheckCfgBlockForDeadStores(block, lva.GetLiveOut(block), lva.CapturedVariables, node, declaration, context);
            }
        }
        private static void ReportOnDeadParametersAtEntry(BaseMethodDeclarationSyntax declaration, IMethodSymbol methodSymbol,
                                                          IImmutableList <IParameterSymbol> noReportOnParameters, SyntaxNodeAnalysisContext context)
        {
            if (!declaration.IsKind(SyntaxKind.MethodDeclaration) ||
                declaration.Body == null)
            {
                return;
            }

            var excludedParameters = noReportOnParameters;

            if (methodSymbol.IsExtensionMethod)
            {
                excludedParameters = excludedParameters.Add(methodSymbol.Parameters.First());
            }

            excludedParameters = excludedParameters.AddRange(methodSymbol.Parameters.Where(p => p.RefKind != RefKind.None));

            var candidateParameters = methodSymbol.Parameters.Except(excludedParameters);

            if (!candidateParameters.Any())
            {
                return;
            }

            IControlFlowGraph cfg;

            if (!CSharpControlFlowGraph.TryGet(declaration.Body, context.SemanticModel, out cfg))
            {
                return;
            }

            var lva            = CSharpLiveVariableAnalysis.Analyze(cfg, methodSymbol, context.SemanticModel);
            var liveParameters = lva.GetLiveIn(cfg.EntryBlock).OfType <IParameterSymbol>();

            ReportOnUnusedParameters(declaration, candidateParameters.Except(liveParameters).Except(lva.CapturedVariables), MessageDead,
                                     context, isRemovable: false);
        }
Example #16
0
        public void ExportFunction(MethodDeclarationSyntax method)
        {
            if (method.Body == null)
            {
                return;
            }

            if (IsTooComplexForMlirOrTheCfg(method))
            {
                writer.WriteLine($"// Skipping function {method.Identifier.ValueText}{GetAnonymousArgumentsString(method)}, it contains poisonous unsupported syntaxes");
                writer.WriteLine();
                return;
            }

            if (!CSharpControlFlowGraph.TryGet(method.Body, semanticModel, out var cfg))
            {
                writer.WriteLine($"// Skipping function {method.Identifier.ValueText}{GetAnonymousArgumentsString(method)}, failed to generate CFG");
                writer.WriteLine();
                return;
            }
            blockCounter = 0;
            blockMap.Clear();
            opCounter = 0;
            opMap.Clear();

            var returnType = HasNoReturn(method) ? "()" : MlirType(method.ReturnType);

            writer.WriteLine($"func @{GetMangling(method)}{GetAnonymousArgumentsString(method)} -> {returnType} {GetLocation(method)} {{");
            CreateEntryBlock(method);

            foreach (var block in cfg.Blocks)
            {
                ExportBlock(block, method, returnType);
            }
            writer.WriteLine("}");
        }