Ir al contenido
Molto

Formato y análisis

molto fmt y molto lint, los dos archivos JSON que los configuran, y por qué tu repositorio nunca acumula un .clang-format.

Molto es dueño de la configuración; las herramientas hacen el trabajo.

Molto no formatea ni analiza código, y nunca lo hará. De lo que es dueño es del vocabulario: tú escribes indent_width y brace_style, y Molto lo traduce a la configuración propia del backend en .bin/style/ justo antes de ejecutarlo. Tu repositorio nunca acumula un .clang-format, y cambiar de backend más adelante es modificar una línea, no reescribirlo todo.

Conseguir las herramientas

Molto no instala nada. Se lo pregunta a pickup:

$ pickup tools
KIND       NAME          VERSION                      SOURCE
formatter  clang-format  clang-format version 22.1.8  pickup
linter     clang-tidy    LLVM version 22.1.8          pickup

Toma la ruta que pickup reporta y la ejecuta: no busca en tu PATH. MOLTO_CLANG_FORMAT y MOLTO_CLANG_TIDY se saltan la resolución por completo y no se cachean. --refresh-tools vuelve a preguntarle a pickup en vez de reutilizar la respuesta registrada.

No tener linter no es un error. molto lint sigue ejecutando los diagnósticos del propio compilador, que es la mayor parte del valor y no necesita instalar nada.

molto fmt

molto fmt              # reescribe src/ e include/ en el sitio
molto fmt --check      # dice qué cambiaría, no escribe nada; sale con 1 si hay algo
molto fmt --diff       # imprime el diff unificado, no escribe nada

--check es la forma para CI. --diff es la misma pregunta con la respuesta enseñada como parche. Pasar los dos es un error de uso: dos respuestas a una sola pregunta. Las cabeceras también se formatean.

molto lint

molto lint                     # diagnósticos del compilador, luego el linter
molto lint --format json       # legible por máquinas, para CI
molto lint --profile release   # analiza con los defines del perfil release

Dos pasadas sobre cada fuente de src/: el compilador declarado en [target], sólo sintaxis y sin escribir objetos; y después el linter, si lo hay, configurado desde linter.json y con los mismos flags de compilación que usaría el build.

--profile importa más de lo que parece. El perfil decide qué defines están en vigor, y un #ifdef decide qué llega siquiera a compilarse. Analizar con el perfil equivocado revisa código que el build nunca ve.

--format no tiene valor por defecto. Ausente no es lo mismo que un --format text explícito: ausente significa «lo que le convenga al flujo», que es rich en una terminal —bloques enmarcados, un cursor bajo el carácter que la herramienta señaló, color— y text a través de una tubería. Pídelo por su nombre cuando la salida tenga que ser estable vaya donde vaya; la CI quiere text o json escritos, no deducidos.

[
  {
    "file": "src/foo.c",
    "line": 12,
    "column": 5,
    "severity": "warning",
    "message": "…",
    "rule": "…"
  }
]

Códigos de salida: 0 cuando no hubo ningún diagnóstico de severidad error, 1 cuando lo hubo, 2 por configuración inválida y 4 por mal uso. Un warning se reporta y aun así tiene éxito.

Los dos comandos son incrementales

Un archivo que no ha cambiado no se vuelve a analizar: los diagnósticos quedan registrados en la base de datos del workspace y se reproducen, y lo que ves es idéntico de una forma u otra: mismos diagnósticos, mismo orden, mismo código de salida.

Un archivo se reanaliza cuando cambia su contenido, cuando cambia una cabecera que incluye, cuando el comando sería distinto, cuando cambia linter.json o cuando cambia la versión del linter. --refresh-analysis ignora los resultados registrados y lo ejecuta todo.

molto fmt registra que un archivo está en su forma final bajo el estilo actual, así que formatear un proyecto deja a fmt --check sin nada que hacer. Ahí las cabeceras no intervienen: un formateador sólo lee el archivo que se le da.

Ninguno de los dos escribe compile_commands.json, eso lo hace un build. Un clang-tidy externo que lea esa base de datos está leyendo lo que dejó el último molto build, no esta ejecución.

format.json

Opcional; si no está, valen los valores por defecto de abajo.

