CodeRabbit: automatická analýza a recenze zdrojových kódů

24. 7. 2025
Doba čtení: 24 minut

Sdílet

CodeRabbit, zajíc proti zapadajícímu Slunci
Autor: Root.cz s využitím Zoner AI
S aplikacemi a službami využívajícími umělou inteligenci se v posledních letech můžeme setkat doslova na každém kroku. Dnes se seznámíme s nástrojem CodeRabbit, jenž analyzuje změny ve zdrojových kódech a navrhuje vylepšení.

Obsah

1. Automatická analýza a recenze zdrojových kódů s využitím nástroje CodeRabbit

2. Integrace CodeRabbita s repositářem

3. Knihovna naprogramovaná v jazyku C

4. Analýza a recenze zdrojového kódu knihovny

5. Vygenerování komentářů k analyzovanému zdrojovému kódu

6. Odvození sekvenčního diagramu z analyzovaného zdrojového kódu

7. Podpora programovacích jazyků, které nepatří do mainstreamu

8. Program pro smazání obrazovky na ZX Spectru

9. Programová smyčka realizovaná instrukcí DJNZ

10. Program vytvořený v assembleru mikroprocesorů Intel 8086 pro IBM PC+DOS 

11. Výpočet největšího společného dělitele v BBC BASICu

12. Analýza a recenze výpočtu

13. Oficiální demonstrační příklad pro jazyk C3

14. Analýza a recenze příkladu

15. Rekurzivní výpočet naprogramovaný v jazyce Standard ML

16. Analýza a recenze příkladu

17. Rekurzivní výpočet naprogramovaný v jazyce OCaml

18. Analýza a recenze příkladu

19. Nepatrně složitější příklad naprogramovaný v jazyce OCaml

20. Analýza a recenze příkladu

21. Odkazy na Internetu

1. Automatická analýza a recenze zdrojových kódů s využitím nástroje CodeRabbit

„AI doesn’t invent. It recycles. It’s trained on other people’s ideas, imitates patterns, and doesn’t jump the curve.“

Jak již bylo napsáno v perexu dnešního článku, s aplikacemi a nástroji, které do nějaké míry využívají umělou inteligenci, se v posledních několika letech doslova roztrhl pytel. V oblasti vývojářských nástrojů se můžeme setkat s mnoha doplňky umožňujícími tvorbu programového kódu (vibe coding), se všemi pozitivními ale i mnoha negativními důsledky, které tato technologie přináší. Ovšem prozatím ponechme programování na lidech a zaměřme se na jinou oblast. Konkrétně se bude jednat o nástroje, které umožňují analyzovat změny prováděné ve zdrojových kódech (pull request, change request), hledat v těchto změnách nedostatky, navrhovat vylepšení popř. opravovat zjevné chyby nebo dokonce navrhovat vylepšení implementovaných algoritmů. Takové nástroje mohou být velmi užitečné a tím, že se soustředí pouze na analýzu změn, je je možné využít i na CI.

Nástroj CodeRabbit

Obrázek 1: CodeRabbit dokáže navrhovat i vylepšení dokumentace.

Autor: tisnik, podle licence: Rights Managed

V dnešním článku se seznámíme s nástrojem CodeRabbit, který lze velmi dobře integrovat s GitHubem a GitLabem. Tento nástroj podporuje velké množství programovacích jazyků, pochopitelně zejména těch mainstreamových. Ovšem navíc dokáže provádět analýzu toku dat, kontroluje konfigurační soubory, definiční soubory, specifikace API atd. A navíc je jeho použití pro veřejně dostupné repositáře zdarma, takže integraci CodeRabbita vlastně nic nestojí v cestě [*].

Poznámka: zdrojové kódy ve veřejně dostupných repositářích jsou stejně už dávno „vytěžovány“ mnoha komerčními nástroji, takže nasazení CodeRabbita by nemělo být problematické ani z hlediska morálky (na druhou stranu tvorba kódu je již zapeklitější problém).
Nástroj CodeRabbit

Obrázek 2: Další návrhy na úpravu dokumentace provedenou nástrojem CodeRabbit.

Autor: tisnik, podle licence: Rights Managed

2. Integrace CodeRabbita s repositářem

CodeRabbit dokáže analyzovat každý pull request do repositáře, ovšem pochopitelně je nutné jeho propojení se zvoleným repositářem nebo repositáři. To je snadné, ovšem za předpokladu, že máte práva ke konfiguraci repositáře. Nejdříve je nutné se přihlásit na stránce https://app.coderabbit.ai/login. Předpokládejme, že přihlášení bude provedeno přes GitHub. V takovém případě CodeRabbit automaticky nabídne seznam repositářů, do kterých se může přidat. Přitom vyžaduje práva pro čtení a současně i zápis, což sice zní divně, ovšem většinou to znamená práva pro vytvoření pull requestů (typicky je totiž hlavní větev zamčená pro přímé zápisy – pokud není, je nejvyšší čas ji zamčít :-). CodeRabbit se následně v nastavení repositáře objeví v sekci GitHub Apps pod označením coderabbitai Developed by coderabbitai.

Nástroj CodeRabbit

Obrázek 3: Přehledová tabulka s informacemi o tom, kolik změn bylo analyzováno a kolik navrhovaných úprav bylo akceptováno.

Autor: tisnik, podle licence: Rights Managed

