A powerful supplement to the best practices
discussion in the SDK is the Best Practices tool. This tool is the
MorphX version of a static code analysis tool, similar to FxCop for the
Microsoft .NET Framework and PREfix and PREfast for C and C++. The Best
Practices tool is embedded in the compiler, and the results are located
on the Best Practices tab of the Compiler Output dialog box.
The purpose of static
code analysis is to automatically detect defects in the code. The longer
a defect exists, the more costly it becomes to fix—a bug found in the
design phase is much cheaper to correct than a bug in shipped code
running at several customer sites. The Best Practices tool allows any
developer to run an analysis of his or her code and application model to
ensure that it conforms to a set of predefined rules. Developers can
run analysis during development, and they should always do so before
implementations are tested.
The Best Practices tool displays deviations from the best practice rules, as shown in Figure 1 in this article. Double-clicking a line on the Best Practices tab opens the X++ code editor on the violating line of code.
Understanding Rules
The Best Practices
tool includes about 350 rules, a small subset of the best practices
mentioned in the SDK. You can define the best practice rules that you
want to run in the Best Practice Parameters dialog box: from the
Microsoft Dynamics AX drop-down menu, click Tools\Options and then the
Best Practices button.
Note
You
must set the compiler error level to 4 if you want best practice rule
violations to be reported. To turn off the Best Practices tool, click
Tools\Options\Compiler, and then set the diagnostic level to less than
4. |
The best practice rules are divided into categories. By default, all categories are turned on, as shown in Figure 1.
The best practice rules are divided into three levels of severity:
Errors
The majority of the rules focus on errors. Any check-in attempt with a
best practice error is rejected. You must take all errors seriously and
fix them as soon as possible.
Warnings
Follow a 95/5 rule for warnings. This means that you should treat 95
percent of all warnings as errors; the remaining 5 percent constitute
exceptions to the rule. You should provide valid explanations in the
design document for all warnings you choose to ignore.
Information
In some situations, your implementation might have a side effect that
isn’t obvious to you or the user (e.g., if you’re assigning a value to a
variable but you never use the variable again). These are typically
reported as information messages.
Suppressing Errors and Warnings
The Best Practices
tool allows you to suppress errors and warnings. A suppressed best
practice deviation is reported as information. This gives you a way to
identify the deviation as reviewed and accepted. To identify a
suppressed error or warning, place a line containing the following text
just before the deviation.
//BP Deviation Documented
|
Only
a small subset of the best practice rules can be suppressed. Use the
following guidelines for selecting which rules to suppress:
Where
exceptions exist that are impossible to detect automatically, you should
examine each error to ensure the correct implementation. Dangerous APIs
are often responsible for such exceptions. A dangerous API is an API
that can compromise a system’s security when used incorrectly. If a
dangerous API is used, a suppressible error is reported. You are allowed
to use some so-called dangerous APIs when you take certain precautions,
such as using code access security. You can suppress the error after
you apply the appropriate mitigations.
About
5 percent of all warnings are false positives and can be suppressed.
Note that only warnings caused by actual code can be suppressed, not
warnings caused by metadata.
After you set up the
best practices, the compiler automatically runs the best practices check
whenever an element is compiled. The results are displayed on the Best
Practices tab in the Compiler Output dialog box.
Adding Custom Rules
The X++ Best Practices tool allows you to create your own set of rules. The classes used to check for rules are named SysBPCheck<ElementKind>. You call the init, check, and dispose methods once for each node in the AOT for the element being compiled.
One of the most interesting classes is SysBPCheckMemberFunction,
which is called for each piece of X++ code whether it is a class
method, form method, macro, or other method. For example, if developers
don’t want to include their names in the source code, you can implement a
best practice check by creating the following method on the SysBPCheckMemberFunction class.
protected void checkUseOfNames() { #Define.MyErrorCode(50000) container devNames = ["Arthur", "Lars", "Michael"]; int i; int j; int pos; str line; int lineLen;
for (i=scanner.lines(); i; i--) { line = scanner.sourceLine(i); lineLen = strlen(line); for (j=conlen(devNames); j; j--) { pos = strscan(line, conpeek(devNames, j), 1, lineLen); if (pos) { sysBPCheck.addError(#MyErrorCode, i, pos, "Don't use your name!"); } } } }
|
To enlist the rule, make sure to call the preceding method from the check method. Compiling this sample code results in the best practice errors shown in Table 1.
Table 1. Best Practice Errors in checkUseOfNames
Message | Line | Column |
---|
Method contains text constant: ‘Arthur’ | 4 | 27 |
Don’t use your name! | 4 | 28 |
Method contains text constant: ‘Lars’ | 4 | 37 |
Don’t use your name! | 4 | 38 |
Method contains text constant: ‘Michael’ | 4 | 45 |
Don’t use your name! | 4 | 46 |
Method contains text constant: ‘Don’t use your name!’ | 20 | 59 |
In a real-world
implementation, names of developers would probably be read from a file.
Make sure to cache the names to prevent the compiler from going to the
disk to read the names for each method being compiled.