1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-09-08 02:53:12 +02:00

Determination of output parameters for extracting function from

inside conditional statements and loops.
This commit is contained in:
Sergey Prigogin 2012-03-13 17:37:27 -07:00
parent 723ed9f912
commit 0f4f703be2
29 changed files with 3417 additions and 412 deletions

View file

@ -259,7 +259,8 @@ public class DeclarationWriter extends NodeWriter {
private void writeFunctionDefinition(IASTFunctionDefinition funcDef) {
IASTDeclSpecifier declSpecifier = funcDef.getDeclSpecifier();
declSpecifier.accept(visitor);
if (declSpecifier != null)
declSpecifier.accept(visitor);
if (declSpecifier instanceof IASTSimpleDeclSpecifier) {
IASTSimpleDeclSpecifier simDeclSpec = (IASTSimpleDeclSpecifier) declSpecifier;
if (simDeclSpec.getType() != IASTSimpleDeclSpecifier.t_unspecified) {

View file

@ -0,0 +1,20 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class BlockFlowInfo extends GenericSequentialFlowInfo {
public BlockFlowInfo() {
}
}

View file

@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import java.util.HashSet;
import org.eclipse.cdt.core.dom.ast.IASTName;
class BranchFlowInfo extends FlowInfo {
public BranchFlowInfo(IASTName label, FlowContext context) {
super(NO_RETURN);
fBranches= new HashSet<String>(2);
fBranches.add(makeString(label));
}
}

View file

@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class ConditionalFlowInfo extends FlowInfo {
public ConditionalFlowInfo() {
super(NO_RETURN);
}
public void mergeCondition(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void merge(FlowInfo truePart, FlowInfo falsePart, FlowContext context) {
if (truePart == null && falsePart == null)
return;
GenericConditionalFlowInfo cond= new GenericConditionalFlowInfo();
if (truePart != null)
cond.mergeAccessMode(truePart, context);
if (falsePart != null)
cond.mergeAccessMode(falsePart, context);
if (truePart == null || falsePart == null)
cond.mergeEmptyCondition(context);
mergeAccessModeSequential(cond, context);
}
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class DoWhileFlowInfo extends FlowInfo {
private boolean fActionBranches;
public void mergeAction(FlowInfo info, FlowContext context) {
if (info == null)
return;
fActionBranches= info.branches();
assign(info);
if (fActionBranches && fReturnKind == VALUE_RETURN) {
fReturnKind= PARTIAL_RETURN;
}
}
public void mergeCondition(FlowInfo info, FlowContext context) {
if (fActionBranches || fReturnKind == VALUE_RETURN || fReturnKind == VOID_RETURN || info == null)
return;
mergeAccessModeSequential(info, context);
}
}

View file

@ -0,0 +1,136 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
public class FlowContext extends LocalVariableIndex {
private static class ComputeMode {
}
public static final ComputeMode MERGE = new ComputeMode();
public static final ComputeMode ARGUMENTS = new ComputeMode();
public static final ComputeMode RETURN_VALUES = new ComputeMode();
private boolean fConsiderAccessMode;
private boolean fLoopReentranceMode;
private ComputeMode fComputeMode;
private IVariable[] fLocals;
private final List<ICPPASTCatchHandler[]> fExceptionStack;
public FlowContext(IASTFunctionDefinition functionDefinition) {
super(functionDefinition);
fExceptionStack= new ArrayList<ICPPASTCatchHandler[]>(3);
}
public void setConsiderAccessMode(boolean b) {
fConsiderAccessMode= b;
}
public void setComputeMode(ComputeMode mode) {
fComputeMode= mode;
}
void setLoopReentranceMode(boolean b) {
fLoopReentranceMode= b;
}
int getArrayLength() {
return getNumLocalVariables();
}
boolean considerAccessMode() {
return fConsiderAccessMode;
}
boolean isLoopReentranceMode() {
return fLoopReentranceMode;
}
boolean computeMerge() {
return fComputeMode == MERGE;
}
boolean computeArguments() {
return fComputeMode == ARGUMENTS;
}
boolean computeReturnValues() {
return fComputeMode == RETURN_VALUES;
}
public IVariable getLocalFromIndex(int index) {
if (fLocals == null || index > fLocals.length)
return null;
return fLocals[index];
}
/**
* Adds a local variable to the context.
* @param localVariable the local variable to manage.
*/
void manageLocal(IVariable localVariable) {
int index = getIndexFromLocal(localVariable);
if (index >= 0) {
if (fLocals == null)
fLocals= new IVariable[getNumLocalVariables()];
fLocals[index] = localVariable;
}
}
//---- Exception handling --------------------------------------------------------
void pushExceptions(ICPPASTTryBlockStatement node) {
ICPPASTCatchHandler[] catchHandlers = node.getCatchHandlers();
fExceptionStack.add(catchHandlers);
}
void popExceptions() {
Assert.isTrue(fExceptionStack.size() > 0);
fExceptionStack.remove(fExceptionStack.size() - 1);
}
boolean isExceptionCaught(IType exceptionType) {
for (ICPPASTCatchHandler[] catchHandlers : fExceptionStack) {
for (ICPPASTCatchHandler catchHandler : catchHandlers) {
if (catchHandler.isCatchAll())
return true;
IASTDeclaration caughtException= catchHandler.getDeclaration();
if (caughtException instanceof IASTSimpleDeclaration) {
IASTDeclarator[] declarators = ((IASTSimpleDeclaration) caughtException).getDeclarators();
IType caughtType = CPPVisitor.createType(declarators[0]);
while (caughtType != null) {
// 15.3
if (caughtType.isSameType(exceptionType))
return true;
// TODO(sprigogin): Implement the rest of 15.3 matching logic.
}
}
}
}
return false;
}
}

View file

@ -0,0 +1,428 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] missing return type when code can throw exception - https://bugs.eclipse.org/bugs/show_bug.cgi?id=97413
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
public abstract class FlowInfo {
// Return statement handling.
protected static final int NOT_POSSIBLE= 0;
protected static final int UNDEFINED= 1;
protected static final int NO_RETURN= 2;
protected static final int PARTIAL_RETURN= 3;
protected static final int VOID_RETURN= 4;
protected static final int VALUE_RETURN= 5;
protected static final int THROW= 6;
// Local access handling.
public static final int READ= PDOMName.READ_ACCESS; // 1 << 9
public static final int WRITE= PDOMName.WRITE_ACCESS; // 1 << 10
public static final int UNUSED= 1 << 0;
public static final int READ_POTENTIAL= 1 << 1;
public static final int WRITE_POTENTIAL= 1 << 2;
public static final int UNKNOWN= 1 << 3;
// Table to merge access modes for condition statements (e.g branch[x] || branch[y]).
private static final int[][] ACCESS_MODE_CONDITIONAL_TABLE= {
/* UNUSED READ READ_POTENTIAL WRITE WRITE_POTENTIAL UNKNOWN */
/* UNUSED */ { UNUSED, READ_POTENTIAL, READ_POTENTIAL, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN },
/* READ */ { READ_POTENTIAL, READ, READ_POTENTIAL, UNKNOWN, UNKNOWN, UNKNOWN },
/* READ_POTENTIAL */ { READ_POTENTIAL, READ_POTENTIAL, READ_POTENTIAL, UNKNOWN, UNKNOWN, UNKNOWN },
/* WRITE */ { WRITE_POTENTIAL, UNKNOWN, UNKNOWN, WRITE, WRITE_POTENTIAL, UNKNOWN },
/* WRITE_POTENTIAL */ { WRITE_POTENTIAL, UNKNOWN, UNKNOWN, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN },
/* UNKNOWN */ { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN }
};
// Table to change access mode if there is an open branch statement
private static final int[] ACCESS_MODE_OPEN_BRANCH_TABLE= {
/* UNUSED READ READ_POTENTIAL WRITE WRITE_POTENTIAL UNKNOWN */
UNUSED, READ_POTENTIAL, READ_POTENTIAL, WRITE_POTENTIAL, WRITE_POTENTIAL, UNKNOWN
};
// Table to merge return modes for condition statements (y: fReturnKind, x: other.fReturnKind)
private static final int[][] RETURN_KIND_CONDITIONAL_TABLE = {
/* NOT_POSSIBLE UNDEFINED NO_RETURN PARTIAL_RETURN VOID_RETURN VALUE_RETURN THROW */
/* NOT_POSSIBLE */ { NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE },
/* UNDEFINED */ { NOT_POSSIBLE, UNDEFINED, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
/* NO_RETURN */ { NOT_POSSIBLE, NO_RETURN, NO_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, NO_RETURN },
/* PARTIAL_RETURN */ { NOT_POSSIBLE, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN },
/* VOID_RETURN */ { NOT_POSSIBLE, VOID_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, VOID_RETURN, NOT_POSSIBLE, VOID_RETURN },
/* VALUE_RETURN */ { NOT_POSSIBLE, VALUE_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, NOT_POSSIBLE, VALUE_RETURN, VALUE_RETURN },
/* THROW */ { NOT_POSSIBLE, THROW, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW }
};
// Table to merge return modes for sequential statements (y: fReturnKind, x: other.fReturnKind)
private static final int[][] RETURN_KIND_SEQUENTIAL_TABLE = {
/* NOT_POSSIBLE UNDEFINED NO_RETURN PARTIAL_RETURN VOID_RETURN VALUE_RETURN THROW */
/* NOT_POSSIBLE */ { NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE, NOT_POSSIBLE },
/* UNDEFINED */ { NOT_POSSIBLE, UNDEFINED, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
/* NO_RETURN */ { NOT_POSSIBLE, NO_RETURN, NO_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, THROW },
/* PARTIAL_RETURN */ { NOT_POSSIBLE, PARTIAL_RETURN, PARTIAL_RETURN, PARTIAL_RETURN, VOID_RETURN, VALUE_RETURN, VALUE_RETURN },
/* VOID_RETURN */ { NOT_POSSIBLE, VOID_RETURN, VOID_RETURN, PARTIAL_RETURN, VOID_RETURN, NOT_POSSIBLE, NOT_POSSIBLE },
/* VALUE_RETURN */ { NOT_POSSIBLE, VALUE_RETURN, VALUE_RETURN, PARTIAL_RETURN, NOT_POSSIBLE, VALUE_RETURN, NOT_POSSIBLE },
/* THROW */ { NOT_POSSIBLE, THROW, THROW, VALUE_RETURN, VOID_RETURN, VALUE_RETURN, THROW }
};
protected static final String UNLABELED = "@unlabeled"; //$NON-NLS-1$
protected int fReturnKind;
protected int[] fAccessModes;
protected Set<String> fBranches;
protected FlowInfo() {
this(UNDEFINED);
}
protected FlowInfo(int returnKind) {
fReturnKind= returnKind;
}
//---- General Helpers ----------------------------------------------------------
protected void assignExecutionFlow(FlowInfo right) {
fReturnKind= right.fReturnKind;
fBranches= right.fBranches;
}
protected void assignAccessMode(FlowInfo right) {
fAccessModes= right.fAccessModes;
}
protected void assign(FlowInfo right) {
assignExecutionFlow(right);
assignAccessMode(right);
}
protected void mergeConditional(FlowInfo info, FlowContext context) {
mergeAccessModeConditional(info, context);
mergeExecutionFlowConditional(info);
}
protected void mergeSequential(FlowInfo info, FlowContext context) {
mergeAccessModeSequential(info, context);
mergeExecutionFlowSequential(info);
}
//---- Return Kind ------------------------------------------------------------------
public void setNoReturn() {
fReturnKind= NO_RETURN;
}
public boolean isUndefined() {
return fReturnKind == UNDEFINED;
}
public boolean isNoReturn() {
return fReturnKind == NO_RETURN;
}
public boolean isPartialReturn() {
return fReturnKind == PARTIAL_RETURN;
}
public boolean isVoidReturn() {
return fReturnKind == VOID_RETURN;
}
public boolean isValueReturn() {
return fReturnKind == VALUE_RETURN;
}
public boolean isThrow() {
return fReturnKind == THROW;
}
public boolean isReturn() {
return fReturnKind == VOID_RETURN || fReturnKind == VALUE_RETURN;
}
//---- Branches -------------------------------------------------------------------------
public boolean branches() {
return fBranches != null && !fBranches.isEmpty();
}
protected Set<String> getBranches() {
return fBranches;
}
protected void removeLabel(IASTName label) {
if (fBranches != null) {
fBranches.remove(makeString(label));
if (fBranches.isEmpty())
fBranches= null;
}
}
protected static String makeString(IASTName label) {
if (label == null) {
return UNLABELED;
} else {
return String.valueOf(label.getSimpleID());
}
}
//---- Execution flow -------------------------------------------------------------------
private void mergeExecutionFlowSequential(FlowInfo otherInfo) {
int other= otherInfo.fReturnKind;
if (branches() && other == VALUE_RETURN)
other= PARTIAL_RETURN;
fReturnKind= RETURN_KIND_SEQUENTIAL_TABLE[fReturnKind][other];
mergeBranches(otherInfo);
}
private void mergeExecutionFlowConditional(FlowInfo otherInfo) {
fReturnKind= RETURN_KIND_CONDITIONAL_TABLE[fReturnKind][otherInfo.fReturnKind];
mergeBranches(otherInfo);
}
private void mergeBranches(FlowInfo otherInfo) {
fBranches= mergeSets(fBranches, otherInfo.fBranches);
}
private static <T> Set<T> mergeSets(Set<T> thisSet, Set<T> otherSet) {
if (otherSet != null) {
if (thisSet == null) {
thisSet= otherSet;
} else {
for (T element : otherSet) {
thisSet.add(element);
}
}
}
return thisSet;
}
//---- Local access handling --------------------------------------------------
/**
* Returns a set of <code>IVariable</code> that conform to the given
* access mode <code>mode</code>.
*
* @param context the flow context object used to compute this flow info
* @param mode the access type. Valid values are <code>READ</code>, <code>WRITE</code>,
* <code>UNKNOWN</code> and any combination of them.
* @return an array of local variable bindings conforming to the given type.
*/
public Set<IVariable> get(FlowContext context, int mode) {
if (fAccessModes == null)
return Collections.emptySet();
Set<IVariable> result= new HashSet<IVariable>(fAccessModes.length);
for (int i= 0; i < fAccessModes.length; i++) {
int accessMode= fAccessModes[i];
if ((accessMode & mode) != 0)
result.add(context.getLocalFromIndex(i));
}
return result;
}
/**
* Checks whether the given local variable binding has the given access
* mode.
*
* @param context the flow context used during flow analysis
* @param local local variable of interest
* @param mode the access mode of the local variable
*
* @return <code>true</code> if the binding has the given access mode.
* <code>False</code> otherwise
*/
public boolean hasAccessMode(FlowContext context, IVariable local, int mode) {
boolean unusedMode= (mode & UNUSED) != 0;
if (fAccessModes == null && unusedMode)
return true;
int index= context.getIndexFromLocal(local);
if (index == -1)
return unusedMode;
return (fAccessModes[index] & mode) != 0;
}
/**
* Returns the access mode of the local variable identified by the given binding.
*
* @param context the flow context used during flow analysis
* @param local the local variable of interest
* @return the access mode of the local variable
*/
public int getAccessMode(FlowContext context, IVariable local) {
if (fAccessModes == null)
return UNUSED;
int index= context.getIndexFromLocal(local);
if (index == -1)
return UNUSED;
return fAccessModes[index];
}
protected int[] getAccessModes() {
return fAccessModes;
}
protected void clearAccessMode(IVariable binding, FlowContext context) {
if (fAccessModes == null) // All are unused
return;
int index = context.getIndexFromLocal(binding);
if (index >= 0)
fAccessModes[index]= UNUSED;
}
protected void mergeAccessModeSequential(FlowInfo otherInfo, FlowContext context) {
if (!context.considerAccessMode())
return;
int[] others= otherInfo.fAccessModes;
if (others == null) // others are all unused. So nothing to do
return;
// Must not consider return kind since a return statement can't control execution flow
// inside a method. It always leaves the method.
if (branches()) {
for (int i= 0; i < others.length; i++)
others[i]= ACCESS_MODE_OPEN_BRANCH_TABLE[getIndex(others[i])];
}
if (fAccessModes == null) { // all current variables are unused
fAccessModes= others;
return;
}
if (context.computeArguments()) {
handleComputeArguments(others);
} else if (context.computeReturnValues()) {
handleComputeReturnValues(others);
} else if (context.computeMerge()) {
handleMergeValues(others);
}
}
private void handleComputeReturnValues(int[] others) {
for (int i= 0; i < fAccessModes.length; i++) {
int accessmode= fAccessModes[i];
int othermode= others[i];
if (accessmode == WRITE)
continue;
if (accessmode == WRITE_POTENTIAL) {
if (othermode == WRITE)
fAccessModes[i]= WRITE;
continue;
}
if (others[i] != UNUSED)
fAccessModes[i]= othermode;
}
}
private void handleComputeArguments(int[] others) {
for (int i= 0; i < fAccessModes.length; i++) {
int accessMode= fAccessModes[i];
int otherMode= others[i];
if (accessMode == UNUSED) {
fAccessModes[i]= otherMode;
} else if (accessMode == WRITE_POTENTIAL && (otherMode == READ || otherMode == READ_POTENTIAL)) {
// Read always supersedes a potential write even if the read is potential as well
// (we have to consider the potential read as an argument then).
fAccessModes[i]= otherMode;
} else if (accessMode == WRITE_POTENTIAL && otherMode == WRITE) {
fAccessModes[i]= WRITE;
}
}
}
private void handleMergeValues(int[] others) {
for (int i= 0; i < fAccessModes.length; i++) {
fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE[getIndex(fAccessModes[i])][getIndex(others[i])];
}
}
protected void createAccessModeArray(FlowContext context) {
fAccessModes= new int[context.getArrayLength()];
for (int i= 0; i < fAccessModes.length; i++) {
fAccessModes[i]= UNUSED;
}
}
protected void mergeAccessModeConditional(FlowInfo otherInfo, FlowContext context) {
if (!context.considerAccessMode())
return;
int[] others= otherInfo.fAccessModes;
// first access
if (fAccessModes == null) {
if (others != null) {
fAccessModes= others;
} else {
createAccessModeArray(context);
}
return;
} else {
if (others == null) {
for (int i= 0; i < fAccessModes.length; i++) {
int unused_index= getIndex(UNUSED);
fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE[getIndex(fAccessModes[i])][unused_index];
}
} else {
for (int i= 0; i < fAccessModes.length; i++) {
fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE[getIndex(fAccessModes[i])][getIndex(others[i])];
}
}
}
}
protected void mergeEmptyCondition(FlowContext context) {
if (fReturnKind == VALUE_RETURN || fReturnKind == VOID_RETURN)
fReturnKind= PARTIAL_RETURN;
if (!context.considerAccessMode())
return;
if (fAccessModes == null) {
createAccessModeArray(context);
return;
}
int unused_index= getIndex(UNUSED);
for (int i= 0; i < fAccessModes.length; i++) {
fAccessModes[i]= ACCESS_MODE_CONDITIONAL_TABLE[getIndex(fAccessModes[i])][unused_index];
}
}
private static int getIndex(int accessMode) {
// Fast log function
switch (accessMode) {
case UNUSED:
return 0;
case READ:
return 1;
case READ_POTENTIAL:
return 2;
case WRITE:
case READ | WRITE:
return 3;
case WRITE_POTENTIAL:
return 4;
case UNKNOWN:
return 5;
}
return -1;
}
}

View file

@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class ForFlowInfo extends FlowInfo {
public void mergeInitializer(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void mergeCondition(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void mergeIncrement(FlowInfo info, FlowContext context) {
if (info == null)
return;
info.mergeEmptyCondition(context);
mergeAccessModeSequential(info, context);
}
public void mergeAction(FlowInfo info, FlowContext context) {
if (info == null)
return;
info.mergeEmptyCondition(context);
mergeSequential(info, context);
}
}

View file

@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class FunctionCallFlowInfo extends FlowInfo {
public FunctionCallFlowInfo() {
super(NO_RETURN);
}
public void mergeArgument(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeSequential(info, context);
}
public void mergeReceiver(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeSequential(info, context);
}
}

View file

@ -0,0 +1,35 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class GenericConditionalFlowInfo extends FlowInfo {
public GenericConditionalFlowInfo() {
super(UNDEFINED);
}
public void merge(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeConditional(info, context);
}
public void mergeAccessMode(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeConditional(info, context);
}
}

View file

@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class GenericSequentialFlowInfo extends FlowInfo {
public GenericSequentialFlowInfo() {
super(NO_RETURN);
}
public void merge(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeSequential(info, context);
}
public void mergeAccessMode(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
}

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class IfFlowInfo extends FlowInfo {
public void mergeCondition(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void merge(FlowInfo thenPart, FlowInfo elsePart, FlowContext context) {
if (thenPart == null && elsePart == null)
return;
GenericConditionalFlowInfo cond= new GenericConditionalFlowInfo();
if (thenPart != null)
cond.merge(thenPart, context);
if (elsePart != null)
cond.merge(elsePart, context);
if (thenPart == null || elsePart == null)
cond.mergeEmptyCondition(context);
mergeSequential(cond, context);
}
}

View file

@ -0,0 +1,143 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTDeclarationStatement;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
public class InOutFlowAnalyzer extends FlowAnalyzer {
public InOutFlowAnalyzer(FlowContext context) {
super(context);
}
public FlowInfo perform(IASTNode[] selectedNodes) {
FlowContext context= getFlowContext();
GenericSequentialFlowInfo result= createSequential();
for (int i= 0; i < selectedNodes.length; i++) {
IASTNode node= selectedNodes[i];
node.accept(this);
result.merge(getFlowInfo(node), context);
}
return result;
}
@Override
protected boolean traverseNode(IASTNode node) {
// We are only traversing the selected nodes.
return true;
}
@Override
protected boolean createReturnFlowInfo(IASTReturnStatement node) {
// We are only traversing selected nodes.
return true;
}
@Override
public int leave(IASTCompoundStatement node) {
super.leave(node);
clearAccessMode(accessFlowInfo(node), node.getStatements());
return PROCESS_SKIP;
}
@Override
public int leave(ICPPASTCatchHandler node) {
super.leave(node);
clearAccessMode(accessFlowInfo(node), node.getDeclaration());
return PROCESS_SKIP;
}
@Override
public int leave(IASTForStatement node) {
super.leave(node);
clearAccessMode(accessFlowInfo(node), node.getInitializerStatement());
return PROCESS_SKIP;
}
@Override
public int leave(ICPPASTRangeBasedForStatement node) {
super.leave(node);
clearAccessMode(accessFlowInfo(node), node.getDeclaration());
return PROCESS_SKIP;
}
@Override
public int leave(IASTFunctionDefinition node) {
super.leave(node);
FlowInfo info= accessFlowInfo(node);
IASTFunctionDeclarator declarator = node.getDeclarator();
if (declarator instanceof IASTStandardFunctionDeclarator) {
for (IASTParameterDeclaration param : ((IASTStandardFunctionDeclarator) declarator).getParameters()) {
clearAccessMode(info, param.getDeclarator());
}
} else if (declarator instanceof ICASTKnRFunctionDeclarator) {
for (IASTDeclaration param : ((ICASTKnRFunctionDeclarator) declarator).getParameterDeclarations()) {
clearAccessMode(info, param);
}
}
return PROCESS_SKIP;
}
private void clearAccessMode(FlowInfo info, IASTStatement[] statements) {
if (statements == null || statements.length == 0 || info == null)
return;
for (IASTStatement statement : statements) {
clearAccessMode(info, statement);
}
}
private void clearAccessMode(FlowInfo info, IASTStatement statement) {
if (statement instanceof IASTDeclarationStatement) {
IASTDeclaration declaration = ((IASTDeclarationStatement) statement).getDeclaration();
clearAccessMode(info, declaration);
}
}
private void clearAccessMode(FlowInfo info, IASTDeclaration declaration) {
if (declaration instanceof IASTSimpleDeclaration) {
IASTDeclarator[] declarators = ((IASTSimpleDeclaration) declaration).getDeclarators();
for (IASTDeclarator declarator : declarators) {
clearAccessMode(info, declarator);
}
}
}
private void clearAccessMode(FlowInfo info, IASTDeclarator declarator) {
declarator = CPPVisitor.findInnermostDeclarator(declarator);
IASTName name = declarator.getName();
IBinding binding= name.resolveBinding();
if (binding instanceof IVariable && !(binding instanceof IField))
info.clearAccessMode((IVariable) binding, fFlowContext);
}
}

View file

@ -0,0 +1,304 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for
* o inline call that is used in a field initializer
* (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137)
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] Missing return value,
* while extracting code out of a loop - https://bugs.eclipse.org/bugs/show_bug.cgi?id=213519
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.IRegion;
import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression;
import org.eclipse.cdt.core.dom.ast.IASTDoStatement;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTForStatement;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTIfStatement;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.core.dom.ast.IASTStatement;
import org.eclipse.cdt.core.dom.ast.IASTSwitchStatement;
import org.eclipse.cdt.core.dom.ast.IASTWhileStatement;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement;
import org.eclipse.cdt.internal.corext.util.ASTNodes;
public class InputFlowAnalyzer extends FlowAnalyzer {
private static class LoopReentranceVisitor extends FlowAnalyzer {
private Selection fSelection;
private IASTNode fLoopNode;
public LoopReentranceVisitor(FlowContext context, Selection selection, IASTNode loopNode) {
super(context);
fSelection= selection;
fLoopNode= loopNode;
}
@Override
protected boolean traverseNode(IASTNode node) {
return true; // end <= fSelection.end || fSelection.enclosedBy(start, end);
}
@Override
protected boolean createReturnFlowInfo(IASTReturnStatement node) {
// Make sure that the whole return statement is selected or located before the selection.
return ASTNodes.endOffset(node) <= fSelection.getEnd();
}
protected IASTNode getLoopNode() {
return fLoopNode;
}
public void process(IASTNode node) {
try {
fFlowContext.setLoopReentranceMode(true);
node.accept(this);
} finally {
fFlowContext.setLoopReentranceMode(false);
}
}
@Override
public int leave(IASTDoStatement node) {
if (skipNode(node))
return PROCESS_SKIP;
DoWhileFlowInfo info= createDoWhile();
setFlowInfo(node, info);
info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
// No need to merge the condition. It was already considered by the InputFlowAnalyzer.
info.removeLabel(null);
return PROCESS_SKIP;
}
@Override
public int leave(ICPPASTRangeBasedForStatement node) {
if (skipNode(node))
return PROCESS_SKIP;
FlowInfo paramInfo= getFlowInfo(node.getDeclaration());
FlowInfo expressionInfo= getFlowInfo(node.getInitializerClause());
FlowInfo actionInfo= getFlowInfo(node.getBody());
RangeBasedForFlowInfo forInfo= createEnhancedFor();
setFlowInfo(node, forInfo);
// If the for statement is the outermost loop then we only have to consider
// the action. The parameter and expression are only evaluated once.
if (node == fLoopNode) {
forInfo.mergeAction(actionInfo, fFlowContext);
} else {
// Inner for loops are evaluated in the sequence expression, parameter,
// action.
forInfo.mergeInitializerClause(expressionInfo, fFlowContext);
forInfo.mergeDeclaration(paramInfo, fFlowContext);
forInfo.mergeAction(actionInfo, fFlowContext);
}
forInfo.removeLabel(null);
return PROCESS_SKIP;
}
@Override
public int leave(IASTForStatement node) {
if (skipNode(node))
return PROCESS_SKIP;
FlowInfo initInfo= createSequential(node.getInitializerStatement());
FlowInfo conditionInfo= getFlowInfo(node.getConditionExpression());
FlowInfo incrementInfo= createSequential(node.getIterationExpression());
FlowInfo actionInfo= getFlowInfo(node.getBody());
ForFlowInfo forInfo= createFor();
setFlowInfo(node, forInfo);
// The for statement is the outermost loop. In this case we only have
// to consider the increment, condition and action.
if (node == fLoopNode) {
forInfo.mergeIncrement(incrementInfo, fFlowContext);
forInfo.mergeCondition(conditionInfo, fFlowContext);
forInfo.mergeAction(actionInfo, fFlowContext);
} else {
// We have to merge two different cases. One if we reenter the for statement
// immediatelly (that means we have to consider increments, condition and action)
// and the other case if we reenter the for in the next loop of
// the outer loop. Then we have to consider initializations, condtion and action.
// For a conditional flow info that means:
// (initializations | increments) & condition & action.
GenericConditionalFlowInfo initIncr= new GenericConditionalFlowInfo();
initIncr.merge(initInfo, fFlowContext);
initIncr.merge(incrementInfo, fFlowContext);
forInfo.mergeAccessModeSequential(initIncr, fFlowContext);
forInfo.mergeCondition(conditionInfo, fFlowContext);
forInfo.mergeAction(actionInfo, fFlowContext);
}
forInfo.removeLabel(null);
return PROCESS_SKIP;
}
}
private Selection fSelection;
private boolean fDoLoopReentrance;
private LoopReentranceVisitor fLoopReentranceVisitor;
public InputFlowAnalyzer(FlowContext context, Selection selection, boolean doLoopReentrance) {
super(context);
fSelection= selection;
Assert.isNotNull(fSelection);
fDoLoopReentrance= doLoopReentrance;
}
public FlowInfo perform(IASTFunctionDefinition node) {
node.accept(this);
return getFlowInfo(node);
}
@Override
protected boolean traverseNode(IASTNode node) {
return ASTNodes.endOffset(node) > fSelection.getEnd();
}
@Override
protected boolean createReturnFlowInfo(IASTReturnStatement node) {
// Make sure that the whole return statement is located after the selection.
// There can be cases like return i + [x + 10] * 10; In this case we must not create
// a return info node.
return ASTNodes.offset(node) >= fSelection.getEnd();
}
@Override
public int visit(IASTDoStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
@Override
public int visit(ICPPASTRangeBasedForStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
@Override
public int visit(IASTForStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
@Override
public int visit(IASTWhileStatement node) {
createLoopReentranceVisitor(node);
return super.visit(node);
}
private void createLoopReentranceVisitor(IASTNode node) {
if (fLoopReentranceVisitor == null && fDoLoopReentrance && fSelection.coveredBy(node)) {
fLoopReentranceVisitor= new LoopReentranceVisitor(fFlowContext, fSelection, node);
}
}
@Override
public int leave(IASTConditionalExpression node) {
if (skipNode(node))
return PROCESS_SKIP;
IASTExpression thenPart= node.getPositiveResultExpression();
IASTExpression elsePart= node.getNegativeResultExpression();
if ((thenPart != null && fSelection.coveredBy(thenPart)) ||
(elsePart != null && fSelection.coveredBy(elsePart))) {
GenericSequentialFlowInfo info= createSequential();
setFlowInfo(node, info);
endVisitConditional(info, node.getLogicalConditionExpression(), new IASTNode[] { thenPart, elsePart });
return PROCESS_SKIP;
}
return super.leave(node);
}
@Override
public int leave(IASTDoStatement node) {
super.leave(node);
handleLoopReentrance(node);
return PROCESS_SKIP;
}
@Override
public int leave(IASTIfStatement node) {
if (skipNode(node))
return PROCESS_SKIP;
IASTStatement thenPart= node.getThenClause();
IASTStatement elsePart= node.getElseClause();
if ((thenPart != null && fSelection.coveredBy(thenPart)) ||
(elsePart != null && fSelection.coveredBy(elsePart))) {
GenericSequentialFlowInfo info= createSequential();
setFlowInfo(node, info);
endVisitConditional(info, node.getConditionExpression(), new IASTNode[] { thenPart, elsePart });
return PROCESS_SKIP;
}
return super.leave(node);
}
@Override
public int leave(ICPPASTRangeBasedForStatement node) {
super.leave(node);
handleLoopReentrance(node);
return PROCESS_SKIP;
}
@Override
public int leave(IASTForStatement node) {
super.leave(node);
handleLoopReentrance(node);
return PROCESS_SKIP;
}
@Override
public int leave(IASTSwitchStatement node) {
if (skipNode(node))
return PROCESS_SKIP;
SwitchData data= createSwitchData(node);
IRegion[] ranges= data.getRanges();
for (int i= 0; i < ranges.length; i++) {
IRegion range= ranges[i];
if (fSelection.coveredBy(range)) {
GenericSequentialFlowInfo info= createSequential();
setFlowInfo(node, info);
info.merge(getFlowInfo(node.getControllerExpression()), fFlowContext);
info.merge(data.getInfo(i), fFlowContext);
info.removeLabel(null);
return PROCESS_SKIP;
}
}
return super.leave(node, data);
}
@Override
public int leave(IASTWhileStatement node) {
super.leave(node);
handleLoopReentrance(node);
return PROCESS_SKIP;
}
private void endVisitConditional(GenericSequentialFlowInfo info, IASTNode condition, IASTNode[] branches) {
info.merge(getFlowInfo(condition), fFlowContext);
for (int i= 0; i < branches.length; i++) {
IASTNode branch= branches[i];
if (branch != null && fSelection.coveredBy(branch)) {
info.merge(getFlowInfo(branch), fFlowContext);
break;
}
}
}
private void handleLoopReentrance(IASTNode node) {
if (fLoopReentranceVisitor == null || fLoopReentranceVisitor.getLoopNode() != node)
return;
fLoopReentranceVisitor.process(node);
GenericSequentialFlowInfo info= createSequential();
info.merge(getFlowInfo(node), fFlowContext);
info.merge(fLoopReentranceVisitor.getFlowInfo(node), fFlowContext);
setFlowInfo(node, info);
}
}

View file

@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import org.eclipse.cdt.core.dom.ast.IVariable;
class LocalFlowInfo extends FlowInfo {
private final int fVariableIndex;
public LocalFlowInfo(IVariable binding, int localAccessMode, FlowContext context) {
super(NO_RETURN);
fVariableIndex= context.getIndexFromLocal(binding);
if (fVariableIndex < 0)
throw new IllegalStateException("Invalid local variable \"" + binding.getName() + "\" for the context."); //$NON-NLS-1$ //$NON-NLS-2$
if (context.considerAccessMode()) {
createAccessModeArray(context);
context.manageLocal(binding);
fAccessModes[fVariableIndex]= localAccessMode;
}
}
public LocalFlowInfo(LocalFlowInfo info, int localAccessMode, FlowContext context) {
super(NO_RETURN);
fVariableIndex= info.fVariableIndex;
if (context.considerAccessMode()) {
createAccessModeArray(context);
fAccessModes[fVariableIndex]= localAccessMode;
}
}
public void setWriteAccess(FlowContext context) {
if (context.considerAccessMode()) {
fAccessModes[fVariableIndex]= FlowInfo.WRITE;
}
}
}

View file

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2012 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.internal.corext.refactoring.code.flow;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName;
/**
* Index of local variables inside a function. Each variable is assigned an integer index in normal
* code reading order. A variable with a smaller index is declared before a variable with a larger
* one.
*/
public class LocalVariableIndex {
private final Map<IVariable, Integer> variableMap = new HashMap<IVariable, Integer>();
public LocalVariableIndex(IASTFunctionDefinition functionDefinition) {
functionDefinition.accept(new ASTVisitor() {
{
shouldVisitNames = true;
}
@Override
public int visit(IASTName name) {
if (name instanceof ICPPASTQualifiedName || name.isQualified() || !name.isDeclaration()) {
return PROCESS_CONTINUE;
}
IBinding binding = name.resolveBinding();
if (binding instanceof IVariable && !(binding instanceof IField)) {
IVariable variable = (IVariable) binding;
if (!variableMap.containsKey(variable)) {
variableMap.put(variable, variableMap.size());
}
}
return PROCESS_CONTINUE;
}
});
}
/**
* Returns the number of local variables in the index.
*/
public int getNumLocalVariables() {
return variableMap.size();
}
/**
* Returns the index for the given local variable.
* @param variable the local variable
* @return the index of the variable, or -1 if the variable in not contained in the index.
*/
public int getIndexFromLocal(IVariable variable) {
Integer index = variableMap.get(variable);
return index != null ? index.intValue() : -1;
}
}

View file

@ -0,0 +1,36 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class RangeBasedForFlowInfo extends FlowInfo {
public void mergeDeclaration(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void mergeInitializerClause(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void mergeAction(FlowInfo info, FlowContext context) {
if (info == null)
return;
info.mergeEmptyCondition(context);
mergeSequential(info, context);
}
}

View file

@ -0,0 +1,40 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTReturnStatement;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil;
class ReturnFlowInfo extends FlowInfo {
public ReturnFlowInfo(IASTReturnStatement node) {
super(getReturnFlag(node));
}
public void merge(FlowInfo info, FlowContext context) {
if (info == null)
return;
assignAccessMode(info);
}
private static int getReturnFlag(IASTReturnStatement node) {
IASTExpression expression= node.getReturnValue();
if (expression == null || SemanticUtil.isVoidType(expression.getExpressionType()))
return VOID_RETURN;
return VALUE_RETURN;
}
}

View file

@ -0,0 +1,164 @@
/*******************************************************************************
* Copyright (c) 2000, 2011 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.IRegion;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
public class Selection {
/** Flag indicating that the AST node somehow intersects with the selection. */
public static final int INTERSECTS= 0;
/** Flag that indicates that an AST node appears before the selected nodes. */
public static final int BEFORE= 1;
/** Flag indicating that an AST node is covered by the selection. */
public static final int SELECTED= 2;
/** Flag indicating that an AST nodes appears after the selected nodes. */
public static final int AFTER= 3;
private int fStart;
private int fLength;
private int fEnd;
protected Selection() {
}
/**
* Creates a new selection from the given start and length.
*
* @param start the start offset of the selection (inclusive)
* @param length the length of the selection
* @return the created selection object
*/
public static Selection createFromStartLength(int start, int length) {
Assert.isTrue(start >= 0 && length >= 0);
Selection result= new Selection();
result.fStart= start;
result.fLength= length;
result.fEnd= start + length;
return result;
}
/**
* Creates a new selection from the given start and end.
*
* @param start the start offset of the selection (inclusive)
* @param end the end offset of the selection (exclusive)
* @return the created selection object
*/
public static Selection createFromStartEnd(int start, int end) {
Assert.isTrue(start >= 0 && end >= start);
Selection result= new Selection();
result.fStart= start;
result.fLength= end - start;
result.fEnd= result.fStart + result.fLength;
return result;
}
public int getOffset() {
return fStart;
}
public int getLength() {
return fLength;
}
public int getEnd() {
return fEnd;
}
/**
* Returns the selection mode of the given AST node regarding this selection. Possible
* values are <code>INTERSECTS</code>, <code>BEFORE</code>, <code>SELECTED</code>, and
* <code>AFTER</code>.
*
* @param node the node to return the visit mode for
*
* @return the selection mode of the given AST node regarding this selection
* @see #INTERSECTS
* @see #BEFORE
* @see #SELECTED
* @see #AFTER
*/
public int getVisitSelectionMode(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int nodeStart= location.getNodeOffset();
int nodeEnd= nodeStart + location.getNodeLength();
if (nodeEnd <= fStart) {
return BEFORE;
} else if (covers(node)) {
return SELECTED;
} else if (fEnd <= nodeStart) {
return AFTER;
}
return INTERSECTS;
}
public int getEndVisitSelectionMode(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int nodeStart= location.getNodeOffset();
int nodeEnd= nodeStart + location.getNodeLength();
if (nodeEnd <= fStart) {
return BEFORE;
} else if (covers(node)) {
return SELECTED;
} else if (nodeEnd >= fEnd) {
return AFTER;
}
return INTERSECTS;
}
// cover* methods do a closed interval check.
public boolean covers(int position) {
return fStart <= position && position < fStart + fLength;
}
public boolean covers(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int nodeStart= location.getNodeOffset();
return fStart <= nodeStart && nodeStart + location.getNodeLength() <= fEnd;
}
public boolean coveredBy(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int nodeStart= location.getNodeOffset();
return nodeStart <= fStart && fEnd <= nodeStart + location.getNodeLength();
}
public boolean coveredBy(IRegion region) {
int regionStart= region.getOffset();
return regionStart <= fStart && fEnd <= regionStart + region.getLength();
}
public boolean endsIn(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int nodeStart= location.getNodeOffset();
return nodeStart < fEnd && fEnd < nodeStart + location.getNodeLength();
}
public boolean liesOutside(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
int nodeStart= location.getNodeOffset();
return fEnd < nodeStart || nodeStart + location.getNodeLength() < fStart;
}
@Override
public String toString() {
return "<start == " + fStart + ", length == " + fLength + "/>"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}

View file

@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class SwitchFlowInfo extends FlowInfo {
private GenericConditionalFlowInfo fCases;
private boolean fHasNullCaseInfo;
public SwitchFlowInfo() {
fCases= new GenericConditionalFlowInfo();
}
public void mergeTest(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeSequential(info, context);
}
public void mergeCase(FlowInfo info, FlowContext context) {
if (info == null) {
fHasNullCaseInfo= true;
return;
}
fCases.mergeConditional(info, context);
}
public void mergeDefault(boolean defaultCaseExists, FlowContext context) {
if (!defaultCaseExists || fHasNullCaseInfo)
fCases.mergeEmptyCondition(context);
mergeSequential(fCases, context);
}
}

View file

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class ThrowFlowInfo extends FlowInfo {
public ThrowFlowInfo() {
super(THROW);
}
public void merge(FlowInfo info, FlowContext context) {
if (info == null)
return;
assignAccessMode(info);
}
}

View file

@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class TryFlowInfo extends FlowInfo {
public TryFlowInfo() {
super();
}
public void mergeTry(FlowInfo info, FlowContext context) {
if (info == null)
return;
assign(info);
}
public void mergeCatch(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeConditional(info, context);
}
public void mergeFinally(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeSequential(info, context);
}
}

View file

@ -0,0 +1,32 @@
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.corext.refactoring.code.flow;
class WhileFlowInfo extends FlowInfo {
public void mergeCondition(FlowInfo info, FlowContext context) {
if (info == null)
return;
mergeAccessModeSequential(info, context);
}
public void mergeAction(FlowInfo info, FlowContext context) {
if (info == null)
return;
info.mergeEmptyCondition(context);
mergeSequential(info, context);
}
}

View file

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2012 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.internal.corext.util;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTNode;
/**
* Collection of helper methods for common operations on AST nodes.
*/
public class ASTNodes {
// Not instantiatable.
private ASTNodes() {
}
/**
* Returns the offset of an AST node.
*/
public static int offset(IASTNode node) {
return node.getFileLocation().getNodeOffset();
}
/**
* Returns the exclusive end offset of an AST node.
*/
public static int endOffset(IASTNode node) {
IASTFileLocation location = node.getFileLocation();
return location.getNodeOffset() + location.getNodeLength();
}
}

View file

@ -46,10 +46,7 @@ public class NameInformation {
private final IASTName name;
private IASTName declarationName;
private final List<IASTName> references;
private final List<IASTName> referencesBeforeSelection;
private final List<IASTName> referencesInSelection;
private final List<IASTName> referencesAfterSelection;
private boolean isOutput;
private boolean mustBeReturnValue;
private boolean isWriteAccess;
@ -65,10 +62,7 @@ public class NameInformation {
public NameInformation(IASTName name) {
this.name = name;
this.newName = String.valueOf(name.getSimpleID());
references = new ArrayList<IASTName>();
referencesBeforeSelection = new ArrayList<IASTName>();
referencesInSelection = new ArrayList<IASTName>();
referencesAfterSelection = new ArrayList<IASTName>();
}
public static NameInformation createInfoForAddedParameter(String type, String name,
@ -205,14 +199,9 @@ public class NameInformation {
}
void addReference(IASTName name, int startOffset, int endOffset) {
references.add(name);
int nodeOffset = name.getFileLocation().getNodeOffset();
if (nodeOffset >= endOffset) {
referencesAfterSelection.add(name);
} else if (nodeOffset >= startOffset) {
if (nodeOffset >= startOffset && nodeOffset < endOffset) {
referencesInSelection.add(name);
} else {
referencesBeforeSelection.add(name);
}
}
@ -244,22 +233,10 @@ public class NameInformation {
return writer.toString();
}
public List<IASTName> getReferencesBeforeSelection() {
return referencesBeforeSelection;
}
public List<IASTName> getReferencesInSelection() {
return referencesInSelection;
}
public List<IASTName> getReferencesAfterSelection() {
return referencesAfterSelection;
}
public boolean isReferencedAfterSelection() {
return !referencesAfterSelection.isEmpty();
}
public IASTParameterDeclaration getParameterDeclaration(INodeFactory nodeFactory) {
return getParameterDeclaration(nodeFactory, newName);
}

View file

@ -28,6 +28,7 @@ import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -44,7 +45,13 @@ import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVariableReadWriteFlags;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.FlowContext;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.FlowInfo;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.InputFlowAnalyzer;
import org.eclipse.cdt.internal.corext.refactoring.code.flow.Selection;
import org.eclipse.cdt.internal.corext.util.ASTNodes;
public class NodeContainer {
private final List<IASTNode> nodes;
@ -152,6 +159,8 @@ public class NodeContainer {
if (interfaceNames == null) {
findAllNames();
Set<IVariable> externalReads = getVariablesReadOutside();
Set<IASTName> declarations = new HashSet<IASTName>();
interfaceNames = new ArrayList<NameInformation>();
@ -159,7 +168,7 @@ public class NodeContainer {
IASTName declarationName = nameInfo.getDeclarationName();
if (declarations.add(declarationName)) {
if (isDeclaredInSelection(nameInfo)) {
if (nameInfo.isReferencedAfterSelection()) {
if (externalReads.contains(nameInfo.getName().resolveBinding())) {
nameInfo.setMustBeReturnValue(true);
interfaceNames.add(nameInfo);
}
@ -175,7 +184,8 @@ public class NodeContainer {
}
}
}
if (nameInfo.isWriteAccess() && nameInfo.isReferencedAfterSelection()) {
if (nameInfo.isWriteAccess() &&
externalReads.contains(nameInfo.getName().resolveBinding())) {
nameInfo.setOutput(true);
}
}
@ -188,6 +198,25 @@ public class NodeContainer {
return interfaceNames;
}
private Set<IVariable> getVariablesReadOutside() {
if (nodes.isEmpty())
return Collections.emptySet();
IASTNode firstNode = nodes.get(0);
IASTFunctionDefinition enclosingFunction =
CPPVisitor.findAncestorWithType(firstNode, IASTFunctionDefinition.class);
FlowContext flowContext= new FlowContext(enclosingFunction);
flowContext.setConsiderAccessMode(true);
flowContext.setComputeMode(FlowContext.ARGUMENTS);
// Compute a selection that exactly covers the selected nodes
Selection selection= Selection.createFromStartEnd(ASTNodes.offset(firstNode),
ASTNodes.endOffset(nodes.get(nodes.size() - 1)));
InputFlowAnalyzer analyzer = new InputFlowAnalyzer(flowContext, selection, true);
FlowInfo argInfo= analyzer.perform(enclosingFunction);
return argInfo.get(flowContext, FlowInfo.READ | FlowInfo.READ_POTENTIAL | FlowInfo.UNKNOWN);
}
public static boolean hasReferenceOperator(IASTDeclarator declarator) {
IASTPointerOperator[] operators = declarator.getPointerOperators();
return operators.length != 0 && operators[operators.length - 1] instanceof ICPPASTReferenceOperator;

View file

@ -58,7 +58,8 @@ public class StatementExtractor extends FunctionExtractor {
NameInformation returnVariable) {
if (returnVariable != null) {
IASTNode decl = ASTHelper.getDeclarationForNode(returnVariable.getDeclarationName());
return ASTHelper.getDeclarationSpecifier(decl).copy(CopyStyle.withLocations);
IASTDeclSpecifier declSpec = ASTHelper.getDeclarationSpecifier(decl);
return declSpec != null ? declSpec.copy(CopyStyle.withLocations) : null;
}
IASTSimpleDeclSpecifier declSpec = new CPPASTSimpleDeclSpecifier();
declSpec.setType(IASTSimpleDeclSpecifier.t_void);