I've inherited some code where the author had an aversion to semicolons. Is it possible to fix all the mlint messages in one go (at least all the ones with an automatic fix), rather than having to click each one and press ALT+ENTER?
-
3Note that I appreciate the existing answers, but hope to find a more generally applicable solution. – Dennis Jaheruddin Aug 16 '13 at 20:15
-
They replaced mlint with checkcode recently. You may use it as well. – NKN Aug 23 '13 at 21:43
3 Answers
NOTE: This answer uses the function MLINT, which is no longer recommended in newer versions of MATLAB. The newer function CHECKCODE is preferred, and the code below will still work by simply replacing the call to MLINT with a call to this newer function.
I don't know of a way in general to automatically fix code based on MLINT messages. However, in your specific case there is an automated way for you to add semicolons to lines that throw an MLINT warning.
First, let's start with this sample script junk.m
:
a = 1
b = 2;
c = 'a'
d = [1 2 3]
e = 'hello';
The first, third, and fourth lines will give you the MLINT warning message "Terminate statement with semicolon to suppress output (within a script).". Using the functional form of MLINT, we can find the lines in the file where this warning occurs. Then, we can read all the lines of code from the file, add a semicolon to the ends of the lines where the warning occurs, and write the lines of code back to the file. Here's the code to do so:
%# Find the lines where a given mlint warning occurs:
fileName = 'junk.m';
mlintID = 'NOPTS'; %# The ID of the warning
mlintData = mlint(fileName,'-id'); %# Run mlint on the file
index = strcmp({mlintData.id},mlintID); %# Find occurrences of the warnings...
lineNumbers = [mlintData(index).line]; %# ... and their line numbers
%# Read the lines of code from the file:
fid = fopen(fileName,'rt');
linesOfCode = textscan(fid,'%s','Delimiter',char(10)); %# Read each line
fclose(fid);
%# Modify the lines of code:
linesOfCode = linesOfCode{1}; %# Remove the outer cell array encapsulation
linesOfCode(lineNumbers) = strcat(linesOfCode(lineNumbers),';'); %# Add ';'
%# Write the lines of code back to the file:
fid = fopen(fileName,'wt');
fprintf(fid,'%s\n',linesOfCode{1:end-1}); %# Write all but the last line
fprintf(fid,'%s',linesOfCode{end}); %# Write the last line
fclose(fid);
And now the file junk.m
should have semicolons at the end of every line. If you want, you can put the above code in a function so that you can easily run it on every file of your inherited code.

