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); }
/// <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); }