{
  "backend": "clang-format@22.1.8",
  "preset": "molto",
  "exclude": ["src/vendor/**"],
  "style": {
    "indent_width": 4,
    "line_width": 100,
    "brace_style": "attach",
    "pointer_alignment": "right",
    "sort_includes": true
  }
}
Clave Tipo Por defecto Significado
indent_width int 4 Columnas por nivel de indentación
use_tabs bool false Indentar con tabuladores en vez de espacios
line_width int 100 Columna máxima antes de partir
brace_style string attach attach, break, linux, allman
pointer_alignment string right left para int* p, right para int *p
sort_includes bool true Ordenar los bloques de #include
space_before_paren bool false Espacio entre una palabra clave y (
column_limit_comments bool true Partir los comentarios en line_width

Al formateador se le dice qué lenguaje lee

No hay clave para el lenguaje: Molto lo toma de [target].cpp_std.

Un .h no lleva el lenguaje en la extensión, así que clang-format por su cuenta asume el C++ más nuevo. Un struct de C con un campo llamado requires pasa entonces a leerse como una requires-clause de C++20, y la declaración queda destrozada en tres líneas. Por eso Molto emite Standard:

[target].cpp_std Standard
Ausente, un proyecto en C c++03, el único dialecto donde requires y concept son identificadores normales
c++11, c++17, c++20, gnu++20 Ese estándar
Cualquier otra cosa Latest

Un proyecto de C++ que nunca declara cpp_std se formatea como C, y sus cabeceras de C++20 sufren la imagen especular del mismo problema. Decláralo.

linter.json

Opcional. Un mapa de severidades al estilo de ESLint: cada clave nombra una regla o una familia, y cada valor es off, warn o error.

{
  "backend": "clang-tidy@22.1.8",
  "preset": "molto",
  "exclude": ["src/generated/**"],
  "rules": {
    "bugprone": "error",
    "readability_magic_numbers": "warn",
    "modernize": "off"
  }
}

Los nombres de las reglas son de Molto, no del backend:

Regla Cubre
bugprone Patrones que suelen ser un bug
performance, portability, modernize, readability Las familias del mismo nombre
dataflow, security El analizador sensible a caminos, por mitades
naming_snake_case Nomenclatura de identificadores
readability_magic_numbers, identifier_length Comprobaciones sueltas de legibilidad
swappable_parameters, spurious_wakeup Dos checks de bugprone, con nombre propio para poder rechazar uno sin tirar la familia
unused, shadow, uninitialized, implicit_conversion, sign_compare Diagnósticos del compilador, por nombre

Una regla que Molto no puede expresar para el backend elegido es un error que nombra la regla y el backend, nunca algo que se descarta en silencio.

El analizador sensible a caminos

Queda fuera del preset por defecto a propósito, y se pide en dos mitades:

  • "dataflow": "warn" recorre caminos en vez de sintaxis, y encuentra lo que eso compra: un desreferenciado nulo, una fuga, el uso de un campo sin inicializar, una escritura muerta.
  • "security": "warn" añade la familia de seguridad, menos el check que exige las funciones del Anexo K de C11 que glibc no trae: ese salta en cada llamada a la librería estándar y entierra todo lo demás.

Espera falsos positivos de cualquiera de los dos, y espera que ambos sean lentos. Son la razón de que --refresh-analysis exista como flag separado de --refresh-tools.

Claves compartidas

preset — molto (por defecto) o none. El preset molto le pide al compilador -Wall -Wextra -Wpedantic y al linter clang-diagnostic-* y bugprone-*. kernel y gnu aparecen en la RFC pero no están implementados; declarar uno es un error, no una sustitución silenciosa.

exclude — patrones glob contrastados con la ruta relativa a la raíz del proyecto. Un asterisco cruza barras, así que vendor/* casa con vendor/a/b.c; un /** final casa también con el propio directorio.

backend — fija un nombre y opcionalmente una versión. Molto verifica la fijación contra lo que reporta pickup; no instala. Una fijación incumplida es un error que nombra ambas versiones, porque dos releases de un mismo formateador producen salidas distintas para el mismo archivo: justo el ruido que un formateador existe para eliminar.

La configuración de estilo falla en cerrado

Una clave desconocida, un valor desconocido, una regla sin traducción, una lista que se desborda, una fijación incumplida: cada una es un error que nombra qué está mal. Nada de format.json ni de linter.json se ignora.

Es deliberado. Una clave que en silencio no hace nada es peor que una rechazada: mantendrías la línea en tu configuración durante años creyendo que se aplicaba. Esto no se extiende a Project.toml, que descarta las claves desconocidas sin avisar.

Resolución de problemas

Síntoma Causa Arreglo
this machine has no formatter Pickup no reporta ninguno pickup install clang-format
unknown key 'styl' Una errata Compruébala en las tablas de arriba
rule 'x' is not supported by clang-tidy Nombre de regla no canónico Usa uno de la tabla de reglas
backend is pinned to '…' but this machine has '…' La versión instalada no coincide Instala la fijada, o cámbiala
preset is not implemented yet kernel o gnu Usa molto o none
El lint no reporta nada del linter No hay linter instalado Mira pickup tools; la pasada del compilador sí corrió
El lint reporta lo que el build no Perfil equivocado molto lint --profile release