ide.analysis.modernize/src/org/netbeans/modules/ide/analysis/modernize/impl/ModernizeErrorProvider.java
1.1 --- a/ide.analysis.modernize/src/org/netbeans/modules/ide/analysis/modernize/impl/ModernizeErrorProvider.java Sun Jun 25 18:17:37 2017 +0200
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,422 +0,0 @@
1.4 -/*
1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
1.6 - *
1.7 - * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
1.8 - *
1.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
1.10 - * Other names may be trademarks of their respective owners.
1.11 - *
1.12 - * The contents of this file are subject to the terms of either the GNU
1.13 - * General Public License Version 2 only ("GPL") or the Common
1.14 - * Development and Distribution License("CDDL") (collectively, the
1.15 - * "License"). You may not use this file except in compliance with the
1.16 - * License. You can obtain a copy of the License at
1.17 - * http://www.netbeans.org/cddl-gplv2.html
1.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
1.19 - * specific language governing permissions and limitations under the
1.20 - * License. When distributing the software, include this License Header
1.21 - * Notice in each file and include the License file at
1.22 - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
1.23 - * particular file as subject to the "Classpath" exception as provided
1.24 - * by Oracle in the GPL Version 2 section of the License file that
1.25 - * accompanied this code. If applicable, add the following below the
1.26 - * License Header, with the fields enclosed by brackets [] replaced by
1.27 - * your own identifying information:
1.28 - * "Portions Copyrighted [year] [name of copyright owner]"
1.29 - *
1.30 - * If you wish your version of this file to be governed by only the CDDL
1.31 - * or only the GPL Version 2, indicate your decision by adding
1.32 - * "[Contributor] elects to include this software in this distribution
1.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
1.34 - * single choice of license, a recipient has the option to distribute
1.35 - * your version of this file under either the CDDL, the GPL Version 2 or
1.36 - * to extend the choice of license to its licensees as provided above.
1.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
1.38 - * Version 2 license, then the option applies only if the new code is
1.39 - * made subject to such option by the copyright holder.
1.40 - *
1.41 - * Contributor(s): Ilia Gromov
1.42 - */
1.43 -package org.netbeans.modules.ide.analysis.modernize.impl;
1.44 -
1.45 -import org.netbeans.modules.ide.analysis.modernize.impl.ModernizeAnalyzerImpl.ResponseImpl;
1.46 -import org.netbeans.modules.ide.analysis.modernize.impl.YamlParser.Replacement;
1.47 -import org.netbeans.modules.ide.analysis.modernize.options.AnalyzerPreferences;
1.48 -import org.netbeans.modules.ide.analysis.modernize.options.ClangAnalyzerOptions;
1.49 -import static org.netbeans.modules.ide.analysis.modernize.utils.AnalyticsTools.fatalError;
1.50 -import static org.netbeans.modules.ide.analysis.modernize.utils.AnalyticsTools.findItem;
1.51 -import java.io.File;
1.52 -import java.io.IOException;
1.53 -import java.util.ArrayList;
1.54 -import java.util.Collection;
1.55 -import java.util.Collections;
1.56 -import java.util.List;
1.57 -import java.util.logging.Level;
1.58 -import java.util.logging.Logger;
1.59 -import java.util.prefs.Preferences;
1.60 -import javax.swing.JComponent;
1.61 -import javax.swing.JLabel;
1.62 -import org.netbeans.modules.cnd.analysis.api.AbstractCustomizerProvider;
1.63 -import org.netbeans.modules.cnd.analysis.api.AnalyzerResponse;
1.64 -import org.netbeans.modules.cnd.api.model.CsmFile;
1.65 -import org.netbeans.modules.cnd.api.model.syntaxerr.AbstractCodeAudit;
1.66 -import org.netbeans.modules.cnd.api.model.syntaxerr.AuditPreferences;
1.67 -import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAudit;
1.68 -import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditFactory;
1.69 -import org.netbeans.modules.cnd.api.model.syntaxerr.CodeAuditProvider;
1.70 -import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfo;
1.71 -import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfo.Severity;
1.72 -import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorInfoHintProvider;
1.73 -import org.netbeans.modules.cnd.api.model.syntaxerr.CsmErrorProvider;
1.74 -import org.netbeans.modules.cnd.api.project.NativeFileItem.Language;
1.75 -import org.netbeans.modules.cnd.api.project.NativeProject;
1.76 -import org.netbeans.modules.cnd.api.remote.RemoteProject;
1.77 -import org.netbeans.modules.cnd.makeproject.api.MakeProject;
1.78 -import org.netbeans.modules.cnd.makeproject.api.configurations.Item;
1.79 -import org.netbeans.modules.cnd.modelutil.CsmUtilities;
1.80 -import org.netbeans.modules.cnd.utils.MIMENames;
1.81 -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironment;
1.82 -import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory;
1.83 -import org.netbeans.modules.nativeexecution.api.util.ConnectionManager;
1.84 -import org.netbeans.spi.editor.hints.Fix;
1.85 -import org.openide.awt.Actions;
1.86 -import org.openide.filesystems.FileObject;
1.87 -import org.openide.filesystems.FileUtil;
1.88 -import org.openide.util.Exceptions;
1.89 -import org.openide.util.Lookup;
1.90 -import org.openide.util.NbBundle;
1.91 -import org.openide.util.lookup.ServiceProvider;
1.92 -import org.openide.util.lookup.ServiceProviders;
1.93 -
1.94 -@ServiceProviders({
1.95 - @ServiceProvider(service = CsmErrorProvider.class, position = 2100)
1.96 - ,
1.97 - @ServiceProvider(service = CodeAuditProvider.class, position = 2100)
1.98 -})
1.99 -public final class ModernizeErrorProvider extends CsmErrorProvider implements CodeAuditProvider, AbstractCustomizerProvider {
1.100 -
1.101 - public static final Logger LOG = Logger.getLogger("ide.analysis.tidy"); //NOI18N
1.102 - private Collection<CodeAudit> audits;
1.103 - public static final String NAME = "Modernize"; //NOI18N
1.104 -
1.105 - public static ModernizeErrorProvider getInstance() {
1.106 - for (CsmErrorProvider provider : Lookup.getDefault().lookupAll(CsmErrorProvider.class)) {
1.107 - if (NAME.equals(provider.getName()) && provider instanceof ModernizeErrorProvider) {
1.108 - return (ModernizeErrorProvider) provider;
1.109 - }
1.110 - }
1.111 - return null;
1.112 - }
1.113 -
1.114 - @Override
1.115 - protected boolean validate(Request request) {
1.116 - CsmFile file = request.getFile();
1.117 - return file != null;
1.118 - }
1.119 -
1.120 - @Override
1.121 - public boolean hasHintControlPanel() {
1.122 - return true;
1.123 - }
1.124 -
1.125 - @Override
1.126 - public String getName() {
1.127 - return NAME;
1.128 - }
1.129 -
1.130 - @Override
1.131 - public String getDisplayName() {
1.132 - return NbBundle.getMessage(ModernizeErrorProvider.class, "Modernize_NAME"); //NOI18N
1.133 - }
1.134 -
1.135 - @Override
1.136 - public String getDescription() {
1.137 - return NbBundle.getMessage(ModernizeErrorProvider.class, "Modernize_DESCRIPTION"); //NOI18N
1.138 - }
1.139 -
1.140 - @Override
1.141 - public String getMimeType() {
1.142 - return MIMENames.SOURCES_MIME_TYPE;
1.143 - }
1.144 -
1.145 - @Override
1.146 - public boolean isSupportedEvent(EditorEvent kind) {
1.147 - return kind == EditorEvent.FileBased;
1.148 - }
1.149 -
1.150 - @Override
1.151 - protected void doGetErrors(CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
1.152 - CsmFile file = request.getFile();
1.153 - if (file != null) {
1.154 - if (request.isCancelled()) {
1.155 - return;
1.156 - }
1.157 - Object platformProject = file.getProject().getPlatformProject();
1.158 - if (platformProject instanceof NativeProject) {
1.159 - Lookup.Provider project = ((NativeProject) platformProject).getProject();
1.160 - if (project != null) {
1.161 - if (request.isCancelled()) {
1.162 - return;
1.163 - }
1.164 - Thread currentThread = Thread.currentThread();
1.165 - currentThread.setName("Provider " + getName() + " prosess " + file.getAbsolutePath()); // NOI18N
1.166 - RemoteProject info = project.getLookup().lookup(RemoteProject.class);
1.167 - if (info != null) {
1.168 - ExecutionEnvironment execEnv = info.getDevelopmentHost();
1.169 - if (execEnv != null) {
1.170 - if (request.isCancelled()) {
1.171 - return;
1.172 - }
1.173 - if (ConnectionManager.getInstance().isConnectedTo(execEnv)) {
1.174 - Item item = findItem(file, project);
1.175 - if (item != null) {
1.176 - if (request.isCancelled()) {
1.177 - return;
1.178 - }
1.179 - // Temporarily analyzing even excluded items
1.180 - if (/* !item.isExcluded() && */(item.getLanguage() == Language.C || item.getLanguage() == Language.CPP || item.getLanguage() == Language.C_HEADER)) {
1.181 - analyze(execEnv, item, project, request, response);
1.182 - }
1.183 - }
1.184 - }
1.185 - }
1.186 - }
1.187 - }
1.188 - }
1.189 - }
1.190 - }
1.191 -
1.192 - public void analyze(ExecutionEnvironment execEnv, Item item, Lookup.Provider project, CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
1.193 - String binaryPath = ClangAnalyzerOptions.getClangAnalyzerPath();
1.194 - boolean isAnalyzer = response instanceof ModernizeAnalyzerImpl.ResponseImpl;
1.195 - if (binaryPath == null) {
1.196 - Level level = isAnalyzer ? Level.INFO : Level.FINE;
1.197 - LOG.log(level, "clang-tidy needs to be installed as a plugin"); //NOI18N
1.198 - return;
1.199 - }
1.200 -
1.201 - if (isAnalyzer && isNewRun()) {
1.202 - AnalyzedFiles.getDefault().clear();
1.203 - }
1.204 -
1.205 - DiagnosticsTool diagnosticsTool = new DiagnosticsTool(execEnv, item, (MakeProject) project, binaryPath);
1.206 - try {
1.207 - CsmFile csmFile = request.getFile();
1.208 - Collection<String> checks = /*isAnalyzer ? Collections.singleton("*") : */ getEnabledChecks(); //NOI18N
1.209 -
1.210 - Collection<CsmFile> tu = new ArrayList<CsmFile>();
1.211 - if (isAnalyzer) {
1.212 - tu.add(csmFile);
1.213 - } else {
1.214 - if (AnalyzedFiles.getDefault().isStartFile(csmFile)) {
1.215 - tu.add(csmFile);
1.216 - } else {
1.217 - tu.addAll(AnalyzedFiles.getDefault().getStartFiles(csmFile));
1.218 - }
1.219 - }
1.220 -
1.221 - if (!isAnalyzer) {
1.222 - response = new ResponseMerger(response);
1.223 - }
1.224 -
1.225 - for (CsmFile startFile : tu) {
1.226 - int exitCode = diagnosticsTool.process(checks, startFile, true);
1.227 - if (exitCode != DiagnosticsTool.STATUS_OK) {
1.228 - String error = NbBundle.getMessage(ModernizeErrorProvider.class, "compile.file.error"); //NOI18N
1.229 - String info = NbBundle.getMessage(ModernizeErrorProvider.class, "compile.file.error.info", "" + exitCode); //NOI18N
1.230 - fatalError(AnalyzerResponse.AnalyzerSeverity.FileError, "fatal.analyze.error", error + "\n" + info, csmFile, response); //NOI18N
1.231 - return;
1.232 - }
1.233 - List<YamlParser.Diagnostics> results = YamlParser.getDefault().parseYaml(diagnosticsTool.getYamlAsString());
1.234 - postProcess(isAnalyzer, startFile, project, results, request, response);
1.235 - }
1.236 -
1.237 - if (!isAnalyzer) {
1.238 - response.done();
1.239 - }
1.240 -
1.241 - } catch (ConnectionManager.CancellationException ex) {
1.242 - Exceptions.printStackTrace(ex);
1.243 - } catch (IOException ex) {
1.244 - Exceptions.printStackTrace(ex);
1.245 - }
1.246 - }
1.247 -
1.248 - private static CsmErrorProvider last;
1.249 -
1.250 - private boolean isNewRun() {
1.251 - if (last == null || this != last) {
1.252 - last = this;
1.253 - return true;
1.254 - }
1.255 - return false;
1.256 - }
1.257 -
1.258 - public void postProcess(boolean isAnalyzer, CsmFile startFile, Lookup.Provider project, List<YamlParser.Diagnostics> results, CsmErrorProvider.Request request, CsmErrorProvider.Response response) {
1.259 - CsmFile file = request.getFile();
1.260 - List<CsmFile> otherCsmFiles = new ArrayList<CsmFile>();
1.261 -
1.262 - for (YamlParser.Diagnostics diag : results) {
1.263 - // TODO: don't add "Configure Hint" fix multiple times for one line
1.264 - FileObject fo = FileUtil.toFileObject(new File(diag.getMessageFilePath()));
1.265 - CsmFile csmFile = CsmUtilities.getCsmFile(fo, false, false);
1.266 -
1.267 - // Composing a preview message. Showing a start file for compilation unit
1.268 - // in case we analysing a header file
1.269 - String message;
1.270 - if (startFile.equals(file) && csmFile.equals(file)) {
1.271 - message = String.format("[%s]: %s", diag.getCheckName(), diag.getMessage()); //NOI18N
1.272 - } else {
1.273 - message = String.format("[%s; %s]: %s", diag.getCheckName(), startFile.getName(), diag.getMessage()); //NOI18N
1.274 - }
1.275 -
1.276 - ModernizeErrorInfo info = new ModernizeErrorInfo(diag, message, project); //NOI18N
1.277 -
1.278 - if (isAnalyzer) {
1.279 - // Add found errors for all files (can be other files from compileUnit)
1.280 - ((ResponseImpl) response).addError(AnalyzerResponse.AnalyzerSeverity.DetectedError, null, fo, info);
1.281 -
1.282 - if (!csmFile.equals(file)) {
1.283 - // May be not header (e.g BBB.cc: AAA.cc -> (includes) BBB.cc -> ... )
1.284 - otherCsmFiles.add(csmFile);
1.285 - }
1.286 - } else if (fo.equals(file.getFileObject())) {
1.287 - // Add found errors only for file displayed in Editor
1.288 - response.addError(info);
1.289 - }
1.290 - }
1.291 -
1.292 - if (isAnalyzer /* and not empty? */) {
1.293 - AnalyzedFiles.getDefault().cacheHierarchy(file, otherCsmFiles);
1.294 - }
1.295 - }
1.296 -
1.297 - @ServiceProvider(path = CodeAuditFactory.REGISTRATION_PATH + ModernizeErrorProvider.NAME, service = CodeAuditFactory.class, position = 4000)
1.298 - public static final class Factory implements CodeAuditFactory {
1.299 -
1.300 - @Override
1.301 - public AbstractCodeAudit create(AuditPreferences preferences) {
1.302 - String id = NbBundle.getMessage(ModernizeCodeAudit.class, "LBL_ProviderName"); // NOI18N
1.303 - String description = NbBundle.getMessage(ModernizeCodeAudit.class, "LBL_ProviderDescription"); // NOI18N
1.304 - return new ModernizeCodeAudit(id, id, description, "error", false, preferences); // NOI18N
1.305 - }
1.306 - }
1.307 -
1.308 - private String oldPath;
1.309 -
1.310 - @Override
1.311 - public synchronized Collection<CodeAudit> getAudits() {
1.312 - String path = ClangAnalyzerOptions.getClangAnalyzerPath();
1.313 -
1.314 - if (path == null) {
1.315 - return Collections.emptyList();
1.316 - }
1.317 -
1.318 - if (oldPath == null) {
1.319 - oldPath = path;
1.320 - }
1.321 -
1.322 - if (audits == null || !oldPath.equals(path)) {
1.323 - List<CodeAudit> res = DiagnosticsTool.getAudits(path, ExecutionEnvironmentFactory.getLocal(), AnalyzerPreferences.getAuditPreferences());
1.324 -
1.325 - audits = res;
1.326 - oldPath = path;
1.327 - }
1.328 - return audits;
1.329 - }
1.330 -
1.331 - public Collection<String> getEnabledChecks() {
1.332 - Collection<CodeAudit> auditList = getAudits();
1.333 - List<String> enabled = new ArrayList<String>();
1.334 - for (CodeAudit codeAudit : auditList) {
1.335 - if (codeAudit.isEnabled()) {
1.336 - enabled.add(codeAudit.getID());
1.337 - }
1.338 - }
1.339 - return enabled.size() == auditList.size() ? Collections.singleton("*") : enabled; //NOI18N
1.340 - }
1.341 -
1.342 - @Override
1.343 - public AuditPreferences getPreferences() {
1.344 - return AnalyzerPreferences.getAuditPreferences();
1.345 - }
1.346 -
1.347 - @Override
1.348 - public JComponent createComponent(Preferences context) {
1.349 - return new JLabel();
1.350 - }
1.351 -
1.352 - public static interface ErrorInfoWithId {
1.353 -
1.354 - String getId();
1.355 - }
1.356 -
1.357 - public static final class FatalErrorInfo implements CsmErrorInfo, ErrorInfoWithId {
1.358 -
1.359 - private final String id;
1.360 - private final String message;
1.361 -
1.362 - public FatalErrorInfo(String id, String message) {
1.363 - this.id = id;
1.364 - this.message = message;
1.365 - }
1.366 -
1.367 - @Override
1.368 - public String getMessage() {
1.369 - return message;
1.370 - }
1.371 -
1.372 - @Override
1.373 - public Severity getSeverity() {
1.374 - return Severity.WARNING;
1.375 - }
1.376 -
1.377 - @Override
1.378 - public int getStartOffset() {
1.379 - return 0;
1.380 - }
1.381 -
1.382 - @Override
1.383 - public int getEndOffset() {
1.384 - return 1;
1.385 - }
1.386 -
1.387 - @Override
1.388 - public String getId() {
1.389 - return id;
1.390 - }
1.391 - }
1.392 -
1.393 - @ServiceProvider(service = CsmErrorInfoHintProvider.class, position = 9100)
1.394 - public final static class ModerinzeHintProvider extends CsmErrorInfoHintProvider {
1.395 -
1.396 - @Override
1.397 - protected List<Fix> doGetFixes(CsmErrorInfo info, List<Fix> alreadyFound) {
1.398 - if (info instanceof ModernizeErrorInfo) {
1.399 - alreadyFound.add(new ConfigureHintsFix((ModernizeErrorInfo) info));
1.400 - }
1.401 - return alreadyFound;
1.402 - }
1.403 - }
1.404 -
1.405 - @ServiceProvider(service = CsmErrorInfoHintProvider.class, position = 1600)
1.406 - public static final class ModernizeFixProvider extends CsmErrorInfoHintProvider {
1.407 -
1.408 - @Override
1.409 - protected List<Fix> doGetFixes(CsmErrorInfo info, List<Fix> alreadyFound) {
1.410 - alreadyFound.addAll(createFixes(info));
1.411 - return alreadyFound;
1.412 - }
1.413 - }
1.414 -
1.415 - private static List<? extends Fix> createFixes(CsmErrorInfo info) {
1.416 - if (info instanceof ModernizeErrorInfo) {
1.417 - ModernizeErrorInfo mei = (ModernizeErrorInfo) info;
1.418 - List<Replacement> replacements = mei.getDiagnostics().getReplacements();
1.419 - if (!replacements.isEmpty()) {
1.420 - return Collections.singletonList(new ModernizeFix(replacements, mei.getId()));
1.421 - }
1.422 - }
1.423 - return Collections.EMPTY_LIST;
1.424 - }
1.425 -}