Search code examples
if-statementfull-text-searchcode-search

How can I find all if-statements without an else?


I've got quite a lot of code in which sometimes if-statements have been written, without a matching else-statement. In some places, this is an issue, because when something fails, the failure is not handled correctly. I want to manually confirm all places an if-without-an-else occurs.

I've tried using regex, but I haven't succeeded in finding a way to match the right bracket, since regex cannot really handle nested brackets.

The code I want to find looks like this:

if( ... ) {

...

}

The code I don't want to find, looks like this:

if( ... ) {

...

} 
else {

... 

}

How can I list all these places, so I can go through them?


Solution

  • As you noticed by yourself, generally this has to be done with some sort of parser. With regex it's simply just pain and there are a lot of parser / grammar modules out there.

    I created a one time solution script (in nodejs), which uses regex and the number of indents for matching starting to closing if / else statements. It works if following conditions are fullfilled:

    • The file is properly formatted, a starting if has the same numbers of indents as the closing else

    this works:

    if(a) {
       if(b) {
          
       } else { // i have same amount of indents as my matching if, works!
         
       }
    }
    
    

    this does NOT work:

    if(a) {
        if(b) {
            } else { // i have to many indents.
               
            }
      
    }
    
    
    • There cannot be if and else on the same line ! (should not be ususally..)

    Here's the script i came up with, it stores all line numbers with an if which does not have an else. U can check line numbers and click on a given if, then vs code highlights the ending parenthesis and you can add the else. It uses lodash but only for flattening results in the end. It's not truely dependent..

    import { EOL } from 'os'
    import * as fs from 'fs'
    import { flatten } from 'lodash'
    
    // Import your code file:
    const code = fs.readFileSync('code.js').toString()
    
    // Regex for matching lines holding if's..
    // if(...)
    // if() {
    // if
    const match_if = /if\s?\([^)]+\)\s+?/
    
    // Regex for matching lines holding else:
    // }else
    // } else
    // else {
    // }else{
    // ...
    const match_else = /(\n|})\s?else({|\s|$)/
    
    
    // small helper fpr calculating indents for first character appearing..
    const calculate_indent = (line: string) => {
        let spaces = 0
        while(line.charAt(spaces) === ' ') {
            spaces++
        }
        return spaces
    }
    
    // split up the lines by system operator
    const lines = code.split(EOL)
    const indexes = []
    
    // iterate all lines.
    for(let i =0; i < lines.length; i++) {
        const line = lines[i]
    
        // do not process empty lines..
        if(!line || line.trim().length < 2) {
            continue
        }
    
    
        // Match the line against if regex
        const if_match = line.match(match_if)
    
        // process the match if there is an if found!
        if(if_match && if_match.length > 0) {
    
            // Count the indents, we need this latere
            // to match any found "else" to the correct if.
            // make sure your code is properly formatted and indent levels are correct!!
            const level = calculate_indent(line)
    
    
            // Make sure under given level is an array
            if(Array.isArray(indexes[level]) === false) {
                indexes[level] = []
            }
    
            // store line number to the given level. 
            // For a human the line number is index + 1
            indexes[level].push(i+1)
        }
    
    
        // We check the line for else. Note:
        // The whole function won't work for any if() else combinations on the same line..
        const else_match = line.match(match_else)
        if(else_match && else_match.length > 0) {
            
        
            // count index to identify level for matching the correct if.
            const level = calculate_indent(line)
    
            if(Array.isArray(indexes[level])) {
                // remove the last stored if. We know it ended here!
                indexes[level].splice(indexes[level].length-1, 1)
            } 
    
        }
    
    }
    
    console.log('lines:', lines.length)
    console.log('results:', indexes)
    
    // flatten / remove empty items / sort
    const line_numbers = flatten(indexes).filter(d => !!d).sort((a,b) => a-b)
    
    console.log('final lines:', line_numbers)
    
    

    I tested it with this code:

    if(sdf) {
    
        if(sdf) {
                
        }
    }
    
    
    
    if(asdf) {
    
        if(sdfsdf) {
    
    
            if(sdfsdf) {
    
            } else {
    
            }
        } else {
    
        }
    } else {
        
    }
    
    
    if(asdf) {
    
        if(sdfsdf) {
    
        } else {
            if(sdf) {
    
            }
    
            if(sdf) {
                
            } else {
    
            }
    
    
            if(sdf) {
    
            }
        }
    }
    

    Results => final lines: [ 2, 4, 29, 34, 45 ] You may wanna test a little bit more before using it ! But it should work when the indents are correct. This article is also related and nice to read! => why you should not use regex to parse tree structures....