Staatilise analüüsi hoiatuste lahendamine alati valede tingimuste korral

Kuidas lahendada staatilise analüüsi hoiatusi alati valede tingimuste ja kättesaamatute harude korral

Teie staatiline analüsaator on märkinud tingimuse alati vääraks. Hoiatus näeb välja üks järgmistest, olenevalt teie tööriistast:

  • SonarQube: "Tingimuslaused, mis on alati tõesed või väärad, võivad viia kättesaamatu koodini." (RSPEC-2583)
  • IntelliJ / Inspectopedia: "Koodi ei analüüsita, kuna tingimus hinnatakse staatiliselt vääraks"
  • Checkmarx / Fortify: „Kättesaamatu kood, tuvastatud surnud haru”
  • ESLint: „mitte-konstantne-tingimus, ootamatu-konstantne-tingimus”
  • Pylint: „W0125: Konstantse väärtusega tingimuslause kasutamine”

Kõik need ütlevad sama asja: analüsaator on analüüsi ajal, ilma koodi käivitamata, tõestanud, et tingimuslause konkreetne haru ei saa kunagi käivituda. Tingimus, mis seda haru kaitseb, on alati väär. Kõik selle haru sees on surnud kood.

See juhend käsitleb nende hoiatuste põhjuseid, kuidas tuvastada, millisesse kategooriasse iga hoiatus kuulub, iga kategooria õiget lahendust, millal on hoiatuse summutamine asjakohane ja mida alati valed tingimused pärand-COBOL-koodis migratsiooni ulatuse kohta näitavad.

Aegunud ärireeglid on endiselt teie koodis

SMART TS XL tuvastab EVALUATE harud, mis viitavad tootekoodidele, olekuväärtustele ja klienditüüpidele, mida tootmisandmetes enam ei eksisteeri.

SAAGE LISATEAVET…

Kuidas staatiline analüüs tuvastab alati valesid tingimusi

Kaasaegsed staatilised analüsaatorid kasutavad alati valede tingimuste tuvastamiseks sümboolset teostust ja andmevoo analüüsi, mitte lihtsat mustrite sobitamist. Analüsaator jälgib iga muutuja võimalikke väärtusi programmi igas punktis. Tingimusliku tingimuseni jõudes hindab see, kas tingimus saab muutujatele teadaolevate piirangute korral kunagi tõene olla.

Kolm peamist mehhanismi tagavad alati vale tuvastamise:

Sümboolne teostus. Analüsaator määrab muutujatele sümboolsed väärtused ja jälgib, kuidas toimingud neid väärtusi piiravad. Kui muutuja x on määratud 0 real 3 ja võrreldi x > 10 7. real, ilma vahepealse omistamiseta, on tingimus tõestatavalt väär.

Tüübisüsteemi piirangud. Mõned tingimused on muutuja tüübi tõttu võimatud. Mitte-null objektiviite võrdlemine nulliga pärast mitte-null väidet või enum väärtuse võrdlemine juhtumiga väljaspool selle defineeritud hulka.

Boole'i ​​algebra lihtsustus. Liittingimused, mis lihtsustuvad false olenemata muutujate väärtustest: (a && !a), (x > 10 && x < 5), (!flag || false).

SonarQube'i reegel RSPEC-2583 kasutab spetsiifiliselt sümboolset teostust: see jälgib muutujate olekut juhtimisvoo graafiku kaudu ja märgistab tingimused, mille puhul sümboolne olek muudab tingimuse tõestatavalt vääraks. IntelliJ hoiatus „tingimus hinnatakse staatiliselt vääraks” kasutab sama lähenemisviisi.

Neli algpõhjust

Alati vale hoiatuse õige lahendamine sõltub sellest, kas on õige tuvastada, milline neljast algpõhjusest kehtib. Lahendus on iga juhtumi puhul erinev.

Põhjus 1: loogiline vastuolu

Tingimus sisaldab loogilist võimatust, kahte alamtingimust, mis ei saa mõlemad samaaegselt tõesed olla, või liittingimust, mis taandub järgmisele: false Boole'i ​​algebra abil.

Java

// Example: mutually exclusive subconditions
int score = getScore();
if (score > 90 && score < 50) {  // Always false: no integer is both > 90 and < 50
    applyBonus();                 // Never executes
}

// Example: boolean tautology negation
boolean active = isActive();
if (active && !active) {  // Always false: a && !a = false
    sendNotification();   // Never executes
}

// Example: range check already guaranteed
if (value >= 0) {
    if (value < 0) {  // Always false: already know value >= 0
        handleNegative();  // Never executes
    }
}

Resolutsioon: Paranda loogikat. Kas tingimus sisaldab trükiviga (arendaja pidas silmas score > 90 || score < 50) või haru tuleks eemaldada. Vahemiku kontrolli näites on sisemine if on üleliigne ja tuleks kustutada.

