Azure PowerShell: Principalmente inofensivo

Quadro 6

Olá pessoal. Hoje temos outro projeto da Microsoft em cheque. Pelo título deste artigo, você pode supor que desta vez os desenvolvedores não "nos agradaram" com um grande número de erros. Esperamos que os autores do projeto não se ofendam com o título. Afinal, um pequeno número de erros é ótimo, não é? No entanto, ainda conseguimos encontrar algo intrigante no código do Azure PowerShell. Sugerimos conhecer os recursos deste projeto e verificar os erros encontrados no analisador PVS-Studio C #.

Sobre o projeto


O Azure PowerShell é um conjunto de comandos ( cmdlet ) que permite gerenciar os recursos do Azure diretamente na linha de comando do PowerShell . O principal objetivo deste conjunto é simplificar o processo de estudo e iniciar rapidamente o desenvolvimento do Azure. O Azure PowerShell fornece aos administradores e desenvolvedores recursos atraentes para criar, implantar e gerenciar aplicativos do Microsoft Azure.

O Azure PowerShell é desenvolvido no ambiente .NET Standard, com suporte do PowerShell 5.1 para Windows e PowerShell 6.xe posterior para todas as plataformas. O código-fonte do Azure PowerShell está disponível no GitHub.

Recentemente, tenho recebido frequentemente projetos da Microsoft para verificação. Na minha opinião, a qualidade desses projetos geralmente é de primeira. Embora, é claro, não sem exceções, conforme descrito no artigo " WinForms: Erros, Holmes ". Mas desta vez está tudo bem. O projeto é grande: 6845 arquivos de código-fonte .cs contêm aproximadamente 700.000 linhas, excluindo as em branco (não levei em consideração testes e avisos do terceiro nível de certeza). Foram encontrados muito poucos erros para essa quantidade de código: não mais que cem. Havia muitos casos semelhantes, então escolhi os mais interessantes para o artigo. Como sempre, classifiquei os erros pelos números dos avisos do PVS-Studio.

Também me deparei com alguns fragmentos de código que pareciam erros, mas não podiam ser reconhecidos como definitivamente errôneos, pois não estou familiarizado o suficiente com as peculiaridades de desenvolvimento do PowerShell. Espero que entre os leitores haja especialistas nesta edição que me ajudarão. Vou descrevê-lo em detalhes abaixo.

Antes da parte do feedback, gostaria de mencionar a estrutura específica do projeto. O código-fonte do Azure PowerShell consiste em mais de setenta soluções do Visual Studio. Algumas soluções incluem projetos de outras. Essa estrutura atrasou um pouco a análise (não muito). Ainda assim, o cheque não causou outras dificuldades. Por conveniência, na mensagem de erro (entre colchetes), especificarei o nome da solução em que o erro foi encontrado.

Resultados da análise


V3001 Existem sub-expressões idênticas 'strTimespan.Contains ("M")' à esquerda e à direita da '||' 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")) .... } 

Um exemplo de um erro bastante óbvio que apenas um desenvolvedor pode corrigir. Nesse caso, não está absolutamente claro se lidamos com a duplicação de código que afeta nada ou algo mais deve ocorrer em vez de "M" em uma das duas últimas verificações.

V3001 Existem subexpressões idênticas 'this.AggregationType! = Null' à esquerda e à direita do 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; .... } 

Provavelmente não há erro aqui. Este é um exemplo de código redundante. Às vezes, esse código pode indicar falta de conhecimento do desenvolvedor. O ponto é que as verificações this.AggregationType! = Null e this.AggregationType.HasValue são idênticas. Basta usar apenas um deles (qualquer um). Pessoalmente, prefiro a opção com HasValue:

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

V3003 O uso do padrão 'if (A) {...} else if (A) {...}' foi detectado. Há uma probabilidade de presença de erro lógico. Verifique as linhas: 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 ); } .... } 

Duas outras se os blocos são absolutamente idênticos, incluindo a condição e o corpo do bloco. Esses erros geralmente são cometidos ao usar o método copiar e colar. A questão aqui é novamente a criticidade do erro. Se não for uma duplicação simples de código, pode ser a verificação necessária ausente e o conjunto de ações apropriado. O autor definitivamente precisa editar o código.

V3005 A variável 'this.VM.OSProfile.WindowsConfiguration.ProvisionVMAgent' é atribuída a si mesma. SetAzureVMOperatingSystemCommand.cs 298 (Compute)

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

