A group blog from members of the VB team
It’s funny how we come up with a VB feature sometimes. I remember one case pretty well. I was the new dev lead of the VB editor at the time, and I was talking about possible features for “Whidbey” (i.e. Visual Studio 2005) with my program manager, Sam. We’d been talking about snippets, a feature that Sam & I had coincidentally converged upon from different directions, and we were trying to iron out the differences between our designs. Sam brought up a visualization that he’d put together on Power Point, and I quickly prototyped a part of it in C++ (that being the language that the Visual Studio editor was written in starting way back during the original “Vegas” work in ‘96). After we took a look at the result for a few minutes, he suggested a particular visual change (I don’t recall exactly what it was), and I coded it up. Of course, as I’d been doing a lot of coding in VB recently, I forgot to add a semicolon at the end of a line. Thus, when I built it, I got an error which essentially said “You forgot a semicolon.” Already impatient due to wanting to lock down on snippets, I snarled (speaking to the computer as engineers will), “Well, if you knew that I needed a semicolon, then you clearly understood the syntax of everything else around it, so why didn’t you just go ahead & add one if it’s so danged important?”
Sam and I looked at each other. We looked at the error. We looked back at each other, and realized that we were both thinking of the same thing: automated error correction!
And thus a feature was born…
Now, it was unfair of me to have picked on C++ back then (yes, the error message was in fact more polite than I was), as all coding languages have little syntactical quirks that seem like sugar until you miss one. VB (yes, even VB J) certainly has its “gotchas” as well. Furthermore, unless you have a good understanding of the state of a given piece of code, it’s awfully hard to try to correct anything that the user has inadvertently done. (I suppose I could argue that a language should be so intuitive as to not incur mistakes, but alas such a language exists only as a platonic ideal, if at all.) Fortunately, VB has a background compiler which constantly refreshes what we know about the user’s code, so that errors can be corrected as they happen instead of all at once when “Project\Build” is chosen, with that state updated as each error is fixed. That one fact made error correction feasible as a feature for VB.
(As an aside, that’s one thing I’ve always liked about VB – if you make one fix that addresses a bunch of errors, you’ll know that fact immediately –you don’t have to guess how many of the remaining errors you need to deal with. Other languages generally require an explicit rebuild to see what’s left.)
But how to actually fix the errors programmatically? At the time, Visual Basic had slightly less than 1,000 errors, and although some of them were rather arcane, we were in the process of adding yet more errors around generics plus warnings as well and so had a lot of ground to cover. The errors and warnings generally fell into two categories: syntactic and usage. A syntactic error would be something like:
Dim x As Integr ' Oops, bad spelling!
This is a very specific sort of error – just a spelling mistake in the word “Integer,” and easy enough to programmatically determine some possible fixes just by using a dictionary of terms made up of types.
A usage error would be something like:
Shared Sub A1()
Dim x As New A
x.A1() ' Accessing a shared method through an instance gets a warning about lack of change to instance!
But again, the correction is simple: just replace ‘x’ with ‘A,” so that’s no problem either since the machine knows the type of error – it’s likely in this case that the programmer’s intent matches what the machine can figure out. However, consider the following (and deceptively simple) usage error:
Private Sub A1()
Public Sub A2()
x.A1()' Accessing a private method is an error!
Now we have a problem. To the machine, there are two possible solutions: change Private to Public, or eliminate the x.A1() line altogether, neither of which are very reasonable. However, to a human, the most likely fix would be to call A2, which in turn would call A1. (Presumably, A2 would do some other necessary work before/after calling A1.) Without deep and non-performant analysis, that potential solution to the error would certainly elude the machine. Therefore, making suggestions on that sort of error is problematic and essentially beyond the scope of what we could do.
Ultimately, we ended up providing fixes to about 250 errors and warnings – the other 750+ errors were either so esoteric that it didn’t make sense for us to spend time providing corrections for them, or else were of that latter class where the machine would not have (or would not be able to generate) the information to suggest the right call. Fortunately, some user testing showed us that those 250 bugs made up a majority of the types of errors users were likely to run into.
We also spent some time thinking about whether or not errors should be corrected without a user gesture; there was quite a debate on this point. However, ultimately we felt it was right and proper for the user to be responsible whatever went into their code, and so automatically correcting “nteger” to “Integer” (where maybe “UInteger” was meant) was the wrong thing to do and could potentially introduce a nasty problem into the user’s code.
It’s pretty straight-forward to use the error corrector on your errors and warnings. Correctible errors can be identified by a little rectangle glyph which appears at the end of the error squiggle under the offending code. If you hover your mouse over that glyph, it’ll transform into an unexpanded “drop-down” box containing an frantic-looking exclamation mark. Clicking on that box to expand it will do one of two things:
(1) Spelling errors: If the engine thinks that it’s likely that that a spelling error was involved (for example, a type was expected, but the type provided wasn’t recognized), then the user will be presented with a simple context menu with some appropriate spelling choices. Click on one will apply the change to that error instance.
(2) Usage errors and non-spelling syntax errors: The user will be presented with a transitory dialog containing up to three possible fixes. The user is shown what the code would look like after the fix is applied, using strikeout and colors to show the before/after in a panel. Each solution also gets a descriptive name (e.g., “Replace ‘x’ with ‘A’). Either clicking on the name or double-clicking on the panel will apply the fix.
The suggested error corrections may span multiple files, or they may involve adding references or imports in some cases, depending on the type of error. Of course, any corrections you made can be undone (even if they involve multiple files, or just multiple locations within one file) with a simple Ctrl-Z.
There’s more I’d like to see us do with error correction. Certainly, more corrections will likely be added as we add new functionality to the language, and I think that we could probably get creative on a few of the ones we don’t currently solve. Foremost, though, I’d like to batch certain types of error corrections; for example, the aforementioned “accessing shared member through an instance” warning would be a good candidate for that. Those of you who have upgraded projects from VB2002 or VB2003 to VB2005 and suddenly encountered dozens of those warnings will likely know exactly what I mean. J
I’ve got no fun application for you this week, alas (I had to deal with influenza all last week and my brain is reeling). Next week, I’m planning on dissecting snippets to show how you can you can make your own for fun (& profit?). ‘Til then…
I totally agree about compilers being smarter and more helpful. It's one of the reasons I use VB over other languages. I was very disappointed to find that C++/CLI was still fairly unintelligent about things but I guess somethings are immutable.
You're right about another thing, semicolons are more often than not redundant (See T-SQL). Context implies statement boundaries, as the compilers show. So how about taking that a step further in VBx and making the language freeform without mandating line terminators (except where there are ambiguities)?
Actually, getting rid of the line continuation marks (or, to be more correct, making them optional) has been something that we've wanted to do for several years. We even have a pretty good idea of how it might work, what the ambiguous cases are, etc. Alas, time contraints have always pushed it just below the cut line -- it is extremely risky (and ergo QA-intensive) work that cuts across every single scenario, since *any* code might have line continuation marks. However, I can assure you this will remain high on the list of things we really, really, *really* want to look into for what we euphemistically refer to as a "future release."
MSN I NIIPET