Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

Parsing mlint (Code Analyzer) output

April 10, 2013 8 Comments

Mlint, Matlab’s static code-analysis parser, was written by Stephen Johnson (the original developer of the enormously successful lint parser for C/C++ back in 1977), when he was lured by MathWorks in 2002 to develop a similar tool for Matlab. Since its development (in R14 I believe), and especially since its incorporation in Matlab’s Editor in R2006a (Matlab 7.2), mlint has become a very important tool for reporting potential problems in m-files.
Unfortunately, to this day (R2013a), there is no documented manner of programmatically separating mlint warnings and errors, nor for accessing any of the multitude of features that are readily available in mlint. Naturally, there is (and has always been) an undocumented back door.
From its earliest beginnings, mlint has relied on C code (presumably modeled after lint). For many years mlint relied on a mex file (%matlabroot%/toolbox/matlab/codetools/mlintmex.mex*), which is basically just a wrapper for mlint.dll where the core algorithm resides. In recent releases, mlintmex, just like many other core mex files, was ported into a core Matlab library (libmwbuiltins.dll on Windows). However, the name and interface of the mlintmex function have remained unchanged over the years. Wrapping the core mlintmex function is the mlint m-function (%matlabroot%/toolbox/matlab/codetools/mlint.m) that calls mlintmex internally. In R2011b (Matlab 7.13) its official function name has changed to checkcode, although this was never documented in the release notes for some reason. However, using mlint still works even today. Wrapping all that is the mlintrpt function, which calls mlint/checkcode internally.
The core function mlintmex returns a long string with embedded newlines to separate the messages. For example:

>> str = mlintmex('perfTest.m')
str =
L 3 (C 1): The value assigned to variable 'A' might be unused.
L 4 (C 1): The value assigned to variable 'B' might be unused.
L 5 (C 1-3): Variable 'ops', apparently a structure, is changed but the value seems to be unused.
L 12 (C 9): This statement (and possibly following ones) cannot be reached.
L 53 (C 19-25): The function 'subFunc' might be unused.
L 53 (C 27-35): Input argument 'iteration' might be unused. If this is OK, consider replacing it by ~.

>> str = mlintmex('perfTest.m') str = L 3 (C 1): The value assigned to variable 'A' might be unused. L 4 (C 1): The value assigned to variable 'B' might be unused. L 5 (C 1-3): Variable 'ops', apparently a structure, is changed but the value seems to be unused. L 12 (C 9): This statement (and possibly following ones) cannot be reached. L 53 (C 19-25): The function 'subFunc' might be unused. L 53 (C 27-35): Input argument 'iteration' might be unused. If this is OK, consider replacing it by ~.


We can parse this long string ourselves, but there is no need since mlint/checkcode do this for us, returning a struct array:

>> results = mlint('perfTest.m')
results =
6x1 struct array with fields:
    message
    line
    column
    fix
>> results(5)
ans =
    message: 'The function 'subFunc' might be unused.'
       line: 53
     column: [19 25]
        fix: 0

>> results = mlint('perfTest.m') results = 6x1 struct array with fields: message line column fix >> results(5) ans = message: 'The function 'subFunc' might be unused.' line: 53 column: [19 25] fix: 0

As can be seen, the message severity (warning/error) does not appear. This severity is obviously available since it is integrated in the Editor and the Code Analyzer report – orange for warnings, red for errors.
In one of my projects I needed to enable the user to dynamically create executable Matlab code that would then be run interactively. This enabled users to create dynamic data analyses functions without actually needing to know Matlab or to code all the nuts-and-bolts of a regular Matlab function. For this I needed to display warnings and errors-on-the-fly (the dynamic cell tooltips used a custom table cell-renderer). Here’s the end-result:


Analysis definition panel
Analysis definition panel

Dynamic analysis alert tooltips
Dynamic analysis alert tooltips
Dynamic analysis alert tooltips


My solution was to use mlintmex, as follows:

