Platform logo

JetBrains Platform

Plugin and extension development for JetBrains products.

IntelliJ Platform Plugins Qodana

Como ampliar a funcionalidade do Qodana adicionando inspeções personalizadas de código

Read this post in other languages:

O Qodana é uma ferramenta de análise estática de código que traz as inspeções de código e as correções rápidas dos JetBrains IDEs para o domínio da integração contínua. Ele pode ser executado na nuvem ou a partir de um container do Docker, pode ser integrado a pipelines de CI/CD ou invocado a partir de um JetBrains IDE.

O Qodana já oferece um conjunto impressionante de inspeções, mas não se limita ao que já vem instalado. Você pode adicionar inspeções personalizadas para fazer valerem características e convenções específicas de cada projeto.

Por exemplo, imagine um projeto com uma convenção específica para o código:

Todas as classes Kotlin em um pacote service precisam ter o sufixo Service.

Nesse caso, a classe com.jetbrains.service.JetComponent não estaria de acordo com essa convenção, enquanto com.jetbrains.service.BrainComponentService estaria perfeita. A seguir, criaremos um plug-in para implementar essa inspeção, permitindo que o Qodana faça valer essa convenção em projetos futuros.

Podemos implementar essa convenção no código criando uma inspeção de código personalizada, envolta em um plug-in. Os plug-ins do Qodana são desenvolvidos da mesma forma que os dos JetBrains IDEs. Isso quer dizer que precisamos apenas criar um plug-in para a plataforma IntelliJ que possa ser executado no Qodana. Esta é uma rápida visão geral das etapas que realizaremos:

  1. Inicializar o projeto a partir de IntelliJ Platform Plugin Template.
  2. Ajustar as propriedades do projeto, o descritor do plug-in e as dependências necessárias.
  3. Declarar a inspeção local no descritor do plug-in e implementá-la no Kotlin.
  4. Fazer a build do plug-in e empacotá-lo.
  5. No projeto de exemplo de teste, colocar o artefato do plug-in em um diretório apropriado.
  6. Ajustar o arquivo de configurações do Qodana.
  7. Executar o Qodana e examinar o relatório!

Preparação do projeto do plug-in

Para inicializar o projeto, visite o repositório IntelliJ Platform Plugin Template e clique no botão Use this template para criar um repositório para o plug-in. Dê a ele o nome de classname-inspection-qodana-plugin, copie a URL do projeto e abra-a no IntelliJ IDEA. Quando o projeto estiver pronto, personalize gradle.properties declarando pluginGroup, pluginName e pluginRepositoryUrl, conforme necessário. Lembre-se de clicar no botão flutuante Sync Gradle Changes para aplicar as alterações. Para modificar o identificador exclusivo do plug-in, altere o elemento id no descritor plugin.xml.

Declaração de dependências

Nossa inspeção de código tem como alvo classes Kotlin. Então, precisamos adicionar o plug-in do Kotlin às dependências do plug-in do Qodana. O arquivo gradle.properties exige que você declare:

platformBundledPlugins = org.jetbrains.kotlin

Além disso, o descritor plugin.xml precisa conter o mesmo plug-in incorporado do Kotlin entre suas dependências:

org.jetbrains.kotlin

Mais uma vez, lembre-se de sincronizar as alterações com o Gradle, clicando no botão flutuante.

Além disso, a inspeção de classe do Kotlin precisa ter suporte ao compilador Kotlin K2, que é habilitado como padrão desde a versão 2025.1 da plataforma IntelliJ. No descritor do plug-in, declare a extensão org.jetbrains.kotlin.supportsKotlinPluginMode.

    

Criação da inspeção de código

O código em si de qualquer inspeção de código que tenha como alvo classes Kotlin requer três etapas:

  1. Declarar a extensão com.intellij.localInspection no descritor do plug-in, juntamente com os atributos necessários e uma referência totalmente qualificada à classe de implementação.
  2. Criar uma classe de implementação, de preferência em Kotlin.
  3. Fornecer um arquivo separado em HTML, contendo uma descrição da inspeção, diretrizes de uso e um exemplo.

Declaração da extensão

Adicione a seguinte declaração ao arquivo descritor plugin.xml:

    

O atributo language indica que a inspeção se aplica a arquivos de código-fonte em Kotlin. É importante manter a inspeção explicitamente habilitada, como padrão; senão, o Qodana não a executará. Depois, informe um atributo displayName, descritivo e legível por seres humanos, para ser exibido no relatório e nas configurações. O atributo groupName define a categoria da inspeção, mostrada tanto no relatório do Qodana quanto nas configurações do IDE. Por fim, dê um nome totalmente qualificado à classe de implementação.