O valor da propriedade é auto-atribuído. Veja a sua declaração:

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

A descrição JsonProperty afirma: "Instrui o Newtonsoft.Json.JsonSerializer para sempre serializar o membro com o nome especificado." Parece que tudo é inocente e o erro óbvio foi cometido. O uso explícito disso para acessar a propriedade também é bastante confuso. Talvez outra variável não tenha sido especificada por engano em vez disso. Mas não vamos tirar conclusões precipitadas. O fato é que me deparei com muitas dessas atribuições (uma propriedade é auto-atribuída). Aqui está um exemplo de uma atribuição, muito semelhante a um erro:

V3005 A variável 'this.LastHeartbeat' é atribuída a si mesma. PSFabricDetails.cs 804 (RecoveryServices)

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

Vamos dar uma olhada mais de perto na segunda e subseqüentes atribuições. Na parte correta da expressão, os detalhes ocorrem em vez disso. Agora observe a declaração da propriedade this.LastHeartbeat :
 public DateTime? LastHeartbeat { get; set; } 

Por fim, vamos encontrar a propriedade com o mesmo nome na classe InMageAzureV2ReplicationDetails . Essa propriedade é declarada lá:

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

Bem, neste caso, estou disposto a admitir que é um erro real. Mas o que devemos fazer com os próximos avisos? Ao contrário de dois fragmentos de código anteriores, existem várias propriedades auto-atribuídas. Bem, isso parece menos com um erro:

  • V3005 A variável 'this.ResourceGroupName' é atribuída a si mesma. RemoveAzureRmExpressRouteConnectionCommand.cs 84 (CognitiveServices)
  • V3005 A variável 'this.ExpressRouteGatewayName' é atribuída a si mesma. RemoveAzureRmExpressRouteConnectionCommand.cs 85 (CognitiveServices)
  • V3005 A variável 'this.Name' é atribuída a si mesma. 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; } .... } .... } 

O método Execute contém auto-atribuições de três propriedades em uma linha. Por exemplo, citei a declaração completa da classe RemoveExpressRouteConnectionCommand e todos os seus atributos, bem como a declaração da propriedade ResourceGroupName (outras duas propriedades são declaradas de maneira semelhante). Foram esses avisos que me fizeram pensar na pergunta: "É um erro?" Suspeito que alguma mágica interna do desenvolvimento do PowerShell possa estar acontecendo aqui. Espero que entre os leitores haja especialistas que sejam informados sobre esse assunto. Não estou pronto para tirar conclusões neste caso.

V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 259 (RecoveryServices)

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

A palavra-chave throw é omitida. E o comentário diz que a exceção precisa ser lançada. Encontrei vários erros semelhantes na solução RecoveryServices :

  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrTestFailoverJob.cs 305 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 278 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). StartAzureRmRecoveryServicesAsrUnPlannedFailover.cs 322 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 421 (RecoveryServices)
  • V3006 O objeto foi criado, mas não está sendo usado. A palavra-chave 'throw' pode estar ausente: throw new ArgumentException (FOO). UpdateAzureRmRecoveryServicesAsrProtectionDirection.cs 452 (RecoveryServices)

A expressão V3022 'apiType.HasValue' é sempre 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"); } 

A lógica do trabalho foi quebrada. Se apiType contiver um valor, o controle não alcançará a expressão de retorno no final do método (todas as ramificações da opção contêm retorno ). Caso contrário, o método sempre retornará PsApiManagementApiType.Http.ToString ("g") , enquanto o valor apiType.Value.ToString ("g") nunca será retornado.

A expressão V3022 'automationJob! = Null && automationJob == null' é sempre falsa. NodeConfigurationDeployment.cs 199 (automação)

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

Código contra-intuitivo. Dois cheques que se contradizem. Provavelmente, a segunda verificação de nulo contém a variável errada.

A expressão V3022 é sempre 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"); } .... } 

O cheque é inútil e a exceção nunca será lançada. A condição requer a igualdade simultânea da variável linkedServiceType para três valores diferentes. Os operadores && e || provavelmente ficarão confusos. Código fixo:

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

A expressão V3022 'Ekus == null' é sempre falsa. PSKeyVaultCertificatePolicy.cs 129 (KeyVault)

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

Verificação redundante da variável Ekus para null . Provavelmente está bem, mas o código não parece bom.

V3023 Considere inspecionar esta expressão. A expressão é excessiva ou contém uma impressão incorreta. PolicyRetentionObjects.cs 207 (RecoveryServices)

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