% Get the relevant message strings
errMsgs = mlintmex('-m2', srcFileName);
allMsgs = mlintmex('-m0', srcFileName);
% Parse the strings to find newline characters
numErrors = length(strfind(regexprep(errMsgs,'\*\*\*.*',''),char(10)));
numAllMsg = length(strfind(regexprep(allMsgs,'\*\*\*.*',''),char(10)));
numWarns = numAllMsg - numErrors;

% Get the relevant message strings errMsgs = mlintmex('-m2', srcFileName); allMsgs = mlintmex('-m0', srcFileName); % Parse the strings to find newline characters numErrors = length(strfind(regexprep(errMsgs,'\*\*\*.*',''),char(10))); numAllMsg = length(strfind(regexprep(allMsgs,'\*\*\*.*',''),char(10))); numWarns = numAllMsg - numErrors;

(and from the messages themselves [errMsgs,allMsgs] I extracted the actual error/warning location)
Alternatively, I could have used mlint directly, as I have recently explained:

% Note that mlint returns struct arrays, so the following are all structs, not strings
errMsgs = mlint('-m2',srcFileNames); % m2 = errors only
m1Msgs  = mlint('-m1',srcFileNames); % m1 = errors and severe warnings only
allMsgs = mlint('-m0',srcFileNames); % m0 = all errors and warnings

% Note that mlint returns struct arrays, so the following are all structs, not strings errMsgs = mlint('-m2',srcFileNames); % m2 = errors only m1Msgs = mlint('-m1',srcFileNames); % m1 = errors and severe warnings only allMsgs = mlint('-m0',srcFileNames); % m0 = all errors and warnings

The original information about mlintmex and the undocumented -m0/m1/m2 options came from Urs (us) Schwartz, whose contributions are an endless source of such gems. Urs also provided a list of other undocumented mlint options (the comment annotations are mostly mine):

'-all'        % ???
'-allmsg'     % display the full list of possible mlint messages and their codes
'-amb'        % display all possibly-ambiguous identifiers (variable/function)
'-body'       % ???
'-callops'    % display the internal call tree, with nesting levels and function types
'-calls'      % (looks similar to -callops, not sure what the difference is)
'-com'        % ???
'-cyc'        % display McCabe complexity value of all functions in the analyzed file
% '-db'       % == -set + -ud + -tab
'-dty'        % debug info for the mlint parsing tree
'-edit'       % display all encountered identifiers and their assumed types
'-en'         % messages in English
'-id'         % display the mlint code associated with each message
'-ja'         % messages in Japanese
'-lex'        % display the LEX parse-tree for the analyzed file
'-m0'         % + other opt
'-m1'         % + other opt
'-m2'         % + other opt
'-m3'         % + other opt
'-mess'       % debug info for mlint message-reporting (start/end locations etc.)
'-msg'        % (looks similar to -allmsg above, not sure what the difference is)
'-notok'      % disregard %#ok directives and report messages on lines having them
'-pf'         % ???
'-set'        % debug info for the mlint parsing tree
'-spmd'       % ??? (presumably display SPMD-related messages)
'-stmt'       % display the number of statements in each function within the analyzed file
'-tab'        % set-by/used-by table for all identifiers (see -edit)
'-tmtree'     % not valid anymore
'-tmw'        % not valid anymore
'-toks'       % ???
'-tree'       % debug info for the mlint parsing tree
'-ty'         % display the line numbers where each of the file's identifiers are used
'-ud'         % debug info for the mlint parsing tree
'-yacc'       % ONLY: !mlint FILE -yacc -...

