Esta é uma pequena história sobre como o PVS-Studio nos ajudou a encontrar um erro no código fonte da biblioteca usada no PVS-Studio. E não foi um erro teórico, mas um erro real - o erro apareceu na prática ao usar a biblioteca no analisador.
No PVS-Studio_Cmd (assim como em alguns outros utilitários), usamos uma biblioteca especial para analisar argumentos de linha de comando - CommandLine.
Hoje eu apoiei o novo modo no PVS-Studio_Cmd e aconteceu que eu tive que usar essa biblioteca para analisar argumentos de linha de comando. Ao escrever o código, eu também o depuro porque tenho que trabalhar com APIs desconhecidas.
Então, o código é escrito, compilado, executado e ...
A execução do código entra na biblioteca em que ocorre uma exceção do tipo
NullReferenceException . Não é tão claro do lado - não passo nenhuma referência nula no método.
Para ter certeza, eu olho para os comentários do método chamado. É pouco provável que eles descrevam as condições de ocorrência de uma exceção do tipo
NullReferenceException (como me parece geralmente não há exceções desse tipo).
Não há informações sobre
NullReferenceException nos comentários do método (o que, no entanto, é esperado).
Para ver o que exatamente causa a exceção (e onde ela ocorre), decidi baixar o código-fonte do projeto, construí-lo e adicionar uma referência à versão de depuração da biblioteca no analisador. O código fonte do projeto está
disponível no GitHub . Precisamos da versão 1.9.71 da biblioteca. É o usado no analisador agora.
Faço o download da versão correspondente do código fonte, construo a biblioteca, adiciono uma referência à biblioteca de depuração no analisador, executo o código e consulte:
Portanto, o local onde a exceção ocorre é clara -
helpInfo possui um valor
nulo , o que causa uma exceção do tipo
NullReferenceException ao acessar a propriedade
Left .
Comecei a pensar nisso. Recentemente, o PVS-Studio for C # foi bem aprimorado em vários aspectos, incluindo a busca por desreferenciamento de referências potencialmente nulas. Em particular, a análise interprocedural foi aprimorada de várias maneiras. Por isso, fiquei imediatamente interessado em verificar o código fonte para entender se o PVS-Studio poderia encontrar o erro em discussão.
Eu verifiquei o código fonte e, entre outros avisos, vi exatamente o que esperava.
Aviso do PVS-Studio :
V3080 Possível desreferência nula dentro do método em 'helpInfo.Left'. Considere inspecionar o segundo argumento: helpInfo. Parser.cs 405
Sim, é isso! É exatamente disso que precisamos. Vamos dar uma olhada mais detalhada no código fonte.
private bool DoParseArgumentsVerbs( string[] args, object options, ref object verbInstance) { var verbs = ReflectionHelper.RetrievePropertyList<VerbOptionAttribute>(options); var helpInfo = ReflectionHelper.RetrieveMethod<HelpVerbOptionAttribute>(options); if (args.Length == 0) { if (helpInfo != null || _settings.HelpWriter != null) { DisplayHelpVerbText(options, helpInfo, null);
O analisador emite um aviso para chamar o método
DisplayHelpVerbText e avisa sobre o segundo argumento -
helpInfo . Preste atenção que este método está localizado na ramificação
então da instrução
if . A expressão condicional é composta de tal maneira que o ramo
then pode ser executado nos próximos valores das variáveis:
- helpInfo == null ;
- _settings.HelpWriter! = null ;
Vamos ver o corpo do método
DisplayHelpVerbText :
private void DisplayHelpVerbText( object options, Pair<MethodInfo, HelpVerbOptionAttribute> helpInfo, string verb) { string helpText; if (verb == null) { HelpVerbOptionAttribute.InvokeMethod(options, helpInfo, null, out helpText); } else { HelpVerbOptionAttribute.InvokeMethod(options, helpInfo, verb, out helpText); } if (_settings.HelpWriter != null) { _settings.HelpWriter.Write(helpText); } }
Como o
verbo == null (veja a chamada do método), estamos interessados no ramo da instrução
if . Embora a situação seja semelhante ao ramo
else , vamos considerar o ramo porque, no nosso caso particular, a execução passou por ele. Lembre-se de que
helpInfo pode ser
nulo .
Agora vamos ver o corpo do
HelpVerbOptionAttribute . Método
InvokeMethod . Na verdade, você já viu na captura de tela acima:
internal static void InvokeMethod( object target, Pair<MethodInfo, HelpVerbOptionAttribute> helpInfo, string verb, out string text) { text = null; var method = helpInfo.Left; if (!CheckMethodSignature(method)) { throw new MemberAccessException( SR.MemberAccessException_BadSignatureForHelpVerbOptionAttribute .FormatInvariant(method.Name)); } text = (string)method.Invoke(target, new object[] { verb }); }
helpInfo.Left é chamado incondicionalmente, enquanto
helpInfo pode ser
nulo . O analisador alertou sobre isso, e foi o que aconteceu.
ConclusãoÉ bom que tenhamos encontrado um erro no código fonte da biblioteca usada no PVS-Studio com a ajuda do PVS-Studio. Acho que esse é um tipo de resposta para a pergunta "O PVS-Studio encontra erros no código-fonte do PVS-Studio?". :) O analisador pode encontrar erros não apenas no código do PVS-Studio, mas também no código das bibliotecas usadas.
Por fim, sugiro que você
baixe o analisador e tente verificar seu projeto - e se você também encontrar algo interessante lá?