Nyní bude pro každý nový pull request provedena jeho analýza, což je činnost, která může nějakou dobu trvat (řádově minuty). Posléze jsou zobrazeny výsledky analýzy i s navrhovanými změnami, například:

Nástroj CodeRabbit

Obrázek 4: Navrhované úpravy kódu provedené CodeRabbitem

Autor: tisnik, podle licence: Rights Managed

3. Knihovna naprogramovaná v jazyku C

Abychom si otestovali některé základní možnosti nabízené nástrojem CodeRabbit, byla vytvořena jednoduchá knihovna napsaná v céčku. Zdrojový kód této knihovny byl poslán do repositáře v jediném pull requestu (což je trik, jak si zajistit analýzu celého zdrojového kódu). Zdrojový kód této knihovny vypadá následovně (nejedná se o uměle vytvořený kód, protože je na něm založen reálný projekt):

/*
build as shared library: gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC
testlib.c build as executable:
*/
 
#include <stdio.h>
#include <stdlib.h>
 
void render_test_rgb_image(unsigned int width, unsigned int height,
                           unsigned char *pixels, unsigned char green) {
    unsigned int i, j;
    unsigned char *p = pixels;
 
    for (j = 0; j < height; j++) {
        for (i = 0; i < width; i++) {
            *p++ = i;
            *p++ = green;
            *p++ = j;
            p++;
        }
    }
}
 
void ppm_write_ascii_to_stream(unsigned int width, unsigned int height,
                               unsigned char *pixels, FILE *fout) {
    int x, y;
    unsigned char r, g, b;
    unsigned char *p = pixels;
 
    /* header */
    fprintf(fout, "P3 %d %d 255\n", width, height);
 
    /* pixel array */
    for (y = height - 1; y >= 0; y--) {
        for (x = 0; x < width; x++) {
            r = *p++;
            g = *p++;
            b = *p++;
            p++;
            fprintf(fout, "%d %d %d\n", r, g, b);
        }
    }
}
 
int ppm_write_ascii(unsigned int width, unsigned int height,
                    unsigned char *pixels, const char *file_name) {
    FILE *fout;
 
    fout = fopen(file_name, "wb");
    if (!fout) {
        return -1;
    }
 
    ppm_write_ascii_to_stream(width, height, pixels, fout);
 
    if (fclose(fout) == EOF) {
        return -1;
    }
    return 0;
}
 
int main(void) {
#define WIDTH 256
#define HEIGHT 256
    unsigned char *pixels = (unsigned char *)malloc(WIDTH * HEIGHT * 4);
    render_test_rgb_image(WIDTH, HEIGHT, pixels, 0);
    ppm_write_ascii(WIDTH, HEIGHT, pixels, "test_rgb_1.ppm");
    return 0;
}

4. Analýza a recenze zdrojového kódu knihovny

Podívejme se nyní na to, jakým způsobem byl zdrojový kód knihovny uvedené ve druhé kapitole analyzován a jakou recenzi CodeRabbit vytvořil.

Nejdříve se vytvoří text s popisem provedené změny, která je v tomto případě poměrně příhodná:

A new C source file was added that provides functions to generate a simple RGB test image and write it in PPM ASCII format. The code includes image rendering, PPM writing utilities, and a main function that generates a 256x256 image and saves it to disk. Build instructions are included in comments.

Některé navrhované změny ponechám v článku zobrazeny v textové podobě, například nekonzistenci v pojmenování knihovny:

1-4: Fix inconsistencies in build instructions.
 
The build instructions contain several issues:
 
    Line 3 references "testlib.c" but the actual filename is "render_image_1.c"
    The shared library build command uses "testlib" names that don't match the file
    The executable build instruction is incomplete
 
Apply this diff to correct the build instructions:

Navrhovaná změna je zobrazena formou patche:

 /*
-build as shared library: gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC
-testlib.c build as executable:
+build as shared library: gcc -shared -Wl,-soname,render_image_1 -o render_image_1.so -fPIC render_image_1.c
+build as executable: gcc -o render_image_1 render_image_1.c
 */

Další navrhované změny a problémy si již zobrazíme formou screenshotů:

Navrhované úpravy kódu naprogramovaného v céčku

Obrázek 5: V novém kódu jsem zapomněl na kontrolu návratového kódu funkce fprintf, která může skončit s chybou.

Autor: tisnik, podle licence: Rights Managed
Navrhované úpravy kódu naprogramovaného v céčku

Obrázek 6: Důležitá je i kontrola vstupních parametrů funkce, zejména ukazatelů. A navíc CodeRabbit navrhuje i explicitní omezení zapisovaných hodnot na rozsah jednoho bajtu (což mi popravdě připadne až moc „profesorské“, ale budiž)

Autor: tisnik, podle licence: Rights Managed
Navrhované úpravy kódu naprogramovaného v céčku

Obrázek 7: Toto je zajímavá potenciální chyba, která mě popravdě při psaní kódu ani nenapadla, takže je dobré, že na ni CodeRabbit upozornil

Autor: tisnik, podle licence: Rights Managed
Navrhované úpravy kódu naprogramovaného v céčku

Obrázek 8: Jazyk C je nakonec snadno použitelný, když všechny potřebné kontroly doplňují boti

Autor: tisnik, podle licence: Rights Managed
Navrhované úpravy kódu naprogramovaného v céčku