'-all' % ??? '-allmsg' % display the full list of possible mlint messages and their codes '-amb' % display all possibly-ambiguous identifiers (variable/function) '-body' % ??? '-callops' % display the internal call tree, with nesting levels and function types '-calls' % (looks similar to -callops, not sure what the difference is) '-com' % ??? '-cyc' % display McCabe complexity value of all functions in the analyzed file % '-db' % == -set + -ud + -tab '-dty' % debug info for the mlint parsing tree '-edit' % display all encountered identifiers and their assumed types '-en' % messages in English '-id' % display the mlint code associated with each message '-ja' % messages in Japanese '-lex' % display the LEX parse-tree for the analyzed file '-m0' % + other opt '-m1' % + other opt '-m2' % + other opt '-m3' % + other opt '-mess' % debug info for mlint message-reporting (start/end locations etc.) '-msg' % (looks similar to -allmsg above, not sure what the difference is) '-notok' % disregard %#ok directives and report messages on lines having them '-pf' % ??? '-set' % debug info for the mlint parsing tree '-spmd' % ??? (presumably display SPMD-related messages) '-stmt' % display the number of statements in each function within the analyzed file '-tab' % set-by/used-by table for all identifiers (see -edit) '-tmtree' % not valid anymore '-tmw' % not valid anymore '-toks' % ??? '-tree' % debug info for the mlint parsing tree '-ty' % display the line numbers where each of the file's identifiers are used '-ud' % debug info for the mlint parsing tree '-yacc' % ONLY: !mlint FILE -yacc -...

to which were added in recent years ‘-eml’, ‘-codegen’ etc. – see the checkcode doc page. Also note that not all Matlab releases support all options. For example, ‘-tmw’ is ignored in R2013a, returning the same data as ‘-all’ plus a warning about the ignored option.
Urs prepared a short utility called doli that accepts an m-file name and returns a struct whose fields are the respective outputs of mlint for each of the corresponding options:

>> results = doli('perfTest.m')
MLINT >   C:\Yair\Books\MATLAB Performance Tuning\Code\perfTest.m
OPTION>   -all       6
OPTION>   -allmsg    501
OPTION>   -amb       17
OPTION>   -body      6
OPTION>   -callops   15
OPTION>   -calls     15
OPTION>   -com       6
OPTION>   -cyc       8
OPTION>   -dty       162
OPTION>   -edit      92
OPTION>   -en        7
...

>> results = doli('perfTest.m') MLINT > C:\Yair\Books\MATLAB Performance Tuning\Code\perfTest.m OPTION> -all 6 OPTION> -allmsg 501 OPTION> -amb 17 OPTION> -body 6 OPTION> -callops 15 OPTION> -calls 15 OPTION> -com 6 OPTION> -cyc 8 OPTION> -dty 162 OPTION> -edit 92 OPTION> -en 7 ...

Some of these options are used by Urs’ farg and fdep utilities. Their usage of mlint rather than direct m-code parsing, is part of the reason that these functions are so lightningly fast.
For example, we can use the ‘-calls’ options to parse an m-file and get the names, type, and code location of its contained functions (explanation):

>> mlint('-calls','perfTest.m')
M0 1 10 perfTest
E0 51 3 perfTest
U1 3 5 randi
U1 4 5 num2cell
U1 4 14 randn
U1 6 1 whos
U1 7 1 tic
U1 7 6 save
U1 7 45 toc
U1 9 6 savefast
S0 53 19 subFunc
E0 60 3 subFunc
U1 55 8 isempty
U1 56 20 load
U1 57 29 sin

>> mlint('-calls','perfTest.m') M0 1 10 perfTest E0 51 3 perfTest U1 3 5 randi U1 4 5 num2cell U1 4 14 randn U1 6 1 whos U1 7 1 tic U1 7 6 save U1 7 45 toc U1 9 6 savefast S0 53 19 subFunc E0 60 3 subFunc U1 55 8 isempty U1 56 20 load U1 57 29 sin

With so many useful features, I really cannot understand why they were never exposed to the public in a documented manner. After all, they have remained pretty-much unchanged for many years and can provide enormous benefits for developers of unit-tests and interactive analysis frameworks (as I have shown above).
As a side-note, in R2010a (Matlab 7.10), mlint was renamed “Code Analyzer”, but this was really just a name change – its core functionality has changed little in the past decade. Some might argue that new checks were added and the Editor interface has improved by allowing auto-fixes and message suppression. But for a tool that is over a decade old (much more, if you count lint’s development), I contend that these are not much. Don’t get me wrong – I have the utmost respect for Steve. Serious unix C/C++ development relies on his lint and yacc tools on a regular basis. I think they show astonishing ingenuity and intelligence. It’s just that I had expected more after a decade of mlint development (I bet it’s not due to Steve suddenly losing the touch).
Addendum: A little birdie tells me that Steve left MathWorks a few years ago, which does explain things… I apologize to Steve for any misguided snide on my part. As I said above, I have nothing but the utmost respect for his work. The question of why MathWorks left his mlint work hanging without serious continuation remains open.
Addendum 2: Additional and much more detailed information about the nature of functions can be found using the semi-documented mtree function (or rather, Matlab class: %matlabroot%/toolbox/matlab/codetools/@mtree/mtree.m). This is a huge class-file (3200+ lines of code) that is well worth a dedicated future article, so stay tuned…

