c.s.tools.ide.analysis.modernize/src/com/sun/tools/ide/analysis/modernize/impl/ModernizeErrorProvider.java
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
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]"
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.
38 * Contributor(s): Ilia Gromov
40 package com.sun.tools.ide.analysis.modernize.impl;
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;
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;
91 @ServiceProvider(service = CsmErrorProvider.class, position = 2100)
93 @ServiceProvider(service = CodeAuditProvider.class, position = 2100)
95 public final class ModernizeErrorProvider extends CsmErrorProvider implements CodeAuditProvider, AbstractCustomizerProvider {
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
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;
111 protected boolean validate(Request request) {
112 CsmFile file = request.getFile();
117 public boolean hasHintControlPanel() {
122 public String getName() {
127 public String getDisplayName() {
128 return NbBundle.getMessage(ModernizeErrorProvider.class, "Modernize_NAME"); //NOI18N
132 public String getDescription() {
133 return NbBundle.getMessage(ModernizeErrorProvider.class, "Modernize_DESCRIPTION"); //NOI18N
137 public String getMimeType() {
138 return MIMENames.SOURCES_MIME_TYPE;
142 public boolean isSupportedEvent(EditorEvent kind) {
143 return kind == EditorEvent.FileBased;
147 protected void doGetErrors(CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
148 CsmFile file = request.getFile();
150 if (request.isCancelled()) {
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()) {
160 Thread currentThread = Thread.currentThread();
161 currentThread.setName("Provider " + getName() + " prosess " + file.getAbsolutePath()); // NOI18N
162 RemoteProject info = project.getLookup().lookup(RemoteProject.class);
164 ExecutionEnvironment execEnv = info.getDevelopmentHost();
165 if (execEnv != null) {
166 if (request.isCancelled()) {
169 if (ConnectionManager.getInstance().isConnectedTo(execEnv)) {
170 Item item = findItem(file, project);
172 if (request.isCancelled()) {
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);
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
197 if (isAnalyzer && isNewRun()) {
198 AnalyzedFiles.getDefault().clear();
201 DiagnosticsTool diagnosticsTool = new DiagnosticsTool(execEnv, item, (MakeProject) project, binaryPath);
203 CsmFile csmFile = request.getFile();
204 Collection<String> checks = /*isAnalyzer ? Collections.singleton("*") : */ getEnabledChecks(); //NOI18N
206 Collection<CsmFile> tu = new ArrayList<CsmFile>();
210 if (AnalyzedFiles.getDefault().isStartFile(csmFile)) {
213 tu.addAll(AnalyzedFiles.getDefault().getStartFiles(csmFile));
218 response = new ResponseMerger(response);
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
229 List<YamlParser.Diagnostics> results = YamlParser.getDefault().parseYaml(diagnosticsTool.getYamlAsString());
230 postProcess(isAnalyzer, startFile, project, results, request, response);
237 } catch (ConnectionManager.CancellationException ex) {
238 Exceptions.printStackTrace(ex);
239 } catch (IOException ex) {
240 Exceptions.printStackTrace(ex);
244 private static CsmErrorProvider last;
246 private boolean isNewRun() {
247 if (last == null || this != last) {
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>();
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);
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);
270 info = ModernizeErrorInfo.withMutableMessage(diag, diag.getCheckName(), startFile.getName().toString(), diag.getMessage(), project);
274 // Add found errors for all files (can be other files from compileUnit)
275 ((ResponseImpl) response).addError(AnalyzerResponse.AnalyzerSeverity.DetectedError, null, fo, info);
277 if (!csmFile.equals(file)) {
278 // May be not header (e.g BBB.cc: AAA.cc -> (includes) BBB.cc -> ... )
279 otherCsmFiles.add(csmFile);
281 } else if (fo.equals(file.getFileObject())) {
282 // Add found errors only for file displayed in Editor
283 response.addError(info);
287 if (isAnalyzer /* and not empty? */) {
288 AnalyzedFiles.getDefault().cacheHierarchy(file, otherCsmFiles);
292 @ServiceProvider(path = CodeAuditFactory.REGISTRATION_PATH + ModernizeErrorProvider.NAME, service = CodeAuditFactory.class, position = 4000)
293 public static final class Factory implements CodeAuditFactory {
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
303 private String oldPath;
306 public synchronized Collection<CodeAudit> getAudits() {
307 String path = ClangAnalyzerOptions.getClangAnalyzerPath();
310 return Collections.emptyList();
313 if (oldPath == null) {
317 if (audits == null || !oldPath.equals(path)) {
318 List<CodeAudit> res = DiagnosticsTool.getAudits(path, ExecutionEnvironmentFactory.getLocal(), AnalyzerPreferences.getAuditPreferences());
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());
334 return enabled.size() == auditList.size() ? Collections.singleton("*") : enabled; //NOI18N
338 public AuditPreferences getPreferences() {
339 return AnalyzerPreferences.getAuditPreferences();
343 public JComponent createComponent(Preferences context) {
347 public static interface ErrorInfoWithId {
352 public static final class FatalErrorInfo implements CsmErrorInfo, ErrorInfoWithId {
354 private final String id;
355 private final String message;
357 public FatalErrorInfo(String id, String message) {
359 this.message = message;
363 public String getMessage() {
368 public Severity getSeverity() {
369 return Severity.WARNING;
373 public int getStartOffset() {
378 public int getEndOffset() {
383 public String getId() {
388 @ServiceProvider(service = CsmErrorInfoHintProvider.class, position = 9100)
389 public final static class ModerinzeHintProvider extends CsmErrorInfoHintProvider {
392 protected List<Fix> doGetFixes(CsmErrorInfo info, List<Fix> alreadyFound) {
393 if (info instanceof ModernizeErrorInfo) {
394 alreadyFound.add(new ConfigureHintsFix((ModernizeErrorInfo) info));
400 @ServiceProvider(service = CsmErrorInfoHintProvider.class, position = 1600)
401 public static final class ModernizeFixProvider extends CsmErrorInfoHintProvider {
404 protected List<Fix> doGetFixes(CsmErrorInfo info, List<Fix> alreadyFound) {
405 alreadyFound.addAll(createFixes(info));
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()));
418 return Collections.EMPTY_LIST;