Obrázek 9: Tato změna mě popravdě překvapila, protože jsem neočekával, že CodeRabbit kódu porozumí až do takové míry, že odhalí typ vytvářeného souboru (a pochopitelně po změně typu souboru na binární PPM toto upozornění zmizí)

Autor: tisnik, podle licence: Rights Managed
Poznámka: v tomto případě tedy CodeRabbit kód analyzoval poměrně dobře a navrhované změny dávají smysl a naprostou většinu z nich bychom do výsledného kódu přijali.

5. Vygenerování komentářů k analyzovanému zdrojovému kódu

Další potenciálně užitečnou funkcí CodeRabbita je jeho schopnost vygenerovat komentáře k analyzovanému zdrojovému kódu. Někdy jsou tyto komentáře až moc detailní, ovšem samozřejmě se jedná pouze o doporučení. Komentáře se vytvoří ve vlastním pull requestu vytvořeného CodeRabbitem, který pochopitelně musí být schválen a zařazen do repositáře.

Pro zajímavost si změnu provedenou CodeRabbitem zobrazíme formou patche, který by měl být pro vývojáře snadno čitelný:

From c5d1b1090d5b2519ee2c1154f332f673fd24a581 Mon Sep 17 00:00:00 2001
From: "coderabbitai[bot]"
 <136622811+coderabbitai[bot]@users.noreply.github.com>
Date: Wed, 16 Jul 2025 19:14:12 +0000
Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20`render?=
 =?UTF-8?q?=5Fimage=5F1.c`?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
 
Docstrings generation was requested by @tisnik.
 
* https://github.com/tisnik/test-dependabot-no-devs/pull/1055#issuecomment-3079893992
 
The following files were modified:
 
* `render_image_1.c`
---
 render_image_1.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
 
diff --git a/render_image_1.c b/render_image_1.c
index 9d845a65..35f8a681 100644
--- a/render_image_1.c
+++ b/render_image_1.c
@@ -6,6 +6,16 @@ testlib.c build as executable:
 #include <stdio.h>
 #include <stdlib.h>
 
+/**
+ * Fills a pixel buffer with a test RGBA image pattern.
+ *
+ * Sets each pixel's red channel to its x-coordinate, green channel to the specified value, blue channel to its y-coordinate, and leaves the alpha channel unused.
+ * The buffer must be preallocated with at least width * height * 4 bytes.
+ * @param width Image width in pixels.
+ * @param height Image height in pixels.
+ * @param pixels Pointer to the RGBA pixel buffer to fill.
+ * @param green Value to assign to the green channel for all pixels.
+ */
 void render_test_rgb_image(unsigned int width, unsigned int height,
                            unsigned char *pixels, unsigned char green) {
     unsigned int i, j;
@@ -21,6 +31,11 @@ void render_test_rgb_image(unsigned int width, unsigned int height,
     }
 }
 
+/**
+ * Writes pixel data to a file stream in ASCII PPM (P3) format.
+ *
+ * Outputs the image header and RGB values for each pixel, reading from the provided buffer in bottom-to-top row order. The alpha channel in the buffer is ignored.
+ */
 void ppm_write_ascii_to_stream(unsigned int width, unsigned int height,
                                unsigned char *pixels, FILE *fout) {
     int x, y;
@@ -42,6 +57,17 @@ void ppm_write_ascii_to_stream(unsigned int width, unsigned int height,
     }
 }
 
