/******************************************************************************* * Copyright (c) 2009, 2016 Alena Laskavaia * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Alena Laskavaia - initial API and implementation * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.codan.core.cxx.model; import java.util.ArrayList; import java.util.List; import org.eclipse.cdt.codan.core.cxx.Activator; import org.eclipse.cdt.codan.core.model.AbstractCheckerWithProblemPreferences; import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext; import org.eclipse.cdt.codan.core.model.IProblem; import org.eclipse.cdt.codan.core.model.IProblemLocation; import org.eclipse.cdt.codan.core.model.IProblemLocationFactory; import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker; import org.eclipse.cdt.core.dom.ast.IASTComment; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTImageLocation; import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.OperationCanceledException; /** * Convenience implementation of checker that works on index-based AST of a * C/C++ * program. * * Clients may extend this class. */ public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblemPreferences implements ICAstChecker, IRunnableInEditorChecker { private CxxModelsCache modelCache; @Override public synchronized boolean processResource(IResource resource) throws OperationCanceledException { if (!shouldProduceProblems(resource)) return false; if (!(resource instanceof IFile)) return true; processFile((IFile) resource); return false; } private void processFile(IFile file) throws OperationCanceledException { ICheckerInvocationContext context = getContext(); synchronized (context) { modelCache = context.get(CxxModelsCache.class); if (modelCache == null) { ICElement celement = CoreModel.getDefault().create(file); if (!(celement instanceof ITranslationUnit)) { return; } modelCache = new CxxModelsCache((ITranslationUnit) celement); context.add(modelCache); } } try { // Run the checker only if the index is fully initialized. Otherwise it may produce // false positives. if (modelCache.getIndex().isFullyInitialized()) { IASTTranslationUnit ast = modelCache.getAST(); if (ast != null) { synchronized (ast) { processAst(ast); } } } } catch (CoreException e) { Activator.log(e); } finally { modelCache = null; } } /* * (non-Javadoc) * * @see IRunnableInEditorChecker#processModel(Object, * ICheckerInvocationContext) */ @Override public synchronized void processModel(Object model, ICheckerInvocationContext context) { if (model instanceof IASTTranslationUnit) { IASTTranslationUnit ast = (IASTTranslationUnit) model; // Run the checker only if the index was fully initialized when the file was parsed. // Otherwise the checker may produce false positives. if (ast.isBasedOnIncompleteIndex()) return; setContext(context); synchronized (context) { modelCache = context.get(CxxModelsCache.class); if (modelCache == null) { modelCache = new CxxModelsCache(ast); context.add(modelCache); } } try { processAst(ast); } finally { modelCache = null; setContext(null); } } } @Override public boolean runInEditor() { return true; } public void reportProblem(String id, IASTNode astNode, Object... args) { IProblemLocation loc = getProblemLocation(astNode); if (loc != null) reportProblem(id, loc, args); } public void reportProblem(IProblem problem, IASTNode astNode, Object... args) { IProblemLocation loc = getProblemLocation(astNode); if (loc != null) reportProblem(problem, loc, args); } /** * Checks if problem should be reported, in this case it will check line * comments, later can add filters or what not. * * @param problem - problem kind * @param loc - location * @param args - arguments * @since 3.4 */ @Override protected boolean shouldProduceProblem(IProblem problem, IProblemLocation loc, Object... args) { String suppressionComment = (String) getSuppressionCommentPreference(problem).getValue(); if (suppressionComment.isEmpty()) return super.shouldProduceProblem(problem, loc, args); List<IASTComment> lineComments = getLineCommentsForLocation(loc); for (IASTComment astComment : lineComments) { if (astComment.getRawSignature().contains(suppressionComment)) return false; } return super.shouldProduceProblem(problem, loc, args); } protected List<IASTComment> getLineCommentsForLocation(IProblemLocation loc) { ArrayList<IASTComment> lineComments = new ArrayList<>(); try { IASTComment[] commentsArray = modelCache.getAST().getComments(); for (IASTComment comm : commentsArray) { IASTFileLocation fileLocation = comm.getFileLocation(); if (fileLocation.getStartingLineNumber() == loc.getLineNumber()) { //XXX check on windows portable or os? String problemFile = loc.getFile().getLocation().toPortableString(); String commentFile = fileLocation.getFileName(); if (problemFile.equals(commentFile)) { lineComments.add(comm); } } } } catch (OperationCanceledException | CoreException e) { Activator.log(e); } return lineComments; } protected IProblemLocation getProblemLocation(IASTNode astNode) { IASTFileLocation astLocation = astNode.getFileLocation(); return getProblemLocation(astNode, astLocation); } private IProblemLocation getProblemLocation(IASTNode astNode, IASTFileLocation astLocation) { int line = astLocation.getStartingLineNumber(); IProblemLocationFactory locFactory = getRuntime().getProblemLocationFactory(); if (enclosedInMacroExpansion(astNode) && astNode instanceof IASTName) { IASTImageLocation imageLocation = ((IASTName) astNode).getImageLocation(); if (imageLocation != null) { int start = imageLocation.getNodeOffset(); int end = start + imageLocation.getNodeLength(); return locFactory.createProblemLocation(getFile(), start, end, line); } } // If the raw signature has more than one line, we highlight only the code // related to the problem. However, if the problem is associated with a // node representing a class definition, do not highlight the entire class // definition, because that can result in many lines being highlighted. if (astNode instanceof IASTCompositeTypeSpecifier) { return locFactory.createProblemLocation(getFile(), line); } int start = astLocation.getNodeOffset(); int end = start + astLocation.getNodeLength(); return locFactory.createProblemLocation(getFile(), start, end, line); } protected static boolean enclosedInMacroExpansion(IASTNode node) { IASTNodeLocation[] nodeLocations = node.getNodeLocations(); return nodeLocations.length == 1 && nodeLocations[0] instanceof IASTMacroExpansionLocation; } protected static boolean includesMacroExpansion(IASTNode node) { for (IASTNodeLocation nodeLocation : node.getNodeLocations()) { if (nodeLocation instanceof IASTMacroExpansionLocation) return true; } return false; } protected IFile getFile() { return modelCache.getFile(); } protected IProject getProject() { IFile file = getFile(); return file == null ? null : file.getProject(); } protected CxxModelsCache getModelCache() { return modelCache; } protected ICodanCommentMap getCommentMap() { return modelCache.getCommentedNodeMap(); } }