Aqui está uma verificação excessiva ou uma condição excessiva. A verificação RetentionTimes.Count == 0 é inútil, pois depois disso, a verificação RetentionTimes.Count! = 1 é a seguir.

V3025 Formato incorreto. É esperado um número diferente de itens de formato ao chamar a função 'Format'. Argumentos não 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))) { .... } .... } 

Um erro na linha de formatação. O especificador {0} é usado duas vezes e o método Format recebe dois argumentos. Aqui está a versão correta:

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

Outro erro semelhante:

  • V3025 Formato incorreto. É esperado um número diferente de itens de formato ao chamar a função 'Format'. Argumentos não utilizados: this.ResourceGroupName. RemoveScheduledQueryRuleCommand.cs 88 (Monitor)

V3042 Possível NullReferenceException. O '?' e '.' operadores são usados ​​para acessar membros do objeto 'imageAndOsType' VirtualMachineScaleSetStrategy.cs 81 (Compute)

 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) // <= }, }, .... } 

Ao criar o objeto VirtualMachineScaleSetVMProfile , a variável imageAndOsType é verificada como nula sem nenhuma verificação preliminar. No entanto, além disso, ao criar o VirtualMachineScaleSetStorageProfile , essa variável já é verificada usando o operador de acesso condicional mesmo duas vezes. O código não parece seguro.

V3042 Possível NullReferenceException. O '?' e '.' operadores são usados ​​para acessar membros do 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(....); // <= } .... } 

Na execução normal e como resultado do tratamento de uma exceção, a variável existenteContacts pode obter o valor nulo , após o qual a execução continuará. Além disso, no código, essa variável é usada sem nenhum motivo específico.

V3066 Possível ordem incorreta de argumentos passada para o método 'PersistSyncServerRegistration': 'storageSyncServiceUid' e '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); } .... } 

O analisador suspeitou que a ordem dos argumentos do método PersistSyncServerRegistration estivesse confusa. Declaração do 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 fato, algo está errado aqui com os argumentos número sete e oito. O autor deve verificar o código.

V3077 O configurador da propriedade 'GetGuid' não utiliza seu parâmetro 'value'. RecoveryServicesBackupCmdletBase.cs 54 (RecoveryServices)

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

O setter não usa o parâmetro passado. Em vez disso, ele cria um novo GUID e o atribui ao campo _guid . Eu acho que a maioria dos leitores concorda que esse código parece pelo menos feio. Essa construção não é muito conveniente de usar: ao (re) inicializar a propriedade GetGuid , é necessário atribuir um valor falso a ela, o que não é muito óbvio. Mas, acima de tudo, fiquei divertido com a maneira como os autores usavam esse padrão. Há apenas um lugar onde o GetGuid é tratado. Confira:

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

Brilhante!

V3091 Análise empírica. É possível que um erro de digitação esteja presente dentro da string literal: "ID do grupo de gerenciamento". A palavra 'Id' é suspeita. 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"; } 

O analisador apontou para um possível erro na sequência atribuída para a constante GroupName . A conclusão é baseada na análise empírica de outras atribuições, levando em consideração os nomes das variáveis. Penso que, neste caso, o analisador está certo e o valor das constantes GroupName deve ser uma espécie de "nome do grupo de gerenciamento". Provavelmente, o erro ocorreu devido ao fato de o valor da constante GroupId ter sido copiado, mas não alterado.

Outro erro semelhante:

  • V3091 Análise empírica. É possível que um erro de digitação esteja presente dentro da string literal. A palavra 'Nome' é suspeita. ParamHelpMsgs.cs 153 (RecoveryServices)

V3093 O '|' O operador avalia os dois operandos. Talvez um curto-circuito '||' operador deve ser usado. PSKeyVaultCertificatePolicy.cs 114 (KeyVault)

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

Nesse fragmento, pode ocorrer um erro e no bloco if entre as duas últimas condições, o || operador pode ter sido usado. Mas, como costuma acontecer, apenas o desenvolvedor pode dar a resposta certa.

V3095 O objeto 'certificado' foi usado antes de ser verificado em relação a nulo. Verifique as linhas: 41, 43. CertificateInfo.cs 41 (Automação)

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

Classic. Primeiro, o objeto é usado e somente depois disso a referência é verificada como nula . Encontramos esses erros com muita frequência . Vamos considerar outro erro semelhante.

V3095 O objeto 'clusterCred' foi usado antes de ser verificado em relação a nulo. Verifique as linhas: 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) .... } 