+/**
+ * Writes pixel data to a file in ASCII PPM (P3) format.
+ *
+ * Opens the specified file for writing, writes the image data in ASCII PPM format using the provided pixel buffer, and closes the file.
+ * Returns 0 on success, or -1 if the file cannot be opened or closed.
+ * @param width Image width in pixels.
+ * @param height Image height in pixels.
+ * @param pixels Pointer to the RGBA pixel buffer (only RGB channels are written).
+ * @param file_name Name of the output file.
+ * @return 0 on success, -1 on failure.
+ */
 int ppm_write_ascii(unsigned int width, unsigned int height,
                     unsigned char *pixels, const char *file_name) {
     FILE *fout;
@@ -59,6 +85,10 @@ int ppm_write_ascii(unsigned int width, unsigned int height,
     return 0;
 }
 
+/**
+ * Generates a 256x256 test RGB image and writes it to "test_rgb_1.ppm" in ASCII PPM format.
+ * @returns 0 on successful completion.
+ */
 int main(void) {
 #define WIDTH 256
 #define HEIGHT 256

6. Odvození sekvenčního diagramu z analyzovaného zdrojového kódu

Program resp. možná přesněji řečeno implementovaný algoritmus, je možné do určité míry popsat i graficky, tedy vhodným diagramem. K tomuto účelu se využívají například stavové diagramy. Ty dokážou názorně popsat stavy systému i možné přechody mezi jednotlivými stavy, ovšem v mnoha případech vzniká potřeba podrobněji popsat i interakci mezi popisovaným systémem a jeho okolím, interakci mezi dvěma nebo více moduly systému či (na té nejpodrobnější úrovni) interakci probíhající mezi jednotlivými objekty, z nichž se systém skládá. Pro tento účel slouží takzvané sekvenční diagramy (sequence diagrams), v nichž lze velmi názorným způsobem naznačit časovou posloupnost posílání zpráv mezi různými typy objektů, popř. k zobrazené posloupnosti zpráv přidat další komentáře a značky. Jeden z typických a poměrně často v praxi používaných příkladů použití sekvenčních diagramů je popis komunikace s využitím síťových i jiných protokolů.

CodeRabbit dokáže tyto diagramy relativně dobře odvodit, což je patrné z následujících screenshotů:

Sekvenční diagram vygenerovaný CodeRabbitem

Obrázek 10: Sekvenční diagram odvozený z kódu naprogramovaného v Pythonu

Autor: tisnik, podle licence: Rights Managed
Sekvenční diagram vygenerovaný CodeRabbitem

Obrázek 11: Sekvenční diagram odvozený z kódu naprogramovaného v Pythonu

Autor: tisnik, podle licence: Rights Managed
Poznámka: tyto diagramy jsou dostupné i ve zdrojové podobě určené pro PlantUML.

7. Podpora programovacích jazyků, které nepatří do mainstreamu

Od nástrojů postavených mj. na moderních jazykových modelech můžeme očekávat velmi dobrou podporu mainstreamových programovacích jazyků, mezi něž patří především Python, který je následovaný jazyky C, C++, Javou, C# a pochopitelně taktéž JavaScriptem, TypeScriptem, Go a Rustem. Velmi dobrá podpora těchto programovacích jazyků by neměla být nijak překvapující, ovšem zajímavé bude zjistit, jak a zda vůbec dokáže CodeRabbit zpracovávat a hodnotit zdrojové kódy vytvořené v programovacích jazycích, které nepatří (a pravděpodobně ani nikdy nebudou patřit) mezi mainstream. V navazujících kapitolách proto některé tyto jazyky otestujeme. Bude se jednat jak o historické jazyky a assemblery (a proč nezačít rovnou s legendárním ZX Spectrem a assemblerem mikroprocesoru Zilog Z80?), tak i o jazyky používané v současnosti, které ovšem nepatří mezi (alespoň co se týče používanosti, nikoli nutně kvalit) absolutní špičku.

Poznámka: v oblasti LLM pravděpodobně dojde resp. došlo ke kladné zpětné vazbě – jazyky s mnoha dostupnými zdrojovými kódy budou LLM modely podporovány mnohem lépe, než méně populární jazyky, což ovšem zpětně ovlivní popularitu již populárních (sic) jazyků – dnes totiž mnoho vývojářů očekává od LLM poměrně výraznou pomoc (se všemi z toho plynoucími důsledky).
Analýza a recenze programů psaných v assembleru

Obrázek 12: Rekapitulace trojice zdrojových kódů naprogramovaných v různých assemblerech

Autor: tisnik, podle licence: Rights Managed

8. Program pro smazání obrazovky na ZX Spectru

První program zapsaný v assembleru bude provádět jedinou operaci – zavolá službu resp. subrutinu neboli podprogram, jenž je uložený v paměti ROM ZX Spectra a je tedy programátorům snadno dostupný a navíc i velmi dobře zdokumentovaný. Tento podprogram zajistí smazání obrazovky a otevření kanálu číslo 2 (což je zařízení typu „obrazovka“ neboli screen). Podprogram ROM_CLS (popř. jen CLS, viz například tento popis) je v paměti ROM ZX Spectra umístěn na adrese 0×0DAF a nevyžaduje žádné parametry, které by se jinak předávaly buď v pracovních registrech nebo s využitím speciálním zásobníku numerických hodnot typu float.

Zavolání podprogramu zajišťuje u mikroprocesorů Zilog Z80 (a i na mnoha dalších mikroprocesorových architekturách) instrukce CALL, která uloží návratovou adresu na zásobník a následně provede skok. Náš program se tedy bude skládat ze dvou instrukcí CALL + RET:

ENTRY_POINT   equ $8000
ROM_CLS       equ $0DAF
 
        org ENTRY_POINT
 
start:
        call ROM_CLS    ; smazání obrazovky a otevření kanálu číslo 2 (screen)
        ld   A, 42      ; kód znaku '*' pro tisk
        call 0x10       ; zavolání rutiny v ROM
        ret             ; návrat z programu do BASICu
 
end ENTRY_POINT

Analýza a recenze dopadne poměrně uspokojujícím způsobem – program byl zanalyzován korektně:

Analýza a recenze programů psaných v assembleru

Obrázek 13: Vygenerovaný sekvenční diagram, povšimněte si, že je doplněn i popisek volané služby

Autor: tisnik, podle licence: Rights Managed
Analýza a recenze programů psaných v assembleru

Obrázek 14: Našel se pouze jeden problém – nekontrolujeme, jestli zavolaná služba operačního systému nevrátila informaci o chybě

Autor: tisnik, podle licence: Rights Managed

9. Programová smyčka realizovaná instrukcí DJNZ

Ve druhém programu napsaném v assembleru mikroprocesoru Zilog Z80 pro ZX Spectrum je vytvořena programová smyčka realizovaná instrukcí DJNZ. Program provede 256 zápisů do atributové paměti ZX Spectra a změní tedy barvy v horní části obrazovky:

ATTRIBUTE_ADR equ $5800
ENTRY_POINT   equ $8000
 
        org ENTRY_POINT
 
start:
        ld hl, ATTRIBUTE_ADR  ; adresa pro zápis
        ld b, l               ; zapisovaná hodnota + počitadlo smyčky
 
loop:
        ld (hl),l             ; zápis hodnoty na adresu (HL)
        inc l                 ; zvýšení adresy i zapisované hodnoty
        djnz loop             ; kombinace dec b + jp NZ, loop
                              ; snížení hodnoty počitadla
                              ; skok pokud se ještě nedosáhlo nuly
        ret
 
end ENTRY_POINT

V tomto případě je nalezena potenciální chyba, která spočívá v tom, že v programové smyčce modifikujeme pouze hodnotu osmibitového registru L a nikoli zdvojeného šestnáctibitového registru HL, což je ovšem ve skutečnosti přesně požadované chování (analýza by si mohla všimnout toho, že počitadlo je osmibitové).

Analýza a recenze programů psaných v assembleru

Obrázek 15: Analýza programu s počítanou programovou smyčkou realizovanou instrukcí DJNZ

Autor: tisnik, podle licence: Rights Managed

10. Program vytvořený v assembleru mikroprocesorů Intel 8086 pro IBM PC+DOS

Třetí a současně i poslední program vytvořený v assembleru je pro změnu určen pro počítače IBM PC vybavené (minimálně) mikroprocesorem Intel 8086. V tomto programu je implementována subrutina (podprogram) určená pro vykreslení pixelu zadanou barvou v grafickém režimu karty VGA s rozlišením 320×200 pixelů a s 256 barvami. Subrutina je navržena právě pro původní čip 8086 resp. 8088:

;-----------------------------------------------------------------------------
 
BITS 16         ; 16bitovy vystup pro DOS
CPU 8086        ; specifikace pouziteho instrukcniho souboru
 
;-----------------------------------------------------------------------------
 
; ukonceni procesu a navrat do DOSu
%macro exit 0
        mov     ah, 0x4c
        int     0x21
%endmacro
 
; vyprazdneni bufferu klavesnice a cekani na klavesu
%macro wait_key 0
        xor     ax, ax
        int     0x16
%endmacro
 
; nastaveni grafickeho rezimu
%macro gfx_mode 1
        mov     ah, 0
        mov     al, %1
        int     0x10
%endmacro
 
;-----------------------------------------------------------------------------
org  0x100        ; zacatek kodu pro programy typu COM (vzdy se zacina na 256)
 
start:
        gfx_mode 0x13       ; nastaveni rezimu 320x200 s 256 barvami
        mov ax, 0xa000      ; video RAM v textovem rezimu
        mov es, ax
        xor di, di          ; nyni ES:DI obsahuje adresu prvniho pixelu ve video RAM
 
        mov ax, 0
opak:
        mov bx, ax          ; y-ová souřadnice
 
        push ax
        mov cl, al          ; barva
        call putpixel       ; vykreslení pixelu
        pop ax
 
        push ax
        mov cl, al          ; barva
        add ax, 10          ; horizontalni posun useky
        call putpixel       ; vykreslení pixelu
        pop ax
 
        push ax
        mov cl, al          ; barva
        add ax, 20          ; horizontalni posun useky
        call putpixel       ; vykreslení pixelu
        pop ax
 
        inc ax              ; pusun x+=1, y+=1
        cmp ax, 200         ; hranice obrazovky?
        jne opak            ; ne-opakujeme
 
        wait_key            ; cekani na klavesu
        exit                ; navrat do DOSu
 
; Vykresleni pixelu
; AX - x-ova souradnice
; BX - y-ova souradnice (staci len BL)
; CL - barva
putpixel:
        mov dx, 0xa000     ; zacatek stranky video RAM
        mov es, dx         ; nyni obsahuje ES stranku video RAM
 
        mov di, ax         ; horizontalni posun pocitany v bajtech
 
        mov ax, bx         ; y-ova souradnice
        shl ax, 1          ; y*2
        shl ax, 1          ; y*4
        shl ax, 1          ; y*8
        shl ax, 1          ; y*16
        shl ax, 1          ; y*32
        shl ax, 1          ; y*64
        add di, ax         ; pricist cast y-oveho posunu
        shl ax, 1          ; y*128
        shl ax, 1          ; y*256
        add di, ax         ; pricist zbytek y-oveho posunu
                           ; -> y*64 + y*256 = y*320
 
        mov [es:di], cl     ; vlastni vykresleni pixelu
 
        ret

CodeRabbit korektně odvodí sekvenční diagram popisující činnosti programu:

Analýza a recenze programů psaných v assembleru

Obrázek 16: Sekvenční diagram popisující činnosti programu.

Autor: tisnik, podle licence: Rights Managed

Následně je nabídnuta možnost optimalizace programu, která spočívá v lepším využití pracovních registrů, což je ovšem nekorektní, protože volaná subrutina „ničí“ jak obsah registru AX, tak i DX. Aplikací této úpravy se můžeme snadno přesvědčit, že program již nebude funkční:

Analýza a recenze programů psaných v assembleru

Obrázek 17: Nabídnutá možnost optimalizace využití pracovních registrů

Autor: tisnik, podle licence: Rights Managed

Nakonec je nabídnuta ještě jedna úprava, a to náhrada sekvence instrukcí SHL AX, 1 za SHL AX, jiná_hodnota. Toto řešení není striktně řečeno korektní, protože jsme vyžadovali překlad pro mikroprocesor Intel 8086/8088, který tyto instrukce nepodporuje:

Analýza a recenze programů psaných v assembleru

Obrázek 18: Nabídka úpravy a optimalizace instrukcí pro bitové posuny

Autor: tisnik, podle licence: Rights Managed
Poznámka: v tomto konkrétním případě tedy CodeRabbit úspěšný nebyl a na jeho rady není dobré se spolehnout.

11. Výpočet největšího společného dělitele v BBC BASICu

V dalším kroku se pokusíme o analýzu typického „školního“ algoritmu z osmdesátých let. Jedná se o algoritmus určený pro výpočet největšího společného dělitele, který je realizován ve slavném BBC BASICu, ovšem ve skutečnosti ho bude možné bez větších úprav použít i v dalších interpretrech BASICů (Atari BASIC atd.). Program postupně provádí rozdíl mezivýsledků až do stavu, kdy budou oba mezivýsledky totožné. Zcela jsme tudíž odstranili operaci podílu resp. výpočtu zbytku po dělení:

1 REM *****************************
2 REM Vypocet nejvetsiho spolecneho
3 REM delitele.
4 REM
5 REM Uprava pro BBC BASIC
6 REM
7 REM *****************************
8 REM
9 REM
10 PRINT "X=";
20 INPUT X
30 PRINT "Y=";
40 INPUT Y
50 IF X=Y THEN PRINT "GCD: ";X:END
60 IF X>Y THEN X=X-Y:GOTO 50
70 IF X<Y THEN Y=Y-X:GOTO 50
998 REM finito
999 STOP

12. Analýza a recenze výpočtu

Opět se podívejme, jakým způsobem byl tento zdrojový kód analyzován:

Analýza algoritmu pro výpočet největšího společného dělitele v BASICu

Obrázek 19: CodeRabbit nás zcela jasně upozorní na chybějící podmínky a dokonce doplní kód pro otestování vstupů

Autor: tisnik, podle licence: Rights Managed
Analýza algoritmu pro výpočet největšího společného dělitele v BASICu

Obrázek 20: Ve druhém případě jsme upozorněni na to, že algoritmus není efektivní a je nabídnuta jeho úprava (model evidentně tento algoritmus velmi dobře „zná“

Autor: tisnik, podle licence: Rights Managed

13. Oficiální demonstrační příklad pro jazyk C3

Dále si vyzkoušíme, jakým způsobem dokáže CodeRabbit pracovat s relativně novým programovacím jazykem nazvaným C3, který je popsán na stránkách https://c3-lang.org/. Jedná se o jazyk odvozený od klasického céčka, ovšem s několika poměrně praktickými vylepšeními. Pro otestování jsem zvolil příklad, který je dostupný přímo na oficiálních stránkách tohoto jazyka. Jedná se o ukázku použití výčtového datového typu. Tento příklad jsem ovšem „rozbil“: z výčtového typu byl odstraněn jeden prvek s identifikátorem MEDIUM, takže je zdrojový kód nekorektní:

enum Height : uint
{
    LOW,
    HIGH,
}
 
fn void demo_enum(Height h)
{
    switch (h)
    {
        case LOW:
        case MEDIUM:
            io::printn("Not high");
            // Implicit break.
        case HIGH:
            io::printn("High");
    }
 
    // This also works
    switch (h)
    {
        case LOW:
        case MEDIUM:
            io::printn("Not high");
            // Implicit break.
        case Height.HIGH:
            io::printn("High");
    }
 
    // Completely empty cases are not allowed.
    switch (h)
    {
        case LOW:
            break; // Explicit break required, since switches can't be empty.
        case MEDIUM:
            io::printn("Medium");
        case HIGH:
            break;
    }
 
    // special checking of switching on enum types
    switch (h)
    {
        case LOW:
        case MEDIUM:
        case HIGH:
            break;
        default:    // warning: default label in switch which covers all enumeration value
            break;
    }
 
    // Using "nextcase" will fallthrough to the next case statement,
    // and each case statement starts its own scope.
    switch (h)
    {
        case LOW:
            int a = 1;
            io::printn("A");
            nextcase;
        case MEDIUM:
            int a = 2;
            io::printn("B");
            nextcase;
        case HIGH:
            // a is not defined here
            io::printn("C");
    }
}

14. Analýza a recenze příkladu

V tomto konkrétním případě CodeRabbit velmi přesně zjistil, že chybí jeden z prvků výčtového datového typu a navrhl příslušnou změnu (dokonce i na správném místě ve zdrojovém kódu):

Analýza programu napsaného v jazyku C3.

Obrázek 21: Návrh na změnu zdrojového kódu napsaného v jazyku C3 takovým způsobem, aby ho bylo možné korektně přeložit

Autor: tisnik, podle licence: Rights Managed

15. Rekurzivní výpočet naprogramovaný v jazyce Standard ML

Ve zbylých kapitolách si vyzkoušíme, jakým způsobem dokáže CoreRabbitAI rozpoznat a zpracovat zdrojové kódy naprogramované v některém z programovacích jazyků odvozených od ML (což jsou jazyky s pokročilou typovou inferencí). Jedná se o jazyky Standard ML, OCaml a v neposlední řadě taktéž o jazyk F#. Začneme zdrojovým kódem napsaným ve Standard ML. Jedná se o naprosto typický „školní“ příklad výpočtu Fibonacciho posloupnosti s využitím rekurze (a tedy bez dalších optimalizací):

(* Implementace výpočtu Fibonacciho posloupnosti s využitím pattern matchingu *)
 
fun fib 0 = 0
  | fib 1 = 1
  | fib n = fib (n - 1) + fib (n - 2);
Poznámka: tento příklad jsem zvolil schválně z toho důvodu, že je použitý v manuálech a proto je poměrně velká šance, že na něj bude model natrénován.

16. Analýza a recenze příkladu

V tomto případě dokázal CodeRabbit ne zcela korektně navrhnout, aby došlo k přepisu zdrojového kódu do programovacího jazyka OCaml, který je od Standard ML odvozen. Pravděpodobně bot došel k tomuto závěru na základě koncovky zdrojového kódu, protože v případě Standard ML se může použít i koncovka .sml a nikoli .ml (dejte si tedy pozor na korektní koncovky, aby nebyly jazykové modely „zmateny“):

Výsledek analýzy programu vytvořeného v jazyce Standard ML

Obrázek 22: Výsledek analýzy programu vytvořeného v jazyce Standard ML

Autor: tisnik, podle licence: Rights Managed
Poznámka: samotný přepis je však navržen korektně.

17. Rekurzivní výpočet naprogramovaný v jazyce OCaml

V dalším kroku přejdeme z jazyka Standard ML na jazyk OCaml, což je programovací jazyk patřící do stejné rodiny programovacích jazyků, je ovšem novější, podporuje objektově orientované programování a taktéž je v porovnání se Standard ML poněkud komplikovanější (syntaxe i sémantika). Výpočet Fibonacciho posloupnosti se v tomto jazyku zapisuje sice syntakticky odlišně, ovšem sémantika je prakticky totožná (až na to, že nyní musíme explicitně specifikovat, že definujeme rekurzivní funkci):

let rec fib = function
    0 -> 0
  | 1 -> 1
  | n -> fib (n-1) + fib (n-2)
 
 
 
fib 10

18. Analýza a recenze příkladu

Nyní nastala mnohem zajímavější situace, protože CodeRabbit navrhl, aby se neefektivní algoritmus s přímou rekurzí přepsal do podoby, ve které je možné využít koncové rekurze. A v komentáři dokonce navrhl ještě lepší řešení založené na paměti (mezi)výsledků:

Výsledek analýzy programu vytvořeného v jazyce Standard ML

Obrázek 23: Výsledek analýzy programu vytvořeného v jazyce OCaml

Autor: tisnik, podle licence: Rights Managed

19. Nepatrně složitější příklad naprogramovaný v jazyce OCaml

Poslední zdrojový kód, který si necháme okomentovat nástrojem CodeRabbit, je opět naprogramován v jazyce OCaml. Jedná se o implementaci nového datového typu color, přičemž barvy mohou být reprezentovány různými hodnotami (různé barvové modely, smíchání dvou barev s využitím alfa kanálu atd.):

linux_sprava_tip

type color =
  | BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
  | HSV of float * float * float
  | Mix of float * color * color
;;

Navíc jsou doprogramovány i funkce určené pro konverzi mezi různými reprezentacemi barev:

let scale_component x =
  int_of_float (255.*.x)
;;
 
 
let scale_rgb r g b =
  (scale_component r,
   scale_component g,
   scale_component b)
;;
 
 
let hsv_to_rgb_ h s v =
  let h =
    match h with
    | 1.0 -> 0.0
    | _ -> h
  in
  let i = int_of_float (h*.6.0) in
  let f = h *. 6.0 -. (float i) in
  let w = v *. (1.0 -. s) in
  let q = v *. (1.0 -. s*.f) in
  let t = v *. (1.0 -. s*.(1.0 -. f)) in
  match i with
  | 0 -> scale_rgb v t w
  | 1 -> scale_rgb q v w
  | 2 -> scale_rgb w v t
  | 3 -> scale_rgb w q v
  | 4 -> scale_rgb t w v
  | 5 -> scale_rgb v w q
  | _ -> (0, 0, 0)
;;
 
 
let hsv_to_rgb h s v =
  match s with
  | 0.0 -> (scale_rgb v v v)
  | _ -> (hsv_to_rgb_ h s v)
;;
 
 
type basic_color =
  | Black
  | Red
  | Green
  | Yellow
  | Blue
  | Magenta
  | Cyan
  | White
;;
 
 
type brightness =
  | Dark
  | Bright
;;
 
 
type color =
  | BasicColor of basic_color * brightness
  | Gray of int
  | RGB of int * int * int
  | HSV of float * float * float
  | Mix of float * color * color
;;
 
 
let basic_color_to_rgb = function
  | Black -> (0, 0, 0)
  | Red -> (255, 0,0)
  | Green -> (0, 255, 0)
  | Yellow -> (255, 255, 0)
  | Blue -> (0, 0, 255)
  | Magenta -> (255, 0, 255)
  | Cyan -> (0, 255, 255)
  | White -> (255, 255, 255)
;;
 
 
let darker = function
  | (r, g, b) -> (r/2, g/2, b/2)
;;
 
 
let brightness rgb brightess =
  match brightess with
  | Dark -> darker rgb
  | Bright -> rgb
;;

20. Analýza a recenze příkladu

Popravdě jsem při analýze výše uvedeného příkladu očekával, že CodeRabbit navrhne sofistikovanější řešení založené na OOP atd., ovšem ve skutečnosti byl nalezen pouze jeden nedostatek – špatné jméno parametru funkce brightness (což je dobře, protože takové přepisy dělám poměrně často):

Výsledek analýzy programu vytvořeného v jazyce Standard ML

Obrázek 24: Výsledek analýzy programu vytvořeného v jazyce OCaml

Autor: tisnik, podle licence: Rights Managed

21. Odkazy na Internetu

  1. Code Rabbit home page
    https://www.coderabbit.ai/
  2. Nástroje pro tvorbu UML diagramů:
    http://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/
  3. Unified Modeling Language
    https://en.wikipedia.org/wi­ki/Unified_Modeling_Langu­age
  4. UML basics: The sequence diagram
    http://www.ibm.com/develo­perworks/rational/library/3101­.html
  5. UML 2 State Machine Diagrams: An Agile Introduction
    http://www.agilemodeling.com/ar­tifacts/stateMachineDiagram­.htm
  6. PlantUML (home page)
    http://plantuml.sourceforge.net/
  7. PlantUML (download page)
    http://sourceforge.net/pro­jects/plantuml/files/plan­tuml.jar/download
  8. PlantUML (Language Reference Guide)
    http://plantuml.sourcefor­ge.net/PlantUML_Language_Re­ference_Guide.pdf
  9. Plain-text diagrams take shape in Asciidoctor!
    http://asciidoctor.org/new­s/2014/02/18/plain-text-diagrams-in-asciidoctor/
  10. Graphviz – Graph Visualization Software
    http://www.graphviz.org/
  11. graphviz (Manual Page)
    http://www.root.cz/man/7/graphviz/
  12. dot (Manual page)
    http://www.root.cz/man/1/dot/
  13. Ditaa home page
    http://ditaa.sourceforge.net/
  14. Ditaa introduction
    http://ditaa.sourceforge.net/#intro
  15. Ditaa usage
    http://ditaa.sourceforge.net/#usage
  16. Node, Edge and Graph Attributes
    http://www.graphviz.org/doc/in­fo/attrs.html
  17. Graphviz (Wikipedia)
    http://en.wikipedia.org/wiki/Graphviz
  18. z80 standalone assembler
    https://www.asm80.com/one­page/asmz80.html
  19. Z80 Assembly programming for the ZX Spectrum
    https://www.chibiakumas.com/z80/ZXSpec­trum.php
  20. 8-BIT SMACKDOWN! 65C02 vs. Z80: slithy VLOGS #6
    https://www.youtube.com/wat­ch?v=P1paVoFEvyc
  21. Instrukce mikroprocesoru Z80
    https://clrhome.org/table/
  22. Z80 instructions: adresní režimy atd.
    https://jnz.dk/z80/instructions.html
  23. Z80 Instruction Groups
    https://jnz.dk/z80/instgroups.html
  24. R. T. RUSSELL: The home of BBC BASIC
    http://www.rtrussell.co.uk/
  25. R. T. RUSSELL: A History of BBC BASIC
    http://www.cix.co.uk/~rrus­sell/bbcbasic/history.html
  26. General-Purpose, Industrial-Strength, Expressive, and Safe
    https://ocaml.org/
  27. OCaml playground
    https://ocaml.org/play
  28. Online Ocaml Compiler IDE
    https://www.jdoodle.com/compile-ocaml-online/
  29. Get Started – OCaml
    https://www.ocaml.org/docs
  30. Get Up and Running With OCaml
    https://www.ocaml.org/docs/up-and-running
  31. Better OCaml (Online prostředí)
    https://betterocaml.ml/?ver­sion=4.14.0
  32. Learn X in Y minutes Where X=Standard ML
    https://learnxinyminutes.com/doc­s/standard-ml/
  33. CSE307 Online – Summer 2018: Principles of Programing Languages course
    https://www3.cs.stonybrook­.edu/~pfodor/courses/summer/cse307­.html
  34. CSE307 Principles of Programming Languages course: SML part 1
    https://www.youtube.com/wat­ch?v=p1n0_PsM6hw
  35. CSE 307 – Principles of Programming Languages – SML
    https://www3.cs.stonybrook­.edu/~pfodor/courses/summer/CSE307/L01_SML­.pdf
  36. SML, Some Basic Examples
    https://cs.fit.edu/~ryan/sml/in­tro.html
  37. History of programming languages
    https://devskiller.com/history-of-programming-languages/
  38. History of programming languages (Wikipedia)
    https://en.wikipedia.org/wi­ki/History_of_programming_lan­guages
  39. The F# Survival Guide
    https://web.archive.org/web/20110715231625/htt­p://www.ctocorner.com/fshar­p/book/default.aspx
  40. Object-Oriented Programming — The Trillion Dollar Disaster
    https://betterprogramming.pub/object-oriented-programming-the-trillion-dollar-disaster-92a4b666c7c7
  41. Goodbye, Object Oriented Programming
    https://cscalfani.medium.com/goodbye-object-oriented-programming-a59cda4c0e53
  42. So You Want to be a Functional Programmer (Part 1)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-1–1f15e387e536
  43. So You Want to be a Functional Programmer (Part 2)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-2–7005682cec4a
  44. So You Want to be a Functional Programmer (Part 3)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-3–1b0fd14eb1a7
  45. So You Want to be a Functional Programmer (Part 4)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-4–18fbe3ea9e49
  46. So You Want to be a Functional Programmer (Part 5)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-5-c70adc9cf56a
  47. So You Want to be a Functional Programmer (Part 6)
    https://cscalfani.medium.com/so-you-want-to-be-a-functional-programmer-part-6-db502830403 

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.