Azure PowerShell: mayormente inofensivo

Cuadro 6

Hola a todos Hoy tenemos otro proyecto de Microsoft en el cheque. Por el título de este artículo, puede adivinar que esta vez los desarrolladores no nos "complacieron" con una gran cantidad de errores. Esperamos que los autores del proyecto no se ofendan por el título. Después de todo, una pequeña cantidad de errores es excelente, ¿no? Sin embargo, todavía logramos encontrar algo intrigante en el código de Azure PowerShell. Sugerimos conocer las características de este proyecto y verificar los errores que se encuentran utilizando el analizador PVS-Studio C #.

Sobre el proyecto


Azure PowerShell es un conjunto de comandos ( cmdlet ) que le permite administrar los recursos de Azure directamente desde la línea de comandos de PowerShell . El objetivo principal de este conjunto es simplificar el proceso de estudio e iniciar rápidamente el desarrollo para Azure. Azure PowerShell proporciona a los administradores y desarrolladores características atractivas para crear, implementar y administrar aplicaciones de Microsoft Azure.

Azure PowerShell se desarrolla en el entorno .NET Standard, es compatible con PowerShell 5.1 para Windows y PowerShell 6.xy versiones posteriores para todas las plataformas. El código fuente de Azure PowerShell está disponible en GitHub.

Recientemente he estado recibiendo proyectos de Microsoft para un control. En mi opinión, la calidad de estos proyectos suele ser de primera categoría. Aunque, por supuesto, no sin excepciones, como se describe en el artículo " WinForms: Errores, Holmes ". Pero esta vez todo está bien. El proyecto es grande: los archivos de código fuente 6845 .cs contienen aproximadamente 700,000 líneas, excluyendo las en blanco (no tomé en cuenta las pruebas y advertencias del tercer nivel de certeza). Se encontraron muy pocos errores para tal cantidad de código: no más de cien. Hubo muchos casos similares, así que elegí los más interesantes para el artículo. Como de costumbre, clasifiqué los errores por los números de las advertencias de PVS-Studio.

También me topé con algunos fragmentos de código que parecían errores, pero que no podían reconocerse como definitivamente erróneos, ya que no estoy lo suficientemente familiarizado con las peculiaridades del desarrollo de PowerShell. Espero que entre los lectores haya especialistas en este tema que me ayuden. Lo describiré en detalle a continuación.

Antes de la parte de comentarios, me gustaría mencionar la estructura específica del proyecto. El código fuente de Azure PowerShell consta de más de setenta soluciones de Visual Studio. Algunas soluciones incluyen proyectos de otros. Esta estructura ralentizó un poco el análisis (no mucho). Aún así, el cheque no causó ninguna otra dificultad. Por conveniencia, en el mensaje de error (entre paréntesis) especificaré el nombre de la solución donde se encontró el error.

Resultados de analisis


V3001 Hay subexpresiones idénticas 'strTimespan.Contains ("M")' a la izquierda y a la derecha de '||' operador AzureServiceBusCmdletBase.cs 187 (EventGrid)

public static TimeSpan ParseTimespan(string strTimespan) { .... if (strTimespan.Contains("P") || strTimespan.Contains("D") || strTimespan.Contains("T") || strTimespan.Contains("H") || strTimespan.Contains("M") || strTimespan.Contains("M")) .... } 

Un ejemplo de un error bastante obvio que solo un desarrollador puede solucionar. En este caso, no está absolutamente claro si tratamos con la duplicación de código que no afecta a nada o algo más tiene que tener lugar en lugar de "M" en una de las dos últimas verificaciones.

V3001 Hay subexpresiones idénticas 'this.AggregationType! = Null' a la izquierda y a la derecha del operador '&&'. GetAzureRmMetricCommand.cs 156 (Monitor)

 public AggregationType? AggregationType { get; set; } .... protected override void ProcessRecordInternal() { .... string aggregation = (this.AggregationType != null && this.AggregationType.HasValue) ? this.AggregationType.Value.ToString() : null; .... } 

Probablemente no haya ningún error aquí. Este es un ejemplo de código redundante. A veces, dicho código puede indicar falta de conocimiento del desarrollador. El punto es que las comprobaciones this.AggregationType! = Null y this.AggregationType.HasValue son idénticas. Es suficiente usar solo uno de ellos (cualquiera). Personalmente, prefiero la opción con HasValue:

 string aggregation = this.AggregationType.HasValue ? this.AggregationType.Value.ToString() : null; 

