Saltar al contenido principal

Personalización del análisis estático

Usa un archivo de opciones de análisis y comentarios en el código para personalizar el análisis estático.

El análisis estático te permite encontrar problemas antes de ejecutar una sola línea de código. Es una herramienta poderosa que se usa para prevenir errores y garantizar que el código se ajuste a las pautas de estilo.

Con la ayuda de analyzer, puedes encontrar errores tipográficos simples. Por ejemplo, tal vez se coló un punto y coma accidental en una sentencia if:

El analyzer también puede ayudarte a encontrar problemas más sutiles. Por ejemplo, tal vez hayas olvidado cerrar un método sink:

En el ecosistema de Dart, el Dart Analysis Server y otras herramientas usan el paquete analyzer para realizar análisis estáticos.

Puedes personalizar el análisis estático para buscar una variedad de problemas potenciales, incluyendo errores y advertencias especificados en la especificación del lenguaje Dart. También puedes configurar reglas del linter para asegurar que tu código cumpla con la Guía de estilo de Dart y otras pautas sugeridas en Effective Dart. Herramientas como dart analyze, flutter analyze, y IDEs y editores usan el paquete analyzer para evaluar tu código.

Este documento explica cómo personalizar el comportamiento del analyzer usando ya sea un archivo de opciones de análisis o comentarios en el código fuente de Dart. Si deseas agregar análisis estático a tu herramienta, consulta los documentos del paquete analyzer y la Especificación de la API de Analysis Server.

El archivo de opciones de análisis

#

Coloca el archivo de opciones de análisis, analysis_options.yaml, en la raíz del paquete, en el mismo directorio que el archivo pubspec.

Aquí tienes un ejemplo de archivo de opciones de análisis:

analysis_options.yaml
yaml
include: package:lints/recommended.yaml

