c.s.tools.ide.analysis.modernize/src/com/sun/tools/ide/analysis/modernize/impl/DiagnosticsTool.java
author Ilia Gromov <ilia@netbeans.org>
Wed, 07 Jun 2017 20:23:29 +0300
branchrelease82
changeset 18423 b9d9af239a0c
permissions -rw-r--r--
Fixing #270763 - Move clang-tidy integration to nb contrib
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - move wrapper
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - sign nbm
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - move tests
* * *
Fixing #270763 - Move clang-tidy integration to nb contrib - data for a new test
* * *
Fixed #270839 - [clang-tidy] Group checks in Editor hints
(transplanted from 35b6125ef00c470655dac6673075f5c12ec74593)
ilia@18415
     1
/*
ilia@18415
     2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
ilia@18415
     3
 *
ilia@18415
     4
 * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
ilia@18415
     5
 *
ilia@18415
     6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
ilia@18415
     7
 * Other names may be trademarks of their respective owners.
ilia@18415
     8
 *
ilia@18415
     9
 * The contents of this file are subject to the terms of either the GNU
ilia@18415
    10
 * General Public License Version 2 only ("GPL") or the Common
ilia@18415
    11
 * Development and Distribution License("CDDL") (collectively, the
ilia@18415
    12
 * "License"). You may not use this file except in compliance with the
ilia@18415
    13
 * License. You can obtain a copy of the License at
ilia@18415
    14
 * http://www.netbeans.org/cddl-gplv2.html
ilia@18415
    15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
ilia@18415
    16
 * specific language governing permissions and limitations under the
ilia@18415
    17
 * License.  When distributing the software, include this License Header
ilia@18415
    18
 * Notice in each file and include the License file at
ilia@18415
    19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
ilia@18415
    20
 * particular file as subject to the "Classpath" exception as provided
ilia@18415
    21
 * by Oracle in the GPL Version 2 section of the License file that
ilia@18415
    22
 * accompanied this code. If applicable, add the following below the
ilia@18415
    23
 * License Header, with the fields enclosed by brackets [] replaced by
ilia@18415
    24
 * your own identifying information:
ilia@18415
    25
 * "Portions Copyrighted [year] [name of copyright owner]"
ilia@18415
    26
 *
ilia@18415
    27
 * If you wish your version of this file to be governed by only the CDDL
ilia@18415
    28
 * or only the GPL Version 2, indicate your decision by adding
ilia@18415
    29
 * "[Contributor] elects to include this software in this distribution
ilia@18415
    30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
ilia@18415
    31
 * single choice of license, a recipient has the option to distribute
ilia@18415
    32
 * your version of this file under either the CDDL, the GPL Version 2 or
ilia@18415
    33
 * to extend the choice of license to its licensees as provided above.
ilia@18415
    34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
ilia@18415
    35
 * Version 2 license, then the option applies only if the new code is
ilia@18415
    36
 * made subject to such option by the copyright holder.
ilia@18415
    37
 *
ilia@18415
    38
 * Contributor(s): Ilia Gromov
ilia@18415
    39
 */
ilia@18415
    40
package com.sun.tools.ide.analysis.modernize.impl;
ilia@18415
    41
ilia@18415
    42
import com.sun.tools.ide.analysis.modernize.utils.AnalyticsTools;
ilia@18415
    43
import java.io.BufferedReader;
ilia@18415
    44
import java.io.File;
ilia@18415
    45
import java.io.FileReader;
ilia@18415
    46
import java.io.IOException;
ilia@18415
    47
import java.nio.file.Files;
ilia@18415
    48
import java.nio.file.Path;
ilia@18415
    49
import java.util.ArrayList;
ilia@18415
    50
import java.util.Collection;
ilia@18415
    51
import java.util.Collections;
ilia@18415
    52
import java.util.List;
ilia@18415
    53
import java.util.logging.Level;
ilia@18415
    54
import java.util.logging.Logger;
ilia@18415
    55
import org.netbeans.modules.cnd.api.model.CsmFile;
ilia@18415
    56