- 125,304
- 15
- 256
- 359
-
1If you replace '-struct' with '-id' in your call to mlint the structure which is returned contains an additional field called 'id' which is a short id for the mlint message. That might be easier to match against than the full text of the message. The structure also contains a field called 'fix' which is 0 for all warnings in my test file and I haven't figured out what it is for yet -- it is not documented. – High Performance Mark Jul 14 '10 at 17:48
-
@High Performance Mark: The `'fix'` field must be a newer feature, as it does not appear in MATLAB R2009a. – gnovice Jul 14 '10 at 17:56
-
@HighPerformanceMark, I've discovered how to make that `fix` field functional: pass a `-fix` flag to mlint/checkcode! See [my answer](http://stackoverflow.com/a/18388118/176071) for details. – mbauman Aug 23 '13 at 19:40
In order to solve this problem in a general fashion for all available autofix actions, we must resort to horribly undocumented java methods. The implementation of mlint
(and now checkcode
) uses mlintmex
(a builtin; not a mexfile as the name suggests), which simply returns the text output from the linter. No autofixes are exposed; even line and column numbers are emitted as plain text. It seems to be the same as the output from the mlint binary within Matlab's installation ($(matlabroot)/bin/$(arch)/mlint
)
So we must fall back to the java implementation used by the editor itself. Beware: here follows terribly undocumented code for R2013a.
%// Get the java component for the active matlab editor
ed = matlab.desktop.editor.getActive().JavaEditor.getTextComponent();
%// Get the java representation of all mlint messages
msgs = com.mathworks.widgets.text.mcode.MLint.getMessages(ed.getText(),ed.getFilename())
%// Loop through all messages and apply the autofix, if it exits
%// Iterate backwards to try to prevent changing the location of subsequent
%// fixes... but two nearby fixes could still mess each other up.
for i = msgs.size-1:-1:0
if msgs.get(i).hasAutoFix()
com.mathworks.widgets.text.mcode.analyzer.CodeAnalyzerUtils.applyAutoFixes(ed,msgs.get(i).getAutoFixChanges);
end
end
EDIT: AHA! You can get the mlint binary to return the fixes with the -fix
flag... and this applies to the builtin checkcode
, too! Still undocumented (as far as I know), but likely much more robust than the above:
>> 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): <~>
When assigning to a structure, this also reveals the purpose of the new fix
field that @High Performance Mark notes in his comment on @gnovice's answer; it appears to be 1 when there is a fix available, 2 when the message is the FIX MESSAGE
above, and 4 when the message is the CHANGE MESSAGE
.
Here's a quick and dirty Matlab function that returns a 'fixed' string given the path to a m-file. No error checking, etc, and it doesn't save the file back as I don't promise that it'll work. You could also use the matlab.desktop.editor
public (!) API to get the active document (getActive
) and use the getter and setter on the Text
property to modify the document in place without saving it.
function str = applyAutoFixes(filepath)
msgs = checkcode(filepath,'-fix');
fid = fopen(filepath,'rt');
iiLine = 1;
lines = cell(0);
line = fgets(fid);
while ischar(line)
lines{iiLine} = line;
iiLine = iiLine+1;
line = fgets(fid);
end
fclose(fid);
pos = [0 cumsum(cellfun('length',lines))];
str = [lines{:}];
fixes = msgs([msgs.fix] == 4);
%// Iterate backwards to try to prevent changing the indexing of 'str'
%// Note that two changes could still conflict with eachother. You could check
%// for this, or iteratively run mlint and fix one problem at a time.
for fix = fliplr(fixes(:)')
%'// fix.column is a 2x2 - not sure what the second column is used for
change_start = pos(fix.line(1)) + fix.column(1,1);
change_end = pos(fix.line(2)) + fix.column(2,1);
if change_start >= change_end
%// Seems to be an insertion
str = [str(1:change_start) fix.message str(change_start+1:end)];
else
%// Seems to be a replacement
str = [str(1:change_start-1) fix.message str(change_end+1:end)];
end
end
-
Amazing, the first one seems to be exactly what I was looking for! Won't have the chance to test it before the bounty expires, but unless someone can top this, it is coming your way. – Dennis Jaheruddin Aug 23 '13 at 08:44
-
2I added the Java appraoch to a shortcut, found a nice tool icon and it runs smoothly so far. Great solution. – Oleg Aug 23 '13 at 09:25
-
@OlegKomarov: Watch out, two changes on the same line can mess eachother up. I've reversed the iteration order, which seems to fix most cases. More robust, though, would be to iteratively run mlint and fix one message at a time. – mbauman Aug 23 '13 at 20:00
-
I was just wondering if this could be exported to other IDEs. I like editing in vim, probably there is a way for doing it so. Unfortunately I am not a `vimscript` editor, it would be great if someone improve the matlab mlint integration script to have something like this. If no one does it, I may try one day, and this topic will be of great help. – Werner Aug 23 '13 at 20:30
-
@Werner: yes, you could definitely call the mlint binary provided by Mathworks and parse the output yourself. More interesting, though, would be trying to link to the dynamic library `libmwmlint.{dll,so,dylib}` directly. A quick peek at the symbols makes me wonder if it'd be possible (`MLINT::mlint_file(char const*)`, `MLINT::mlint_text(char const*, char const*, unsigned int)`, and most interesting/daunting: `MLINT::set_output_fun(void (*)(char const*, void*), void*)`). – mbauman Aug 23 '13 at 21:49
-
That's why I would think that I am not the right person for doing this hahaha, I didn't know that it was possible to trace a compiled library for functions inside it x(… but who knows if I can get good enough for doing it x) – Werner Aug 23 '13 at 23:39
I know this is an old post but I just needed this recently and improved a bit on the original code so if anybody else needs it here it is. It looks for missing ";" in functions, not just in regular scripts, maintains the whitespace in code and writes only files that have something changed.
function [] = add_semicolon(fileName)
%# Find the lines where a given mlint warning occurs:
mlintIDinScript = 'NOPTS'; %# The ID of the warning
mlintIDinFunction = 'NOPRT';
mlintData = mlint(fileName,'-id'); %# Run mlint on the file
index = strcmp({mlintData.id},mlintIDinScript) | strcmp({mlintData.id},mlintIDinFunction); %# Find occurrences of the warnings...
lineNumbers = [mlintData(index).line]; %# ... and their line numbers
if isempty(lineNumbers)
return;
end;
%# Read the lines of code from the file:
fid = fopen(fileName,'rt');
%linesOfCode = textscan(fid,'%s', 'Whitespace', '\n\r'); %# Read each line
lineNo = 0;
tline = fgetl(fid);
while ischar(tline)
lineNo = lineNo + 1;
linesOfCode{lineNo} = tline;
tline = fgetl(fid);
end
fclose(fid);
%# Modify the lines of code:
%linesOfCode = linesOfCode{1}; %# Remove the outer cell array encapsulation
linesOfCode(lineNumbers) = strcat(linesOfCode(lineNumbers),';'); %# Add ';'
%# Write the lines of code back to the file:
fim = fopen(fileName,'wt');
fprintf(fim,'%s\n',linesOfCode{1:end-1}); %# Write all but the last line
fprintf(fim,'%s',linesOfCode{end}); %# Write the last line
fclose(fim);

- 85
- 1
- 7
-
1Thanks, and welcome to SO. Even old questions are worth adding to. – Richie Cotton Mar 08 '12 at 13:27