private static void Analyze( SyntaxNodeAnalysisContext context, SyntaxNode declaration, ParameterListSyntax parameterList, CSharpSyntaxNode bodyOrExpressionBody) { if (parameterList == null) { return; } if (bodyOrExpressionBody == null) { return; } if (!parameterList.Parameters.Any()) { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(declaration, cancellationToken); SyntaxWalker walker = null; foreach (IParameterSymbol parameter in methodSymbol.Parameters) { cancellationToken.ThrowIfCancellationRequested(); ITypeSymbol type = parameter.Type; if (type.Kind == SymbolKind.ErrorType) { continue; } if (CSharpFacts.IsSimpleType(type.SpecialType)) { continue; } if (!type.IsReadOnlyStruct()) { if (parameter.RefKind == RefKind.In && type.TypeKind == TypeKind.Struct) { var parameterSyntax = (ParameterSyntax)parameter.GetSyntax(cancellationToken); Debug.Assert(parameterSyntax.Modifiers.Contains(SyntaxKind.InKeyword), ""); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.DoNotPassNonReadOnlyStructByReadOnlyReference, parameterSyntax.Identifier); } continue; } if (parameter.RefKind != RefKind.None) { continue; } if (walker == null) { if (methodSymbol.ImplementsInterfaceMember(allInterfaces: true)) { break; } walker = SyntaxWalker.GetInstance(); } else if (walker.Parameters.ContainsKey(parameter.Name)) { walker.Parameters.Clear(); break; } walker.Parameters.Add(parameter.Name, parameter); } if (walker == null) { return; } try { if (walker.Parameters.Count > 0) { walker.SemanticModel = semanticModel; walker.CancellationToken = cancellationToken; if (bodyOrExpressionBody.IsKind(SyntaxKind.Block)) { walker.VisitBlock((BlockSyntax)bodyOrExpressionBody); } else { walker.VisitArrowExpressionClause((ArrowExpressionClauseSyntax)bodyOrExpressionBody); } if (walker.Parameters.Count > 0) { DataFlowAnalysis analysis = (bodyOrExpressionBody.IsKind(SyntaxKind.Block)) ? semanticModel.AnalyzeDataFlow((BlockSyntax)bodyOrExpressionBody) : semanticModel.AnalyzeDataFlow(((ArrowExpressionClauseSyntax)bodyOrExpressionBody).Expression); bool?isReferencedAsMethodGroup = null; foreach (KeyValuePair <string, IParameterSymbol> kvp in walker.Parameters) { var isAssigned = false; foreach (ISymbol assignedSymbol in analysis.AlwaysAssigned) { if (SymbolEqualityComparer.Default.Equals(assignedSymbol, kvp.Value)) { isAssigned = true; break; } } if (isAssigned) { continue; } if (isReferencedAsMethodGroup ??= IsReferencedAsMethodGroup()) { break; } if (kvp.Value.GetSyntaxOrDefault(cancellationToken) is ParameterSyntax parameter) { DiagnosticHelpers.ReportDiagnostic( context, DiagnosticRules.MakeParameterRefReadOnly, parameter.Identifier); } } } } } finally { SyntaxWalker.Free(walker); } bool IsReferencedAsMethodGroup() { switch (declaration.Kind()) { case SyntaxKind.MethodDeclaration: return(MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.Parent, methodSymbol, semanticModel, cancellationToken)); case SyntaxKind.LocalFunctionStatement: return(MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.FirstAncestor <MemberDeclarationSyntax>(), methodSymbol, semanticModel, cancellationToken)); default: return(false); } } }