import org.netbeans.modules.cnd.api.model.syntaxerr.AuditPreferences;
ilia@18415
    57
import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAudit;
ilia@18415
    58
import org.netbeans.modules.cnd.api.toolchain.AbstractCompiler;
ilia@18415
    59
import static org.netbeans.modules.cnd.api.toolchain.PredefinedToolKind.CCCompiler;
ilia@18415
    60
import static org.netbeans.modules.cnd.api.toolchain.PredefinedToolKind.CCompiler;
ilia@18415
    61
import org.netbeans.modules.cnd.api.toolchain.Tool;
ilia@18415
    62
import org.netbeans.modules.cnd.makeproject.api.MakeProject;
ilia@18415
    63
import org.netbeans.modules.cnd.makeproject.api.configurations.CCCCompilerConfiguration;
ilia@18415
    64
import org.netbeans.modules.cnd.makeproject.api.configurations.CCCompilerConfiguration;
ilia@18415
    65
import org.netbeans.modules.cnd.makeproject.api.configurations.CCompilerConfiguration;
ilia@18415
    66
import org.netbeans.modules.cnd.makeproject.api.configurations.Item;
ilia@18415
    67
import org.netbeans.modules.cnd.makeproject.api.configurations.MakeConfiguration;
ilia@18415
    68
import org.netbeans.modules.cnd.makeproject.api.support.MakeProjectOptionsFormat;
ilia@18415
    69
import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment;
ilia@18415
    70
import org.netbeans.modules.nativeexecution.api.HostInfo;
ilia@18415
    71
import org.netbeans.modules.nativeexecution.api.util.ConnectionManager;
ilia@18415
    72
import org.netbeans.modules.nativeexecution.api.util.HostInfoUtils;
ilia@18415
    73
import org.netbeans.modules.nativeexecution.api.util.ProcessUtils;
ilia@18415
    74
import org.openide.filesystems.FileObject;
ilia@18415
    75
import org.openide.filesystems.FileUtil;
ilia@18415
    76
ilia@18415
    77
/**
ilia@18415
    78
 *
ilia@18415
    79
 * @author Ilia Gromov
ilia@18415
    80
 */
ilia@18415
    81