V3003 Se usó el patrón 'if (A) {...} else if (A) {...}'. Hay una probabilidad de presencia de error lógico. Líneas de verificación: 152, 163. GetAzureRmRecoveryServicesBackupProtectionPolicy.cs 152 (RecoveryServices)

 public override void ExecuteCmdlet() { .... if( WorkloadType == Models.WorkloadType.AzureVM ) { .... } .... else if( WorkloadType == Models.WorkloadType.AzureFiles ) { if( BackupManagementType != Models.BackupManagementType.AzureStorage ) { throw new ArgumentException( Resources.AzureFileUnsupportedBackupManagementTypeException ); } serviceClientProviderType = ServiceClientHelpers. GetServiceClientProviderType( Models.WorkloadType.AzureFiles ); } else if( WorkloadType == Models.WorkloadType.AzureFiles ) { if( BackupManagementType != Models.BackupManagementType.AzureStorage ) { throw new ArgumentException( Resources.AzureFileUnsupportedBackupManagementTypeException ); } serviceClientProviderType = ServiceClientHelpers. GetServiceClientProviderType( Models.WorkloadType.AzureFiles ); } .... } 

Dos más si los bloques son absolutamente idénticos, incluyendo tanto la condición como el cuerpo del bloque. Tales errores generalmente se hacen cuando se utiliza el método de copiar y pegar. El problema aquí es nuevamente la criticidad del error. Si no se trata de una simple duplicación de código, puede ser la verificación necesaria ausente y el conjunto apropiado de acciones. El autor definitivamente tiene que editar el código.