Código-fonte da inspeção de código

O plug-in do Kotlin oferece uma classe útil como base para inspeções de Kotlin: AbstractKotlinInspection. Faça override do método buildVisitor e forneça uma instância do padrão Visitor de PSI que percorre os elementos da classe Kotlin de forma type-safe. classVisitor é uma função estilo DSL que retorna esse tipo de visitor PSI e é invocada em qualquer classe Kotlin do projeto inspecionado.

package org.intellij.sdk.codeInspection

import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.classVisitor

class ServicePackageClassNameInspection : AbstractKotlinInspection() {
    override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = classVisitor { klass ->
        val classNamePsi = klass.nameIdentifier ?: return@classVisitor
        val classFqn = klass.fqName?.asString() ?: return@classVisitor
        if (klass.packageLastComponent == "service" && !classFqn.endsWith("Service")) {
            holder.registerProblem(
                classNamePsi,
                "Class name in the 'service' package must have a 'Service' suffix"
            )
        }
    }

    private val KtClass.packageLastComponent: String
        get() = containingKtFile.packageFqName.shortName().asString()
}

A subclasse do visitor extrai o nome totalmente qualificado da classe Kotlin, inspeciona o elemento de pacote mais à direita e verifica o sufixo correspondente. Qualquer nome de classe inadequado será relatado à instância ProblemsHolder com a classe que o contém como elemento PSI e uma descrição legível do problema.

Descrição da inspeção

Toda inspeção local precisa ser acompanhada de um arquivo de descrição, representada em HTML. Se você usar a correção rápida Create description file ServicePackageClassNameInspection.html, será criado um arquivo com o nome src/main/resources/inspectionDescriptions/ServicePackageClassName.html no local apropriado. Você também terá que fornecer uma descrição, que será mostrada no relatório do Qodana e nas configurações do IDE.

Reports class names in the service packages that lack the Service suffix.

Example:

  package com.example.foo.service
  class SomeComponent {
    /* class members */
  }

Build do plug-in

Tudo pronto — é hora de fazer a build! Execute a tarefa buildPlugin do Gradle e veja o artefato build/distributions/qodana-code-inspection-0.0.1.zip, que estará disponível no diretório de saída do Gradle. O arquivo JAR será usado como artefato primário na análise do Qodana. 

Tenha em mente o tipo de artefato do plug-in

O Qodana não tem suporte direto a artefatos de plug-in em formato ZIP que incluam outros arquivos JAR ou dependências de terceiros. Todo plug-in precisa ser empacotado em um só arquivo JAR ou descomprimido em um diretório específico.

Execução do Qodana em um projeto de teste

Vamos criar um projeto de teste em Kotlin que possamos inspecionar com o Qodana, após ampliá-lo com nosso plug-in. Para executar localmente o plug-in do Qodana, certifique-se de que dois componentes de software estejam disponíveis no seu sistema:

Nosso projeto de teste precisa conter uma classe chamada src/main/kotlin/org/intellij/sdk/qodana/service/SomeComponent.kt, que não segue nossa convenção de código, pois não tem o sufixo Service. Há duas maneiras de integrar o plug-in ao Qodana:

  • Publicá-lo no JetBrains Marketplace
  • Para uma implementação mais rápida, colocar o artefato em JAR do plug-in no diretório .qodana do seu projeto.

Para simplificar a build, copie o arquivo build/distributions/qodana-code-inspection-0.0.1.zip do projeto do plug-in para o projeto de teste, como .qodana/qodana-code-inspection-0.0.1.zip. Se o diretório .qodana não existir, crie-o primeiro. Depois, extraia o arquivo com a sua ferramenta ou programa preferido. O Qodana poderá acessar o plug-in no diretório build/distributions/qodana-code-inspection.

Além disso, o Qodana precisa ser configurado para incluir nossa inspeção personalizada de código. Altere o arquivo qodana.yaml, no diretório-raiz do projeto de teste, desta forma:

version: "1.0"
linter: qodana-jvm-community
include:
  - name: org.intellij.sdk.codeInspection.ServicePackageClassNameInspection

O bloco include precisa referenciar o nome totalmente qualificado da classe da inspeção de código disponível no plug-in. Agora, execute o seguinte comando para executar o Qodana a partir do terminal:

qodana scan --volume $PWD/.qodana/qodana-code-inspection:/opt/idea/custom-plugins/qodana-code-inspection

