Is there a way to fix all MATLAB mlint messages at once?

前端 未结 3 787
执念已碎
执念已碎 2020-12-14 12:35

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

3条回答
  •  有刺的猬
    2020-12-14 12:52

    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
    

提交回复
热议问题