Põhjus 2: Ärireeglite akumuleerunud muudatused

See on pikaajaliste koodibaaside kõige olulisem põhjus. Tingimus, mis oli kirjutamise ajal kehtiv ja tähendusrikas, on hilisemate koodimuudatuste tõttu muutunud jäädavalt vääraks, ilma et keegi oleks nüüdseks surnud haru eemaldanud.

Java

// 2018: processType could be A, B, or C
// 2023: type C was deprecated and removed from the system
// The processType field is now guaranteed to never equal "C"
// but nobody removed the branch that handled it

public void process(String processType) {
    if (processType.equals("A")) {
        handleTypeA();
    } else if (processType.equals("B")) {
        handleTypeB();
    } else if (processType.equals("C")) {  // Always false since 2023
        handleTypeC();                      // 47 lines of dead business logic
    }
}

püüton

# Python equivalent: accumulated rule changes
def calculate_rate(customer_tier: str) -> float:
    if customer_tier == "GOLD":
        return 0.05
    elif customer_tier == "SILVER":
        return 0.03
    elif customer_tier == "BRONZE":
        # BRONZE tier was eliminated in Q2 2021
        # All BRONZE customers were migrated to SILVER
        # This branch has been dead for 4+ years
        return 0.01  # Never executes
    return 0.0

Lahendus: Kontrollige ärikonteksti ja eemaldage seejärel surnud haru täielikult. Ärireegel, mis tingimuse võimalikuks tegi, on muutunud. Selle rakendanud koodi pole enam vaja. Dokumenteerige eemaldamine commit-sõnumis: „Pronksist hinnaklassi haru eemaldati, tase kõrvaldati 2021. aasta teises kvartalis, kõik kliendid migreeriti Hõbedasele tasemele.“

Põhjus 3: tulevikukindluse tagamiseks mõeldud kaitseprogramm

Mõned alati väärad tingimused on tahtlikud – arendaja kirjutas need teadmisega, et tingimus ei saa hetkel tõene olla, kaitseks tulevaste koodimuudatuste eest, mis võivad selle tõeseks muuta.

Java

// SonarQube RSPEC-2583 explicitly exempts these:
final boolean DEBUG_MODE = false;
if (DEBUG_MODE) {                // Always false -- intentional
    dumpDiagnostics();           // Not unreachable in intent, only in fact
}

// Type narrowing that appears redundant
String value = getValue();
if (value == null || value.isEmpty()) {
    return;
}
// At this point, static analysis knows value is non-null and non-empty
if (value == null) {  // Always false -- already checked above
    throw new IllegalStateException(); // Defensive guard
}

SonarQube RSPEC-2583 toob selgesõnaliselt välja erandi: „Kui tingimus on üks lõplik tõeväärtus” või „kui tingimus on sõna-sõnalt tõene või väär”, käsitletakse neid tahtlikena ja neid ei märgistata.

Lahendus: Kui tingimus on silumislipp või kompileerimisaja konstant: jätke see alles ja lisage summutamise annotatsioon, kui tööriist selle märgistab. Kui tegemist on kaitsva nullkontrolli või vahemikukaitsega, mis tundub üleliigne, kuid on tegelikult kaitsev: struktureerige kood ümber, et muuta kaitse selgesõnaliseks teisel tasemel, või summutage see dokumenteeritud põhjendusega.

4. algpõhjus: tööriista valepositiivsed tulemused käitusaja konfiguratsioonist

Staatilised analüsaatorid ei näe käitusaja konfiguratsiooni, keskkonnamuutujaid, peegeldust, annotatsioonide töötlemist ega sõltuvuste süstimist. Kood, mis analüsaatori jaoks tundub alati väär, võib olla kättesaadav käitusaja mehhanismi kaudu, mida analüsaator ei suuda modelleerida.

Java

// IntelliJ / Inspectopedia note: "This may cause false-positives if any kind
// of code postprocessing is used, for example, if an annotation processor
// later replaces the method body with something useful."

@ConditionalOnProperty(name = "feature.experimental", havingValue = "true")
public class ExperimentalFeature {
    // This class may appear unreachable if the property is false in the
    // test configuration the analyzer uses -- but it IS reachable in prod
}

// Method contract that the analyzer cannot see
void run() {
    performAction();
    System.out.println("Done"); // IntelliJ flags as unreachable
}
@Contract("-> _") // Tells the analyzer: method does NOT always throw
static void performAction() { throw new AssertionError(); }

Resolutsioon: Lisage sobiv meetodi lepingu annotatsioon (@Contract IntelliJ jaoks @NotNull/@Nullable Kotlini puhul tippige TypeScripti puhul väited), et anda analüsaatorile vajalikku käitusaja teavet. Kui annotatsioonipõhine lahendus pole saadaval, kasutage tööriista summutusmehhanismi koos dokumenteeritud põhjendusega.