Aqui estão alguns erros semelhantes:

  • V3095 O objeto '_profile' foi usado antes de ser verificado contra nulo. Verifique as linhas: 47, 49. RMProfileClient.cs 47 (Contas)
  • V3095 O objeto 'this.LoadBalancer.BackendAddressPools' foi usado antes de ser verificado como nulo. Verifique as linhas: 56, 63. AddAzureRmLoadBalancerBackendAddressPoolConfigCommand.cs 56 (CognitiveServices)
  • De um modo geral, vi muitos erros da V3095 no código do Azure PowerShell. Mas todos eles são bem parecidos, então não vou me debruçar sobre esse assunto.

V3125 O objeto 'startTime' foi usado após a verificação contra nulo. Verifique as linhas: 1752, 1738. AutomationPSClientDSC.cs 1752 (Automation)

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

Também é um tipo bastante amplo de erros. A variável startTime é verificada quanto à presença de valor quando usada pela primeira vez. Mas isso não é feito no uso subsequente. Bem, a situação pode ser ainda pior. Olhe para o segundo se bloco. Eu acho que a variável startTime não deve estar aqui. Em primeiro lugar, não há verificação de presença de valor antes de seu uso. Em segundo lugar, a string formada para ser passada para o método Add também confirma minha sugestão. Outra variável (lastModifiedTime ) é mencionada na primeira parte dessa string.

V3125 O objeto 'firstPage' foi usado depois de verificado contra nulo. Verifique as linhas: 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)) // <= { .... } .... } 

Outro erro óbvio. A variável firstPage é usada sem segurança, apesar do fato de que anteriormente no código essa variável já é usada, sendo verificada preliminarmente como nula .

Encontrei ainda mais avisos da V3125 no código do Azure PowerShell que os da V3095 descritos acima. Todos eles também são do mesmo tipo. Eu acho que dois deles que consideramos são suficientes.

V3137 A variável 'apiVersionSetId' é atribuída, mas não é usada no final da função. 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)) // <= } } 

O analisador relata que a variável local apiVersionSetId foi inicializada, mas não utilizada de forma alguma. Geralmente, esse padrão indica um erro. Eu acho que, nesse caso, é mais provável que seja um erro, especialmente levando em conta o fato de que o nome da variável local apiVersionSetId e o nome da propriedade ApiVersionSetId diferem apenas no caso da primeira letra. Dê uma olhada no código. Depois de inicializar a propriedade apiVersionSetId (de uma maneira ou de outra), somente a propriedade ApiVersionSetId é usada ainda mais no código. Parece extremamente suspeito.

V3137 A variável 'cacheId' é atribuída, mas não é usada no final da função. 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 é o caso quase igual ao descrito anteriormente. A variável local cacheId não é usada após a inicialização de forma alguma. Em vez disso, outra propriedade com um nome muito semelhante CacheId é usada. Não sei ao certo, pode ser que seja apenas um padrão de programação dos desenvolvedores do Azure PowerShell. Enfim, parece um erro.

V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. 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; } // <= } 

O campo partnerType é declarado da seguinte maneira:

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

Apesar do nome da solução (LogicApp) em que um erro foi detectado, não encontro lógica. Modificar valor no setter não é uma ocorrência rara, mas, neste caso, lida com a perda do valor original. Parece estranho. No código, a propriedade é lida apenas uma vez. Talvez, tenhamos que pedir novamente conselhos de especialistas. Talvez eu simplesmente não entenda. O ponto é que me deparei com vários mesmos padrões:

  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. NewAzureIntegrationAccountSchemaCommand.cs 79 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. NewAzureIntegrationAccountSchemaCommand.cs 87 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. UpdateAzureIntegrationAccountPartnerCommand.cs 67 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. UpdateAzureIntegrationAccountSchemaCommand.cs 80 (LogicApp)
  • V3143 O parâmetro 'value' é reescrito dentro de um configurador de propriedades e não é usado depois disso. UpdateAzureIntegrationAccountSchemaCommand.cs 88 (LogicApp)

Conclusão


Esses são todos os erros interessantes encontrados no código do Azure PowerShell. Entusiastas e interessados ​​podem revisar os próprios erros neste (ou em qualquer outro) projeto. Eu provavelmente poderia perder algo excêntrico. Para fazer a revisão, você só precisa baixar e instalar o PVS-Studio .

Obrigado por ler até o fim. E, é claro, código sem erros para todos!

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


All Articles