V3005 La variable 'this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent' se asigna a sí misma. SetAzureVMOperatingSystemCommand.cs 298 (Calcular)

 public override void ExecuteCmdlet() { .... // OS Profile this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent = this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent; .... } 

El valor de la propiedad es autoasignado. Echa un vistazo a su declaración:

 [JsonProperty(PropertyName = "provisionVMAgent")] public bool? ProvisionVMAgent { get; set; } 

La descripción de JsonProperty establece: "Indica a Newtonsoft.Json.JsonSerializer que siempre serialice al miembro con el nombre especificado". Parece que todo es inocente y se ha cometido el error obvio. El uso explícito de esto para acceder a la propiedad también es bastante confuso. Tal vez, otra variable no se especificó por error en lugar de esto. Pero no saltemos a conclusiones. El hecho es que me encontré con muchas de esas tareas (una propiedad es autoasignada). Aquí hay un ejemplo de una tarea, muy similar a un error:

V3005 La variable 'this.LastHeartbeat' se asigna a sí misma. PSFabricDetails.cs 804 (RecoveryServices)

 public ASRInMageAzureV2SpecificRPIDetails( InMageAzureV2ReplicationDetails details) { this.LastHeartbeat = this.LastHeartbeat; // <= this.RecoveryAvailabilitySetId = details.RecoveryAvailabilitySetId; this.AgentVersion = details.AgentVersion; this.DiscoveryType = details.DiscoveryType; .... } 

Echemos un vistazo más de cerca a la segunda y posteriores tareas. En la parte derecha de la expresión, los detalles tienen lugar en lugar de esto. Ahora mire la declaración de la propiedad this.LastHeartbeat :
 public DateTime? LastHeartbeat { get; set; } 

Por último, busquemos la propiedad con el mismo nombre en la clase InMageAzureV2ReplicationDetails . Dicha propiedad se declara allí:

 public class InMageAzureV2ReplicationDetails : ReplicationProviderSpecificSettings { .... [JsonProperty(PropertyName = "lastHeartbeat")] public DateTime? LastHeartbeat { get; set; } .... } 

Bueno, en este caso, estoy dispuesto a admitir que es un error real. ¿Pero qué haremos con las siguientes advertencias? A diferencia de dos fragmentos de código anteriores, hay varias propiedades autoasignadas. Bueno, esto parece menos un error:

  • V3005 La variable 'this.ResourceGroupName' se asigna a sí misma. RemoveAzureRmExpressRouteConnectionCommand.cs 84 (CognitiveServices)
  • V3005 La variable 'this.ExpressRouteGatewayName' se asigna a sí misma. RemoveAzureRmExpressRouteConnectionCommand.cs 85 (CognitiveServices)
  • V3005 La variable 'this.Name' se asigna a sí misma. RemoveAzureRmExpressRouteConnectionCommand.cs 86 (CognitiveServices)

 [Cmdlet(VerbsCommon.Remove, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "ExpressRouteConnection", DefaultParameterSetName = CortexParameterSetNames.ByExpressRouteConnectionName, SupportsShouldProcess = true), OutputType(typeof(bool))] public class RemoveExpressRouteConnectionCommand : ExpressRouteConnectionBaseCmdlet { [Parameter( Mandatory = true, ParameterSetName = CortexParameterSetNames.ByExpressRouteConnectionName, HelpMessage = "The resource group name.")] [ResourceGroupCompleter] [ValidateNotNullOrEmpty] public string ResourceGroupName { get; set; } .... public override void Execute() { if (....) { this.ResourceGroupName = this.ResourceGroupName; this.ExpressRouteGatewayName = this.ExpressRouteGatewayName; this.Name = this.Name; } .... } .... } 

El método Execute contiene tres autoasignaciones de propiedades en una fila. Por si acaso, cité la declaración completa de la clase RemoveExpressRouteConnectionCommand y todos sus atributos, así como la declaración de propiedad ResourceGroupName (otras dos propiedades se declaran de manera similar). Fueron estas advertencias las que me hicieron pensar en la pregunta: "¿Es un error?" Sospecho que puede estar ocurriendo algo de magia interna del desarrollo de PowerShell aquí. Espero que entre los lectores haya expertos que estén informados sobre este tema. No estoy listo para sacar conclusiones en este caso.

V3006 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'throw': lanzar una nueva ArgumentException (FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 259 (RecoveryServices)

 private void StartRPITestFailover() { .... if (....) { .... } else { new ArgumentException( Resources .UnsupportedDirectionForTFO); // Throw Unsupported Direction // Exception } .... } 

Se omite la palabra clave throw . Y el comentario dice que solo hay que lanzar la excepción. Encontré varios errores más similares en la solución RecoveryServices :

  • V3006 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'throw': lanzar una nueva ArgumentException (FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 305 (RecoveryServices)
  • V3006 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'throw': lanzar una nueva ArgumentException (FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 278 (RecoveryServices)
  • V3006 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'throw': lanzar una nueva ArgumentException (FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 322 (RecoveryServices)
  • V3006 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'throw': lanzar una nueva ArgumentException (FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 421 (RecoveryServices)
  • V3006 El objeto fue creado pero no se está utilizando. Podría faltar la palabra clave 'throw': lanzar una nueva ArgumentException (FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 452 (RecoveryServices)

V3022 La expresión 'apiType.HasValue' siempre es falsa. ApiManagementClient.cs 1134 (ApiManagement)

 private string GetApiTypeForImport(...., PsApiManagementApiType? apiType) { .... if (apiType.HasValue) { switch(apiType.Value) { case PsApiManagementApiType.Http: return SoapApiType.SoapToRest; case PsApiManagementApiType.Soap: return SoapApiType.SoapPassThrough; default: return SoapApiType.SoapPassThrough; } } return apiType.HasValue ? // <= apiType.Value.ToString("g") : PsApiManagementApiType.Http.ToString("g"); } 

La lógica del trabajo se ha roto. Si apiType contiene un valor, el control no alcanzará la expresión de retorno al final del método (todas las ramas del interruptor contienen retorno ). De lo contrario, el método siempre devolverá PsApiManagementApiType.Http.ToString ("g") , mientras que el valor apiType.Value.ToString ("g") nunca se devolverá.

V3022 La expresión 'automationJob! = Null && automationJob == null' siempre es falsa. NodeConfigurationDeployment.cs 199 (Automatización)

 public NodeConfigurationDeployment( ...., Management.Automation.Models.Job automationJob = null, ....) { .... if (automationJob != null && automationJob == null) return; .... } 

Código contra intuitivo. Dos controles que se contradicen entre sí. Probablemente la segunda comprobación de nulo contiene la variable incorrecta.

V3022 La expresión siempre es falsa. DataFactoryClient.Encrypt.cs 37 (DataFactory)

 public virtual string OnPremisesEncryptString(....) { .... if ( linkedServiceType == LinkedServiceType.OnPremisesSqlLinkedService && linkedServiceType == LinkedServiceType.OnPremisesOracleLinkedService && linkedServiceType == LinkedServiceType.OnPremisesFileSystemLinkedService && (value == null || value.Length == 0)) { throw new ArgumentNullException("value"); } .... } 

El cheque no tiene sentido y la excepción nunca será lanzada. La condición requiere la igualdad simultánea de la variable LinkedServiceType con tres valores diferentes. Los operadores && y || Es probable que se confundan. Código fijo:

 if (( linkedServiceType == LinkedServiceType.OnPremisesSqlLinkedService || linkedServiceType == LinkedServiceType.OnPremisesOracleLinkedService || linkedServiceType == LinkedServiceType.OnPremisesFileSystemLinkedService) && (value == null || value.Length == 0)) .... 

V3022 La expresión 'Ekus == null' siempre es falsa. PSKeyVaultCertificatePolicy.cs 129 (KeyVault)

 internal CertificatePolicy ToCertificatePolicy() { .... if (Ekus != null) { x509CertificateProperties.Ekus = Ekus == null ? null : new List<string>(Ekus); } .... } 

Comprobación redundante de la variable Ekus para nulo . Probablemente esté bien, pero el código no se ve bien.

V3023 Considere inspeccionar esta expresión. La expresión es excesiva o contiene un error de imprenta. PolicyRetentionObjects.cs 207 (RecoveryServices)

 public virtual void Validate() { if (RetentionTimes == null || RetentionTimes.Count == 0 || RetentionTimes.Count != 1) { throw new ArgumentException( Resources.InvalidRetentionTimesInPolicyException); } } 

Aquí hay un control excesivo o una condición excesiva. El check RetentionTimes.Count == 0 no tiene sentido, ya que después de eso, sigue el check RetentionTimes.Count! = 1 .

V3025 Formato incorrecto. Se espera un número diferente de elementos de formato al llamar a la función 'Formatear'. Argumentos no utilizados: this.ResourceGroupName. NewScheduledQueryRuleCommand.cs 117 (Monitor)

 protected override void ProcessRecordInternal() { .... if (this.ShouldProcess(this.Name, string.Format("Creating Log Alert Rule '{0}' in resource group {0}", this.Name, this.ResourceGroupName))) { .... } .... } 

Un error en la línea de formato. El especificador {0} se usa dos veces, y el método de formato pasa dos argumentos. Aquí está la versión correcta:

 if (this.ShouldProcess(this.Name, string.Format("Creating Log Alert Rule '{0}' in resource group {1}", this.Name, this.ResourceGroupName))) .... 

Otro error similar:

  • V3025 Formato incorrecto. Se espera un número diferente de elementos de formato al llamar a la función 'Formatear'. Argumentos no utilizados: this.ResourceGroupName. RemoveScheduledQueryRuleCommand.cs 88 (Monitor)

V3042 Posible NullReferenceException. El '?.' y '.' Los operadores se utilizan para acceder a los miembros del objeto 'imageAndOsType' VirtualMachineScaleSetStrategy.cs 81 (Calcular)

 internal static ResourceConfig<VirtualMachineScaleSet> CreateVirtualMachineScaleSetConfig(...., ImageAndOsType imageAndOsType, ....) { .... VirtualMachineProfile = new VirtualMachineScaleSetVMProfile { OsProfile = new VirtualMachineScaleSetOSProfile { ...., WindowsConfiguration = imageAndOsType.CreateWindowsConfiguration(), // <= ...., }, StorageProfile = new VirtualMachineScaleSetStorageProfile { ImageReference = imageAndOsType?.Image, // <= DataDisks = DataDiskStrategy.CreateVmssDataDisks( imageAndOsType?.DataDiskLuns, dataDisks) // <= }, }, .... } 

Al crear el objeto VirtualMachineScaleSetVMProfile , la variable imageAndOsType se verifica para nulo sin ninguna verificación preliminar. Sin embargo, al crear VirtualMachineScaleSetStorageProfile , esta variable ya se verifica utilizando el operador de acceso condicional incluso dos veces. El código no parece seguro.

V3042 Posible NullReferenceException. El '?.' y '.' Los operadores se utilizan para acceder a los miembros del objeto 'existenteContacts' RemoveAzureKeyVaultCertificateContact.cs 123 (KeyVault)

 public override void ExecuteCmdlet() { .... List<PSKeyVaultCertificateContact> existingContacts; try { existingContacts = this.DataServiceClient. GetCertificateContacts(VaultName)?.ToList(); } catch (KeyVaultErrorException exception) { .... existingContacts = null; } foreach (var email in EmailAddress) { existingContacts.RemoveAll(....); // <= } .... } 

Tanto en la ejecución normal como como resultado del manejo de una excepción, la variable existenteContacts puede obtener el valor nulo , después de lo cual la ejecución continuará. Además en el código, esta variable se usa sin ningún motivo específico.

V3066 Posible orden incorrecto de argumentos pasados ​​al método 'PersistSyncServerRegistration': 'storageSyncServiceUid' y 'discoveryUri'. EcsManagementInteropClient.cs 364 (StorageSync)

 public class EcsManagementInteropClient : IEcsManagement { .... public int PersistSyncServerRegistration(....) { return m_managementObject.PersistSyncServerRegistration( serviceUri, subscriptionId, storageSyncServiceName, resourceGroupName, clusterId, clusterName, storageSyncServiceUid, // <= discoveryUri, // <= serviceLocation, resourceLocation); } .... } 

El analizador sospecha que el orden de los argumentos del método PersistSyncServerRegistration es confuso. Declaración del método:

 public interface IEcsManagement : IDisposable { .... int PersistSyncServerRegistration( [In, MarshalAs(UnmanagedType.BStr)] string serviceUri, [In, MarshalAs(UnmanagedType.BStr)] string subscriptionId, [In, MarshalAs(UnmanagedType.BStr)] string storageSyncServiceName, [In, MarshalAs(UnmanagedType.BStr)] string resourceGroupName, [In, MarshalAs(UnmanagedType.BStr)] string clusterId, [In, MarshalAs(UnmanagedType.BStr)] string clusterName, [In, MarshalAs(UnmanagedType.BStr)] string discoveryUri, // <= [In, MarshalAs(UnmanagedType.BStr)] string storageSyncServiceUid, // <= [In, MarshalAs(UnmanagedType.BStr)] string serviceLocation, [In, MarshalAs(UnmanagedType.BStr)] string resourceLocation); .... } 

De hecho, algo anda mal aquí con los argumentos número siete y ocho. El autor tiene que verificar el código.

V3077 El configurador de la propiedad 'GetGuid' no utiliza su parámetro 'valor'. RecoveryServicesBackupCmdletBase.cs 54 (RecoveryServices)

 public abstract class RecoveryServicesBackupCmdletBase : AzureRMCmdlet { .... static string _guid; protected static string GetGuid { get { return _guid; } set { _guid = Guid.NewGuid().ToString(); } } .... } 

El setter no usa el parámetro pasado. En cambio, crea un nuevo GUID y lo asigna al campo _guid . Creo que la mayoría de los lectores estarían de acuerdo en que ese código se ve al menos feo. Esta construcción no es muy conveniente de usar: cuando (re) inicializa la propiedad GetGuid , uno tiene que asignarle un valor falso, que no es muy obvio. Pero, sobre todo, me divertía la forma en que los autores usaban este patrón. Solo hay un lugar, donde se maneja GetGuid . Compruébalo:

 public override void ExecuteCmdlet() { .... var itemResponse = ServiceClientAdapter.CreateOrUpdateProtectionIntent( GetGuid ?? Guid.NewGuid().ToString(), ....); .... } 

Brillante!

V3091 Análisis empírico. Es posible que haya un error tipográfico dentro del literal de cadena: "Id. De grupo de administración". La palabra 'Id' es sospechosa. Constants.cs 36 (Recursos)

 public class HelpMessages { public const string SubscriptionId = "Subscription Id of the subscription associated with the management"; public const string GroupId = "Management Group Id"; // <= public const string Recurse = "Recursively list the children of the management group"; public const string ParentId = "Parent Id of the management group"; public const string GroupName = "Management Group Id"; // <= public const string DisplayName = "Display Name of the management group"; public const string Expand = "Expand the output to list the children of the management group"; public const string Force = "Force the action and skip confirmations"; public const string InputObject = "Input Object from the Get call"; public const string ParentObject = "Parent Object"; } 

El analizador señaló un posible error en la cadena asignada para la constante GroupName . La conclusión se basa en el análisis empírico de otras tareas, teniendo en cuenta los nombres de las variables. Creo que en este caso el analizador tiene razón, y el valor de las constantes de GroupName debería ser una especie de "Nombre del grupo de administración". Probablemente el error se produjo debido al hecho de que el valor de la constante GroupId se copió, pero no se modificó.

Otro error similar:

  • V3091 Análisis empírico. Es posible que haya un error tipográfico dentro del literal de cadena. La palabra 'Nombre' es sospechosa. ParamHelpMsgs.cs 153 (RecoveryServices)

V3093 El '|' El operador evalúa ambos operandos. Quizás un cortocircuito '||' operador debe utilizarse en su lugar. PSKeyVaultCertificatePolicy.cs 114 (KeyVault)

 internal CertificatePolicy ToCertificatePolicy() { .... if (!string.IsNullOrWhiteSpace(SubjectName) || DnsNames != null || Ekus != null || KeyUsage != null | // <= ValidityInMonths.HasValue) { .... } .... } 

En este fragmento podría producirse un error y en el bloque if entre dos últimas condiciones, || operador podría haber sido utilizado. Pero como sucede a menudo, solo el desarrollador puede dar la respuesta correcta.

V3095 El objeto 'certificado' se usó antes de que se verificara como nulo. Líneas de verificación: 41, 43. CertificateInfo.cs 41 (Automatización)

 public CertificateInfo( ...., Azure.Management.Automation.Models.Certificate certificate) { .... this.Name = certificate.Name; if (certificate == null) return; .... } 

Clásico Primero se usa el objeto y solo después se verifica que la referencia sea nula . Nos encontramos con tales errores muy a menudo . Consideremos otro error similar.

V3095 El objeto 'clusterCred' se usó antes de que se verificara como nulo. Líneas de verificación: 115, 118. InvokeHiveCommand.cs 115 (HDInsight)

 public override void ExecuteCmdlet() { .... _credential = new BasicAuthenticationCloudCredentials { Username = clusterCred.UserName, Password = clusterCred.Password.ConvertToString() }; if (clusterConnection == null || clusterCred == null) .... } 

Aquí hay un par de errores similares:

  • V3095 El objeto '_profile' se usó antes de que se verificara como nulo. Líneas de verificación: 47, 49. RMProfileClient.cs 47 (Cuentas)
  • V3095 El objeto 'this.LoadBalancer.BackendAddressPools' se usó antes de que se verificara como nulo. Líneas de verificación: 56, 63. AddAzureRmLoadBalancerBackendAddressPoolConfigCommand.cs 56 (CognitiveServices)
  • En términos generales, vi muchos errores V3095 en el código de Azure PowerShell. Pero todos ellos son bastante similares, por lo que no me detendré en este tema.

V3125 El objeto 'startTime' se usó después de que se verificó contra nulo. Líneas de verificación: 1752, 1738. AutomationPSClientDSC.cs 1752 (Automatización)

 private string GetNodeReportListFilterString( ...., DateTimeOffset? startTime, ...., DateTimeOffset? lastModifiedTime) { .... if (startTime.HasValue) { odataFilter.Add("properties/startTime ge " + this.FormatDateTime(startTime.Value)); // <= } .... if (lastModifiedTime.HasValue) { odataFilter.Add("properties/lastModifiedTime ge " + this.FormatDateTime(startTime.Value)); // <= } .... } 

También es un tipo de errores bastante extendido. La variable startTime se verifica para detectar la presencia de un valor cuando se usa por primera vez. Pero no se hace en el uso posterior. Bueno, la situación puede ser aún peor. Mira el segundo bloque if . Creo que la variable startTime no debe estar aquí en absoluto. En primer lugar, no se verifica la presencia de un valor antes de su uso. En segundo lugar, la cadena formada para pasar al método Add también confirma mi sugerencia. Otra variable (lastModifiedTime ) se menciona en la primera parte de esta cadena.

V3125 El objeto 'firstPage' se usó después de que se verificó contra nulo. Líneas de verificación: 113, 108. IntegrationAccountAgreementOperations.cs 113 (LogicApp)

 public IList<IntegrationAccountAgreement> ListIntegrationAccountAgreements(....) { var compositeList = new List<IntegrationAccountAgreement>(); var firstPage = this.LogicManagementClient. IntegrationAccountAgreements.List(....); if (firstPage != null) { compositeList.AddRange(firstPage); } if (!string.IsNullOrEmpty(firstPage.NextPageLink)) // <= { .... } .... } 

Otro error obvio. La variable firstPage se usa de forma insegura a pesar del hecho de que anteriormente en el código esta variable ya se usa y se verifica preliminarmente para nulo .

Encontré aún más advertencias de V3125 en el código de Azure PowerShell que las de V3095 descritas anteriormente. Todos ellos también son del mismo tipo. Creo que dos de ellos que hemos considerado son suficientes.

V3137 La variable 'apiVersionSetId' se asigna pero no se usa al final de la función. GetAzureApiManagementApiVersionSet.cs 69 (ApiManagement)

 public String ApiVersionSetId { get; set; } .... public override void ExecuteApiManagementCmdlet() { .... string apiVersionSetId; if (ParameterSetName.Equals(ContextParameterSet)) { .... apiVersionSetId = ApiVersionSetId; } else { apiVersionSetId = ....; } if (string.IsNullOrEmpty(ApiVersionSetId)) // <= { WriteObject(....); } else { WriteObject(Client.GetApiVersionSet(...., ApiVersionSetId)) // <= } } 

El analizador informa que la variable local apiVersionSetId se inicializó, pero no se usó de ninguna manera. A menudo, este patrón indica un error. Creo que, en este caso, es muy probable que sea un error, especialmente teniendo en cuenta el hecho de que el nombre de la variable local apiVersionSetId y el nombre de la propiedad ApiVersionSetId difieren solo en el caso de la primera letra. Echa un vistazo al código. Después de inicializar la propiedad apiVersionSetId (de una forma u otra), solo la propiedad ApiVersionSetId se usa más en el código. Se ve extremadamente sospechoso.

V3137 La variable 'cacheId' se asigna pero no se usa al final de la función. RemoveAzureApiManagementCache.cs 94 (ApiManagement)

 public String CacheId { get; set; } .... public override void ExecuteApiManagementCmdlet() { .... string cacheId; if (....) { .... cacheId = InputObject.CacheId; } else if (....) { .... cacheId = cache.CacheId; } else { .... cacheId = CacheId; } var actionDescription = string.Format(...., CacheId); // <= var actionWarning = string.Format(...., CacheId); // <= .... Client.CacheRemove(resourceGroupName, serviceName, CacheId); // <= .... } 

Este es el caso que es casi el mismo que el descrito anteriormente. La variable local cacheId no se usa después de la inicialización de ninguna manera. En cambio, se usa otra propiedad con un nombre muy similar CacheId . No estoy seguro, puede ser que sea solo un patrón de programación de los desarrolladores de Azure PowerShell. De todos modos, parece un error.

V3143 El parámetro 'valor' se reescribe dentro de un establecedor de propiedades, y no se usa después de eso. NewAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)

 [Parameter(Mandatory = false, HelpMessage = "The integration account partner type.", ValueFromPipelineByPropertyName = false)] [ValidateSet("B2B", IgnoreCase = false)] [ValidateNotNullOrEmpty] public string PartnerType { get { return this.partnerType; } set { value = this.partnerType; } // <= } 

El campo partnerType se declara de la siguiente manera:

 /// <summary> /// Default partner type. /// </summary> private string partnerType = "B2B"; 

A pesar del nombre de la solución (LogicApp) donde se detectó un error, no encuentro lógica en él. No es raro que se modifique el valor en el setter, pero en este caso se trata de una pérdida del valor original. Se ve raro En el código la propiedad se lee solo una vez. Quizás, necesitamos pedir el consejo de expertos nuevamente. Tal vez simplemente no lo entiendo. El punto es que me encontré con varios patrones:

  • V3143 El parámetro 'valor' se reescribe dentro de un establecedor de propiedades, y no se usa después de eso. NewAzureIntegrationAccountSchemaCommand.cs 79 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un establecedor de propiedades, y no se usa después de eso. NewAzureIntegrationAccountSchemaCommand.cs 87 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un establecedor de propiedades, y no se usa después de eso. UpdateAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un establecedor de propiedades, y no se usa después de eso. UpdateAzureIntegrationAccountSchemaCommand.cs 80 (LogicApp)
  • V3143 El parámetro 'valor' se reescribe dentro de un establecedor de propiedades, y no se usa después de eso. UpdateAzureIntegrationAccountSchemaCommand.cs 88 (LogicApp)

Conclusión


Todos estos son errores interesantes que se encontraron en el código de Azure PowerShell. Los entusiastas y los interesados ​​pueden revisar los errores en este (o en cualquier otro) proyecto. Probablemente podría perder algo fuera de lo común. Para hacer la revisión, solo necesita descargar e instalar PVS-Studio .

Gracias por leer hasta el final. Y, por supuesto, ¡código sin errores para todos!

Source: https://habr.com/ru/post/470728/


All Articles