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

前端 未结 3 788
执念已碎
执念已碎 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:44

    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);
    
    0 讨论(0)
  • 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  <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
    
    0 讨论(0)
  • 2020-12-14 12:53

    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.

    0 讨论(0)
提交回复
热议问题