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, DiagnosticDescriptors.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; } if (walker.Parameters.Count > 0) { walker.SemanticModel = semanticModel; walker.CancellationToken = cancellationToken; if (bodyOrExpressionBody is BlockSyntax body) { walker.VisitBlock(body); } else { walker.VisitArrowExpressionClause((ArrowExpressionClauseSyntax)bodyOrExpressionBody); } if (walker.Parameters.Count > 0 && !IsReferencedAsMethodGroup()) { foreach (KeyValuePair <string, IParameterSymbol> kvp in walker.Parameters) { if (kvp.Value.GetSyntaxOrDefault(cancellationToken) is ParameterSyntax parameter) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.MakeParameterRefReadOnly, parameter.Identifier); } } } } 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); } } }
private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) { var methodDeclaration = (MethodDeclarationSyntax)context.Node; if (methodDeclaration.ContainsDiagnostics) { return; } if (!methodDeclaration.IsParentKind(SyntaxKind.ClassDeclaration, SyntaxKind.StructDeclaration, SyntaxKind.RecordDeclaration, SyntaxKind.RecordStructDeclaration)) { return; } if (methodDeclaration.Modifiers.ContainsAny( SyntaxKind.AbstractKeyword, SyntaxKind.VirtualKeyword, SyntaxKind.OverrideKeyword, SyntaxKind.PartialKeyword)) { return; } ParameterInfo parameterInfo = SyntaxInfo.ParameterInfo(methodDeclaration); if (!parameterInfo.Success) { return; } if (ContainsOnlyThrowNewExpression(parameterInfo.Body)) { return; } IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken); if (methodSymbol == null) { return; } if (SymbolUtility.IsEventHandlerMethod(methodSymbol)) { return; } if (!methodSymbol.ExplicitInterfaceImplementations.IsDefaultOrEmpty) { return; } if (methodSymbol.ImplementsInterfaceMember(allInterfaces: true)) { return; } UnusedParameterWalker walker = null; try { walker = UnusedParameterWalker.GetInstance(); walker.SetValues(context.SemanticModel, context.CancellationToken); FindUnusedNodes(parameterInfo, walker); if (walker.Nodes.Count > 0 && !MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(methodDeclaration, methodSymbol, context.SemanticModel, context.CancellationToken)) { foreach (KeyValuePair <string, NodeSymbolInfo> kvp in walker.Nodes) { ReportDiagnostic(context, kvp.Value.Node); } } } finally { if (walker != null) { UnusedParameterWalker.Free(walker); } } }
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(); if (parameter.RefKind == RefKind.None) { ITypeSymbol type = parameter.Type; //TODO: ITypeSymbol.IsReadOnly, https://github.com/dotnet/roslyn/issues/23792 if (type.IsReadOnlyStruct()) { if (walker == null) { if (methodSymbol.ImplementsInterfaceMember(allInterfaces: true)) { return; } walker = SyntaxWalker.GetInstance(); } if (walker.Parameters.ContainsKey(parameter.Name)) { SyntaxWalker.Free(walker); return; } walker.Parameters.Add(parameter.Name, parameter); } } } if (walker == null) { return; } walker.SemanticModel = semanticModel; walker.CancellationToken = cancellationToken; if (bodyOrExpressionBody is BlockSyntax body) { walker.VisitBlock(body); } else { walker.VisitArrowExpressionClause((ArrowExpressionClauseSyntax)bodyOrExpressionBody); } switch (declaration.Kind()) { case SyntaxKind.MethodDeclaration: { if (MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.Parent, methodSymbol, semanticModel, cancellationToken)) { return; } break; } case SyntaxKind.LocalFunctionStatement: { if (MethodReferencedAsMethodGroupWalker.IsReferencedAsMethodGroup(declaration.FirstAncestor <MemberDeclarationSyntax>(), methodSymbol, semanticModel, cancellationToken)) { return; } break; } } foreach (KeyValuePair <string, IParameterSymbol> kvp in walker.Parameters) { if (kvp.Value.GetSyntaxOrDefault(cancellationToken) is ParameterSyntax parameter) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.MakeParameterRefReadOnly, parameter.Identifier); } } SyntaxWalker.Free(walker); }