Delay's Blog is the blog of David Anson, a Microsoft developer who works with C#, XAML, HTML, and Azure.
My previous post outlined the benefits of automated code analysis and introduced the Delay.FxCop custom code analysis assembly. The initial release of Delay.FxCop included only one rule, DF1000: Check spelling of all string literals, which didn't seem like enough to me, so today's update doubles the number of rules! :) The new rule is DF1001: Resources should be referenced - but before getting into that I'm going to spend a moment more on spell-checking...
DF1000: Check spelling of all string literals
DF1001: Resources should be referenced
What I planned to write for the second code analysis rule was something to check the spelling of .NET string resources (i.e., strings from a RESX file). This seemed like another place misspellings might occur and I'd heard of other custom rules that performed this same task (for example, here's a sample by Jason Kresowaty). However, in the process of doing research, I discovered rule CA1703: Resource strings should be spelled correctly which is part of the default set of rules!
To make sure it did what I expected, I started a new application, added a misspelled string resource, and ran code analysis. To my surprise, the misspelling was not detected... However, I noticed a different warning that seemed related: CA1824: Mark assemblies with NeutralResourcesLanguageAttribute "Because assembly 'Application.exe' contains a ResX-based resource file, mark it with the NeutralResourcesLanguage attribute, specifying the language of the resources within the assembly." Sure enough, when I un-commented the (project template-provided) NeutralResourcesLanguage line in AssemblyInfo.cs, the desired warning showed up:
CA1703 : Microsoft.Naming : In resource 'WpfApplication.Properties.Resources.resx', referenced by name
'SampleResource', correct the spelling of 'mispelling' in string value 'This string has a mispelling.'.
In my experience, a some people suppress CA1824 instead of addressing it. But as we've just discovered, they're also giving up on free spell checking for their assembly's string resources. That seems silly, so I recommend setting NeutralResourcesLanguageAttribute for its helpful side-effects!
Note: For expository purposes, I've included an example in the download: CA1703 : Microsoft.Naming : In resource 'WpfApplication.Properties.Resources.resx', referenced by name 'IncorrectSpelling', correct the spelling of 'mispelling' in string value 'This string has a single mispelling.'.
CA1703 : Microsoft.Naming : In resource 'WpfApplication.Properties.Resources.resx', referenced by name 'IncorrectSpelling', correct the spelling of 'mispelling' in string value 'This string has a single mispelling.'.
Once I realized resource spell checking was unnecessary, I decided to focus on a different pet peeve of mine: unused resources in an assembly. In much the same way stale chunks of unused code can be found in most applications, it's pretty common to find resources that aren't referenced and are just taking up valuable space. But while there's a built-in rule to detect certain kinds of uncalled code (CA1811: Avoid uncalled private code), I'm not aware of anything similar for resources... And though it's possible to perform this check manually (by searching for the use of each individual resource), this is the kind of boring, monotonous task that computers are made for! :)
Therefore, I've created the second Delay.FxCop rule, DF1001: Resources should be referenced, which compares the set of resource references in an assembly with the set of resources that are actually present. Any cases where a resource exists (whether it's a string, stream, or object), but is not referenced in code will result in an instance of the DF1001 warning during code analysis.
Aside: For directions about how to run the Delay.FxCop rules on a standalone assembly or integrate them into a project, please refer to the steps in my original post.
As a word of caution, there can be cases where DF1001 reports that a resource isn't referenced from code, but that resource is actually used by an assembly. While I don't think it will miss typical uses from code (either via the automatically-generated Resources class or one of the lower-level ResourceManager methods), the catch is that not all resource references show up in code! For example, the markup for a Silverlight or WPF application is included as a XAML/BAML resource which is loaded at run-time without an explicit reference from the assembly itself. DF1001 will (correctly; sort of) report this resource as unused, so please remember that global code analysis suppressions can be used to squelch false-positives:
[assembly: SuppressMessage("Usage", "DF1001:ResourcesShouldBeReferenced", MessageId = "mainwindow.baml",
Scope = "resource", Target = "WpfApplication.g.resources", Justification = "Loaded by WPF for MainWindow.xaml.")]
Aside: There are other ways to "fool" DF1001, such as by loading a resource from a different assembly or passing a variable to ResourceManager.GetString. But in terms of how things are done 95% of the time, the rule's current implementation should be accurate. Of course, if you find cases where it misreports unused resources, please let me know and I'll look into whether it's possible to improve things in a future release!
[Click here to download the Delay.FxCop rule assembly, associated .ruleset files, samples, and the complete source code.]
Stale references are an unnecessary annoyance: they bloat an assembly, waste time and money (for example, when localized unnecessarily), confuse new developers, and generally just get in the way. Fortunately, detecting them in an automated fashion is easy with DF1001: Resources should be referenced! After making sure unused resources really are unused, remove them from your project - and enjoy the benefits of a smaller, leaner application!
No matter how polished the appearance of an application, web site, or advertisement is, the presence of even a single spelling error can make it look sloppy and unprofessional. The bad news is that spelling errors are incredibly easy to make - either due to mistyping or because one forgot which of the many, conflicting special cases applies in a particular circumstance. The good news is that technology to detect and correct spelling errors exists and is readily available. By making regular use of a spell-checker, you don't have to be a good speller to look like one. Trust me! ;)
Spell-checking of documents is pretty well covered these days, with all the popular word processors offering automated, interactive assistance. However, spell-checking of code is not quite so far along - even high-end editors like Visual Studio don't tend to offer interactive spell-checking support. Fortunately, it's possible - even easy! - to augment the capabilities of many development tools to integrate spell-checking into the development workflow. There are a few different ways of doing this: one is to incorporate the checking into the editing experience (like this plugin by coworker Mikhail Arkhipov) and another is to do the checking as part of the code analysis workflow (like code analysis rule CA1703: ResourceStringsShouldBeSpelledCorrectly). I'd already been toying with the idea of implementing my own code analysis rules, so I decided to experiment with the latter approach...
Aside: If you're not familiar with Visual Studio's code analysis feature, I highly recommend the MSDN article Analyzing Managed Code Quality by Using Code Analysis. Although the fully integrated experience is only available on higher-end Visual Studio products, the same exact code analysis functionality is available to everyone with the standalone FxCop tool which is free as part of the Microsoft Windows SDK for Windows 7 and .NET Framework 4. (FxCop has a dedicated download page with handy links, but it directs you to the SDK to do the actual install.)
Unrelated aside: In the ideal world, all of an application's strings would probably come from a resource file where they can be easily translated to other languages - and therefore string literals wouldn't need spell-checking. However, in the real world, there are often cases where user-visible text ends up in string literals (ex: exception messages) and therefore a rule like this seems to have practical value. If the string resources of your application are already perfectly separated, congratulations! However, if your application doesn't use resources (or uses them incompletely!), please continue reading... :)
As you might expect, it's possible to create custom code analysis rules and easily integrate them into your build environment; a great walk-through can be found on the Code Analysis Team Blog. If you still have questions after reading that, this post by Tatham Oddie is also quite good. And once you have an idea what you're doing, this documentation by Jason Kresowaty is a great resource for technical information.
Code analysis is a powerful tool and has a lot of potential for improving the development process. But for now, I'm just going to discuss a single rule I created: DF1000: CheckSpellingOfAllStringLiterals. As its name suggests, this rule checks the spelling of all string literals in an assembly. To be clear, there are other rules that check spelling (including some of the default FxCop/Visual Studio ones), but I didn't see any that checked all the literals, so this seemed like an interesting place to start.
Aside: Programs tend to have a lot of strings and those strings aren't always words (ex: namespace prefixes, regular expressions, etc.). Therefore, this rule will almost certainly report a lot of warnings run for the first time! Be prepared for that - and be ready to spend some time suppressing warnings that don't matter to you. :)
As I typically do, I've published a pre-compiled binary and complete source code, so you can see exactly how CheckSpellingOfAllStringLiterals works (it's quite simple, really, as it uses the existing introspection and spell-checking APIs). I'm not going to spend a lot of time talking about how this rule is implemented, but I did want to show how to use it so others can experiment with their own projects.
Important: Everything I show here was done with the Visual Studio 2010/.NET 4 toolset. Past updates to the code analysis infrastructure are such that things may not work with older (or newer) releases.
To add the Delay.FxCop rules to a project, you'll want to know a little about rule sets - the MSDN article Using Rule Sets to Group Managed Code Analysis Rules is a good place to start. I've provided two .ruleset files in the download: Delay.FxCop.ruleset which contains just the custom rule I've written and AllRules_Delay.FxCop.ruleset which contains my custom rule and everything in the shipping "Microsoft All Rules" ruleset. (Of course, creating and using your own .ruleset is another option!) Incorporating a custom rule set into a Visual Studio project is as easy as: Project menu, ProjectName Properties..., Code Analysis tab, Run this rule set:, Browse..., specify the path to the custom rule set, Build menu, Run Code Analysis on ProjectName.
Note: For WPF projects, you may also want to uncheck Suppress results from generated code in the "Code Analysis" tab above because the XAML compiler adds GeneratedCodeAttribute to all classes with an associated .xaml file and that automatically suppresses code analysis warnings for those classes. (Silverlight and Windows Phone projects don't set this attribute, so the default "ignore" behavior is fine.)
Assuming your project contains a string literal that's not in the dictionary, the Error List window should show one or more warnings like this:
DF1000 : Spelling : The word 'recieve' is not in the dictionary.
At this point, you have a few options (examples of which can be found in the TestProjects\ConsoleApplication directory of the sample):
Fix the misspelling.
Suppress the instance.
If it's an isolated use of the word and is correct, then simply right-clicking the warning and choosing Suppress Message(s), In Source will add something like the following attribute to the code which will silence the warning:
[SuppressMessage("Spelling", "DF1000:CheckSpellingOfAllStringLiterals", MessageId = "leet")]
While you're at it, feel free to add a Justification message if the reason might not be obvious to someone else.
Suppress the entire method.
If a method contains no user-visible text, but has lots of strings that cause warnings, you can suppress the entire method by omitting the MessageId parameter like so:
Add the word to the custom dictionary.
If the "misspelled" word is correct and appears throughout the application, you'll probably want to add it to the project's custom dictionary which will silence all relevant warnings at once. MSDN has a great overview of the custom dictionary format as well as the exact steps to take to add a custom dictionary to a project in the article How to: Customize the Code Analysis Dictionary.
Alternatively, if you're a command-line junkie or don't want to modify your Visual Studio project, you can use FxCopCmd directly by running it from a Visual Studio Command Prompt like so:
C:\T>"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\FxCopCmd.exe"
Microsoft (R) FxCop Command-Line Tool, Version 10.0 (10.0.30319.1) X86
Copyright (C) Microsoft Corporation, All Rights Reserved.
Initializing Introspection engine...
Writing 1 messages...
C:\T\ConsoleApplication\Program.cs(12,1) : warning : DF1000 : Spelling : The word 'recieve' is not in the dictionary.
Or else you can install the standalone FxCop tool to get the benefits of a graphical user interface without changing anything about your existing workflow!
[Click here to download the Delay.FxCop rule assembly, associated .ruleset files, samples, and the complete source code.]
Spelling is one of those things that's easy to get wrong - and also easy to get right if you apply the proper technology and discipline. I can't hope to make anyone a better speller ('i' before 'e', except after 'c'!), but I can help out a little on the technology front. I plan to add new code analysis rules to Delay.FxCop over time - but for now I hope people put DF1000: CheckSpellingOfAllStringLiterals to good use finding spelling mistakes in their applications!