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?
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:
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.
}
}
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....