COBOL: kus alati valed tingimused paljastavad aastakümneid surnud loogikat

COBOLi ja vanemate suurarvutisüsteemide puhul on alati valedel tingimustel erinev iseloom kui tänapäevases rakenduskoodis ja erinev tähendus meeskondade jaoks, kes neid hooldavad või kaasajastavad.

COBOL-programme ei refaktoreerita nii nagu tänapäevast koodi. Kui ärireegel muutub, maksukood muudetakse, tootetasand kaotatakse või regulatiivne nõue uuendatakse, on tavaks lisada uus loogika ilma vana eemaldamata. Vana EVALUATE WHEN või IF haru jääb lähtekoodi, mida kaitseb tingimus, mis töödeldavate andmete praeguse oleku juures ei saa kunagi tõene olla.

kobol

       EVALUATE WS-PRODUCT-TYPE
           WHEN 'STD'
               PERFORM CALC-STANDARD-RATE
           WHEN 'PRM'
               PERFORM CALC-PREMIUM-RATE
           WHEN 'BAS'
      *        BAS product type retired June 2009
      *        All BAS customers converted to STD
      *        This branch has been dead for 15+ years
               PERFORM CALC-BASIC-RATE
           WHEN 'LEG'
      *        LEG (legacy) type eliminated in 2001 system migration
      *        Should have been removed but was left as "just in case"
               PERFORM CALC-LEGACY-RATE
           WHEN OTHER
               PERFORM HANDLE-UNKNOWN-TYPE
       END-EVALUATE.

Kaks surnud oksa selles HINDAMISES, BAS ja LEG, ei ole vead. Need on ajaloolised artefaktid: ärireeglid, mis kehtisid süsteemi ajaloo teatud punktides ja muutusid jäädavalt kättesaamatuks, kui need tootetüübid eemaldati. Keegi ei eemaldanud neid, sest keegi polnud kindel, kas neid võiks veel vaja minna, ja eksimise hind tundus olevat kõrgem kui nende allesjätmise hind.

Mida COBOL-i alati väärad tingimused näitavad:

Surnud oksad esindavad äriloogikat, mis enam ei kehti. PERFORM CALC-BASIC-RATE ja PERFORM CALC-LEGACY-RATE Sihtmärgid ise võivad olla terved lõigud või COBOL-koodi osad, potentsiaalselt sadu ridu igaüks, mis on sarnaselt surnud. Alati väär tingimus on sisenemispunkt surnud koodi puusse, mis levib PERFORM-seoste kaudu.

100 000-realises COBOL-programmis võivad aegunud ärireeglitest pärit surnud harud moodustada 20–35% kogu koodist. See protsent on migratsiooni ulatus, mida ei pea moderniseerimisprogrammis teisendama, testima ega valideerima. Selle tuvastamine enne migratsiooni planeerimist toob kaasa otsese ulatuse vähendamise ning ulatuse vähendamine planeerimisetapis on alati vale tingimusanalüüsi kõige väärtuslikum rakendus.

Resolutsiooni otsustuspuu

Always-false condition warning received
            │
            ▼
    Is this a compile-time constant
    or debug flag? (DEBUG = false, etc.)
            │
     Yes ───┼─── No
            │         │
    Intentional.      ▼
    Suppress or   Does the condition
    leave as-is.  contain a logic error?
                  (a > 10 && a < 5, etc.)
                        │
                 Yes ───┼─── No
                        │          │
                  Fix the        Has the condition
                  logic bug.     become false due to
                                 a past business rule
                                 change?
                                        │
                                 Yes ───┼─── No
                                        │          │
                              Verify,         Could a runtime
                              then remove     mechanism make
                              the dead        it reachable?
                              branch.               │
                                           Yes ──── No
                                                    │
                                             Add contract
                                             annotation.     Remove the
                                                             dead branch.

Maha surumine: millal ja kuidas

Varjamine on asjakohane, kui tingimus on tõeliselt tahtlik ja tööriistale ei ole võimalik anda lepinguinfot, et mõista, miks. Varjamine ei ole sobiv otsetee, et vältida hoiatuse tegeliku tähenduse uurimist.

Java (SonarQube):

Java

@SuppressWarnings("java:S2583")
if (DEBUG_MODE) {  // Intentional: compile-time debug flag
    dumpState();
}

Java (IntelliJ):

Java

//noinspection ConstantConditions
if (possiblyNull != null) {  // Analyzer cannot see the runtime injection
    process(possiblyNull);
}

Python (Pylint):

püüton

if False:  # pylint: disable=W0125
    # Intentional: placeholder for future implementation
    pass

