
Estamos anunciando un nuevo linter (analizador estático) para Go , que también es un espacio aislado para crear prototipos de sus ideas en el mundo del análisis estático.
Go-Critic se basa en las siguientes observaciones:
- Es mejor tener una implementación "suficientemente buena" de la prueba que no tenerla en absoluto
- Si el cheque es controvertido, esto no significa que no pueda ser útil. Marcamos como "obstinado" y vertimos
- Escribir un linter desde cero suele ser más difícil que agregar una nueva verificación a un marco existente si el marco en sí es fácil de entender.
En esta publicación veremos el uso y la arquitectura de go-critical, algunas de las comprobaciones implementadas en él , y también describiremos los pasos principales para agregarle nuestra función de analizador.
Inicio rápido
$ cd $GOPATH $ go get -u github.com/go-critic/go-critic/... $ ./bin/gocritic check-package strings $GOROOT/src/strings/replace.go:450:22: unslice: could simplify s[:] to s $GOROOT/src/strings/replace.go:148:2: elseif: should rewrite if-else to switch statement $GOROOT/src/strings/replace.go:156:3: elseif: should rewrite if-else to switch statement $GOROOT/src/strings/replace.go:219:3: elseif: should rewrite if-else to switch statement $GOROOT/src/strings/replace.go:370:1: paramTypeCombine: func(pattern string, value string) *singleStringReplacer could be replaced with func(pattern, value string) *singleStringReplacer $GOROOT/src/strings/replace.go:259:2: rangeExprCopy: copy of r.mapping (256 bytes) can be avoided with &r.mapping $GOROOT/src/strings/replace.go:264:2: rangeExprCopy: copy of r.mapping (256 bytes) can be avoided with &r.mapping $GOROOT/src/strings/strings.go:791:1: paramTypeCombine: func(s string, cutset string) string could be replaced with func(s, cutset string) string $GOROOT/src/strings/strings.go:800:1: paramTypeCombine: func(s string, cutset string) string could be replaced with func(s, cutset string) string $GOROOT/src/strings/strings.go:809:1: paramTypeCombine: func(s string, cutset string) string could be replaced with func(s, cutset string) string $GOROOT/src/strings/strings.go:44:1: unnamedResult: consider to give name to results $GOROOT/src/strings/strings.go:61:1: unnamedResult: consider to give name to results $GOROOT/src/strings/export_test.go:28:3: rangeExprCopy: copy of r.mapping (256 bytes) can be avoided with &r.mapping $GOROOT/src/strings/export_test.go:42:1: unnamedResult: consider to give name to results
(Se ha editado el formato de las advertencias; los originales están disponibles en gist .)
La utilidad gocritic puede verificar paquetes individuales por su ruta de importación ( check-package
), y también recorrer recursivamente todos los directorios ( check-project
). Por ejemplo, puede verificar todo $GOROOT
o $GOPATH
con un solo comando:
$ gocritic check-project $GOROOT/src $ gocritic check-project $GOPATH/src
Hay soporte para la "lista blanca" para las comprobaciones con el fin de enumerar explícitamente qué comprobaciones se deben realizar (indicador -enable
). De forma predeterminada, se VeryOpinionated
todas las comprobaciones que no están marcadas con el icono Experimental
o VeryOpinionated
.
Se planean integraciones en golangci-lint y gometalinter .
Como empezó todo
Al realizar la próxima revisión de código del proyecto Go, o al auditar alguna biblioteca de terceros, puede notar los mismos problemas una y otra vez.
Para su pesar, no fue posible encontrar un linter que diagnosticara esta clase de problemas.
Su primer paso puede ser un intento de categorizar el problema y contactar a los autores de la interfaz existente, sugiriéndoles que agreguen una nueva verificación. Las posibilidades de que su propuesta sea aceptada dependen en gran medida del proyecto y pueden ser bastante bajas. Además, lo más probable es que sigan meses de expectativa.
Pero, ¿qué sucede si el cheque es completamente ambiguo y alguien puede percibirlo como demasiado subjetivo o insuficientemente preciso?
¿Quizás tenga sentido intentar escribir este cheque usted mismo?
go-critic
existe para convertirse en el hogar de pruebas experimentales que son más fáciles de implementar por nosotros mismos que adjuntarlas a los analizadores estáticos existentes. El dispositivo go-critic
sí mismo minimiza la cantidad de contexto y las acciones que se necesitan para agregar una nueva verificación; podemos decir que solo necesita agregar un archivo (sin contar las pruebas).
¿Cómo funciona Go-Critical?
Un crítico es un conjunto de reglas que describen las propiedades de verificación y microverificadores que implementan la inspección de código para el cumplimiento de una regla.
Una aplicación que incorpora un linter (por ejemplo, cmd / gocritic o golangci-lint ) recibe una lista de reglas compatibles, las filtra de una manera específica, crea una función de verificación para cada regla seleccionada y lanza cada una de ellas sobre el paquete en estudio.
El trabajo de agregar un nuevo verificador se reduce a tres pasos principales:
- Agregar pruebas.
- La implementación de la verificación en sí.
- Agregar documentación para el linter.
Revisaremos todos estos puntos usando la regla captLocal de ejemplo, que requiere la ausencia de nombres locales que comiencen con una letra mayúscula.

