| /******************************************************************************* |
| * Copyright (c) 2008, 2013 Wind River Systems, Inc. and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Markus Schorn - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.ui.actions; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintStream; |
| import java.util.ArrayList; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.SortedMap; |
| import java.util.SortedSet; |
| import java.util.TreeMap; |
| import java.util.TreeSet; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.dom.ILinkage; |
| import org.eclipse.cdt.core.dom.ast.ASTVisitor; |
| import org.eclipse.cdt.core.dom.ast.IASTName; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; |
| import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; |
| import org.eclipse.cdt.core.dom.ast.IASTProblem; |
| import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; |
| import org.eclipse.cdt.core.dom.ast.IBinding; |
| import org.eclipse.cdt.core.dom.ast.IProblemBinding; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; |
| import org.eclipse.cdt.core.index.IIndex; |
| import org.eclipse.cdt.core.index.IIndexFile; |
| import org.eclipse.cdt.core.index.IIndexFileLocation; |
| import org.eclipse.cdt.core.index.IIndexInclude; |
| import org.eclipse.cdt.core.index.IndexLocationFactory; |
| import org.eclipse.cdt.core.model.CModelException; |
| import org.eclipse.cdt.core.model.CoreModelUtil; |
| import org.eclipse.cdt.core.model.ICElement; |
| import org.eclipse.cdt.core.model.ICProject; |
| import org.eclipse.cdt.core.model.ILanguage; |
| import org.eclipse.cdt.core.model.ITranslationUnit; |
| import org.eclipse.cdt.core.model.IWorkingCopy; |
| import org.eclipse.cdt.core.parser.ExtendedScannerInfo; |
| import org.eclipse.cdt.core.parser.ISignificantMacros; |
| import org.eclipse.cdt.core.settings.model.ICConfigurationDescription; |
| import org.eclipse.cdt.core.settings.model.ICProjectDescription; |
| import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager; |
| import org.eclipse.cdt.internal.core.model.ASTCache; |
| import org.eclipse.cdt.internal.core.model.TranslationUnit; |
| import org.eclipse.cdt.internal.core.pdom.ASTFilePathResolver; |
| import org.eclipse.cdt.internal.core.pdom.PDOM; |
| import org.eclipse.cdt.internal.core.pdom.indexer.ProjectIndexerInputAdapter; |
| import org.eclipse.cdt.internal.ui.editor.ASTProvider; |
| import org.eclipse.cdt.internal.ui.editor.CEditor; |
| import org.eclipse.cdt.ui.CDTUITools; |
| import org.eclipse.cdt.ui.CUIPlugin; |
| import org.eclipse.cdt.ui.IWorkingCopyManager; |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.action.IAction; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.text.ITextSelection; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.widgets.FileDialog; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IEditorReference; |
| import org.eclipse.ui.IObjectActionDelegate; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.ide.IDE; |
| |
| @SuppressWarnings("nls") |
| public class CreateParserLogAction implements IObjectActionDelegate { |
| private static final String INDENT = " "; |
| |
| private static final class MyVisitor extends ASTVisitor { |
| List<IASTProblem> fProblems = new ArrayList<>(); |
| List<IProblemBinding> fProblemBindings = new ArrayList<>(); |
| List<Exception> fExceptions = new ArrayList<>(); |
| |
| MyVisitor() { |
| shouldVisitProblems = true; |
| shouldVisitNames = true; |
| } |
| |
| @Override |
| public int visit(IASTProblem problem) { |
| fProblems.add(problem); |
| return PROCESS_SKIP; |
| } |
| |
| @Override |
| public int visit(IASTName name) { |
| if (name instanceof ICPPASTQualifiedName) { |
| return PROCESS_CONTINUE; |
| } |
| try { |
| IBinding binding = name.resolveBinding(); |
| if (binding instanceof IProblemBinding) { |
| fProblemBindings.add((IProblemBinding) binding); |
| } |
| } catch (RuntimeException e) { |
| fExceptions.add(e); |
| } |
| return PROCESS_CONTINUE; |
| } |
| } |
| |
| private static final Comparator<String> COMP_INSENSITIVE = new Comparator<>() { |
| @Override |
| public int compare(String o1, String o2) { |
| return o1.toUpperCase().compareTo(o2.toUpperCase()); |
| } |
| }; |
| |
| private ISelection fSelection; |
| private IWorkbenchPartSite fSite; |
| |
| private boolean fWroteUnresolvedTitle; |
| |
| @Override |
| public void setActivePart(IAction action, IWorkbenchPart targetPart) { |
| fSite = targetPart.getSite(); |
| } |
| |
| @Override |
| public void run(IAction action) { |
| if (!(fSelection instanceof IStructuredSelection) && !(fSelection instanceof ITextSelection)) { |
| return; |
| } |
| |
| List<IWorkingCopy> workingCopies = new ArrayList<>(); |
| final IWorkbenchPage activePage = fSite.getWorkbenchWindow().getActivePage(); |
| for (IEditorReference eref : activePage.getEditorReferences()) { |
| IEditorPart editor = eref.getEditor(false); |
| if (editor instanceof CEditor) { |
| ICElement inputElement = ((CEditor) editor).getInputCElement(); |
| if (inputElement instanceof IWorkingCopy) |
| workingCopies.add((IWorkingCopy) inputElement); |
| } |
| } |
| |
| ArrayList<ITranslationUnit> tuSelection = getSelectedTranslationUnits(); |
| for (int i = 0; i < tuSelection.size(); i++) { |
| if (!(tuSelection.get(i) instanceof IWorkingCopy)) { |
| tuSelection.set(i, convertToWorkingCopy(tuSelection.get(i), workingCopies)); |
| } |
| } |
| |
| ITranslationUnit[] tuArray = tuSelection.toArray(new ITranslationUnit[tuSelection.size()]); |
| if (tuArray.length == 0) { |
| return; |
| } |
| FileDialog dlg = new FileDialog(fSite.getShell(), SWT.SAVE); |
| String title = ActionMessages.CreateParserLogAction_title; |
| dlg.setText(title); |
| dlg.setFilterExtensions(new String[] { "*.log" }); |
| String path = null; |
| while (path == null) { |
| path = dlg.open(); |
| if (path == null) |
| return; |
| |
| File file = new File(path); |
| if (file.exists()) { |
| if (!file.canWrite()) { |
| final String msg = NLS.bind(ActionMessages.CreateParserLogAction_readOnlyFile, path); |
| MessageDialog.openError(fSite.getShell(), title, msg); |
| path = null; |
| } else { |
| final String msg = NLS.bind(ActionMessages.CreateParserLogAction_existingFile, path); |
| if (!MessageDialog.openQuestion(fSite.getShell(), title, msg)) { |
| path = null; |
| } |
| } |
| } |
| } |
| |
| try { |
| PrintStream out = new PrintStream(path); |
| try { |
| boolean needsep = false; |
| for (ITranslationUnit tu : tuArray) { |
| if (needsep) { |
| out.println(); |
| out.println(); |
| } |
| createLog(out, tu, new NullProgressMonitor()); |
| needsep = true; |
| } |
| } finally { |
| out.close(); |
| } |
| try { |
| IDE.openEditorOnFileStore(activePage, EFS.getStore(URIUtil.toURI(path))); |
| } catch (Exception e) { |
| } |
| } catch (IOException e) { |
| MessageDialog.openError(fSite.getShell(), title, e.getMessage()); |
| } |
| |
| } |
| |
| private ITranslationUnit convertToWorkingCopy(ITranslationUnit tu, List<IWorkingCopy> workingCopies) { |
| for (IWorkingCopy wc : workingCopies) { |
| if (tu.equals(wc.getOriginalElement())) |
| return wc; |
| } |
| return tu; |
| } |
| |
| private void createLog(final PrintStream out, final ITranslationUnit tu, IProgressMonitor pm) { |
| try { |
| tu.open(pm); |
| } catch (CModelException e) { |
| CUIPlugin.log(e); |
| } |
| ASTProvider.getASTProvider().runOnAST(tu, ASTProvider.WAIT_IF_OPEN, pm, new ASTCache.ASTRunnable() { |
| @Override |
| public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) throws CoreException { |
| if (ast != null) |
| return createLog(out, tu, lang, ast); |
| return Status.CANCEL_STATUS; |
| } |
| }); |
| } |
| |
| protected IStatus createLog(PrintStream out, ITranslationUnit tu, ILanguage lang, IASTTranslationUnit ast) { |
| IStatus status = Status.OK_STATUS; |
| final ICProject cproject = tu.getCProject(); |
| final String projectName = cproject == null ? null : cproject.getElementName(); |
| final IIndex index = ast.getIndex(); |
| |
| ITranslationUnit configureWith = tu; |
| int ctxLinkage = 0; |
| ISignificantMacros ctxSigMacros = null; |
| if (tu instanceof TranslationUnit) { |
| TranslationUnit itu = (TranslationUnit) tu; |
| IIndexFile[] ctxToHeader = itu.getContextToHeader(index, |
| ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT); |
| if (ctxToHeader != null) { |
| try { |
| final IIndexFile ctxFile = ctxToHeader[0]; |
| ctxLinkage = ctxToHeader[0].getLinkageID(); |
| ctxSigMacros = ctxFile.getSignificantMacros(); |
| configureWith = CoreModelUtil.findTranslationUnitForLocation(ctxFile.getLocation(), cproject); |
| } catch (CoreException e) { |
| } |
| if (configureWith == null) { |
| configureWith = tu; |
| ctxToHeader = null; |
| } |
| } |
| } |
| |
| final ExtendedScannerInfo scfg = new ExtendedScannerInfo(configureWith.getScannerInfo(true)); |
| final MyVisitor visitor = new MyVisitor(); |
| ast.accept(visitor); |
| |
| out.println("Project: " + projectName); |
| out.println("File: " + tu.getLocationURI()); |
| out.println("Language: " + lang.getName()); |
| out.println("Index Version: " + PDOM.versionString(PDOM.getDefaultVersion())); |
| out.println("Build Configuration: " + getBuildConfig(cproject)); |
| if (configureWith == tu) { |
| out.println("Context: none"); |
| } else { |
| out.println("Context: " + configureWith.getLocationURI()); |
| out.println(INDENT + getLinkageName(ctxLinkage) + ", " + ctxSigMacros); |
| } |
| |
| try { |
| IIndexFile[] versions = index.getFiles(IndexLocationFactory.getIFL(tu)); |
| out.println("Versions in Index: " + versions.length); |
| for (IIndexFile f : versions) { |
| int[] count = countNames(f); |
| out.println(INDENT + getLinkageName(f.getLinkageID()) + ": " + f.getSignificantMacros() + "; " |
| + count[0] + " macros, " + count[1] + " includes, " + count[2] + " names;"); |
| } |
| } catch (CoreException e) { |
| status = e.getStatus(); |
| } |
| out.println(); |
| |
| output(out, "Include Search Path (option -I):", scfg.getIncludePaths()); |
| output(out, "Local Include Search Path (option -iquote):", scfg.getLocalIncludePath()); |
| output(out, "Preincluded files (option -include):", scfg.getIncludeFiles()); |
| output(out, "Preincluded macro files (option -imacros):", scfg.getMacroFiles()); |
| |
| HashSet<String> reported = new HashSet<>(); |
| output(out, "Macro definitions (option -D):", scfg.getDefinedSymbols(), reported); |
| output(out, "Macro definitions (from language + headers in index):", ast.getBuiltinMacroDefinitions(), |
| reported); |
| output(out, "Macro definitions (from files actually parsed):", ast.getMacroDefinitions(), reported); |
| |
| try { |
| outputUnresolvedIncludes(cproject, ast.getIndex(), out, ast.getIncludeDirectives(), |
| ast.getLinkage().getLinkageID()); |
| } catch (CoreException e) { |
| status = e.getStatus(); |
| } |
| output(out, "Scanner problems:", ast.getPreprocessorProblems()); |
| output(out, "Parser problems:", visitor.fProblems.toArray(new IASTProblem[0])); |
| output(out, "Unresolved names:", visitor.fProblemBindings.toArray(new IProblemBinding[0])); |
| output(out, "Exceptions in name resolution:", visitor.fExceptions); |
| out.println("Written on " + new Date().toString()); |
| return status; |
| } |
| |
| private int[] countNames(IIndexFile f) { |
| int[] result = { 0, 0, 0 }; |
| try { |
| result[0] = f.getMacros().length; |
| result[1] = f.getIncludes().length; |
| result[2] = f.findNames(0, Integer.MAX_VALUE).length; |
| } catch (CoreException e) { |
| } |
| return result; |
| } |
| |
| private String getLinkageName(int linkageID) { |
| switch (linkageID) { |
| case ILinkage.NO_LINKAGE_ID: |
| return ILinkage.NO_LINKAGE_NAME; |
| case ILinkage.C_LINKAGE_ID: |
| return ILinkage.C_LINKAGE_NAME; |
| case ILinkage.CPP_LINKAGE_ID: |
| return ILinkage.CPP_LINKAGE_NAME; |
| case ILinkage.FORTRAN_LINKAGE_ID: |
| return ILinkage.FORTRAN_LINKAGE_NAME; |
| case ILinkage.OBJC_LINKAGE_ID: |
| return ILinkage.OBJC_LINKAGE_NAME; |
| } |
| return String.valueOf(linkageID); |
| } |
| |
| private String getBuildConfig(ICProject cproject) { |
| ICProjectDescriptionManager prjDescMgr = CCorePlugin.getDefault().getProjectDescriptionManager(); |
| ICProjectDescription prefs = prjDescMgr.getProjectDescription(cproject.getProject(), false); |
| if (prefs != null) { |
| ICConfigurationDescription cfg = prefs.getDefaultSettingConfiguration(); |
| if (cfg != null) |
| return cfg.getName(); |
| } |
| return "unknown"; |
| } |
| |
| private void outputUnresolvedIncludes(ICProject prj, IIndex index, PrintStream out, |
| IASTPreprocessorIncludeStatement[] includeDirectives, int linkageID) throws CoreException { |
| fWroteUnresolvedTitle = false; |
| ASTFilePathResolver resolver = new ProjectIndexerInputAdapter(prj); |
| HashSet<IIndexFile> handled = new HashSet<>(); |
| for (IASTPreprocessorIncludeStatement include : includeDirectives) { |
| if (include.isResolved()) { |
| IIndexFileLocation ifl = resolver.resolveASTPath(include.getPath()); |
| IIndexFile ifile = index.getFile(linkageID, ifl, include.getSignificantMacros()); |
| outputUnresolvedIncludes(index, out, ifl, ifile, handled); |
| } |
| } |
| if (fWroteUnresolvedTitle) |
| out.println(); |
| } |
| |
| private void outputUnresolvedIncludes(IIndex index, PrintStream out, IIndexFileLocation ifl, IIndexFile ifile, |
| Set<IIndexFile> handled) throws CoreException { |
| if (ifile == null) { |
| writeUnresolvedTitle(out); |
| out.println(INDENT + ifl.getURI() + " is not indexed"); |
| } else if (handled.add(ifile)) { |
| IIndexInclude[] includes = ifile.getIncludes(); |
| for (IIndexInclude inc : includes) { |
| if (inc.isActive()) { |
| if (inc.isResolved()) { |
| IIndexFile next = index.resolveInclude(inc); |
| outputUnresolvedIncludes(index, out, inc.getIncludesLocation(), next, handled); |
| } else { |
| writeUnresolvedTitle(out); |
| out.println(INDENT + "Unresolved inclusion: " + inc.getFullName() + " in file " |
| + inc.getIncludedByLocation().getURI()); |
| } |
| } |
| } |
| } |
| } |
| |
| public void writeUnresolvedTitle(PrintStream out) { |
| if (!fWroteUnresolvedTitle) { |
| fWroteUnresolvedTitle = true; |
| out.println("Unresolved includes (from headers in index):"); |
| } |
| } |
| |
| private void output(PrintStream out, String label, String[] list) { |
| if (list.length > 0) { |
| out.println(label); |
| for (String line : list) { |
| out.println(INDENT + line); |
| } |
| out.println(); |
| } |
| } |
| |
| private void output(PrintStream out, String label, Map<String, String> definedSymbols, HashSet<String> reported) { |
| if (!definedSymbols.isEmpty()) { |
| out.println(label); |
| |
| SortedMap<String, String> sorted = new TreeMap<>(COMP_INSENSITIVE); |
| sorted.putAll(definedSymbols); |
| for (Entry<String, String> entry : sorted.entrySet()) { |
| final String macro = entry.getKey() + '=' + entry.getValue(); |
| if (reported.add(macro)) { |
| out.println(INDENT + macro); |
| } |
| } |
| out.println(); |
| } |
| } |
| |
| private void output(PrintStream out, String label, IASTPreprocessorMacroDefinition[] defs, |
| HashSet<String> reported) { |
| if (defs.length > 0) { |
| out.println(label); |
| SortedSet<String> macros = new TreeSet<>(COMP_INSENSITIVE); |
| for (IASTPreprocessorMacroDefinition def : defs) { |
| macros.add(def.toString()); |
| } |
| |
| for (String macro : macros) { |
| if (reported.add(macro)) { |
| out.println(INDENT + macro); |
| } |
| } |
| out.println(); |
| } |
| } |
| |
| private void output(PrintStream out, String label, IASTProblem[] preprocessorProblems) { |
| if (preprocessorProblems.length > 0) { |
| out.println(label); |
| for (IASTProblem problem : preprocessorProblems) { |
| out.println(INDENT + problem.getMessageWithLocation()); |
| } |
| out.println(); |
| } |
| } |
| |
| private void output(PrintStream out, String label, IProblemBinding[] list) { |
| if (list.length > 0) { |
| out.println(label); |
| for (IProblemBinding problem : list) { |
| String file = problem.getFileName(); |
| int line = problem.getLineNumber(); |
| out.println(INDENT + problem.getMessage() + " in file " + file + ':' + line); |
| } |
| out.println(); |
| } |
| } |
| |
| private void output(PrintStream out, String label, List<Exception> list) { |
| if (!list.isEmpty()) { |
| out.println(label); |
| for (Exception problem : list) { |
| problem.printStackTrace(out); |
| } |
| out.println(); |
| } |
| } |
| |
| @Override |
| public void selectionChanged(IAction action, ISelection selection) { |
| fSelection = selection; |
| } |
| |
| public IWorkingCopy getTranslationUnitForSelectedEditorInput() { |
| IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
| if (window != null) { |
| IWorkbenchPart workbenchPart = window.getPartService().getActivePart(); |
| if (workbenchPart instanceof IEditorPart) { |
| IEditorPart editorPart = (IEditorPart) workbenchPart; |
| IEditorInput editorInput = editorPart.getEditorInput(); |
| IWorkingCopyManager manager = CDTUITools.getWorkingCopyManager(); |
| IWorkingCopy tu = manager.getWorkingCopy(editorInput); |
| return tu; |
| } |
| } |
| return null; |
| } |
| |
| public boolean isEnabledFor(ISelection selection) { |
| selectionChanged(null, selection); |
| ArrayList<ITranslationUnit> tus = getSelectedTranslationUnits(); |
| return tus.size() > 0; |
| } |
| |
| private ArrayList<ITranslationUnit> getSelectedTranslationUnits() { |
| ArrayList<ITranslationUnit> tuSelection = new ArrayList<>(); |
| if (fSelection instanceof IStructuredSelection) { |
| IStructuredSelection cElements = SelectionConverter.convertSelectionToCElements(fSelection); |
| Iterator<?> i = cElements.iterator(); |
| while (i.hasNext()) { |
| Object o = i.next(); |
| if (o instanceof ITranslationUnit) { |
| tuSelection.add((ITranslationUnit) o); |
| } |
| } |
| } else if (fSelection == null || fSelection instanceof ITextSelection) { |
| IWorkingCopy tu = getTranslationUnitForSelectedEditorInput(); |
| if (tu != null) { |
| tuSelection.add(tu); |
| } |
| } |
| return tuSelection; |
| } |
| } |