Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find category of MATLAB mlint warning ID

I'm using the checkcode function in MATLAB to give me a struct of all error messages in a supplied filename along with their McCabe complexity and ID associated with that error. i.e;

info = checkcode(fileName, '-cyc','-id');

In MATLAB's preferences, there is a list of all possible errors, and they are broken down into categories. Such as "Aesthetics and Readability", "Syntax Errors", "Discouraged Function Usage" etc.

Is there a way to access these categories using the error ID gained from the above line of code?

like image 333
D. Sill Avatar asked Mar 09 '16 17:03

D. Sill


People also ask

How do you find the error code in MATLAB?

Select MATLAB > Code Analyzer, and then select the Enable integrated warning and error messages check box. Set the Underlining option to Underline warnings and errors . When continuous code checking is enabled, MATLAB displays warning and error messages about your code in the Editor and Live Editor.

How do I use MATLAB code analyzer?

In MATLAB, select the Home tab and then click Preferences. In the Preferences dialog box, select Code Analyzer. In the MATLAB Code Analyzer Preferences pane, verify that Enable integrated warning and error messages is selected.

What is Mlint MATLAB?

mlint('lengthofline') MATLAB displays the M-Lint messages for lengthofline. m in the Command Window: L 22 (C 1-9): The value assigned here to variable 'nothandle' might never be used. L 23 (C 12-15): NUMEL(x) is usually faster than PROD(SIZE(x)).


1 Answers

I tossed around different ideas in my head for this question and was finally able to come up with a mostly elegant solution for how to handle this.

The Solution

The critical component of this solution is the undocumented -allmsg flag of checkcode (or mlint). If you supply this argument, then a full list of mlint IDs, severity codes, and descriptions are printed. More importantly, the categories are also printed in this list and all mlint IDs are listed underneath their respective mlint category.

The Execution

Now we can't simply call checkcode (or mlint) with only the -allmsg flag because that would be too easy. Instead, it requires an actual file to try to parse and check for errors. You can pass any valid m-file, but I have opted to pass the built-in sum.m because the actual file itself only contains help information (as it's real implementation is likely C++) and mlint is therefore able to parse it very rapidly with no warnings.

checkcode('sum.m', '-allmsg');

An excerpt of the output printed to the command window is:

   INTER    ========== Internal Message Fragments ==========
   MSHHH  7   this is used for %#ok and should never be seen!
    BAIL  7   done with run due to error
   INTRN    ========== Serious Internal Errors and Assertions ==========
   NOLHS  3   Left side of an assignment is empty.
   TMMSG  3   More than 50,000 Code Analyzer messages were generated, leading to some being deleted.
  MXASET  4   Expression is too complex for code analysis to complete.
   LIN2L  3   A source file line is too long for Code Analyzer.
    QUIT  4   Earlier syntax errors confused Code Analyzer (or a possible Code Analyzer bug).
   FILER    ========== File Errors ==========
   NOSPC  4   File <FILE> is too large or complex to analyze.
    MBIG  4   File <FILE> is too big for Code Analyzer to handle.
   NOFIL  4   File <FILE> cannot be opened for reading.
   MDOTM  4   Filename <FILE> must be a valid MATLAB code file.
   BDFIL  4   Filename <FILE> is not formed from a valid MATLAB identifier.
   RDERR  4   Unable to read file <FILE>.
   MCDIR  2   Class name <name> and @directory name do not agree: <FILE>.
   MCFIL  2   Class name <name> and file name do not agree: <file>.
   CFERR  1   Cannot open or read the Code Analyzer settings from file <FILE>. Using default settings instead.
   ...
    MCLL  1   MCC does not allow C++ files to be read directly using LOADLIBRARY.
   MCWBF  1   MCC requires that the first argument of WEBFIGURE not come from   FIGURE(n).
   MCWFL  1   MCC requires that the first argument of WEBFIGURE not come from FIGURE(n) (line <line #>).
    NITS    ========== Aesthetics and Readability ==========
    DSPS  1   DISP(SPRINTF(...)) can usually be replaced by FPRINTF(...).
   SEPEX  0   For better readability, use newline, semicolon, or comma before this statement.
   NBRAK  0   Use of brackets [] is unnecessary. Use parentheses to group, if needed.
   ...

The first column is clearly the mlint ID, the second column is actually a severity number (0 = mostly harmless, 1 = warning, 2 = error, 4-7 = more serious internal issues), and the third column is the message that is displayed.

As you can see, all categories also have an identifier but no severity, and their message format is ===== Category Name =====.

So now we can just parse this information and create some data structure that allows us to easily look up the severity and category for a given mlint ID.

Again, though, it can't always be so easy. Unfortunately, checkcode (or mlint) simply prints this information out to the command window and doesn't assign it to any of our output variables. Because of this, it is necessary to use evalc (shudder) to capture the output and store it as a string. We can then easily parse this string to get the category and severity associated with each mlint ID.

An Example Parser

I have put all of the pieces I discussed previously together into a little function which will generate a struct where all of the fields are the mlint IDs. Within each field you will receive the following information:

warnings = mlintCatalog();
warnings.DWVRD

             id: 'DWVRD'
       severity: 2
        message: 'WAVREAD has been removed. Use AUDIOREAD instead.'
       category: 'Discouraged Function Usage'
    category_id: 17

And here's the little function if you're interested.

function [warnings, categories] = mlintCatalog()
    % Get a list of all categories, mlint IDs, and severity rankings
    output = evalc('checkcode sum.m -allmsg');

    % Break each line into it's components
    lines = regexp(output, '\n', 'split').';
    pattern = '^\s*(?<id>[^\s]*)\s*(?<severity>\d*)\s*(?<message>.*?\s*$)';
    warnings = regexp(lines, pattern, 'names');
    warnings = cat(1, warnings{:});

    % Determine which ones are category names
    isCategory = cellfun(@isempty, {warnings.severity});
    categories = warnings(isCategory);

    % Fix up the category names
    pattern = '(^\s*=*\s*|\s*=*\s*$)';
    messages = {categories.message};
    categoryNames = cellfun(@(x)regexprep(x, pattern, ''), messages, 'uni', 0);
    [categories.message] = categoryNames{:};

    % Now pair each mlint ID with it's category
    comp = bsxfun(@gt, 1:numel(warnings), find(isCategory).');
    [category_id, ~] = find(diff(comp, [], 1) == -1);
    category_id(end+1:numel(warnings)) = numel(categories);

    % Assign a category field to each mlint ID
    [warnings.category] = categoryNames{category_id};

    category_id = num2cell(category_id);
    [warnings.category_id] = category_id{:};

    % Remove the categories from the warnings list
    warnings = warnings(~isCategory);

    % Convert warning severity to a number
    severity = num2cell(str2double({warnings.severity}));
    [warnings.severity] = severity{:};

    % Save just the categories
    categories = rmfield(categories, 'severity');

    % Convert array of structs to a struct where the MLINT ID is the field
    warnings = orderfields(cell2struct(num2cell(warnings), {warnings.id}));
end

Summary

This is a completely undocumented but fairly robust way of getting the category and severity associated with a given mlint ID. This functionality existed in 2010 and maybe even before that, so it should work with any version of MATLAB that you have to deal with. This approach is also a lot more flexible than simply noting what categories a given mlint ID is in because the category (and severity) will change from release to release as new functions are added and old functions are deprecated.

Thanks for asking this challenging question, and I hope that this answer provides a little help and insight!

like image 118
Suever Avatar answered Oct 01 '22 03:10

Suever