Agregar pruebas
Para agregar datos de prueba para una nueva verificación, debe crear un nuevo directorio en lint / testdata .
Cada uno de estos directorios debe tener un archivo positive_tests.go , que describe ejemplos de código en los que debería funcionar la verificación. Para probar la ausencia de falsos positivos, las pruebas se complementan con un código "correcto", en el que la nueva verificación no debería encontrar ningún problema ( negative_tests.go ).
Ejemplos:
Puede ejecutar pruebas después de agregar un nuevo linter.
Implementación de Verificación
Cree un archivo con el nombre del verificador: lint/captLocal_checker.go
.
Por convención, todos los archivos de microinterruptor tienen el sufijo _checker
.
package lint
checkerBase es un tipo que debe integrarse en cada verificador.
Proporciona implementaciones predeterminadas, lo que le permite escribir menos código en cada interfaz.
Entre otras cosas, checkerBase incluye un puntero a lint.context
, que contiene información de tipo y otros metadatos sobre el archivo que se está comprobando.
El campo upcaseNames
contendrá una tabla de nombres conocidos, que ofreceremos para reemplazar con las strings.ToLower(name)
. Versión strings.ToLower(name)
. Para aquellos nombres que no figuran en el mapa, se sugerirá que no se use una letra mayúscula, pero no se proporcionará un reemplazo correcto.
El estado interno se inicializa una vez para cada instancia.
Se requiere que el método Init()
se defina solo para aquellos linters que necesitan llevar a cabo una inicialización preliminar.
func (c *captLocalChecker) Init() { c.upcaseNames = map[string]bool{ "IN": true, "OUT": true, "INOUT": true, } }
Ahora necesita definir la función de verificación en sí.
En el caso de captLocal
, debemos verificar todos los ast.Ident
locales que introducen nuevas variables.
Para verificar todas las definiciones locales de nombres, debe implementar un método con la siguiente firma en su verificador:
VisitLocalDef(name astwalk.Name, initializer ast.Expr)
La lista de interfaces de visitante disponibles se puede encontrar en el archivo lint / internal / visitor.go .
captLocal
implementa LocalDefVisitor
.
Por convención, los métodos que generan advertencias generalmente se presentan en métodos separados. Hay raras excepciones, pero seguir esta regla se considera una buena práctica.
Agregar documentación
Otro método de implementación necesario es InitDocumentation
:
func (c *captLocalChecker) InitDocumentation(d *Documentation) { d.Summary = "Detects capitalized names for local variables" d.Before = `func f(IN int, OUT *int) (ERR error) {}` d.After = `func f(in int, out *int) (err error) {}` }
Por lo general, solo complete 3 campos:
Summary
: una descripción de la acción de validación en una oración.Before
: código antes de la corrección.After
- código después de la corrección (no debe causar una advertencia).
Generación de documentaciónRegenerar la documentación no es un requisito previo para una nueva interfaz, quizás en un futuro próximo este paso se automatizará por completo. Pero si aún desea verificar cómo se verá el archivo de reducción de salida, use el comando make docs
. El archivo docs/overview.md
se actualizará.
Registre un nuevo linter y ejecute pruebas
El toque final es registrar un nuevo linter:
addChecker
espera un puntero al valor cero del nuevo linter. Luego viene el argumento variadic, que le permite pasar cero o más atributos que describen las propiedades de la implementación de la regla.
attrSyntaxOnly
es un marcador opcional para linters que no usan información de tipo en su implementación, lo que le permite ejecutarlos sin realizar verificaciones de tipo. golangci-lint
marca tales linters con el indicador "rápido" (porque corren mucho más rápido).
attrExperimental
es un atributo asignado a todas las implementaciones nuevas. La eliminación de este atributo solo es posible después de la estabilización de la verificación implementada.
Ahora que el nuevo linter está registrado a través de addChecker, puede ejecutar las pruebas:
Fusión optimista (casi)
Cuando consideramos solicitudes de extracción, intentamos adherirnos a la estrategia de fusión optimista . Esto se expresa principalmente en la aceptación de aquellos RP a los que el revisor puede tener algunas, en particular, afirmaciones puramente subjetivas. Inmediatamente después de la inyección de dicho parche, un revisor puede seguir al revisor, que corrige estas deficiencias, el autor del parche original se agrega a CC (copia).
También tenemos dos marcadores de linter que se pueden usar para evitar banderas rojas en ausencia de un consenso total:
Experimental
: una implementación puede tener una gran cantidad de falsos positivos, ser ineficaz (se identifica la fuente del problema) o "caer" en algunas situaciones. Puede infundir dicha implementación si la marca con el atributo attrExperimental
. A veces, con la ayuda de experimental, se indican esas comprobaciones que no pudieron encontrar un buen nombre desde la primera confirmación.VeryOpinionated
: si el cheque puede tener defensores y enemigos, vale la pena marcarlo con el atributo attrVeryOpinionated
. De esta manera, podemos evitar rechazar ideas sobre el estilo de código que pueden no coincidir con el gusto de algunos gophers.
Experimental
es una propiedad de implementación potencialmente temporal y reparable. VeryOpinionated
es una propiedad de regla más fundamental que es independiente de la implementación.
Se recomienda crear un ticket [checker-request]
en github antes de enviar la implementación, pero si ya ha enviado una solicitud de extracción, puede abrir el problema correspondiente.
Para obtener más detalles sobre el proceso de desarrollo, vea CONTRIBUTING.md .
Las reglas básicas se enumeran en la sección de reglas principales .
Palabras de despedida
Puede participar en el proyecto no solo agregando un nuevo linter.
Hay muchas otras formas:
- Pruébelo en sus proyectos o proyectos de código abierto grandes / conocidos e informe falsos positivos, falsos negativos y otras deficiencias. Le agradeceríamos que también agregue una nota sobre el problema encontrado / solucionado en la página de trofeos .
- Sugerir ideas para nuevas inspecciones. Es suficiente para crear un problema en nuestro rastreador.
- Agregar pruebas para linters existentes.
go-critic
critica su código Go con las voces de todos los programadores involucrados en su desarrollo. Todos pueden criticar, por lo tanto, ¡únete!
