mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-25 18:05:33 +02:00
Bug 337486 - AbstractIndexAstChecker allows AST to outlive index read lock.
This commit is contained in:
parent
c24ce74075
commit
87ae09ee72
11 changed files with 477 additions and 327 deletions
|
@ -6,7 +6,7 @@
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.internal.checkers;
|
package org.eclipse.cdt.codan.internal.checkers;
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import java.util.Iterator;
|
||||||
|
|
||||||
import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
|
import org.eclipse.cdt.codan.core.cxx.CxxAstUtils;
|
||||||
import org.eclipse.cdt.codan.core.cxx.model.AbstractAstFunctionChecker;
|
import org.eclipse.cdt.codan.core.cxx.model.AbstractAstFunctionChecker;
|
||||||
import org.eclipse.cdt.codan.core.cxx.model.CxxModelsCache;
|
|
||||||
import org.eclipse.cdt.codan.core.model.IProblem;
|
import org.eclipse.cdt.codan.core.model.IProblem;
|
||||||
import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy;
|
import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy;
|
||||||
import org.eclipse.cdt.codan.core.model.cfg.ICfgData;
|
import org.eclipse.cdt.codan.core.model.cfg.ICfgData;
|
||||||
|
@ -156,7 +155,7 @@ public class ReturnChecker extends AbstractAstFunctionChecker {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected boolean endsWithNoExitNode(IASTFunctionDefinition func) {
|
protected boolean endsWithNoExitNode(IASTFunctionDefinition func) {
|
||||||
IControlFlowGraph graph = CxxModelsCache.getInstance().getControlFlowGraph(func);
|
IControlFlowGraph graph = getModelCache().getControlFlowGraph(func);
|
||||||
Iterator<IExitNode> exitNodeIterator = graph.getExitNodeIterator();
|
Iterator<IExitNode> exitNodeIterator = graph.getExitNodeIterator();
|
||||||
boolean noexitop = false;
|
boolean noexitop = false;
|
||||||
for (; exitNodeIterator.hasNext();) {
|
for (; exitNodeIterator.hasNext();) {
|
||||||
|
|
|
@ -6,13 +6,14 @@
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.core.cxx.model;
|
package org.eclipse.cdt.codan.core.cxx.model;
|
||||||
|
|
||||||
import org.eclipse.cdt.codan.core.CodanCorePlugin;
|
|
||||||
import org.eclipse.cdt.codan.core.cxx.Activator;
|
import org.eclipse.cdt.codan.core.cxx.Activator;
|
||||||
import org.eclipse.cdt.codan.core.model.AbstractCheckerWithProblemPreferences;
|
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.IProblem;
|
||||||
import org.eclipse.cdt.codan.core.model.IProblemLocation;
|
import org.eclipse.cdt.codan.core.model.IProblemLocation;
|
||||||
import org.eclipse.cdt.codan.core.model.IProblemLocationFactory;
|
import org.eclipse.cdt.codan.core.model.IProblemLocationFactory;
|
||||||
|
@ -23,114 +24,87 @@ import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTName;
|
import org.eclipse.cdt.core.dom.ast.IASTName;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
import org.eclipse.cdt.core.dom.ast.IASTNode;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||||
import org.eclipse.cdt.core.index.IIndex;
|
import org.eclipse.cdt.core.model.CoreModel;
|
||||||
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
|
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.IFile;
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IPath;
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
import org.eclipse.core.runtime.Path;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience implementation of checker that work on index based ast of a c/c++
|
* Convenience implementation of checker that works on index-based AST of a C/C++
|
||||||
* program.
|
* program.
|
||||||
*
|
*
|
||||||
* Clients may extend this class.
|
* Clients may extend this class.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblemPreferences implements ICAstChecker,
|
public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblemPreferences
|
||||||
IRunnableInEditorChecker {
|
implements ICAstChecker, IRunnableInEditorChecker {
|
||||||
private IFile file;
|
private CxxModelsCache modelCache;
|
||||||
private ICodanCommentMap commentmap;
|
|
||||||
|
|
||||||
protected IFile getFile() {
|
@Override
|
||||||
return file;
|
public synchronized boolean processResource(IResource resource) throws OperationCanceledException {
|
||||||
}
|
|
||||||
|
|
||||||
protected IProject getProject() {
|
|
||||||
return file == null ? null : file.getProject();
|
|
||||||
}
|
|
||||||
|
|
||||||
void processFile(IFile file) throws CoreException, InterruptedException {
|
|
||||||
commentmap = null;
|
|
||||||
IASTTranslationUnit ast = CxxModelsCache.getInstance().getAst(file);
|
|
||||||
if (ast == null)
|
|
||||||
return;
|
|
||||||
// lock the index for read access
|
|
||||||
IIndex index = CxxModelsCache.getInstance().getIndex(file);
|
|
||||||
index.acquireReadLock();
|
|
||||||
try {
|
|
||||||
// traverse the ast using the visitor pattern.
|
|
||||||
this.file = file;
|
|
||||||
processAst(ast);
|
|
||||||
} finally {
|
|
||||||
this.file = null;
|
|
||||||
index.releaseReadLock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean processResource(IResource resource) {
|
|
||||||
if (!shouldProduceProblems(resource))
|
if (!shouldProduceProblems(resource))
|
||||||
return false;
|
return false;
|
||||||
if (resource instanceof IFile) {
|
if (!(resource instanceof IFile))
|
||||||
IFile file = (IFile) resource;
|
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 {
|
||||||
|
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)
|
||||||
|
*/
|
||||||
|
public synchronized void processModel(Object model, ICheckerInvocationContext context) {
|
||||||
|
if (model instanceof IASTTranslationUnit) {
|
||||||
|
setContext(context);
|
||||||
|
IASTTranslationUnit ast = (IASTTranslationUnit) model;
|
||||||
|
synchronized (context) {
|
||||||
|
modelCache = context.get(CxxModelsCache.class);
|
||||||
|
if (modelCache == null) {
|
||||||
|
modelCache = new CxxModelsCache(ast);
|
||||||
|
context.add(modelCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
processFile(file);
|
processAst(ast);
|
||||||
} catch (CoreException e) {
|
} finally {
|
||||||
CodanCorePlugin.log(e);
|
modelCache = null;
|
||||||
} catch (InterruptedException e) {
|
setContext(null);
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("restriction")
|
|
||||||
protected IProblemLocation getProblemLocation(IASTNode astNode) {
|
|
||||||
IASTFileLocation astLocation = astNode.getFileLocation();
|
|
||||||
IPath location = new Path(astLocation.getFileName());
|
|
||||||
IFile astFile = ResourceLookup.selectFileForLocation(location, getProject());
|
|
||||||
if (astFile == null) {
|
|
||||||
astFile = file;
|
|
||||||
}
|
|
||||||
if (astFile == null) {
|
|
||||||
Activator.log("Cannot resolve location: " + location); //$NON-NLS-1$
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getProblemLocation(astNode, astLocation, astFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IProblemLocation getProblemLocation(IASTNode astNode, IASTFileLocation astLocation, IFile astFile) {
|
|
||||||
int line = astLocation.getStartingLineNumber();
|
|
||||||
IProblemLocationFactory locFactory = getRuntime().getProblemLocationFactory();
|
|
||||||
if (hasMacroLocation(astNode) && astNode instanceof IASTName) {
|
|
||||||
IASTImageLocation imageLocation = ((IASTName) astNode).getImageLocation();
|
|
||||||
if (imageLocation != null) {
|
|
||||||
int start = imageLocation.getNodeOffset();
|
|
||||||
int end = start + imageLocation.getNodeLength();
|
|
||||||
return locFactory.createProblemLocation(astFile, start, end, line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (line == astLocation.getEndingLineNumber()) {
|
|
||||||
return locFactory.createProblemLocation(astFile, astLocation.getNodeOffset(),
|
|
||||||
astLocation.getNodeOffset() + astLocation.getNodeLength(), line);
|
|
||||||
}
|
|
||||||
return locFactory.createProblemLocation(astFile, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasMacroLocation(IASTNode astNode) {
|
|
||||||
return astNode.getNodeLocations().length == 1 && astNode.getNodeLocations()[0] instanceof IASTMacroExpansionLocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -138,40 +112,60 @@ public abstract class AbstractIndexAstChecker extends AbstractCheckerWithProblem
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
public void reportProblem(String id, IASTNode astNode, Object... args) {
|
||||||
* (non-Javadoc)
|
IProblemLocation loc = getProblemLocation(astNode);
|
||||||
*
|
if (loc != null)
|
||||||
* @see
|
reportProblem(id, loc, args);
|
||||||
* org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker#processModel
|
}
|
||||||
* (java.lang.Object)
|
|
||||||
*/
|
public void reportProblem(IProblem problem, IASTNode astNode, Object... args) {
|
||||||
@SuppressWarnings("restriction")
|
IProblemLocation loc = getProblemLocation(astNode);
|
||||||
public synchronized void processModel(Object model) {
|
if (loc != null)
|
||||||
if (model instanceof IASTTranslationUnit) {
|
reportProblem(problem, loc, args);
|
||||||
CxxModelsCache.getInstance().clearCash();
|
}
|
||||||
IASTTranslationUnit ast = (IASTTranslationUnit) model;
|
|
||||||
IPath location = new Path(ast.getFilePath());
|
protected IProblemLocation getProblemLocation(IASTNode astNode) {
|
||||||
IFile astFile = ResourceLookup.selectFileForLocation(location, getProject());
|
IASTFileLocation astLocation = astNode.getFileLocation();
|
||||||
file = astFile;
|
return getProblemLocation(astNode, astLocation);
|
||||||
commentmap = null;
|
}
|
||||||
processAst(ast);
|
|
||||||
|
private IProblemLocation getProblemLocation(IASTNode astNode, IASTFileLocation astLocation) {
|
||||||
|
int line = astLocation.getStartingLineNumber();
|
||||||
|
IProblemLocationFactory locFactory = getRuntime().getProblemLocationFactory();
|
||||||
|
if (hasMacroLocation(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 (line == astLocation.getEndingLineNumber()) {
|
||||||
|
return locFactory.createProblemLocation(getFile(), astLocation.getNodeOffset(),
|
||||||
|
astLocation.getNodeOffset() + astLocation.getNodeLength(), line);
|
||||||
|
}
|
||||||
|
return locFactory.createProblemLocation(getFile(), line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasMacroLocation(IASTNode astNode) {
|
||||||
|
return astNode.getNodeLocations().length == 1 &&
|
||||||
|
astNode.getNodeLocations()[0] instanceof IASTMacroExpansionLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
protected ICodanCommentMap getCommentMap() {
|
||||||
if (commentmap == null) {
|
return modelCache.getCommentedNodeMap();
|
||||||
try {
|
|
||||||
CxxModelsCache cxxcache = CxxModelsCache.getInstance();
|
|
||||||
synchronized (cxxcache) {
|
|
||||||
IASTTranslationUnit ast = cxxcache.getAst(getFile());
|
|
||||||
commentmap = cxxcache.getCommentedNodeMap(ast);
|
|
||||||
return commentmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Activator.log(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return commentmap;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.core.cxx.model;
|
package org.eclipse.cdt.codan.core.cxx.model;
|
||||||
|
|
||||||
|
@ -15,32 +16,71 @@ import java.util.WeakHashMap;
|
||||||
import org.eclipse.cdt.codan.core.cxx.Activator;
|
import org.eclipse.cdt.codan.core.cxx.Activator;
|
||||||
import org.eclipse.cdt.codan.core.cxx.internal.model.CodanCommentMap;
|
import org.eclipse.cdt.codan.core.cxx.internal.model.CodanCommentMap;
|
||||||
import org.eclipse.cdt.codan.core.cxx.internal.model.cfg.CxxControlFlowGraph;
|
import org.eclipse.cdt.codan.core.cxx.internal.model.cfg.CxxControlFlowGraph;
|
||||||
|
import org.eclipse.cdt.codan.core.model.ICodanDisposable;
|
||||||
import org.eclipse.cdt.codan.core.model.cfg.IControlFlowGraph;
|
import org.eclipse.cdt.codan.core.model.cfg.IControlFlowGraph;
|
||||||
import org.eclipse.cdt.core.CCorePlugin;
|
import org.eclipse.cdt.core.CCorePlugin;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
|
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||||
import org.eclipse.cdt.core.index.IIndex;
|
import org.eclipse.cdt.core.index.IIndex;
|
||||||
import org.eclipse.cdt.core.model.CoreModel;
|
import org.eclipse.cdt.core.model.CoreModel;
|
||||||
import org.eclipse.cdt.core.model.ICElement;
|
import org.eclipse.cdt.core.model.ICProject;
|
||||||
import org.eclipse.cdt.core.model.ITranslationUnit;
|
import org.eclipse.cdt.core.model.ITranslationUnit;
|
||||||
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
|
import org.eclipse.cdt.internal.core.dom.rewrite.commenthandler.ASTCommenter;
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
|
import org.eclipse.core.runtime.Assert;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache data models for resource so checkers can share it
|
* Cache data models for resource so checkers can share it
|
||||||
*/
|
*/
|
||||||
public class CxxModelsCache {
|
public class CxxModelsCache implements ICodanDisposable {
|
||||||
private IFile file;
|
private static final int PARSE_MODE = ITranslationUnit.AST_SKIP_ALL_HEADERS
|
||||||
private IASTTranslationUnit ast;
|
| ITranslationUnit.AST_CONFIGURE_USING_SOURCE_CONTEXT
|
||||||
private ITranslationUnit tu;
|
| ITranslationUnit.AST_SKIP_TRIVIAL_EXPRESSIONS_IN_AGGREGATE_INITIALIZERS
|
||||||
private IIndex index;
|
| ITranslationUnit.AST_PARSE_INACTIVE_CODE;
|
||||||
private WeakHashMap<IASTFunctionDefinition, IControlFlowGraph> cfgmap = new WeakHashMap<IASTFunctionDefinition, IControlFlowGraph>(0);
|
|
||||||
private ICodanCommentMap commentMap;
|
|
||||||
private static CxxModelsCache instance = new CxxModelsCache();
|
|
||||||
|
|
||||||
public static CxxModelsCache getInstance() {
|
private final IFile file;
|
||||||
return instance;
|
private final ITranslationUnit tu;
|
||||||
|
private IASTTranslationUnit ast;
|
||||||
|
private IIndex index;
|
||||||
|
private final WeakHashMap<IASTFunctionDefinition, IControlFlowGraph> cfgmap;
|
||||||
|
private ICodanCommentMap commentMap;
|
||||||
|
private boolean disposed;
|
||||||
|
|
||||||
|
CxxModelsCache(ITranslationUnit tu) {
|
||||||
|
this.tu = tu;
|
||||||
|
this.file = tu != null ? (IFile) tu.getResource() : null;
|
||||||
|
cfgmap = new WeakHashMap<IASTFunctionDefinition, IControlFlowGraph>(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CxxModelsCache(IASTTranslationUnit ast) {
|
||||||
|
this(ast.getOriginatingTranslationUnit());
|
||||||
|
this.ast = ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IASTTranslationUnit getAST() throws OperationCanceledException, CoreException {
|
||||||
|
return getAST(tu);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IASTTranslationUnit getAST(ITranslationUnit tu)
|
||||||
|
throws OperationCanceledException, CoreException {
|
||||||
|
if (!this.tu.equals(tu)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (ast == null) {
|
||||||
|
getIndex();
|
||||||
|
ast= tu.getAST(index, PARSE_MODE);
|
||||||
|
}
|
||||||
|
return ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITranslationUnit getTranslationUnit() {
|
||||||
|
return tu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFile getFile() {
|
||||||
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized IControlFlowGraph getControlFlowGraph(IASTFunctionDefinition func) {
|
public synchronized IControlFlowGraph getControlFlowGraph(IASTFunctionDefinition func) {
|
||||||
|
@ -48,85 +88,67 @@ public class CxxModelsCache {
|
||||||
if (cfg != null)
|
if (cfg != null)
|
||||||
return cfg;
|
return cfg;
|
||||||
cfg = CxxControlFlowGraph.build(func);
|
cfg = CxxControlFlowGraph.build(func);
|
||||||
if (cfgmap.size() > 20) { // if too many function better drop the cash XXX should be LRU
|
// TODO(Alena Laskavaia): Change to LRU.
|
||||||
|
if (cfgmap.size() > 20) { // if too many function better drop the cash
|
||||||
cfgmap.clear();
|
cfgmap.clear();
|
||||||
}
|
}
|
||||||
cfgmap.put(func, cfg);
|
cfgmap.put(func, cfg);
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized IASTTranslationUnit getAst(IFile file) throws CoreException, InterruptedException {
|
public synchronized ICodanCommentMap getCommentedNodeMap() {
|
||||||
if (file.equals(this.file)) {
|
return getCommentedNodeMap(tu);
|
||||||
return ast;
|
}
|
||||||
|
|
||||||
|
public synchronized ICodanCommentMap getCommentedNodeMap(ITranslationUnit tu) {
|
||||||
|
if (!this.tu.equals(tu)) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
// create translation unit and access index
|
if (commentMap == null) {
|
||||||
ICElement celement = CoreModel.getDefault().create(file);
|
if (ast == null) {
|
||||||
if (!(celement instanceof ITranslationUnit))
|
throw new IllegalStateException("getCommentedNodeMap called before getAST"); //$NON-NLS-1$
|
||||||
return null; // not a C/C++ file
|
}
|
||||||
clearCash();
|
commentMap = new CodanCommentMap(ASTCommenter.getCommentedNodeMap(ast));
|
||||||
this.file = file;
|
}
|
||||||
//System.err.println("Making ast for "+file);
|
return commentMap;
|
||||||
tu = (ITranslationUnit) celement;
|
}
|
||||||
index = CCorePlugin.getIndexManager().getIndex(tu.getCProject());
|
|
||||||
// lock the index for read access
|
/**
|
||||||
index.acquireReadLock();
|
* Returns the index that can be safely used for reading until the cache is disposed.
|
||||||
try {
|
*
|
||||||
// create index based ast
|
* @return The index.
|
||||||
ast = tu.getAST(index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS);
|
*/
|
||||||
if (ast == null)
|
public synchronized IIndex getIndex() throws CoreException, OperationCanceledException {
|
||||||
return null;//
|
Assert.isTrue(!disposed, "CxxASTCache is already disposed."); //$NON-NLS-1$
|
||||||
return ast;
|
if (this.index == null) {
|
||||||
} finally {
|
ICProject[] projects = CoreModel.getDefault().getCModel().getCProjects();
|
||||||
|
IIndex index = CCorePlugin.getIndexManager().getIndex(projects);
|
||||||
|
try {
|
||||||
|
index.acquireReadLock();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
}
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see IDisposable#dispose()
|
||||||
|
* This method should not be called concurrently with any other method.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
Assert.isTrue(!disposed, "CxxASTCache.dispose() called more than once."); //$NON-NLS-1$
|
||||||
|
disposed = true;
|
||||||
|
if (index != null) {
|
||||||
index.releaseReadLock();
|
index.releaseReadLock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public synchronized ICodanCommentMap getCommentedNodeMap(IASTTranslationUnit ast) {
|
protected void finalize() throws Throwable {
|
||||||
if (this.ast == ast) {
|
if (!disposed)
|
||||||
try {
|
Activator.log("CxxASTCache was not disposed."); //$NON-NLS-1$
|
||||||
index.acquireReadLock();
|
super.finalize();
|
||||||
try {
|
|
||||||
commentMap = new CodanCommentMap(ASTCommenter.getCommentedNodeMap(ast));
|
|
||||||
} finally {
|
|
||||||
index.releaseReadLock();
|
|
||||||
}
|
|
||||||
return commentMap;
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Not cached");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICodanCommentMap getCommentedNodeMap(IFile file) {
|
|
||||||
try {
|
|
||||||
IASTTranslationUnit ast = getAst(file);
|
|
||||||
return getCommentedNodeMap(ast);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
return null;
|
|
||||||
} catch (CoreException e) {
|
|
||||||
Activator.log(e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear cash for current file
|
|
||||||
*/
|
|
||||||
public void clearCash() {
|
|
||||||
cfgmap.clear();
|
|
||||||
ast = null;
|
|
||||||
tu = null;
|
|
||||||
index = null;
|
|
||||||
commentMap = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized IIndex getIndex(IFile file) throws CoreException, InterruptedException {
|
|
||||||
if (file.equals(this.file)) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
getAst(file); // to init variables
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,11 @@ import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.eclipse.cdt.codan.core.CodanRuntime;
|
import org.eclipse.cdt.codan.core.CodanRuntime;
|
||||||
import org.eclipse.cdt.codan.core.model.IChecker;
|
import org.eclipse.cdt.codan.core.model.IChecker;
|
||||||
|
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
|
||||||
import org.eclipse.cdt.codan.core.model.IProblemLocation;
|
import org.eclipse.cdt.codan.core.model.IProblemLocation;
|
||||||
import org.eclipse.cdt.codan.core.model.IProblemReporter;
|
import org.eclipse.cdt.codan.core.model.IProblemReporter;
|
||||||
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
|
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
|
||||||
|
import org.eclipse.cdt.codan.internal.core.CheckerInvocationContext;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTProblem;
|
import org.eclipse.cdt.core.dom.ast.IASTProblem;
|
||||||
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
|
||||||
import org.eclipse.cdt.core.dom.parser.ISourceCodeParser;
|
import org.eclipse.cdt.core.dom.parser.ISourceCodeParser;
|
||||||
|
@ -71,7 +73,7 @@ public abstract class CodanFastCxxAstTestCase extends TestCase {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public IASTTranslationUnit parse(String code) {
|
public IASTTranslationUnit parse(String code) {
|
||||||
return parse(code, isCpp() ? ParserLanguage.CPP : ParserLanguage.C, true);
|
return parse(code, isCpp() ? ParserLanguage.CPP : ParserLanguage.C, true);
|
||||||
|
@ -116,7 +118,7 @@ public abstract class CodanFastCxxAstTestCase extends TestCase {
|
||||||
/**
|
/**
|
||||||
* Override if any of code that test tried to parse has errors, otherwise
|
* Override if any of code that test tried to parse has errors, otherwise
|
||||||
* parse method would assert
|
* parse method would assert
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected boolean hasCodeErrors() {
|
protected boolean hasCodeErrors() {
|
||||||
|
@ -153,11 +155,13 @@ public abstract class CodanFastCxxAstTestCase extends TestCase {
|
||||||
codanproblems.add(new ProblemInstance(problemId, loc, args));
|
codanproblems.add(new ProblemInstance(problemId, loc, args));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ICheckerInvocationContext context = new CheckerInvocationContext(null);
|
||||||
try {
|
try {
|
||||||
IChecker checker = getChecker();
|
IChecker checker = getChecker();
|
||||||
((IRunnableInEditorChecker) checker).processModel(tu);
|
((IRunnableInEditorChecker) checker).processModel(tu, context);
|
||||||
} finally {
|
} finally {
|
||||||
CodanRuntime.getInstance().setProblemReporter(problemReporter);
|
CodanRuntime.getInstance().setProblemReporter(problemReporter);
|
||||||
|
context.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2009, 2010 Alena Laskavaia
|
* Copyright (c) 2009, 2010 Alena Laskavaia
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.core.model;
|
package org.eclipse.cdt.codan.core.model;
|
||||||
|
|
||||||
|
@ -14,22 +15,21 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.cdt.codan.core.CodanRuntime;
|
import org.eclipse.cdt.codan.core.CodanRuntime;
|
||||||
import org.eclipse.cdt.codan.internal.core.CheckerInvocationContext;
|
|
||||||
import org.eclipse.cdt.codan.internal.core.CheckersRegistry;
|
import org.eclipse.cdt.codan.internal.core.CheckersRegistry;
|
||||||
import org.eclipse.core.resources.IFile;
|
import org.eclipse.core.resources.IFile;
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience implementation of IChecker interface. Has a default
|
* Convenience implementation of IChecker interface. Has a default
|
||||||
* implementation for common methods.
|
* implementation for common methods.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractChecker implements IChecker {
|
public abstract class AbstractChecker implements IChecker {
|
||||||
protected String name;
|
|
||||||
/**
|
/**
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
protected ICheckerInvocationContext context;
|
private ICheckerInvocationContext context;
|
||||||
|
private IProblemReporter problemReporter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor
|
* Default constructor
|
||||||
|
@ -47,7 +47,7 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reports a simple problem for given file and line
|
* Reports a simple problem for given file and line
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id
|
||||||
* - problem id
|
* - problem id
|
||||||
* @param file
|
* @param file
|
||||||
|
@ -66,7 +66,7 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
/**
|
/**
|
||||||
* Finds an instance of problem by given id, in user profile registered for
|
* Finds an instance of problem by given id, in user profile registered for
|
||||||
* specific file
|
* specific file
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id
|
||||||
* - problem id
|
* - problem id
|
||||||
* @param file
|
* @param file
|
||||||
|
@ -104,7 +104,7 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
/**
|
/**
|
||||||
* Reports a simple problem for given file and line, error message comes
|
* Reports a simple problem for given file and line, error message comes
|
||||||
* from problem definition
|
* from problem definition
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id
|
||||||
* - problem id
|
* - problem id
|
||||||
* @param file
|
* @param file
|
||||||
|
@ -121,16 +121,12 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public IProblemReporter getProblemReporter() {
|
public IProblemReporter getProblemReporter() {
|
||||||
try {
|
return problemReporter;
|
||||||
return getContext().getProblemReporter();
|
|
||||||
} catch (Exception e) {
|
|
||||||
return CodanRuntime.getInstance().getProblemReporter();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method to return codan runtime
|
* Convenience method to return codan runtime
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected CodanRuntime getRuntime() {
|
protected CodanRuntime getRuntime() {
|
||||||
|
@ -139,7 +135,7 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method to create and return instance of IProblemLocation
|
* Convenience method to create and return instance of IProblemLocation
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* - file where problem is found
|
* - file where problem is found
|
||||||
* @param line
|
* @param line
|
||||||
|
@ -152,7 +148,7 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method to create and return instance of IProblemLocation
|
* Convenience method to create and return instance of IProblemLocation
|
||||||
*
|
*
|
||||||
* @param file
|
* @param file
|
||||||
* - file where problem is found
|
* - file where problem is found
|
||||||
* @param startChar
|
* @param startChar
|
||||||
|
@ -176,7 +172,7 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* report a problem
|
* report a problem
|
||||||
*
|
*
|
||||||
* @param problemId - id of a problem
|
* @param problemId - id of a problem
|
||||||
* @param loc - problem location
|
* @param loc - problem location
|
||||||
* @param args - extra problem arguments
|
* @param args - extra problem arguments
|
||||||
|
@ -186,56 +182,72 @@ public abstract class AbstractChecker implements IChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get invocation context.
|
* Returns the invocation context.
|
||||||
*
|
*
|
||||||
* @return checker invocation context
|
* @return checker invocation context
|
||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public ICheckerInvocationContext getContext() {
|
protected ICheckerInvocationContext getContext() {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the invocation context. Usually called by codan builder.
|
|
||||||
* Object that calls this should also synchronize of checker object
|
|
||||||
* to prevent multi-thread access to a running context
|
|
||||||
*
|
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public void setContext(ICheckerInvocationContext context) {
|
protected void setContext(ICheckerInvocationContext context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public boolean before(IResource resource) {
|
public void before(IResource resource) {
|
||||||
IChecker checker = this;
|
|
||||||
IProblemReporter problemReporter = CodanRuntime.getInstance().getProblemReporter();
|
IProblemReporter problemReporter = CodanRuntime.getInstance().getProblemReporter();
|
||||||
IProblemReporter sessionReporter = problemReporter;
|
this.problemReporter = problemReporter;
|
||||||
if (problemReporter instanceof IProblemReporterSessionPersistent) {
|
if (problemReporter instanceof IProblemReporterSessionPersistent) {
|
||||||
// create session problem reporter
|
// create session problem reporter
|
||||||
sessionReporter = ((IProblemReporterSessionPersistent) problemReporter).createReporter(resource, checker);
|
this.problemReporter = ((IProblemReporterSessionPersistent) problemReporter).createReporter(resource, this);
|
||||||
((IProblemReporterSessionPersistent) sessionReporter).start();
|
((IProblemReporterSessionPersistent) this.problemReporter).start();
|
||||||
} else if (problemReporter instanceof IProblemReporterPersistent) {
|
} else if (problemReporter instanceof IProblemReporterPersistent) {
|
||||||
// delete markers if checker can possibly run on this
|
// delete markers if checker can possibly run on this
|
||||||
// resource this way if checker is not enabled markers would be
|
// resource this way if checker is not enabled markers would be
|
||||||
// deleted too
|
// deleted too
|
||||||
((IProblemReporterPersistent) problemReporter).deleteProblems(resource, checker);
|
((IProblemReporterPersistent) problemReporter).deleteProblems(resource, this);
|
||||||
}
|
}
|
||||||
((AbstractChecker) checker).setContext(new CheckerInvocationContext(resource, sessionReporter));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public boolean after(IResource resource) {
|
public void after(IResource resource) {
|
||||||
if (getContext().getProblemReporter() instanceof IProblemReporterSessionPersistent) {
|
if (problemReporter instanceof IProblemReporterSessionPersistent) {
|
||||||
// delete general markers
|
// Delete general markers
|
||||||
((IProblemReporterSessionPersistent) getContext().getProblemReporter()).done();
|
((IProblemReporterSessionPersistent) problemReporter).done();
|
||||||
|
}
|
||||||
|
problemReporter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param resource the resource to process.
|
||||||
|
* @return true if framework should traverse children of the resource and
|
||||||
|
* run this checkers on them again.
|
||||||
|
* @throws OperationCanceledException if the checker was interrupted.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public abstract boolean processResource(IResource resource) throws OperationCanceledException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see IChecker#processResource(IResource, ICheckerInvocationContext)
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public synchronized boolean processResource(IResource resource, ICheckerInvocationContext context)
|
||||||
|
throws OperationCanceledException {
|
||||||
|
this.setContext(context);
|
||||||
|
try {
|
||||||
|
return processResource(resource);
|
||||||
|
} finally {
|
||||||
|
this.setContext(null);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2009, 2010 Alena Laskavaia
|
* Copyright (c) 2009, 2010 Alena Laskavaia
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.core.model;
|
package org.eclipse.cdt.codan.core.model;
|
||||||
|
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that checker must implement (through extending directly or
|
* Interface that checker must implement (through extending directly or
|
||||||
* indirectly {@link AbstractChecker}.
|
* indirectly {@link AbstractChecker}.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
|
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
|
||||||
* of a work in progress. There is no guarantee that this API will work or that
|
* of a work in progress. There is no guarantee that this API will work or that
|
||||||
* it will remain the same.
|
* it will remain the same.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @noextend This interface is not intended to be extended by clients.
|
* @noextend This interface is not intended to be extended by clients.
|
||||||
* @noimplement This interface is not intended to be implemented by clients.
|
* @noimplement This interface is not intended to be implemented by clients.
|
||||||
* Extend {@link AbstractChecker} class instead.
|
* Extend {@link AbstractChecker} class instead.
|
||||||
|
@ -29,25 +31,36 @@ import org.eclipse.core.resources.IResource;
|
||||||
public interface IChecker {
|
public interface IChecker {
|
||||||
/**
|
/**
|
||||||
* Main method that checker should implement that actually detects errors
|
* Main method that checker should implement that actually detects errors
|
||||||
*
|
*
|
||||||
* @param resource
|
* @param resource the resource to run on.
|
||||||
* - resource to run on
|
* @param context container object for sharing data between different checkers
|
||||||
|
* operating on the resource.
|
||||||
* @return true if framework should traverse children of the resource and
|
* @return true if framework should traverse children of the resource and
|
||||||
* run this checkers on them again
|
* run this checkers on them again.
|
||||||
*/
|
* @throws OperationCanceledException if the checker was interrupted.
|
||||||
boolean processResource(IResource resource);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
boolean before(IResource resource);
|
boolean processResource(IResource resource, ICheckerInvocationContext context)
|
||||||
|
throws OperationCanceledException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Called before processing a resource.
|
||||||
|
*
|
||||||
|
* @param resource the resource that is about to be processed.
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
boolean after(IResource resource);
|
void before(IResource resource);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Called before processing a resource.
|
||||||
|
*
|
||||||
|
* @param resource the resource that has been processed.
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
void after(IResource resource);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the problem reporter.
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
IProblemReporter getProblemReporter();
|
IProblemReporter getProblemReporter();
|
||||||
|
@ -56,10 +69,9 @@ public interface IChecker {
|
||||||
* Implement this method to trim down type of resource you are interested
|
* Implement this method to trim down type of resource you are interested
|
||||||
* in, usually it will be c/c++ files only. This method should be
|
* in, usually it will be c/c++ files only. This method should be
|
||||||
* independent from current user preferences.
|
* independent from current user preferences.
|
||||||
*
|
*
|
||||||
* @param resource
|
* @param resource the resource to run on.
|
||||||
* - resource to run on
|
* @return true if checker should be run on this resource.
|
||||||
* @return - true if checker should be run on this resource
|
|
||||||
*/
|
*/
|
||||||
boolean enabledInContext(IResource resource);
|
boolean enabledInContext(IResource resource);
|
||||||
|
|
||||||
|
@ -70,7 +82,7 @@ public interface IChecker {
|
||||||
* {@link IRunnableInEditorChecker}.
|
* {@link IRunnableInEditorChecker}.
|
||||||
* Checker should return false if check is non-trivial and takes a long
|
* Checker should return false if check is non-trivial and takes a long
|
||||||
* time.
|
* time.
|
||||||
*
|
*
|
||||||
* @return true if need to be run in editor as user types, and false
|
* @return true if need to be run in editor as user types, and false
|
||||||
* otherwise
|
* otherwise
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,34 +1,60 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2009,2010 QNX Software Systems
|
* Copyright (c) 2009, 2011 QNX Software Systems
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
|
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.core.model;
|
package org.eclipse.cdt.codan.core.model;
|
||||||
|
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since there is only one instance of checker available this object keeps
|
* Context object that can be used to store data shared between different
|
||||||
* track of invocation context - which would usually contain resource and some
|
* checkers operating on the same resource. The context and all objects stored
|
||||||
* other object that checker require
|
* in it are disposed of at the end of processing of a resource. May store
|
||||||
|
* objects of arbitrary types but only a single instance per type.
|
||||||
|
* <p>
|
||||||
|
* Implementations of this interface are guaranteed to be thread-safe.
|
||||||
* <p>
|
* <p>
|
||||||
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
|
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as part
|
||||||
* of a work in progress. There is no guarantee that this API will work or that
|
* of a work in progress. There is no guarantee that this API will work or that
|
||||||
* it will remain the same.
|
* it will remain the same.
|
||||||
* </p>
|
*
|
||||||
*
|
|
||||||
* @noextend This interface is not intended to be extended by clients.
|
* @noextend This interface is not intended to be extended by clients.
|
||||||
* @noimplement This interface is not intended to be implemented by clients.
|
* @noimplement This interface is not intended to be implemented by clients.
|
||||||
*
|
*
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
public interface ICheckerInvocationContext {
|
public interface ICheckerInvocationContext extends ICodanDisposable {
|
||||||
|
/**
|
||||||
|
* @return the resource this context is associated with.
|
||||||
|
*/
|
||||||
IResource getResource();
|
IResource getResource();
|
||||||
|
|
||||||
IProblemReporter getProblemReporter();
|
/**
|
||||||
|
* Returns the object of the given type. Lookup by an interface or a superclass
|
||||||
|
* is also possible, but in case when there are multiple objects implementing
|
||||||
|
* the interface, an arbitrary one will be returned.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the object.
|
||||||
|
* @param objectClass the class of the object to retrieve.
|
||||||
|
* @return the object of the given type, or <code>null</code> if not present in the context.
|
||||||
|
*/
|
||||||
|
public <T> T get(Class<T> objectClass);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an object to the context. The context accepts only a single
|
||||||
|
* instance of each class.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the object.
|
||||||
|
* @param object the object to add to the context.
|
||||||
|
* @throws IllegalArgumentException if an attempt is made to add second instance
|
||||||
|
* of the same class.
|
||||||
|
*/
|
||||||
|
public <T extends ICodanDisposable> void add(T object);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2011 Google, Inc and others.
|
||||||
|
* 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:
|
||||||
|
* Sergey Prigogin (Google) - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.eclipse.cdt.codan.core.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* An interface for objects that own resources that have to be explicitly
|
||||||
|
* released. A disposable object is guaranteed to receive a {@link #dispose()} call
|
||||||
|
* when it is not longer needed. At this point, the object must release all resources
|
||||||
|
* and detach all listeners. A disposable object can only be disposed once; it cannot
|
||||||
|
* be reused.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* This interface can be extended or implemented by clients.
|
||||||
|
* </p>
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface ICodanDisposable {
|
||||||
|
/**
|
||||||
|
* Disposes of the cache. This method has to be called exactly once during
|
||||||
|
* the life cycle of the cache.
|
||||||
|
*/
|
||||||
|
public void dispose();
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2009, 2010 Alena Laskavaia
|
* Copyright (c) 2009, 2010 Alena Laskavaia
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.core.model;
|
package org.eclipse.cdt.codan.core.model;
|
||||||
|
|
||||||
|
@ -18,12 +19,15 @@ package org.eclipse.cdt.codan.core.model;
|
||||||
* of a work in progress. There is no guarantee that this API will work or that
|
* of a work in progress. There is no guarantee that this API will work or that
|
||||||
* it will remain the same.
|
* it will remain the same.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @noextend This interface is not intended to be extended by clients.
|
* @noextend This interface is not intended to be extended by clients.
|
||||||
*/
|
*/
|
||||||
public interface IRunnableInEditorChecker {
|
public interface IRunnableInEditorChecker {
|
||||||
/**
|
/**
|
||||||
* @param model
|
* @param model the model to check.
|
||||||
|
* @param context container object for sharing data between different checkers
|
||||||
|
* operating on the model.
|
||||||
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
void processModel(Object model);
|
void processModel(Object model, ICheckerInvocationContext context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,78 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Copyright (c) 2009,2010 QNX Software Systems
|
* Copyright (c) 2009, 2011 QNX Software Systems
|
||||||
* All rights reserved. This program and the accompanying materials
|
* All rights reserved. This program and the accompanying materials
|
||||||
* are made available under the terms of the Eclipse Public License v1.0
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
* which accompanies this distribution, and is available at
|
* which accompanies this distribution, and is available at
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
|
* QNX Software Systems (Alena Laskavaia) - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.internal.core;
|
package org.eclipse.cdt.codan.internal.core;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
|
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
|
||||||
import org.eclipse.cdt.codan.core.model.IProblemReporter;
|
import org.eclipse.cdt.codan.core.model.ICodanDisposable;
|
||||||
import org.eclipse.core.resources.IResource;
|
import org.eclipse.core.resources.IResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of ICheckerInvocationContext
|
* Implementation of ICheckerInvocationContext.
|
||||||
|
* This class is thread-safe.
|
||||||
*/
|
*/
|
||||||
public class CheckerInvocationContext implements ICheckerInvocationContext {
|
public class CheckerInvocationContext implements ICheckerInvocationContext {
|
||||||
private IResource resource;
|
private final IResource resource;
|
||||||
private IProblemReporter sessionReporter;
|
private final Map<Class<?>, Object> objectStorage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param resource
|
* @param resource the resource this context is associated with.
|
||||||
* @param sessionReporter
|
|
||||||
*/
|
*/
|
||||||
public CheckerInvocationContext(IResource resource, IProblemReporter sessionReporter) {
|
public CheckerInvocationContext(IResource resource) {
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
this.sessionReporter = sessionReporter;
|
objectStorage = new HashMap<Class<?>, Object>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IResource getResource() {
|
public IResource getResource() {
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProblemReporter getProblemReporter() {
|
@SuppressWarnings("unchecked")
|
||||||
return sessionReporter;
|
public synchronized <T> T get(Class<T> objectClass) {
|
||||||
|
T object = (T) objectStorage.get(objectClass);
|
||||||
|
if (object != null)
|
||||||
|
return object;
|
||||||
|
for (Map.Entry<Class<?>, Object> entry : objectStorage.entrySet()) {
|
||||||
|
if (objectClass.isAssignableFrom(entry.getKey()))
|
||||||
|
return (T) entry.getValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see ICheckerInvocationContext#add(Object)
|
||||||
|
*/
|
||||||
|
public synchronized <T extends ICodanDisposable> void add(T object) {
|
||||||
|
Object old = objectStorage.put(object.getClass(), object);
|
||||||
|
if (old != null && object != old) {
|
||||||
|
objectStorage.put(old.getClass(), old); // Restore old value.
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see IDisposableCache#dispose()
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
for (Map.Entry<Class<?>, Object> entry : objectStorage.entrySet()) {
|
||||||
|
Object obj = entry.getValue();
|
||||||
|
if (obj instanceof ICodanDisposable) {
|
||||||
|
((ICodanDisposable) obj).dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
objectStorage.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* http://www.eclipse.org/legal/epl-v10.html
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
*
|
*
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* Alena Laskavaia - initial API and implementation
|
* Alena Laskavaia - initial API and implementation
|
||||||
|
* Sergey Prigogin (Google)
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.codan.internal.core;
|
package org.eclipse.cdt.codan.internal.core;
|
||||||
|
|
||||||
|
@ -17,6 +18,7 @@ import org.eclipse.cdt.codan.core.Messages;
|
||||||
import org.eclipse.cdt.codan.core.model.CheckerLaunchMode;
|
import org.eclipse.cdt.codan.core.model.CheckerLaunchMode;
|
||||||
import org.eclipse.cdt.codan.core.model.Checkers;
|
import org.eclipse.cdt.codan.core.model.Checkers;
|
||||||
import org.eclipse.cdt.codan.core.model.IChecker;
|
import org.eclipse.cdt.codan.core.model.IChecker;
|
||||||
|
import org.eclipse.cdt.codan.core.model.ICheckerInvocationContext;
|
||||||
import org.eclipse.cdt.codan.core.model.ICodanBuilder;
|
import org.eclipse.cdt.codan.core.model.ICodanBuilder;
|
||||||
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
|
import org.eclipse.cdt.codan.core.model.IRunnableInEditorChecker;
|
||||||
import org.eclipse.core.resources.IContainer;
|
import org.eclipse.core.resources.IContainer;
|
||||||
|
@ -27,6 +29,7 @@ import org.eclipse.core.resources.IResourceDeltaVisitor;
|
||||||
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
import org.eclipse.core.resources.IncrementalProjectBuilder;
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IProgressMonitor;
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
|
import org.eclipse.core.runtime.OperationCanceledException;
|
||||||
import org.eclipse.core.runtime.SubProgressMonitor;
|
import org.eclipse.core.runtime.SubProgressMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +37,7 @@ import org.eclipse.core.runtime.SubProgressMonitor;
|
||||||
*/
|
*/
|
||||||
public class CodanBuilder extends IncrementalProjectBuilder implements ICodanBuilder {
|
public class CodanBuilder extends IncrementalProjectBuilder implements ICodanBuilder {
|
||||||
/**
|
/**
|
||||||
* codan builder id
|
* Codan builder id
|
||||||
*/
|
*/
|
||||||
public static final String BUILDER_ID = "org.eclipse.cdt.codan.core.codanBuilder"; //$NON-NLS-1$
|
public static final String BUILDER_ID = "org.eclipse.cdt.codan.core.codanBuilder"; //$NON-NLS-1$
|
||||||
|
|
||||||
|
@ -126,39 +129,43 @@ public class CodanBuilder extends IncrementalProjectBuilder implements ICodanBui
|
||||||
// System.err.println("processing " + resource);
|
// System.err.println("processing " + resource);
|
||||||
monitor.beginTask(Messages.CodanBuilder_Code_Analysis_On + resource, checkers + memsize * tick);
|
monitor.beginTask(Messages.CodanBuilder_Code_Analysis_On + resource, checkers + memsize * tick);
|
||||||
try {
|
try {
|
||||||
for (IChecker checker : chegistry) {
|
ICheckerInvocationContext context = new CheckerInvocationContext(resource);
|
||||||
try {
|
try {
|
||||||
if (monitor.isCanceled())
|
for (IChecker checker : chegistry) {
|
||||||
return;
|
try {
|
||||||
if (doesCheckerSupportLaunchMode(checker, checkerLaunchMode)
|
if (monitor.isCanceled())
|
||||||
&& checker.enabledInContext(resource)
|
return;
|
||||||
&& chegistry.isCheckerEnabledForLaunchMode(checker, resource, checkerLaunchMode)) {
|
if (doesCheckerSupportLaunchMode(checker, checkerLaunchMode)
|
||||||
synchronized (checker) {
|
&& checker.enabledInContext(resource)
|
||||||
try {
|
&& chegistry.isCheckerEnabledForLaunchMode(checker, resource, checkerLaunchMode)) {
|
||||||
checker.before(resource);
|
synchronized (checker) {
|
||||||
if (chegistry.isCheckerEnabled(checker, resource)) {
|
try {
|
||||||
//long time = System.currentTimeMillis();
|
checker.before(resource);
|
||||||
if (checkerLaunchMode == CheckerLaunchMode.RUN_AS_YOU_TYPE) {
|
if (chegistry.isCheckerEnabled(checker, resource)) {
|
||||||
((IRunnableInEditorChecker) checker).processModel(model);
|
//long time = System.currentTimeMillis();
|
||||||
} else {
|
if (checkerLaunchMode == CheckerLaunchMode.RUN_AS_YOU_TYPE) {
|
||||||
checker.processResource(resource);
|
((IRunnableInEditorChecker) checker).processModel(model, context);
|
||||||
|
} else {
|
||||||
|
checker.processResource(resource, context);
|
||||||
|
}
|
||||||
|
// System.err.println("Checker "
|
||||||
|
// + checker.getClass() + " worked "
|
||||||
|
// + (System.currentTimeMillis() - time));
|
||||||
}
|
}
|
||||||
// System.err
|
} finally {
|
||||||
// .println("Checker "
|
checker.after(resource);
|
||||||
// + checker.getClass()
|
|
||||||
// + " worked "
|
|
||||||
// + (System
|
|
||||||
// .currentTimeMillis() - time));
|
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
checker.after(resource);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
monitor.worked(1);
|
||||||
|
} catch (OperationCanceledException e) {
|
||||||
|
return;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
CodanCorePlugin.log(e);
|
||||||
}
|
}
|
||||||
monitor.worked(1);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
CodanCorePlugin.log(e);
|
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
context.dispose();
|
||||||
}
|
}
|
||||||
if (resource instanceof IContainer
|
if (resource instanceof IContainer
|
||||||
&& (checkerLaunchMode == CheckerLaunchMode.RUN_ON_FULL_BUILD || checkerLaunchMode == CheckerLaunchMode.RUN_ON_DEMAND)) {
|
&& (checkerLaunchMode == CheckerLaunchMode.RUN_ON_FULL_BUILD || checkerLaunchMode == CheckerLaunchMode.RUN_ON_DEMAND)) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue