c.s.tools.ide.analysis.modernize/src/com/sun/tools/ide/analysis/modernize/impl/ModernizeErrorProvider.java
author Ilia Gromov <ilia@netbeans.org>
Wed, 07 Jun 2017 20:23:29 +0300
branchrelease82
changeset 18423 b9d9af239a0c
child 18417 853976f2c616
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)
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * If you wish your version of this file to be governed by only the CDDL
    28  * or only the GPL Version 2, indicate your decision by adding
    29  * "[Contributor] elects to include this software in this distribution
    30  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    31  * single choice of license, a recipient has the option to distribute
    32  * your version of this file under either the CDDL, the GPL Version 2 or
    33  * to extend the choice of license to its licensees as provided above.
    34  * However, if you add GPL Version 2 code and therefore, elected the GPL
    35  * Version 2 license, then the option applies only if the new code is
    36  * made subject to such option by the copyright holder.
    37  *
    38  * Contributor(s): Ilia Gromov
    39  */
    40 package com.sun.tools.ide.analysis.modernize.impl;
    41 
    42 import com.sun.tools.ide.analysis.modernize.impl.ModernizeAnalyzerImpl.ResponseImpl;
    43 import com.sun.tools.ide.analysis.modernize.impl.YamlParser.Replacement;
    44 import com.sun.tools.ide.analysis.modernize.options.AnalyzerPreferences;
    45 import com.sun.tools.ide.analysis.modernize.options.ClangAnalyzerOptions;
    46 import static com.sun.tools.ide.analysis.modernize.utils.AnalyticsTools.fatalError;
    47 import static com.sun.tools.ide.analysis.modernize.utils.AnalyticsTools.findItem;
    48 import java.io.File;
    49 import java.io.IOException;
    50 import java.util.ArrayList;
    51 import java.util.Collection;
    52 import java.util.Collections;
    53 import java.util.List;
    54 import java.util.logging.Level;
    55 import java.util.logging.Logger;
    56 import java.util.prefs.Preferences;
    57 import javax.swing.JComponent;
    58 import javax.swing.JLabel;
    59 import org.netbeans.modules.cnd.analysis.api.AbstractCustomizerProvider;
    60 import org.netbeans.modules.cnd.analysis.api.AnalyzerResponse;
    61 import org.netbeans.modules.cnd.api.model.CsmFile;
    62 import org.netbeans.modules.cnd.api.model.syntaxerr.AbstractCodeAudit;
    63 import org.netbeans.modules.cnd.api.model.syntaxerr.AuditPreferences;
    64 import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAudit;
    65 import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditFactory;
    66 import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditProvider;
    67 import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfo;
    68 import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfo.Severity;
    69 import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfoHintProvider;
    70 import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorProvider;
    71 import org.netbeans.modules.cnd.api.project.NativeFileItem.Language;
    72 import org.netbeans.modules.cnd.api.project.NativeProject;
    73 import org.netbeans.modules.cnd.api.remote.RemoteProject;
    74 import org.netbeans.modules.cnd.makeproject.api.MakeProject;
    75 import org.netbeans.modules.cnd.makeproject.api.configurations.Item;
    76 import org.netbeans.modules.cnd.modelutil.CsmUtilities;
    77 import org.netbeans.modules.cnd.utils.MIMENames;
    78 import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment;
    79 import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory;
    80 import org.netbeans.modules.nativeexecution.api.util.ConnectionManager;
    81 import org.netbeans.spi.editor.hints.Fix;
    82 import org.openide.filesystems.FileObject;
    83 import org.openide.filesystems.FileUtil;
    84 import org.openide.util.Exceptions;
    85 import org.openide.util.Lookup;
    86 import org.openide.util.NbBundle;
    87 import org.openide.util.lookup.ServiceProvider;
    88 import org.openide.util.lookup.ServiceProviders;
    89 
    90 @ServiceProviders({
    91     @ServiceProvider(service = CsmErrorProvider.class, position = 2100)
    92     ,
    93     @ServiceProvider(service = CodeAuditProvider.class, position = 2100)
    94 })
    95 public final class ModernizeErrorProvider extends CsmErrorProvider implements CodeAuditProvider, AbstractCustomizerProvider {
    96 
    97     public static final Logger LOG = Logger.getLogger("ide.analysis.tidy"); //NOI18N
    98     private Collection<CodeAudit> audits;
    99     public static final String NAME = "Modernize"; //NOI18N
   100 
   101     public static ModernizeErrorProvider getInstance() {
   102         for (CsmErrorProvider provider : Lookup.getDefault().lookupAll(CsmErrorProvider.class)) {
   103             if (NAME.equals(provider.getName()) && provider instanceof ModernizeErrorProvider) {
   104                 return (ModernizeErrorProvider) provider;
   105             }
   106         }
   107         return null;
   108     }
   109 
   110     @Override
   111     protected boolean validate(Request request) {
   112         CsmFile file = request.getFile();
   113         return file != null;
   114     }
   115 
   116     @Override
   117     public boolean hasHintControlPanel() {
   118         return true;
   119     }
   120 
   121     @Override
   122     public String getName() {
   123         return NAME;
   124     }
   125 
   126     @Override
   127     public String getDisplayName() {
   128         return NbBundle.getMessage(ModernizeErrorProvider.class, "Modernize_NAME"); //NOI18N
   129     }
   130 
   131     @Override
   132     public String getDescription() {
   133         return NbBundle.getMessage(ModernizeErrorProvider.class, "Modernize_DESCRIPTION"); //NOI18N
   134     }
   135 
   136     @Override
   137     public String getMimeType() {
   138         return MIMENames.SOURCES_MIME_TYPE;
   139     }
   140 
   141     @Override
   142     public boolean isSupportedEvent(EditorEvent kind) {
   143         return kind == EditorEvent.FileBased;
   144     }
   145 
   146     @Override
   147     protected void doGetErrors(CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
   148         CsmFile file = request.getFile();
   149         if (file != null) {
   150             if (request.isCancelled()) {
   151                 return;
   152             }
   153             Object platformProject = file.getProject().getPlatformProject();
   154             if (platformProject instanceof NativeProject) {
   155                 Lookup.Provider project = ((NativeProject) platformProject).getProject();
   156                 if (project != null) {
   157                     if (request.isCancelled()) {
   158                         return;
   159                     }
   160                     Thread currentThread = Thread.currentThread();
   161                     currentThread.setName("Provider " + getName() + " prosess " + file.getAbsolutePath()); // NOI18N
   162                     RemoteProject info = project.getLookup().lookup(RemoteProject.class);
   163                     if (info != null) {
   164                         ExecutionEnvironment execEnv = info.getDevelopmentHost();
   165                         if (execEnv != null) {
   166                             if (request.isCancelled()) {
   167                                 return;
   168                             }
   169                             if (ConnectionManager.getInstance().isConnectedTo(execEnv)) {
   170                                 Item item = findItem(file, project);
   171                                 if (item != null) {
   172                                     if (request.isCancelled()) {
   173                                         return;
   174                                     }
   175                                     // Temporarily analyzing even excluded items
   176                                     if (/* !item.isExcluded() &&  */(item.getLanguage() == Language.C || item.getLanguage() == Language.CPP || item.getLanguage() == Language.C_HEADER)) {
   177                                         analyze(execEnv, item, project, request, response);
   178                                     }
   179                                 }
   180                             }
   181                         }
   182                     }
   183                 }
   184             }
   185         }
   186     }
   187 
   188     public void analyze(ExecutionEnvironment execEnv, Item item, Lookup.Provider project, CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
   189         String binaryPath = ClangAnalyzerOptions.getClangAnalyzerPath();
   190         boolean isAnalyzer = response instanceof ModernizeAnalyzerImpl.ResponseImpl;
   191         if (binaryPath == null) {
   192             Level level = isAnalyzer ? Level.INFO : Level.FINE;
   193             LOG.log(level, "clang-tidy needs to be installed as a plugin"); //NOI18N
   194             return;
   195         }
   196 
   197         if (isAnalyzer && isNewRun()) {
   198             AnalyzedFiles.getDefault().clear();
   199         }
   200 
   201         DiagnosticsTool diagnosticsTool = new DiagnosticsTool(execEnv, item, (MakeProject) project, binaryPath);
   202         try {
   203             CsmFile csmFile = request.getFile();
   204             Collection<String> checks = /*isAnalyzer ? Collections.singleton("*") : */ getEnabledChecks(); //NOI18N
   205 
   206             Collection<CsmFile> tu = new ArrayList<CsmFile>();
   207             if (isAnalyzer) {
   208                 tu.add(csmFile);
   209             } else {
   210                 if (AnalyzedFiles.getDefault().isStartFile(csmFile)) {
   211                     tu.add(csmFile);
   212                 } else {
   213                     tu.addAll(AnalyzedFiles.getDefault().getStartFiles(csmFile));
   214                 }
   215             }
   216 
   217             if (!isAnalyzer) {
   218                 response = new ResponseMerger(response);
   219             }
   220 
   221             for (CsmFile startFile : tu) {
   222                 int exitCode = diagnosticsTool.process(checks, startFile, true);
   223                 if (exitCode != DiagnosticsTool.STATUS_OK) {
   224                     String error = NbBundle.getMessage(ModernizeErrorProvider.class, "compile.file.error"); //NOI18N
   225                     String info = NbBundle.getMessage(ModernizeErrorProvider.class, "compile.file.error.info", "" + exitCode); //NOI18N
   226                     fatalError(AnalyzerResponse.AnalyzerSeverity.FileError, "fatal.analyze.error", error + "\n" + info, csmFile, response); //NOI18N
   227                     return;
   228                 }
   229                 List<YamlParser.Diagnostics> results = YamlParser.getDefault().parseYaml(diagnosticsTool.getYamlAsString());
   230                 postProcess(isAnalyzer, startFile, project, results, request, response);
   231             }
   232 
   233             if (!isAnalyzer) {
   234                 response.done();
   235             }
   236 
   237         } catch (ConnectionManager.CancellationException ex) {
   238             Exceptions.printStackTrace(ex);
   239         } catch (IOException ex) {
   240             Exceptions.printStackTrace(ex);
   241         }
   242     }
   243 
   244     private static CsmErrorProvider last;
   245 
   246     private boolean isNewRun() {
   247         if (last == null || this != last) {
   248             last = this;
   249             return true;
   250         }
   251         return false;
   252     }
   253 
   254     public void postProcess(boolean isAnalyzer, CsmFile startFile, Lookup.Provider project, List<YamlParser.Diagnostics> results, CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
   255         CsmFile file = request.getFile();
   256         List<CsmFile> otherCsmFiles = new ArrayList<CsmFile>();
   257 
   258         for (YamlParser.Diagnostics diag : results) {
   259             // TODO: don't add "Configure Hint" fix multiple times for one line
   260             FileObject fo = FileUtil.toFileObject(new File(diag.getMessageFilePath()));
   261             CsmFile csmFile = CsmUtilities.getCsmFile(fo, false, false);
   262 
   263             // Composing a preview message. Showing a start file for compilation unit
   264             // in case we analysing a header file
   265             ModernizeErrorInfo info;
   266             if (startFile.equals(file) && csmFile.equals(file)) {
   267                 String message = String.format("[%s]: %s", diag.getCheckName(), diag.getMessage()); //NOI18N
   268                 info = ModernizeErrorInfo.withFixedMessage(diag, message, project);
   269             } else {
   270                 info = ModernizeErrorInfo.withMutableMessage(diag, diag.getCheckName(), startFile.getName().toString(), diag.getMessage(), project);
   271             }
   272 
   273             if (isAnalyzer) {
   274                 // Add found errors for all files (can be other files from compileUnit)
   275                 ((ResponseImpl) response).addError(AnalyzerResponse.AnalyzerSeverity.DetectedError, null, fo, info);
   276 
   277                 if (!csmFile.equals(file)) {
   278                     // May be not header (e.g BBB.cc: AAA.cc -> (includes) BBB.cc -> ... )
   279                     otherCsmFiles.add(csmFile);
   280                 }
   281             } else if (fo.equals(file.getFileObject())) {
   282                 // Add found errors only for file displayed in Editor
   283                 response.addError(info);
   284             }
   285         }
   286 
   287         if (isAnalyzer /* and not empty? */) {
   288             AnalyzedFiles.getDefault().cacheHierarchy(file, otherCsmFiles);
   289         }
   290     }
   291 
   292     @ServiceProvider(path = CodeAuditFactory.REGISTRATION_PATH + ModernizeErrorProvider.NAME, service = CodeAuditFactory.class, position = 4000)
   293     public static final class Factory implements CodeAuditFactory {
   294 
   295         @Override
   296         public AbstractCodeAudit create(AuditPreferences preferences) {
   297             String id = NbBundle.getMessage(ModernizeCodeAudit.class, "LBL_ProviderName");  // NOI18N
   298             String description = NbBundle.getMessage(ModernizeCodeAudit.class, "LBL_ProviderDescription");  // NOI18N
   299             return new ModernizeCodeAudit(id, id, description, "error", false, preferences);  // NOI18N
   300         }
   301     }
   302 
   303     private String oldPath;
   304 
   305     @Override
   306     public synchronized Collection<CodeAudit> getAudits() {
   307         String path = ClangAnalyzerOptions.getClangAnalyzerPath();
   308 
   309         if (path == null) {
   310             return Collections.emptyList();
   311         }
   312 
   313         if (oldPath == null) {
   314             oldPath = path;
   315         }
   316 
   317         if (audits == null || !oldPath.equals(path)) {
   318             List<CodeAudit> res = DiagnosticsTool.getAudits(path, ExecutionEnvironmentFactory.getLocal(), AnalyzerPreferences.getAuditPreferences());
   319 
   320             audits = res;
   321             oldPath = path;
   322         }
   323         return audits;
   324     }
   325 
   326     public Collection<String> getEnabledChecks() {
   327         Collection<CodeAudit> auditList = getAudits();
   328         List<String> enabled = new ArrayList<String>();
   329         for (CodeAudit codeAudit : auditList) {
   330             if (codeAudit.isEnabled()) {
   331                 enabled.add(codeAudit.getID());
   332             }
   333         }
   334         return enabled.size() == auditList.size() ? Collections.singleton("*") : enabled; //NOI18N
   335     }
   336 
   337     @Override
   338     public AuditPreferences getPreferences() {
   339         return AnalyzerPreferences.getAuditPreferences();
   340     }
   341 
   342     @Override
   343     public JComponent createComponent(Preferences context) {
   344         return new JLabel();
   345     }
   346 
   347     public static interface ErrorInfoWithId {
   348 
   349         String getId();
   350     }
   351 
   352     public static final class FatalErrorInfo implements CsmErrorInfo, ErrorInfoWithId {
   353 
   354         private final String id;
   355         private final String message;
   356 
   357         public FatalErrorInfo(String id, String message) {
   358             this.id = id;
   359             this.message = message;
   360         }
   361 
   362         @Override
   363         public String getMessage() {
   364             return message;
   365         }
   366 
   367         @Override
   368         public Severity getSeverity() {
   369             return Severity.WARNING;
   370         }
   371 
   372         @Override
   373         public int getStartOffset() {
   374             return 0;
   375         }
   376 
   377         @Override
   378         public int getEndOffset() {
   379             return 1;
   380         }
   381 
   382         @Override
   383         public String getId() {
   384             return id;
   385         }
   386     }
   387 
   388     @ServiceProvider(service = CsmErrorInfoHintProvider.class, position = 9100)
   389     public final static class ModerinzeHintProvider extends CsmErrorInfoHintProvider {
   390 
   391         @Override
   392         protected List<Fix> doGetFixes(CsmErrorInfo info, List<Fix> alreadyFound) {
   393             if (info instanceof ModernizeErrorInfo) {
   394                 alreadyFound.add(new ConfigureHintsFix((ModernizeErrorInfo) info));
   395             }
   396             return alreadyFound;
   397         }
   398     }
   399 
   400     @ServiceProvider(service = CsmErrorInfoHintProvider.class, position = 1600)
   401     public static final class ModernizeFixProvider extends CsmErrorInfoHintProvider {
   402 
   403         @Override
   404         protected List<Fix> doGetFixes(CsmErrorInfo info, List<Fix> alreadyFound) {
   405             alreadyFound.addAll(createFixes(info));
   406             return alreadyFound;
   407         }
   408     }
   409 
   410     private static List<? extends Fix> createFixes(CsmErrorInfo info) {
   411         if (info instanceof ModernizeErrorInfo) {
   412             ModernizeErrorInfo mei = (ModernizeErrorInfo) info;
   413             List<Replacement> replacements = mei.getDiagnostics().getReplacements();
   414             if (!replacements.isEmpty()) {
   415                 return Collections.singletonList(new ModernizeFix(replacements, mei.getId()));
   416             }
   417         }
   418         return Collections.EMPTY_LIST;
   419     }
   420 }