C# (SonarQube):

csharp

#pragma warning disable S2589
if (IsDebugBuild) {  // Compile-time constant -- intentional
    LogDiagnostics();
}
#pragma warning restore S2589

COBOL (SMART TS XL / kohandatud summutamise kommentaar):

kobol

      *>> SUPPRESS UNREACHABLE: BAS branch retained for audit trail
           WHEN 'BAS'
               PERFORM CALC-BASIC-RATE

Maha surumise distsipliin: Iga mahasurumisega peaks kaasnema kommentaar, mis selgitab, miks tingimus on tahtlik, mitte ainult seda, et seda maha surutakse. Maha surumise kommentaar, mis ütleb // suppress unreachable on hullem kui hoiatuse puudumine, see peidab hoiatuse, selgitamata, miks hoiatus on vale. Hoiatus, mis ütleb // suppress: DEBUG_MODE is a compile-time constant, branch is retained for IDE visibility on vastuvõetav.

Kuidas SMART TS XL Tuvastab portfelli skaalal alati valed tingimused

SMART TS XL'S staatilise koodi analüüs rakendab andmevoo ja juhtimisvoo analüüsi COBOLi, JCLi, Java, Pythoni ja kõigi teiste keskkondades, et tuvastada tingimusi, mis on staatiliselt tõestatavalt alati väärad, portfoolio skaalal, tuhandetes programmides samaaegselt, mitte ükshaaval arendaja IDE-s.

Täpsemalt COBOL-portfellide puhul tähendab see järgmist: iga EVALUATE WHEN-klauslit ja iga IF-tingimust igas programmis analüüsitakse teadaolevate piirangute suhtes sinna sisenevatele andmetele. Tingimused, mis viitavad tootekoodidele, olekumärkidele või tüübikoodidele, mille kehtivad väärtuskomplektid on programmi ajaloo jooksul muutunud, märgistatakse potentsiaalselt surnud harudena. Väljund on surnud koodi inventuur: iga programm, iga surnud haru ja hinnanguline surnud koodi ridade arv, milleni igast alati väärast tingimusest on võimalik jõuda.

Pärandi moderniseerimise analüüs teisendab selle inventuuri ulatuse vähendamiseks: iga surnud haru jäetakse migratsioonispetsifikatsioonist välja enne mis tahes konverteerimise alustamist. Programmil, millel näib olevat 2,000 äriloogika rida, võib olla 600 rida surnud äriloogikat aegunud tootetüüpidest, elimineeritud klienditasanditest ja aegunud arvutusteedest. 1,400 rea migreerimine 2,000 rea asemel vähendab selle programmi ulatust 30% ja surnud harud on migreerimise ulatuse kõige riskikam osa, sest keegi ei tea, mida nad algselt tegema pidid.

Ettevõtte otsinguvõimalus muudab alati vale väärtusega inventuuri päringutega hallatavaks: leia iga COBOL-programm, mis sisaldab EVALUATE WHEN-klauslit, mis viitab tootekoodile, mida praeguses tootekataloogis pole; iga programm, mis otsib olekulipu väärtust, mida andmebaasis pole alates konkreetsest aastast olemas olnud; iga IF-tingimus, mis viitab andmestruktuuri väljale, mida on pärast tingimuse kirjutamist ümber kasutatud. Need päringud loovad sihitud surnud koodi inventuuri, mille käsitsi koodi läbivaatamine suures COBOL-portfellis võtaks kuid.

Hoiatus on tavaliselt õige

Alati väärate tingimuste staatilise analüüsi hoiatused on enamasti täpsed leiud. Tingimus on alati väär. Haru ei käivitu kunagi. Selle sees olev kood on surnud.

Sobiv vastus ei ole mahasurumine, vaid uurimine. Milline algpõhjus kehtib? Loogiline vastuolu, akumuleerunud ärireeglite muutmine, tahtlik kaitseprogrammeerimine või tööriista valepositiivne tulemus käitusaja konfiguratsioonist? Igal neist on õige lahendus. Kolm neljast viivad surnud koodi eemaldamiseni. Üks viib põhjendusega mahasurumiseni.

Vananenud COBOL-süsteemide puhul ei ole portfelli skaalal alati vale seisundi analüüs eelkõige koodi kvaliteedi harjutus, vaid ulatuse vähendamise harjutus. Ärireeglite muudatuste käigus aastakümnete jooksul kogunenud surnud oksad esindavad migreerimispingutust, mida ei tohiks teha. Nende leidmine enne migreerimisplaani lukustamist on alati vale seisundi analüüsi kõige väärtuslikum rakendus iga COBOL-i moderniseerimisprogrammi planeeriva meeskonna jaoks.

Analüsaator räägib sulle midagi tõsi. Kuula seda.