analyzer:
  exclude: [build/**]
  language:
    strict-casts: true
    strict-raw-types: true

linter:
  rules:
    - cancel_subscriptions

El ejemplo ilustra las entradas de nivel superior más comunes:

Si el analyzer no puede encontrar un archivo de opciones de análisis en la raíz del paquete, sube por el árbol de directorios buscando uno. Si no hay ningún archivo disponible, el analyzer usará por defecto las comprobaciones estándar.

Considera la siguiente estructura de directorios para un proyecto grande:

la raíz del proyecto contiene analysis_options.yaml (#1) y 3 paquetes, uno de los cuales (my_package) contiene un archivo analysis_options.yaml (#2).

El analyzer usa el archivo #1 para analizar el código en my_other_package y my_other_other_package, y el archivo #2 para analizar el código en my_package.

Habilitar comprobaciones de tipo más estrictas

#

Si deseas comprobaciones estáticas más estrictas de lo que requiere el sistema de tipos de Dart, considera habilitar los modos de lenguaje strict-casts, strict-inference y strict-raw-types:

analysis_options.yaml
yaml
analyzer:
  language:
    strict-casts: true
    strict-inference: true
    strict-raw-types: true

Puedes usar los modos juntos o por separado; todos tienen por defecto el valor false.

strict-casts: <bool>

Un valor de true asegura que el motor de inferencia de tipos nunca realice una conversión implícita (cast) de dynamic a un tipo más específico. El siguiente código Dart válido incluye una conversión implícita hacia abajo del valor dynamic devuelto por jsonDecode a List<String> que podría fallar en tiempo de ejecución. Este modo reporta el error potencial, requiriendo que agregues un cast explícito o ajustes tu código de otra manera.

análisis estático: fallo dart
void foo(List<String> lines) {
  ...
}

void bar(String jsonText) {
  foo(jsonDecode(jsonText)); // Implicit cast
}
error - The argument type 'dynamic' can't be assigned to the parameter type 'List<String>'. - argument_type_not_assignable
strict-inference: <bool>

Un valor de true asegura que el motor de inferencia de tipos nunca elija el tipo dynamic cuando no pueda determinar un tipo estático. El siguiente código Dart válido crea un Map cuyo argumento de tipo no se puede inferir, lo que resulta en una advertencia de falla de inferencia por este modo:

análisis estático: fallo dart
final lines = {}; // Inference failure
lines['Dart'] = 10000;
lines['C++'] = 'one thousand';
lines['Go'] = 2000;
print('Lines: ${lines.values.reduce((a, b) => a + b)}'); // Runtime error
warning - The type argument(s) of 'Map' can't be inferred - inference_failure_on_collection_literal
strict-raw-types: <bool>

Un valor de true asegura que el motor de inferencia de tipos nunca elija el tipo dynamic cuando no pueda determinar un tipo estático debido a argumentos de tipo omitidos. El siguiente código Dart válido tiene una variable List con un tipo crudo (raw type), lo que resulta en una advertencia de tipo crudo por este modo:

análisis estático: fallo dart
List numbers = [1, 2, 3]; // List with raw type
for (final n in numbers) {
  print(n.length); // Runtime error
}
warning - The generic type 'List<dynamic>' should have explicit type arguments but doesn't - strict_raw_type

Habilitar y deshabilitar reglas del linter

#

El paquete analyzer también proporciona un linter de código. Hay una amplia variedad de reglas del linter disponibles. Los linters tienden a ser no sectarios: las reglas no tienen que estar de acuerdo entre sí. Por ejemplo, algunas reglas son más apropiadas para paquetes Dart regulares y otras están diseñadas para aplicaciones Flutter. Ten en cuenta que las reglas del linter pueden tener falsos positivos, a diferencia del análisis estático.

Habilitar las reglas del linter recomendadas por el equipo de Dart

#

El equipo de Dart proporciona dos conjuntos de reglas del linter recomendadas en el paquete lints:

Reglas core

Ayuda a identificar problemas críticos que probablemente provoquen problemas al ejecutar o consumir código Dart. Todo el código debería pasar estas reglas del linter. Los paquetes que se suben a pub.dev tienen una puntuación de paquete (package score) que se basa en parte en pasar estas reglas.

Reglas recomendadas

Ayuda a identificar problemas adicionales que podrían provocar problemas al ejecutar o consumir código Dart, y hace cumplir un único estilo y formato idiomático. Recomendamos que todo el código Dart use estas reglas, las cuales son un superconjunto de las reglas core.

Para habilitar cualquiera de los conjuntos de lints, agrega el paquete lints como una dev dependency:

dart pub add --dev lints

Luego edita tu archivo analysis_options.yaml para incluir tu conjunto de reglas preferido:

yaml
include: package:lints/<RULE_SET>.yaml

Por ejemplo, puedes incluir el conjunto de reglas recomendadas así:

yaml
include: package:lints/recommended.yaml

Habilitar reglas individuales

#

Para habilitar una sola regla del linter, agrega linter: al archivo de opciones de análisis como una clave de nivel superior, seguida de rules: como una clave de segundo nivel. En las líneas siguientes, especifica las reglas que deseas aplicar, precedidas por guiones (la sintaxis para una lista YAML). Por ejemplo:

yaml
linter:
  rules:
    - always_declare_return_types
    - annotate_redeclares
    - cancel_subscriptions
    - close_sinks
    - combinators_ordering
    - comment_references
    - invalid_case_patterns
    - one_member_abstracts
    - only_throw_errors

Deshabilitar reglas individuales

#

Si incluyes un archivo de opciones de análisis como el de lints, es posible que desees deshabilitar algunas de las reglas incluidas. Deshabilitar reglas individuales es similar a habilitarlas, pero requiere el uso de un mapa en lugar de una lista como valor para la entrada rules:, por lo que cada línea debe contener el nombre de una regla seguido de : false o : true.

Aquí tienes un ejemplo de un archivo de opciones de análisis que usa todas las reglas recomendadas de lints excepto avoid_shadowing_type_parameters. También habilita el lint await_only_futures:

analysis_options.yaml
yaml
include: package:lints/recommended.yaml

linter:
  rules:
    avoid_shadowing_type_parameters: false
    await_only_futures: true

Incluir opciones compartidas

#

Un archivo de opciones de análisis puede incluir opciones que están especificadas en otro archivo de opciones, o incluso una lista de otros archivos de opciones. Puedes especificar dichos archivos usando el campo de nivel superior include: field:

analysis_options.yaml
yaml
include: package:flutter_lints/recommended.yaml

Un archivo de opciones incluido se puede especificar con una ruta package:, o una ruta relativa. Se pueden especificar múltiples archivos de opciones de análisis en una lista:

analysis_options.yaml
yaml
include:
  - package:flutter_lints/recommended.yaml
  - ../team_options.yaml

Las opciones en un archivo incluido pueden ser sobrescritas en el archivo que lo incluye, así como por los archivos incluidos posteriores. En otras palabras, las opciones especificadas por un archivo de opciones de análisis se calculan aplicando primero las opciones especificadas en cada uno de los archivos incluidos (aplicando recursivamente este algoritmo), en el orden en que aparecen en la lista, y luego sobrescribiéndolas con cualquier opción definida localmente.

Por ejemplo, dados los siguientes archivos de opciones:

three.yaml
yaml
include: two.yaml
# ...

Y un archivo de opciones final que incluye estos:

analysis_options.yaml
yaml
include:
  - one.yaml
  - three.yaml
# ...

Luego, las opciones de análisis combinadas se calculan aplicando las opciones que se encuentran en one.yaml, luego two.yaml, luego three.yaml y finalmente analysis_options.yaml.

Habilitar plugins de analyzer (experimental)

#

El analyzer tiene soporte experimental para plugins heredados. Estos plugins se integran con el analyzer para agregar funcionalidad como nuevos diagnósticos, soluciones rápidas (quick fixes) y completado de código personalizado. Puedes habilitar solo un plugin por archivo analysis_options.yaml. Habilitar un plugin de analyzer heredado aumenta la cantidad de memoria que usa el analyzer.

No uses plugins de analyzer heredados si tu situación cumple con alguna de las siguientes condiciones:

  • Usas una máquina de desarrollo con menos de 16 GB de memoria.
  • Usas un monorrepisitorio (mono-repo) con más de 10 archivos pubspec.yaml y analysis_options.yaml.

Puedes encontrar algunos plugins de analyzer heredados en pub.dev.

Para habilitar un plugin heredado:

  1. Agrega el paquete que contiene el plugin como una dev dependency.

    dart pub add --dev <your_favorite_analyzer_plugin_package>
    
  2. Edita tu archivo analysis_options.yaml para habilitar el plugin.

    yaml
    analyzer:
      plugins:
        - your_favorite_analyzer_plugin_package
    

    Para indicar la funcionalidad específica del plugin a habilitar, como nuevos diagnósticos, podría requerirse una configuración adicional.

Excluir código del análisis

#

A veces está bien que algún código falle en el análisis. Por ejemplo, es posible que dependas de código generado por un paquete que no te pertenece: el código generado funciona, pero produce advertencias durante el análisis estático. O una regla del linter podría causar un falso positivo que deseas suprimir.

Tienes algunas formas de excluir código del análisis:

  • Excluir archivos completos del análisis.
  • Evitar que se apliquen reglas específicas que no sean de error a archivos individuales.
  • Evitar que se apliquen reglas específicas que no sean de error a líneas de código individuales.

También puedes deshabilitar reglas específicas para todos los archivos o cambiar la severidad de las reglas.

Excluir archivos

#

Para excluir archivos del análisis estático, usa la opción del analyzer exclude:. Puedes listar archivos individuales o usar la sintaxis de patrón glob. Todos los usos de patrones glob deben ser relativos al directorio que contiene el archivo analysis_options.yaml.

yaml
analyzer:
  exclude:
    - lib/client.dart
    - lib/server/*.g.dart
    - test/_data/**

Suprimir diagnósticos para un archivo

#

Para ignorar un diagnóstico específico que no sea de error para un archivo específico, agrega un comentario ignore_for_file al archivo:

dart
// ignore_for_file: unused_local_variable

Esto actúa para todo el archivo, antes o después del comentario, y es particularmente útil para el código generado.

Para suprimir más de un diagnóstico, usa una lista separada por comas:

dart
// ignore_for_file: unused_local_variable, duplicate_ignore, dead_code

Para suprimir todas las reglas del linter, agrega un especificador type=lint:

dart
// ignore_for_file: type=lint

Suprimir diagnósticos para una línea de código

#

Para suprimir un diagnóstico específico que no sea de error en una línea específica de código Dart, coloca un comentario ignore encima de la línea de código. Aquí tienes un ejemplo de cómo ignorar código que causa un error en tiempo de ejecución, como podrías hacer en una prueba de lenguaje:

dart
// ignore: invalid_assignment
int x = '';

Para suprimir más de un diagnóstico, proporciona una lista separada por comas:

dart
// ignore: invalid_assignment, const_initialized_with_non_constant_value
const x = y;

Alternativamente, añade el comentario de ignorar al final de la línea a la que se aplica:

dart
int x = ''; // ignore: invalid_assignment

Suprimir diagnósticos en un archivo pubspec

#

Si necesitas suprimir un diagnóstico que no sea de error del analyzer en un archivo pubspec.yaml, agrega un comentario ignore encima de la línea afectada.

El siguiente ejemplo ignora el lint sort_pub_dependencies ya que desea colocar la dependencia de flutter primero:

pubspec.yaml
yaml
dependencies:
  flutter:
    sdk: flutter

  # ignore: sort_pub_dependencies
  collection: ^1.19.0

Personalizar las reglas de análisis

#

Cada diagnóstico del analyzer y regla del linter tiene una severidad por defecto. Puedes usar el archivo de opciones de análisis para cambiar la severidad de las reglas individuales, o para ignorar siempre algunas reglas.

El analyzer admite tres niveles de severidad:

info

Un mensaje informativo que no causa que el análisis falle. Ejemplo: dead_code

warning

Una advertencia que no hace que el análisis falle a menos que el analyzer esté configurado para tratar las advertencias como errores. Ejemplo: invalid_null_aware_operator

error

Un error que hace que el análisis falle. Ejemplo: invalid_assignment

Ignorar reglas

#

Puedes ignorar diagnósticos del analyzer y reglas del linter específicos usando el campo errors:. Lista la regla, seguida de : ignore. Por ejemplo, el siguiente archivo de opciones de análisis indica a las herramientas de análisis que ignoren la regla TODO:

yaml
analyzer:
  errors:
    todo: ignore

Cambiar la severidad de las reglas

#

Puedes cambiar globalmente la severidad de una regla en particular. Esta técnica funciona tanto para problemas de análisis regulares como para lints. Por ejemplo, el siguiente archivo de opciones de análisis indica a las herramientas de análisis que traten las asignaciones inválidas como advertencias y los retornos faltantes como errores, y que proporcionen información (pero no una advertencia o error) sobre el código muerto (dead code):

yaml
analyzer:
  errors:
    invalid_assignment: warning
    missing_return: error
    dead_code: info

Configurar dart format

#

Puedes configurar el comportamiento de dart format agregando una formatter sección al archivo de opciones de análisis.

Opciones configurables

#

page_width

#

Puedes especificar tu page_width (longitud de línea) preferido. El valor por defecto es 80 caracteres.

Para obtener más información, lee Configuración del ancho de página del formateador.

trailing_commas

#

Puedes configurar cómo maneja el formateador las comas finales (trailing commas) en las listas de argumentos y parámetros usando la opción trailing_commas. Acepta uno de dos valores:

automate (por defecto)

El formateador agrega y elimina comas finales basándose en su decisión de dividir el constructor circundante.

preserve

Una coma final obliga a dividir el constructor circundante. El formateador agrega una coma final cuando divide un constructor, pero no la elimina.

analysis_options.yaml
yaml
formatter:
  page_width: 120
  trailing_commas: preserve

Recursos

#

Usa los siguientes recursos para aprender más sobre el análisis estático en Dart: