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 fi
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
----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
----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