private static async Task <bool> IsApplicableConstructorAsync(
                IMethodSymbol constructor,
                Document document,
                ImmutableArray <string> parameterNamesForSelectedMembers,
                CancellationToken cancellationToken
                )
            {
                var constructorParams = constructor.Parameters;

                if (constructorParams.Length == 2)
                {
                    var compilation = await document.Project
                                      .GetCompilationAsync(cancellationToken)
                                      .ConfigureAwait(false);

                    var deserializationConstructorCheck = new DeserializationConstructorCheck(
                        compilation
                        );
                    if (deserializationConstructorCheck.IsDeserializationConstructor(constructor))
                    {
                        return(false);
                    }
                }

                return(constructorParams.All(parameter => parameter.RefKind == RefKind.None) &&
                       !constructor.IsImplicitlyDeclared &&
                       !constructorParams.Any(p => p.IsParams) &&
                       !SelectedMembersAlreadyExistAsParameters(
                           parameterNamesForSelectedMembers,
                           constructorParams
                           ));
            }
            private bool IsUnusedParameterCandidate(IParameterSymbol parameter)
            {
                // Ignore certain special parameters/methods.
                // Note that "method.ExplicitOrImplicitInterfaceImplementations" check below is not a complete check,
                // as identifying this correctly requires analyzing referencing projects, which is not
                // supported for analyzers. We believe this is still a good enough check for most cases so
                // we don't have to bail out on reporting unused parameters for all public methods.

                if (parameter.IsImplicitlyDeclared ||
                    parameter.Name == DiscardVariableName ||
                    !(parameter.ContainingSymbol is IMethodSymbol method) ||
                    method.IsImplicitlyDeclared ||
                    method.IsExtern ||
                    method.IsAbstract ||
                    method.IsVirtual ||
                    method.IsOverride ||
                    !method.ExplicitOrImplicitInterfaceImplementations().IsEmpty ||
                    method.IsAccessor() ||
                    method.IsAnonymousFunction() ||
                    _compilationAnalyzer.MethodHasHandlesClause(method) ||
                    _deserializationConstructorCheck.IsDeserializationConstructor(method))
                {
                    return(false);
                }

                // Ignore event handler methods "Handler(object, MyEventArgs)"
                // as event handlers are required to match this signature
                // regardless of whether or not the parameters are used.
                if (_eventArgsTypeOpt != null &&
                    method.Parameters.Length == 2 &&
                    method.Parameters[0].Type.SpecialType == SpecialType.System_Object &&
                    method.Parameters[1].Type.InheritsFromOrEquals(_eventArgsTypeOpt))
                {
                    return(false);
                }

                // Ignore flagging parameters for methods with certain well-known attributes,
                // which are known to have unused parameters in real world code.
                if (method.GetAttributes().Any(a => _attributeSetForMethodsToIgnore.Contains(a.AttributeClass)))
                {
                    return(false);
                }

                // Methods used as delegates likely need to have unused parameters for signature compat.
                if (_methodsUsedAsDelegates.ContainsKey(method))
                {
                    return(false);
                }

                return(true);
            }
Exemplo n.º 3
0
            /// <summary>
            /// Returns true if the given symbol meets the following criteria to be
            /// a candidate for dead code analysis:
            ///     1. It is marked as "private".
            ///     2. It is not an implicitly declared symbol.
            ///     3. It is either a method, field, property or an event.
            ///     4. If method, then it is a constructor OR a method with <see cref="MethodKind.Ordinary"/>,
            ///        such that is meets a few criteria (see implementation details below).
            ///     5. If field, then it must not be a backing field for an auto property.
            ///        Backing fields have a non-null <see cref="IFieldSymbol.AssociatedSymbol"/>.
            ///     6. If property, then it must not be an explicit interface property implementation.
            ///     7. If event, then it must not be an explicit interface event implementation.
            /// </summary>
            private bool IsCandidateSymbol(ISymbol memberSymbol)
            {
                Debug.Assert(memberSymbol == memberSymbol.OriginalDefinition);

                if (memberSymbol.DeclaredAccessibility == Accessibility.Private &&
                    !memberSymbol.IsImplicitlyDeclared)
                {
                    switch (memberSymbol.Kind)
                    {
                    case SymbolKind.Method:
                        var methodSymbol = (IMethodSymbol)memberSymbol;
                        switch (methodSymbol.MethodKind)
                        {
                        case MethodKind.Constructor:
                            // It is fine to have an unused private constructor
                            // without parameters.
                            // This is commonly used for static holder types
                            // that want to block instantiation of the type.
                            if (methodSymbol.Parameters.Length == 0)
                            {
                                return(false);
                            }

                            // ISerializable constructor is invoked by the runtime for deserialization
                            // and it is a common pattern to have a private serialization constructor
                            // that is not explicitly referenced in code.
                            if (_deserializationConstructorCheck.IsDeserializationConstructor(methodSymbol))
                            {
                                return(false);
                            }

                            return(true);

                        case MethodKind.Ordinary:
                            // Do not track accessors, as we will track/flag the associated symbol.
                            if (methodSymbol.AssociatedSymbol != null)
                            {
                                return(false);
                            }

                            // Do not flag unused entry point (Main) method.
                            if (IsEntryPoint(methodSymbol))
                            {
                                return(false);
                            }

                            // It is fine to have unused virtual/abstract/overrides/extern
                            // methods as they might be used in another type in the containing
                            // type's type hierarchy.
                            if (methodSymbol.IsAbstract ||
                                methodSymbol.IsVirtual ||
                                methodSymbol.IsOverride ||
                                methodSymbol.IsExtern)
                            {
                                return(false);
                            }

                            // Explicit interface implementations are not referenced explicitly,
                            // but are still used.
                            if (!methodSymbol.ExplicitInterfaceImplementations.IsEmpty)
                            {
                                return(false);
                            }

                            // Ignore methods with special attributes that indicate special/reflection
                            // based access.
                            if (IsMethodWithSpecialAttribute(methodSymbol))
                            {
                                return(false);
                            }

                            // ShouldSerializeXXX and ResetXXX are ok if there is a matching
                            // property XXX as they are used by the windows designer property grid
                            if (IsShouldSerializeOrResetPropertyMethod(methodSymbol))
                            {
                                return(false);
                            }

                            // Ignore methods with event handler signature
                            // as lot of ASP.NET types have many special event handlers
                            // that are invoked with reflection (e.g. Application_XXX, Page_XXX,
                            // OnTransactionXXX, etc).
                            if (methodSymbol.HasEventHandlerSignature(_eventArgsType))
                            {
                                return(false);
                            }

                            return(true);

                        default:
                            return(false);
                        }

                    case SymbolKind.Field:
                        return(((IFieldSymbol)memberSymbol).AssociatedSymbol == null);

                    case SymbolKind.Property:
                        return(((IPropertySymbol)memberSymbol).ExplicitInterfaceImplementations.IsEmpty);

                    case SymbolKind.Event:
                        return(((IEventSymbol)memberSymbol).ExplicitInterfaceImplementations.IsEmpty);
                    }
                }

                return(false);
            }