public class DiagnosticsTool {
ilia@18415
    82
ilia@18415
    83
    public static final Logger LOG = Logger.getLogger("ide.analysis.tidy"); //NOI18N
ilia@18415
    84
    public static final String CACHE_ID = "TIDY-CACHE"; //NOI18N
ilia@18415
    85
ilia@18415
    86
    public static final int STATUS_OK = 0;
ilia@18415
    87
    public static final int STATUS_IO_ERROR = -1;
ilia@18415
    88
ilia@18415
    89
    public static List<CodeAudit> getAudits(String path, ExecutionEnvironment env, AuditPreferences preferences) {
ilia@18415
    90
        List<CodeAudit> res = new ArrayList<>();
ilia@18415
    91
ilia@18415
    92
        ProcessUtils.ExitStatus status = ProcessUtils.execute(env, path, new String[]{"-checks=*", "-list-checks", "dummy", "--"}); // NOI18N
ilia@18415
    93
ilia@18415
    94
        if (status.exitCode == 0) {
ilia@18415
    95
            String out = status.getOutputString();
ilia@18415
    96
            out = out.substring(status.getOutputString().indexOf('\n') + 1);
ilia@18415
    97
            String[] checks = out.split("\n"); // NOI18N
ilia@18415
    98
ilia@18415
    99
            FileObject disabledChecksFolder = FileUtil.getConfigFile("Analysis/Clang-Tidy/Disabled_Default"); // NOI18N
ilia@18415
   100
ilia@18415
   101
            List<String> disabledChecks = new ArrayList<>();
ilia@18415
   102
            for (FileObject fileObject : disabledChecksFolder.getChildren()) {
ilia@18415
   103
                disabledChecks.add(fileObject.getName());
ilia@18415
   104
            }
ilia@18415
   105
ilia@18415
   106
            for (String check : checks) {
ilia@18415
   107
                check = check.trim();
ilia@18415
   108
                ModernizeCodeAudit myCodeAudit = new ModernizeCodeAudit(
ilia@18415
   109
                        check,
ilia@18415
   110
                        check,
ilia@18415
   111
                        check,
ilia@18415
   112
                        "warning", //NOI18N
ilia@18415
   113
                        !disabledChecks.contains(check),
ilia@18415
   114
                        preferences);
ilia@18415
   115
                res.add(myCodeAudit);
ilia@18415
   116
            }
ilia@18415
   117
        }
ilia@18415
   118
ilia@18415
   119
        return res;
ilia@18415
   120
    }
ilia@18415
   121
ilia@18415
   122
    private final ExecutionEnvironment execEnv;
ilia@18415
   123
    private final Item item;
ilia@18415
   124
    private final MakeProject project;
ilia@18415
   125
    private final String binaryPath;
ilia@18415
   126
ilia@18415
   127
    private StringBuilder buf;
ilia@18415
   128
ilia@18415
   129
    public DiagnosticsTool(ExecutionEnvironment execEnv, Item item, MakeProject project, String binaryPath) {
ilia@18415
   130
        this.execEnv = execEnv;
ilia@18415
   131
        this.item = item;
ilia@18415
   132
        this.project = project;
ilia@18415
   133
        this.binaryPath = binaryPath;
ilia@18415
   134
    }
ilia@18415
   135
ilia@18415
   136
    /**
ilia@18415
   137
     * @return clang-tidy's exit code or negative code if some other problem has
ilia@18415
   138
     * occurred.
ilia@18415
   139
     */
ilia@18415
   140
    public int process(Collection<String> checks, CsmFile csmFile, boolean isStartFile) throws ConnectionManager.CancellationException, IOException {
ilia@18415
   141
        // TODO: can we split analyzer (Source -> Inspect) ant editor error providing?
ilia@18415
   142
ilia@18415
   143
        final String directoryMacro = "xDIRx"; //NOI18N
ilia@18415
   144
ilia@18415
   145
        File tmpDir = null;
ilia@18415
   146
        try {
ilia@18415
   147
            List<String> args = new ArrayList<String>();
ilia@18415
   148
ilia@18415
   149
            HostInfo hostInfo = HostInfoUtils.getHostInfo(execEnv);
ilia@18415
   150
ilia@18415
   151
            try {
ilia@18415
   152
                if (execEnv.isRemote()) {
ilia@18415
   153
                    tmpDir = Files.createTempDirectory(hostInfo.getTempDirFile().toPath(), "tidy").toFile();  //NOI18N
ilia@18415
   154
                } else {
ilia@18415
   155
                    tmpDir = Files.createTempDirectory("tidy").toFile();  //NOI18N
ilia@18415
   156
                }
ilia@18415
   157
            } catch (IOException ex) {
ilia@18415
   158
                return -1;
ilia@18415
   159
            }
ilia@18415
   160
ilia@18415
   161
            Path tmpFile = Files.createTempFile(tmpDir.toPath(), null, null);
ilia@18415
   162
ilia@18415
   163
            args.add("-header-filter=.*"); //NOI18N
ilia@18415
   164
            StringBuilder sb = new StringBuilder("-checks=");// NOI18N
ilia@18415
   165
            for (String check : checks) {
ilia@18415
   166
                sb.append(check);
ilia@18415
   167
                sb.append(","); // NOI18N
ilia@18415
   168
            }
ilia@18415
   169
            args.add(sb.toString().substring(0, sb.length() - 1));
ilia@18415
   170
ilia@18415
   171
            args.add("-export-fixes=" + tmpFile); // NOI18N
ilia@18415
   172
            int directoryIdx = args.size() - 1;
ilia@18415
   173
ilia@18415
   174
            Collection<? extends CsmFile> startFiles = Collections.EMPTY_LIST;
ilia@18415
   175
            args.add(csmFile.getFileObject().getPath());
ilia@18415
   176
ilia@18415
   177
            args.add("--");  //NOI18N
ilia@18415
   178
            args.addAll(getAdditionalFlags(csmFile));
ilia@18415
   179
ilia@18415
   180
            List<String> copy = new ArrayList<String>(args);
ilia@18415
   181
ilia@18415
   182
            copy.set(directoryIdx, copy.get(directoryIdx).replace(directoryMacro, tmpDir.getAbsolutePath()));
ilia@18415
   183
ilia@18415
   184
            // TODO: execute in the ${WORKING_DIR} ? Because we can have relative path includes (-Isrc/libs/abcd)
ilia@18415
   185
            ProcessUtils.ExitStatus executeStatus = ProcessUtils.executeInDir(tmpDir.getAbsolutePath(),
ilia@18415
   186
                    execEnv,
ilia@18415
   187
                    binaryPath,
ilia@18415
   188
                    copy.toArray(new String[copy.size()]));
ilia@18415
   189
ilia@18415
   190
            if (executeStatus.exitCode != STATUS_OK) {
ilia@18415
   191
                LOG.log(Level.INFO, "clang-tidy exit code {0}: {1}", new Object[]{executeStatus.exitCode, arrayAsString(binaryPath, copy)});
ilia@18415
   192
                LOG.fine(executeStatus.getOutputString());
ilia@18415
   193
                LOG.fine(executeStatus.getErrorString());
ilia@18415
   194
                return executeStatus.exitCode;
ilia@18415
   195
            } else {
ilia@18415
   196
                LOG.log(Level.FINEST, "{0}", arrayAsString(binaryPath, copy));
ilia@18415
   197
                LOG.finest(executeStatus.getOutputString());
ilia@18415
   198
                LOG.finest(executeStatus.getErrorString());
ilia@18415
   199
            }
ilia@18415
   200
ilia@18415
   201
            File[] listFiles = tmpDir.listFiles();
ilia@18415
   202
ilia@18415
   203
            if (listFiles.length != 1) {
ilia@18415
   204
                return STATUS_IO_ERROR;
ilia@18415
   205
            }
ilia@18415
   206
ilia@18415
   207
            File yamlFile = listFiles[0];
ilia@18415
   208
ilia@18415
   209
            BufferedReader in = new BufferedReader(new FileReader(yamlFile));
ilia@18415
   210
            buf = new StringBuilder();
ilia@18415
   211
            while (true) {
ilia@18415
   212
                String s = in.readLine();
ilia@18415
   213
                if (s == null) {
ilia@18415
   214
                    break;
ilia@18415
   215
                }
ilia@18415
   216
                buf.append(s).append('\n'); //NOI18N
ilia@18415
   217
            }
ilia@18415
   218
ilia@18415
   219
        } finally {
ilia@18415
   220
            try {
ilia@18415
   221
                tmpDir.delete();
ilia@18415
   222
            } catch (Exception x) {
ilia@18415
   223
ilia@18415
   224
            }
ilia@18415
   225
        }
ilia@18415
   226
ilia@18415
   227
        return STATUS_OK;
ilia@18415
   228
    }
ilia@18415
   229
ilia@18415
   230
    private List<String> getAdditionalFlags(CsmFile csmFile) {
ilia@18415
   231
        List<String> all = new ArrayList<String>();
ilia@18415
   232
ilia@18415
   233
        Tool tool = AnalyticsTools.compiler(item, project);
ilia@18415
   234
ilia@18415
   235
        if (tool instanceof AbstractCompiler) {
ilia@18415
   236
            AbstractCompiler compiler = (AbstractCompiler) tool;
ilia@18415
   237
ilia@18415
   238
            for (String systemIncludeDirectory : compiler.getSystemIncludeDirectories()) {
ilia@18415
   239
                all.add("-I" + systemIncludeDirectory); //NOI18N
ilia@18415
   240
            }
ilia@18415
   241
ilia@18415
   242
            MakeConfiguration activeConfiguration = AnalyticsTools.getConfigurationDescriptor(csmFile, project).getActiveConfiguration();
ilia@18415
   243
ilia@18415
   244
            String options = "";
ilia@18415
   245
//            // compileSingleUnmanage as reference
ilia@18415
   246
//            // don't use getAllOptions - we need not -w flag
ilia@18415
   247
            if (tool.getKind() == CCCompiler) {
ilia@18415
   248
                CCCompilerConfiguration ccc = activeConfiguration.getCCCompilerConfiguration();
ilia@18415
   249
                options += ccc.getPreprocessorOptions(compiler.getCompilerSet());
ilia@18415
   250
                options += ccc.getIncludeDirectoriesOptions(compiler.getCompilerSet());
ilia@18415
   251
//            options += getLibrariesFlags();
ilia@18415
   252
                options += compiler.getCppStandardOptions(ccc.getInheritedCppStandard());
ilia@18415
   253
                if (!options.contains("-std=")) { //NOI18N
ilia@18415
   254
                    switch (item.getLanguageFlavor()) {
ilia@18415
   255
                        case CPP98:
ilia@18415
   256
                            options += " -std=c++98"; //NOI18N
ilia@18415
   257
                            break;
ilia@18415
   258
                        case CPP11:
ilia@18415
   259
                            options += " -std=c++11"; //NOI18N
ilia@18415
   260
                            break;
ilia@18415
   261
                        case CPP14:
ilia@18415
   262
                            options += " -std=c++14"; //NOI18N
ilia@18415
   263
                            break;
ilia@18415
   264
                    }
ilia@18415
   265
                }
ilia@18415
   266
            } else if (tool.getKind() == CCompiler) {
ilia@18415
   267
                CCompilerConfiguration cc = activeConfiguration.getCCompilerConfiguration();
ilia@18415
   268
                options += cc.getPreprocessorOptions(compiler.getCompilerSet());
ilia@18415
   269
                options += cc.getIncludeDirectoriesOptions(compiler.getCompilerSet());
ilia@18415
   270
                options += compiler.getCStandardOptions(cc.getInheritedCStandard());
ilia@18415
   271
                if (!options.contains("-std=")) { //NOI18N
ilia@18415
   272
                    switch (item.getLanguageFlavor()) {
ilia@18415
   273
                        case C11:
ilia@18415
   274
                            options += " -std=c11"; //NOI18N
ilia@18415
   275
                            break;
ilia@18415
   276
                        case C89:
ilia@18415
   277
                            options += " -std=c89"; //NOI18N
ilia@18415
   278
                            break;
ilia@18415
   279
                        case C99:
ilia@18415
   280
                            options += " -std=c99"; //NOI18N
ilia@18415
   281
                            break;
ilia@18415
   282
                    }
ilia@18415
   283
                }
ilia@18415
   284
            }
ilia@18415
   285
ilia@18415
   286
            List<String> optionsList = AnalyticsTools.scanCommandLine(MakeProjectOptionsFormat.reformatWhitespaces(options));
ilia@18415
   287
            all.addAll(optionsList);
ilia@18415
   288
        }
ilia@18415
   289
ilia@18415
   290
        return all;
ilia@18415
   291
    }
ilia@18415
   292
ilia@18415
   293
    public String getYamlAsString() {
ilia@18415
   294
        if (buf == null) {
ilia@18415
   295
            throw new IllegalStateException();
ilia@18415
   296
        }
ilia@18415
   297
        return buf.toString();
ilia@18415
   298
    }
ilia@18415
   299
ilia@18415
   300
    private static String arrayAsString(String binary, List<String> list) {
ilia@18415
   301
        StringBuilder sb = new StringBuilder(binary).append(" "); //NOI18N
ilia@18415
   302
        for (String string : list) {
ilia@18415
   303
            sb.append(string).append(" "); //NOI18N
ilia@18415
   304
        }
ilia@18415
   305
        return sb.toString();
ilia@18415
   306
    }
ilia@18415
   307
}