I threw various ideas around me on this issue and, finally, I was able to come up with a mostly elegant solution to how to deal with this.
Decision
A critical component of this solution is the undocumented -allmsg
checkcode
(or mlint
) flag. If you specify this argument, a complete list of mlint
identifiers, severity codes, and descriptions will be printed. More importantly, categories are also printed on this list, and all mlint
identifiers mlint
listed under their corresponding mlint
category.
Performance
Now we canβt just call checkcode
(or mlint
) with the -allmsg
flag -allmsg
, because it will be too easy. Instead, it requires an actual file to try to analyze and check for errors. You can transfer any valid m file, but I decided to transfer the built-in sum.m
, because the actual file itself contains only reference information (since the real implementation is most likely C ++), and mlint
, therefore, can analyze it very quickly. without warning.
checkcode('sum.m', '-allmsg');
The shutter speed displayed in the command window is as follows:
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 clearly contains the identifier mlint
, the second column is the severity number (0 = mostly harmless, 1 = warning, 2 = error, 4-7 = more serious internal problems), and a message is displayed in the third column.
As you can see, all categories also have an identifier, but do not have severity, and their message format ===== Category Name =====
.
So, now we can simply analyze this information and create some data structure that allows us to easily find the severity and category for a given mlint
identifier.
Again, this may not always be so simple. Unfortunately, checkcode
(or mlint
) simply prints this information in the command window and does not assign it to any of our output variables. Because of this, you must use evalc
(shudder) to capture the output and save it as a string. We can then easily parse this string to get the category and severity associated with each mlint
identifier.
Analyzer example
I put all the parts that I discussed earlier in a small function that will generate a structure where all fields are mlint
identifiers. In 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 is a small feature 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 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 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 reliable way to get the category and severity associated with this mlint
identifier. This functionality existed in 2010, and perhaps even before that, so it should work with any version of MATLAB that you have to deal with. This approach is also much more flexible than just specifying the categories in which the given mlint
identifier is mlint
, because the category (and severity) will change from release to release as new features and legacy features are added.
Thank you for asking this difficult question, and I hope this answer will give a little help and understanding!