Related posts:

  1. Runtime code instrumentation – Conditional breakpoints can be used to instrument code with user-specified code. ...
  2. Parsing XML strings – Matlab's xmlread function cannot process XML data directly, but this can easily be overcome. ...
  3. Running VB code in Matlab – Matlab does not natively enable running VB code, but a nice trick enables us to do just that...
  4. Function definition meta-info – There are multiple ways to retrieve meta-info about functions in Matlab. ...
  5. Function call timeline profiling – A new utility enables to interactively explore Matlab function call timeline profiling. ...
  6. MLintFailureFiles or: Why can't I save my m-file?! – Sometimes Matlab gets into a state where it cannot use a valid m-file. This article explains what can be done. ...
Internal component Mex Performance Pure Matlab Undocumented feature
Print Print
« Previous
Next »
8 Responses
  1. Zipuni April 10, 2013 at 11:38 Reply

    Awesome post! What I have been actually wondering is if one can augment the settings of Code Analyzer so that a group can enforce their own programming best practices such as camelBack notation, load() with left side assignment (no magic variables), no eval() etc.

    I would basically love to go under Preferences->Code Analyzer-> Default Settings and add completely new tests, not just enable/disable the existing ones ..

    Of course, one can write their own parser for MATLAB but it’s rather hard with all the syntactic sugar and weak types …

    • Yair Altman April 10, 2013 at 12:12 Reply

      @Zipuni – that’s a very tall order you have there. Note that [almost] everything about mlint is undocumented… In theory, you might be able to use mlint’s lex and yacc outputs for this. But I must say that I believe this to be quite a challenge…

  2. Jim Hokanson May 18, 2013 at 22:43 Reply

    Incredible.

  3. Matt B. August 23, 2013 at 09:40 Reply

    In finding a solution for a StackOverflow question asking “Is there a way to fix all MATLAB mlint messages at once?”, I discovered another mlint flag by trial and error: -fix. It exposes the autofix hints and changes. As an example:

    >> checkcode(matlab.desktop.editor.getActiveFilename(),'-fix')
    L 2 (C 3): Terminate statement with semicolon to suppress output (in functions). (CAN FIX)
    ----FIX MESSAGE <Add a semicolon.>
    ----CHANGE MESSAGE L 2 (C 13); L 2 (C 12): <;>
    L 30 (C 52-53): Input argument 'in' might be unused. If this is OK, consider replacing it by ~. (CAN FIX)
    ----FIX MESSAGE <Replace name by ~.>
    ----CHANGE MESSAGE L 30 (C 52); L 30 (C 53): <~>

  4. Function definition meta-info | Undocumented Matlab September 4, 2013 at 06:38 Reply

    […] few months ago, I wrote about mlint‘s undocumented interface and ability to report much internal information about […]

  5. Oleg Komarov December 25, 2013 at 08:56 Reply

    The flag -pf displays parfor related messages.

  6. Ed Yu September 17, 2015 at 11:42 Reply

    Hi Yair,

    Thank you for this posting… Recently I have delivered a MATLAB database product to a client and I need to check my source code to minimize errors. One of the lacking thing about MATLAB is that it is not very good at capturing the usage of “undefined” variables. Basically I wanted to know if I use a variable that was undefined (due to typing mistakes). I understand the interpreted nature of MATLAB and don’t expect it to behave like a compiled program like Java. But still, MATLAB should provide some help in this regard. When I look into mlint it seems to be useful but when I try to turn on the preference option:

    Code Analyzer cannot determine whether <name> is a variable or a function, and assumes it is a function.
    

    The report came back with more than the preset 500 warnings and truncated the report. This is very annoying as I work with GUIDE output and it usually contains a couple of thousand lines for a more than academic data entry screen. Also the warnings are mostly due to the internal DLL unable to recognize simple MATLAB commands such as get, set, true, false, upper, lower, figure, close, delete, strcmp, regexrep, etc.

    So I ended up copying mlintrpt.m and modifying it to check the output message <name> and see if I can resolve it using command ‘which’ and if it does, just filter out the message. This produces a pretty clean and useful mlint report because I actually found a couple of typos in variable names that would otherwise be delivered to clients until they hit that line of code and matlab produces the “ding” sound. I do log these error messages in a log file but the user won’t necessary know the application has erred out because they have no speakers attached to their computers or turn the sound is turned off.

    If anyone wants to know what I did, just contact me.

    • Dennis July 24, 2019 at 00:05 Reply

      Ed:
      I am interested in how for you have taken the ‘capturing the usage of “undefined” variables’ ‘tool’ you have developed. My current code is about 60000 lines long and guess what — there are A LOT of undefined variables – mostly never used – that need work. I need help.
      Thx
      Dennis — vand1 AT umbc DoT edu

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
ActiveX (6) AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
  • Nicholas (3 days 20 hours ago): Hi Yair, Thanks for the reply. I am on Windows 10. I also forgot to mention that this all works wonderfully out of the editor. It only fails once compiled. So, yes, I have tried a...
  • Nicholas (3 days 20 hours ago): Hi Yair, Thanks for the reply. I am on Windows 10. I also forgot to mention that this all works wonderfully out of the editor. It only fails once compiled. So, yes, I have tried a...
  • Yair Altman (4 days 3 hours ago): Nicholas – yes, I used it in a compiled Windows app using R2022b (no update). You didn’t specify the Matlab code location that threw the error so I can’t help...
  • Nicholas (5 days 0 hours ago): Hi Yair, Have you attempted your displayWebPage utility (or the LightweightHelpPanel in general) within a compiled application? It appears to fail in apps derived from both R2022b...
  • João Neves (8 days 4 hours ago): I am on matlab 2021a, this still works: url = struct(struct(struct(struct(hF ig).Controller).PlatformHost). CEF).URL; but the html document is empty. Is there still a way to do...
  • Yair Altman (11 days 3 hours ago): Perhaps the class() function could assist you. Or maybe just wrap different access methods in a try-catch so that if one method fails you could access the data using another...
  • Jeroen Boschma (11 days 6 hours ago): Never mind, the new UI components have an HTML panel available. Works for me…
  • Alexandre (11 days 7 hours ago): Hi, Is there a way to test if data dictionnatry entry are signal, simulink parameters, variables … I need to access their value, but the access method depends on the data...
  • Nicholas (11 days 21 hours ago): In case anyone is looking for more info on the toolbar: I ran into some problems creating a toolbar with the lightweight panel. Previously, the Browser Panel had an addToolbar...
  • Jeroen Boschma (15 days 4 hours ago): I do not seem to get the scrollbars (horizontal…) working in Matlab 2020b. Snippets of init-code (all based on Yair’s snippets on this site) handles.text_explorer...
  • Yair Altman (43 days 6 hours ago): m_map is a mapping tool, not even created by MathWorks and not part of the basic Matlab system. I have no idea why you think that the customizations to the builtin bar function...
  • chengji chen (43 days 12 hours ago): Hi, I have tried the method, but it didn’t work. I plot figure by m_map toolbox, the xticklabel will add to the yticklabel at the left-down corner, so I want to move down...
  • Yair Altman (51 days 5 hours ago): @Alexander – this is correct. Matlab stopped including sqlite4java in R2021b (it was still included in 21a). You can download the open-source sqlite4java project from...
  • Alexander Eder (57 days 1 hour ago): Unfortunately Matlab stopped shipping sqlite4java starting with R2021(b?)
  • K (63 days 12 hours ago): Is there a way to programmatically manage which figure gets placed where? Let’s say I have 5 figures docked, and I split it into 2 x 1, I want to place 3 specific figures on the...
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top