Isso baixará a imagem do Docker correspondente ao Qodana. Será criado um container do Docker e ele será executado com base na configuração do Qodana. Para tornar o plug-in personalizado acessível durante a execução do Qodana, monte o diretório do plug-in do sistema de arquivos local no diretório apropriado dentro do container do Docker com o Qodana. Depois de alguns minutos, o Qodana gerará um resumo do relatório e o imprimirá na saída-padrão.

Qodana - Detailed summary                                                                                                                                                                                                                
Analysis results: 1 problem detected                                                                                                                                                                                                     
By severity: High - 1                                                                                                                                                                                                                    
-------------------------------------------------------                                                                                                                              
Name                           Severity  Problems count                                                                                                                                                             
-------------------------------------------------------                                                                                                                              
SDK: Discouraged class name    High      1                                                                                                                                                             
-------------------------------------------------------

Abra o relatório completo no seu navegador:

  Do you want to open the latest report [Y/n]Yes

!  Press Ctrl+C to stop serving the report

  Showing Qodana report from http://localhost:8080/... (10s)

O Qodana mostrará que SomeComponent não segue a convenção de código especificada pela nossa inspeção local no plug-in.

Dicas para execuções futuras

Se o plug-in do Qodana for modificado e tiver uma nova build, o cache do Qodana também precisará ser recriado. Nesse caso, use o parâmetro de linha de comando --clear-cache para recarregar todas as dependências de execução do Qodana.

qodana scan --clear-cache --volume $PWD/.qodana/qodana-code-inspection-0.0.1.jar:/opt/idea/custom-plugins/codeinspection.jar

Como integrar o Qodana ao seu IDE

O plug-in do Qodana pode ser instalado no IDE a partir de um disco. Sua inspeção de código será habilitada automaticamente e invocada para qualquer classe Kotlin de um projeto. Para verificar se isso está funcionando, revisite o projeto de teste, abra a classe org.intellij.sdk.qodana.service.SomeComponent e veja se o nome problemático da classe está sublinhado. Por conveniência, ao passar o mouse sobre o nome da classe, aparecerá o resultado da inspeção de código, juntamente com a descrição do problema. Como alternativa, você pode abrir a janela de ferramentas Problems e localizar o alerta na lista de todos os problemas relatados por inspeções de código. 

Agora a inspeção de código se comporta como qualquer inspeção fornecida pelo IDE. Em Settings | Editor | Inspections | Kotlin, você encontrará a inspeção SDK: Discouraged class name, juntamente com a descrição obtida do arquivo em HTML que fornecemos antes.

Como executar o Qodana a partir do IDE

Agora que o plug-in está instalado, você também pode executar o Qodana a partir do seu IDE. Na janela de ferramentas Problems, vá até a aba Qodana e clique no botão Try Locally. O Qodana será executado com a configuração do arquivo qodana.yaml. O relatório do Qodana poderá ser encontrado diretamente na janela de ferramentas. 

Os plug-ins do Qodana e o JetBrains Marketplace

Plug-ins de inspeções personalizadas que tenham tido builds corretas e testes apropriados podem ser publicados no JetBrains Marketplace, eliminando a necessidade de servi-los a partir do diretório .qodana. Você só precisa se certificar de que a configuração do Qodana especifique um identificador público de plug-in igual à do elemento id do descritor do plug-in.

version: "1.0"
linter: qodana-jvm-community
plugins: 
  - id: com.github.novotnyr.qodanacodeinspection

O comando scan do Qodana fica mais simples, pois não é mais necessário montar o diretório .qodana.

qodana scan

O Qodana baixará o plug-in do JetBrains Marketplace e executará todas as suas inspeções, gerando tanto uma saída no console quanto um relatório em HTML, que poderá ser exibido em um navegador de Web.

Resumo

Aqui criamos um plug-in para o Qodana com uma inspeção de código que verifica uma convenção específica do código. Há diversas maneiras de executar esse plug-in:

  • Como um arquivo JAR colocado no diretório .qodana e incluído no arquivo “qodana.yaml”.
  • Como uma referência a um plug-in disponível publicamente no JetBrains Marketplace.
  • Como um arquivo JAR instalado no IDE, com a inspeção sendo aplicada em uma execução local do Qodana.
  • Como um arquivo JAR instalado no IDE, com a inspeção de código incluída entre as integradas. 

Acesse os exemplos de código do IntelliJ SDK para ver exemplos concisos de plug-ins para o Qodana e um projeto de teste.

Artigo original em inglês por:

Róbert Novotný

Róbert Novotný

Róbert Novotný is a Developer Advocate, specializing in the IntelliJ Platform and plugin development.

image description