diff options
author | Sergey Prigogin | 2012-03-14 00:37:27 +0000 |
---|---|---|
committer | Sergey Prigogin | 2012-03-14 00:55:38 +0000 |
commit | 0f4f703be222407956acec285388fd0fd5c369b3 (patch) | |
tree | 3ea50e3daea6c7b8d0df18f2e31eeb23515738b5 | |
parent | 723ed9f9128d127c36e60f1c4e8c7d122494f342 (diff) | |
download | org.eclipse.cdt-0f4f703be222407956acec285388fd0fd5c369b3.tar.gz org.eclipse.cdt-0f4f703be222407956acec285388fd0fd5c369b3.tar.xz org.eclipse.cdt-0f4f703be222407956acec285388fd0fd5c369b3.zip |
Determination of output parameters for extracting function from
inside conditional statements and loops.
29 files changed, 3281 insertions, 276 deletions
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java index 29e21800a75..2e4f6fcc360 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/rewrite/astwriter/DeclarationWriter.java @@ -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) { diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java index 159654b32e7..e7d2716525f 100644 --- a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/refactoring/extractfunction/ExtractFunctionRefactoringTest.java @@ -1,5 +1,4 @@ /******************************************************************************* - * Copyright (c) 2008, 2012 Institute for Software, HSR Hochschule fuer Technik * Rapperswil, University of applied sciences and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -178,19 +177,23 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int A::help() { // return 42; //} - public void testExtractedVariableDefinition() throws Exception { + public void testLocalVariableDeclaration_1() throws Exception { assertRefactoringSuccess(); } + //A.h //#ifndef A_H_ //#define A_H_ // + //#include "B.h" + // //class A { //public: // A(); // virtual ~A(); - // int foo(); + // void foo(); + // B b; // //private: // int help(); @@ -201,15 +204,18 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //#ifndef A_H_ //#define A_H_ // + //#include "B.h" + // //class A { //public: // A(); // virtual ~A(); - // int foo(); + // void foo(); + // B b; // //private: // int help(); - // int extracted(int i); + // void extracted(); //}; // //#endif /*A_H_*/ @@ -223,12 +229,9 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //A::~A() { //} // - //int A::foo() { - // int i = 2; - // //comment - // /*$*/++i; + //void A::foo() { + // /*$*/b = new B(); // help();/*$$*/ - // return i; //} // //int A::help() { @@ -243,69 +246,31 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //A::~A() { //} // - //int A::extracted(int i) { - // //comment - // ++i; + //void A::extracted() { + // b = new B(); // help(); - // return i; //} // - //int A::foo() { - // int i = 2; - // //comment - // i = extracted(i); - // return i; + //void A::foo() { + // extracted(); //} // //int A::help() { // return 42; //} - public void testComment() throws Exception { - assertRefactoringSuccess(); - } - //main.cpp - //int main() { - // int i; - // // Comment - // /*$*/i= 7;/*$$*/ - // return i; - //} - //==================== - //int extracted(int i) { - // // Comment - // i = 7; - // return i; - //} + //B.h + //#ifndef B_H_ + //#define B_H_ // - //int main() { - // int i; - // // Comment - // i = extracted(i); - // return i; - //} - public void testLeadingComment() throws Exception { - assertRefactoringSuccess(); - } - - //main.cpp - //int main() { - // int i; - // /*$*/i= 7;/*$$*/ // Comment - // return i; - //} - //==================== - //int extracted(int i) { - // i = 7; // Comment - // return i; - //} + //class B { + //public: + // B(); + // virtual ~B(); + //}; // - //int main() { - // int i; - // i = extracted(i); - // return i; - //} - public void testTraillingComment() throws Exception { + //#endif /*B_H_*/ + public void testLocalVariableDeclaration_2() throws Exception { assertRefactoringSuccess(); } @@ -347,7 +312,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int A::help() { // return 42; //} - public void testTwoVariableDefinedInScope() throws Exception { + public void testTwoLocalVariables() throws Exception { assertRefactoringFailure(); } @@ -355,14 +320,11 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //#ifndef A_H_ //#define A_H_ // - //#include "B.h" - // //class A { //public: // A(); // virtual ~A(); - // void foo(); - // B b; + // int foo(); // //private: // int help(); @@ -373,18 +335,15 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //#ifndef A_H_ //#define A_H_ // - //#include "B.h" - // //class A { //public: // A(); // virtual ~A(); - // void foo(); - // B b; + // int foo(); // //private: // int help(); - // void extracted(); + // int extracted(int i); //}; // //#endif /*A_H_*/ @@ -398,9 +357,12 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //A::~A() { //} // - //void A::foo() { - // /*$*/b = new B(); + //int A::foo() { + // int i = 2; + // //comment + // /*$*/++i; // help();/*$$*/ + // return i; //} // //int A::help() { @@ -415,31 +377,69 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //A::~A() { //} // - //void A::extracted() { - // b = new B(); + //int A::extracted(int i) { + // //comment + // ++i; // help(); + // return i; //} // - //void A::foo() { - // extracted(); + //int A::foo() { + // int i = 2; + // //comment + // i = extracted(i); + // return i; //} // //int A::help() { // return 42; //} + public void testComment() throws Exception { + assertRefactoringSuccess(); + } - //B.h - //#ifndef B_H_ - //#define B_H_ + //main.cpp + //int main() { + // int i; + // // Comment + // /*$*/i= 7;/*$$*/ + // return i; + //} + //==================== + //int extracted(int i) { + // // Comment + // i = 7; + // return i; + //} // - //class B { - //public: - // B(); - // virtual ~B(); - //}; + //int main() { + // int i; + // // Comment + // i = extracted(i); + // return i; + //} + public void testLeadingComment() throws Exception { + assertRefactoringSuccess(); + } + + //main.cpp + //int main() { + // int i; + // /*$*/i= 7;/*$$*/ // Comment + // return i; + //} + //==================== + //int extracted(int i) { + // i = 7; // Comment + // return i; + //} // - //#endif /*B_H_*/ - public void testNamedTypedField() throws Exception { + //int main() { + // int i; + // i = extracted(i); + // return i; + //} + public void testTraillingComment() throws Exception { assertRefactoringSuccess(); } @@ -531,10 +531,9 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //}; // //#endif /*B_H_*/ - public void testNamedTypedVariableDefinedInScope() throws Exception { + public void testNamedTypedField() throws Exception { assertRefactoringSuccess(); } - //A.h //#ifndef A_H_ //#define A_H_ @@ -647,91 +646,6 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // //private: // int help(); - // int extracted(int i); - //}; - // - //#endif /*A_H_*/ - - //A.cpp - //#include "A.h" - // - //#define ADD(a,b) a + b + 2 - // - //A::A() { - //} - // - //A::~A() { - //} - // - //int A::foo() { - // int i = 2; - // /*$*/++i; - // i = ADD(i, 42); - // help();/*$$*/ - // return i; - //} - // - //int A::help() { - // return 42; - //} - //==================== - //#include "A.h" - // - //#define ADD(a,b) a + b + 2 - // - //A::A() { - //} - // - //A::~A() { - //} - // - //int A::extracted(int i) { - // ++i; - // i = ADD(i, 42); - // help(); - // return i; - //} - // - //int A::foo() { - // int i = 2; - // i = extracted(i); - // return i; - //} - // - //int A::help() { - // return 42; - //} - public void testFunctionStyleMacro() throws Exception { - assertRefactoringSuccess(); - } - - //A.h - //#ifndef A_H_ - //#define A_H_ - // - //class A { - //public: - // A(); - // virtual ~A(); - // int foo(); - // - //private: - // int help(); - //}; - // - //#endif /*A_H_*/ - //==================== - //#ifndef A_H_ - //#define A_H_ - // - //class A { - //public: - // A(); - // virtual ~A(); - // int foo(); - // - //private: - // int help(); // void extracted(int* j); //}; // @@ -1030,7 +944,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int A::help() { // return 42; //} - public void testRefParameter() throws Exception { + public void testReturnValueSelection_1() throws Exception { assertRefactoringSuccess(); } @@ -1121,7 +1035,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int A::help() { // return 42; //} - public void testRefParameterAndSomeMoreNotUsedAfterwards() throws Exception { + public void testReturnValueSelection_2() throws Exception { assertRefactoringSuccess(); } @@ -1156,7 +1070,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // //private: // int help(); - // float extracted(int& i, int y, float x, B* b); + // bool extracted(bool y, float x, int& i, B* b); //}; // //#endif /*A_H_*/ @@ -1174,11 +1088,13 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // int i = 2; // float x = i; // B* b = new B(); - // int y = x + i; + // bool y = false; // /*$*/++i; // b->hello(y); + // y = !y; // i = i + x; // help();/*$$*/ + // b->hello(y); // ++x; // return i; //} @@ -1195,20 +1111,22 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //A::~A() { //} // - //float A::extracted(int& i, int y, float x, B* b) { + //bool A::extracted(bool y, float x, int& i, B* b) { // ++i; // b->hello(y); + // y = !y; // i = i + x; // help(); - // return x; + // return y; //} // //int A::foo() { // int i = 2; // float x = i; // B* b = new B(); - // int y = x + i; - // x = extracted(i, y, x, b); + // bool y = false; + // y = extracted(y, x, i, b); + // b->hello(y); // ++x; // return i; //} @@ -1225,12 +1143,11 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //public: // B(); // virtual ~B(); - // void hello(float y); + // void hello(bool y); //}; // //#endif /*B_H_*/ - public void testExplicitlyAssignedReturnValue() throws Exception { - returnValue = "x"; + public void testReturnValueSelection_3() throws Exception { assertRefactoringSuccess(); } @@ -1265,7 +1182,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // //private: // int help(); - // bool extracted(bool y, float x, int& i, B* b); + // float extracted(int& i, int y, float x, B* b); //}; // //#endif /*A_H_*/ @@ -1283,13 +1200,11 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // int i = 2; // float x = i; // B* b = new B(); - // bool y = false; + // int y = x + i; // /*$*/++i; // b->hello(y); - // y = !y; // i = i + x; // help();/*$$*/ - // b->hello(y); // ++x; // return i; //} @@ -1306,22 +1221,20 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //A::~A() { //} // - //bool A::extracted(bool y, float x, int& i, B* b) { + //float A::extracted(int& i, int y, float x, B* b) { // ++i; // b->hello(y); - // y = !y; // i = i + x; // help(); - // return y; + // return x; //} // //int A::foo() { // int i = 2; // float x = i; // B* b = new B(); - // bool y = false; - // y = extracted(y, x, i, b); - // b->hello(y); + // int y = x + i; + // x = extracted(i, y, x, b); // ++x; // return i; //} @@ -1338,11 +1251,12 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //public: // B(); // virtual ~B(); - // void hello(bool y); + // void hello(float y); //}; // //#endif /*B_H_*/ - public void testReturnValueSelection() throws Exception { + public void testExplicitlyAssignedReturnValue() throws Exception { + returnValue = "x"; assertRefactoringSuccess(); } @@ -1698,7 +1612,132 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { assertRefactoringSuccess(); } - //main.h + //test.cpp + //int test(bool param) { + // int a = 42; + // char b = '0'; + // + // if (param) { + // /*$*/b += a; + // a += b;/*$$*/ + // } else { + // b -= a; + // a -= b; + // } + // return a; + //} + //==================== + //int extracted(char b, int a) { + // b += a; + // a += b; + // return a; + //} + // + //int test(bool param) { + // int a = 42; + // char b = '0'; + // + // if (param) { + // a = extracted(b, a); + // } else { + // b -= a; + // a -= b; + // } + // return a; + //} + public void testOutputParametersDetectionInIfStatement() throws Exception { + assertRefactoringSuccess(); + } + + //main.c + //int main() { + // int a = 42; + // char b = '0'; + // + // switch (a) { + // case 0: + // /*$*/b += a; + // a += b;/*$$*/ + // break; + // case 42: + // b -= a; + // a -= b; + // break; + // default: + // b ++; + // a += b; + // break; + // } + // return b; + //} + //==================== + //char extracted(char b, int a) { + // b += a; + // a += b; + // return b; + //} + // + //int main() { + // int a = 42; + // char b = '0'; + // + // switch (a) { + // case 0: + // b = extracted(b, a); + // break; + // case 42: + // b -= a; + // a -= b; + // break; + // default: + // b ++; + // a += b; + // break; + // } + // return b; + //} + public void testOutputParametersDetectionInSwitchStatement_Bug302406() throws Exception { + assertRefactoringSuccess(); + } + + //test.c + //void print(int i, double a, double b); + // + //void test(double x) { + // double s = 0; + // double y = 1; + // int i; + // for (i = 0; i < 10; i++) { + // print(x, s); + // /*$*/x *= x; + // y *= i; + // s += x / y;/*$$*/ + // } + //} + //==================== + //void print(int i, double a, double b); + // + //extracted(double x, int i, double* y, double* s) { + // x *= x; + // *y *= i; + // *s += x / *y; + // return x; + //} + // + //void test(double x) { + // double s = 0; + // double y = 1; + // int i; + // for (i = 0; i < 10; i++) { + // print(x, s); + // x = extracted(x, i, &y, &s); + // } + //} + public void testOutputParametersDetectionInForLoop() throws Exception { + assertRefactoringSuccess(); + } + + //main.cpp //void method() { // /*$*/for (int var = 0; var < 100; ++var) { // if (var < 50) @@ -1749,39 +1788,6 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //void testFuerRainer() { // int i = int(); // /*$*/++i; - // //Leading Comment - // ASSERT(i); - // //Trailling Comment - // --i;/*$$*/ - //} - //==================== - //#define ASSERTM(msg,cond) if (!(cond)) throw cute::test_failure((msg),__FILE__,__LINE__) - //#define ASSERT(cond) ASSERTM(#cond, cond) - // - //void runTest(int i) { - // ++i; - // //Leading Comment - // ASSERT(i); - // //Trailling Comment - // --i; - //} - // - //void testFuerRainer() { - // int i = int(); - // runTest(i); - //} - public void testMacroCallInSelectedCodeForgetsTheMacro() throws Exception { - extractedFunctionName = "runTest"; - assertRefactoringSuccess(); - } - - //Test.cpp - //#define ASSERTM(msg,cond) if (!(cond)) throw cute::test_failure((msg),__FILE__,__LINE__) - //#define ASSERT(cond) ASSERTM(#cond, cond) - // - //void testFuerRainer() { - // int i = int(); - // /*$*/++i; // ASSERT(i); // --i;/*$$*/ //} @@ -1835,7 +1841,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // string* newElements = runTest(m_capacity); // newElements[0] = "s"; //} - public void testStringArrayProblemInExtractFunction() throws Exception { + public void testStringArray() throws Exception { extractedFunctionName = "runTest"; assertRefactoringSuccess(); } @@ -1902,7 +1908,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // extracted(b, c, a); // return a; //} - public void testBug239059DoubleAmpersandInSignatureOfExtractedFunctions() throws Exception { + public void testReferenceVariable_Bug239059() throws Exception { assertRefactoringSuccess(); } @@ -1972,7 +1978,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //void Test::test() { // RetType v = extracted(); //} - public void testBug241717TypedefCausesVoidAsReturnType() throws Exception { + public void testTypedef_Bug241717() throws Exception { assertRefactoringSuccess(); } @@ -2020,7 +2026,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int main() { // return 0; //} - public void testBug248238ExtractMethodProducesWrongReturnTypeOrJustFailsClasstype() throws Exception { + public void testQualifiedReturnTypeName_Bug248238_1() throws Exception { extractedFunctionName = "startTag"; assertRefactoringSuccess(); } @@ -2072,7 +2078,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int main() { // return 0; //} - public void testBug248238ExtractMethodProducesWrongReturnTypeOrJustFailsTypedef() throws Exception { + public void testQualifiedReturnTypeName_Bug248238_2() throws Exception { extractedFunctionName = "startTag"; assertRefactoringSuccess(); } @@ -2122,7 +2128,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int main() { // return 0; //} - public void testBug248622ExtractFunctionFailsToExtractSeveralExpressionsSelectionAtTheEnd() throws Exception { + public void testReturnStatementExpression_Bug248622_1() throws Exception { extractedFunctionName = "endTag"; assertRefactoringSuccess(); } @@ -2172,7 +2178,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int main() { // return 0; //} - public void testBug248622ExtractFunctionFailsToExtractSeveralExpressionsSelectionInTheMiddle() throws Exception { + public void testReturnStatementExpression_Bug248622_2() throws Exception { assertRefactoringSuccess(); } @@ -2194,7 +2200,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int main(int argc, char** argv) { // extracted(); //} - public void testBug262000ExtractFunctionMisinterpretsArtificialBlocks() throws Exception { + public void testBlock_Bug262000() throws Exception { assertRefactoringSuccess(); } @@ -2299,7 +2305,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // int result = extracted(); // return result; //} - public void testBug264712ExtractFunctionDeletesCommentsInHeader() throws Exception { + public void testCommentsInHeader_Bug264712() throws Exception { assertRefactoringSuccess(); } @@ -2338,7 +2344,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // a = extracted(a); // return a; //} - public void testBug281564ExtractFunctionFailsWhenCatchingAnUnnamedException() throws Exception { + public void testCatchUnnamedException_Bug281564() throws Exception { assertRefactoringSuccess(); } @@ -2358,7 +2364,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // extracted(b, a); // return a; //} - public void testBug282004ExtractFunctionInCProjectNotCPPWontExtractParameters() throws Exception { + public void testCFunction_Bug282004() throws Exception { assertRefactoringSuccess(); } @@ -2379,7 +2385,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // a = extracted(a, b); // return a; //} - public void testBug288268CRefactoringCreatesCPPParameters() throws Exception { + public void testCFunction_Bug288268() throws Exception { assertRefactoringSuccess(); } @@ -3359,7 +3365,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //}; // //#endif /*B_H_*/ - public void testALotRefParameterAndAMethodCall() throws Exception { + public void testMethodCall() throws Exception { assertRefactoringSuccess(); } @@ -3486,7 +3492,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //}; // //#endif /*B_H_*/ - public void testALotRefParameterAndAMethodCallDuplicateIsSimilar() throws Exception { + public void testMethodCallWithDuplicate() throws Exception { assertRefactoringSuccess(); } @@ -3601,6 +3607,91 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { // //private: // int help(); + // int extracted(int i); + //}; + // + //#endif /*A_H_*/ + + //A.cpp + //#include "A.h" + // + //#define ADD(a,b) a + b + 2 + // + //A::A() { + //} + // + //A::~A() { + //} + // + //int A::foo() { + // int i = 2; + // /*$*/++i; + // i = ADD(i, 42); + // help();/*$$*/ + // return i; + //} + // + //int A::help() { + // return 42; + //} + //==================== + //#include "A.h" + // + //#define ADD(a,b) a + b + 2 + // + //A::A() { + //} + // + //A::~A() { + //} + // + //int A::extracted(int i) { + // ++i; + // i = ADD(i, 42); + // help(); + // return i; + //} + // + //int A::foo() { + // int i = 2; + // i = extracted(i); + // return i; + //} + // + //int A::help() { + // return 42; + //} + public void testFunctionStyleMacro_1() throws Exception { + assertRefactoringSuccess(); + } + + //A.h + //#ifndef A_H_ + //#define A_H_ + // + //class A { + //public: + // A(); + // virtual ~A(); + // int foo(); + // + //private: + // int help(); + //}; + // + //#endif /*A_H_*/ + //==================== + //#ifndef A_H_ + //#define A_H_ + // + //class A { + //public: + // A(); + // virtual ~A(); + // int foo(); + // + //private: + // int help(); // int extracted(int ii); //}; // @@ -3655,7 +3746,40 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int A::help() { // return 42; //} - public void testFunctionStyleMacro2() throws Exception { + public void testFunctionStyleMacro_2() throws Exception { + assertRefactoringSuccess(); + } + + //Test.cpp + //#define ASSERTM(msg,cond) if (!(cond)) throw cute::test_failure((msg),__FILE__,__LINE__) + //#define ASSERT(cond) ASSERTM(#cond, cond) + // + //void testFuerRainer() { + // int i = int(); + // /*$*/++i; + // //Leading Comment + // ASSERT(i); + // //Trailling Comment + // --i;/*$$*/ + //} + //==================== + //#define ASSERTM(msg,cond) if (!(cond)) throw cute::test_failure((msg),__FILE__,__LINE__) + //#define ASSERT(cond) ASSERTM(#cond, cond) + // + //void runTest(int i) { + // ++i; + // //Leading Comment + // ASSERT(i); + // //Trailling Comment + // --i; + //} + // + //void testFuerRainer() { + // int i = int(); + // runTest(i); + //} + public void testFunctionStyleMacro_3() throws Exception { + extractedFunctionName = "runTest"; assertRefactoringSuccess(); } @@ -3744,7 +3868,7 @@ public class ExtractFunctionRefactoringTest extends RefactoringTestBase { //int A::help() { // return 42; //} - public void testReturnValueAssignedToMacroCall() throws Exception { + public void testReturnValueAssignedInMacro() throws Exception { returnValue = "b"; assertRefactoringSuccess(); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java new file mode 100644 index 00000000000..4fea1068010 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/BlockFlowInfo.java @@ -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() { + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java new file mode 100644 index 00000000000..bc2452b89bd --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/BranchFlowInfo.java @@ -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)); + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java new file mode 100644 index 00000000000..64474ab1366 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ConditionalFlowInfo.java @@ -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); + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java new file mode 100644 index 00000000000..93a0febee8b --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/DoWhileFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java new file mode 100644 index 00000000000..4047665a564 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java @@ -0,0 +1,1012 @@ +/******************************************************************************* + * 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.HashMap; +import java.util.List; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; + +import org.eclipse.cdt.core.dom.ast.ASTGenericVisitor; +import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator; +import org.eclipse.cdt.core.dom.ast.IASTArraySubscriptExpression; +import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; +import org.eclipse.cdt.core.dom.ast.IASTBreakStatement; +import org.eclipse.cdt.core.dom.ast.IASTCaseStatement; +import org.eclipse.cdt.core.dom.ast.IASTCastExpression; +import org.eclipse.cdt.core.dom.ast.IASTCompoundStatement; +import org.eclipse.cdt.core.dom.ast.IASTConditionalExpression; +import org.eclipse.cdt.core.dom.ast.IASTContinueStatement; +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.IASTDefaultStatement; +import org.eclipse.cdt.core.dom.ast.IASTDoStatement; +import org.eclipse.cdt.core.dom.ast.IASTEqualsInitializer; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTExpressionList; +import org.eclipse.cdt.core.dom.ast.IASTExpressionStatement; +import org.eclipse.cdt.core.dom.ast.IASTFieldReference; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTForStatement; +import org.eclipse.cdt.core.dom.ast.IASTFunctionCallExpression; +import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; +import org.eclipse.cdt.core.dom.ast.IASTGotoStatement; +import org.eclipse.cdt.core.dom.ast.IASTIdExpression; +import org.eclipse.cdt.core.dom.ast.IASTIfStatement; +import org.eclipse.cdt.core.dom.ast.IASTInitializer; +import org.eclipse.cdt.core.dom.ast.IASTInitializerClause; +import org.eclipse.cdt.core.dom.ast.IASTInitializerList; +import org.eclipse.cdt.core.dom.ast.IASTLabelStatement; +import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNullStatement; +import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTProblemExpression; +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.IASTSwitchStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IASTTypeIdExpression; +import org.eclipse.cdt.core.dom.ast.IASTTypeIdInitializerExpression; +import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; +import org.eclipse.cdt.core.dom.ast.IASTWhileStatement; +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.c.ICASTDesignatedInitializer; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCatchHandler; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorChainInitializer; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTConstructorInitializer; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTDeleteExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionWithTryBlock; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNewExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTRangeBasedForStatement; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTSimpleTypeConstructorExpression; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTryBlockStatement; +import org.eclipse.cdt.core.dom.ast.gnu.c.ICASTKnRFunctionDeclarator; + +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVariableReadWriteFlags; +import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor; + +/** + * Special flow analyzer to determine the return value of the extracted method + * and the variables which have to be passed to the method. + * + * Note: This analyzer doesn't do a full flow analysis. For example it doesn't + * do dead code analysis or variable initialization analysis. It analyzes + * the first access to a variable (read or write) and if all execution paths + * return a value. + */ +abstract class FlowAnalyzer extends ASTGenericVisitor { + + static protected class SwitchData { + private boolean fHasDefaultCase; + private final List<IRegion> fRanges= new ArrayList<IRegion>(4); + private final List<FlowInfo> fInfos= new ArrayList<FlowInfo>(4); + + public void setHasDefaultCase() { + fHasDefaultCase= true; + } + + public boolean hasDefaultCase() { + return fHasDefaultCase; + } + + public void add(IRegion range, FlowInfo info) { + fRanges.add(range); + fInfos.add(info); + } + + public IRegion[] getRanges() { + return fRanges.toArray(new IRegion[fRanges.size()]); + } + + public FlowInfo[] getInfos() { + return fInfos.toArray(new FlowInfo[fInfos.size()]); + } + + public FlowInfo getInfo(int index) { + return fInfos.get(index); + } + } + + private final HashMap<IASTNode, FlowInfo> fData = new HashMap<IASTNode, FlowInfo>(100); + FlowContext fFlowContext; + + public FlowAnalyzer(FlowContext context) { + super(true); + fFlowContext= context; + } + + protected abstract boolean createReturnFlowInfo(IASTReturnStatement node); + + protected abstract boolean traverseNode(IASTNode node); + + protected boolean skipNode(IASTNode node) { + return !traverseNode(node); + } + + @Override + protected final int genericVisit(IASTNode node) { + return traverseNode(node) ? PROCESS_CONTINUE : PROCESS_SKIP; + } + + //---- Hooks to create Flow info objects. User may introduce their own infos. + + protected ReturnFlowInfo createReturn(IASTReturnStatement statement) { + return new ReturnFlowInfo(statement); + } + + protected ThrowFlowInfo createThrow() { + return new ThrowFlowInfo(); + } + + protected BranchFlowInfo createBranch(IASTName label) { + return new BranchFlowInfo(label, fFlowContext); + } + + protected GenericSequentialFlowInfo createSequential() { + return new GenericSequentialFlowInfo(); + } + + protected ConditionalFlowInfo createConditional() { + return new ConditionalFlowInfo(); + } + + protected RangeBasedForFlowInfo createEnhancedFor() { + return new RangeBasedForFlowInfo(); + } + + protected ForFlowInfo createFor() { + return new ForFlowInfo(); + } + + protected TryFlowInfo createTry() { + return new TryFlowInfo(); + } + + protected WhileFlowInfo createWhile() { + return new WhileFlowInfo(); + } + + protected IfFlowInfo createIf() { + return new IfFlowInfo(); + } + + protected DoWhileFlowInfo createDoWhile() { + return new DoWhileFlowInfo(); + } + + protected SwitchFlowInfo createSwitch() { + return new SwitchFlowInfo(); + } + + protected BlockFlowInfo createBlock() { + return new BlockFlowInfo(); + } + + protected FunctionCallFlowInfo createFunctionCallFlowInfo() { + return new FunctionCallFlowInfo(); + } + + protected FlowContext getFlowContext() { + return fFlowContext; + } + + //---- Helpers to access flow analysis objects ---------------------------------------- + + protected FlowInfo getFlowInfo(IASTNode node) { + return fData.remove(node); + } + + protected void setFlowInfo(IASTNode node, FlowInfo info) { + fData.put(node, info); + } + + protected FlowInfo assignFlowInfo(IASTNode target, IASTNode source) { + FlowInfo result= getFlowInfo(source); + setFlowInfo(target, result); + return result; + } + + protected FlowInfo accessFlowInfo(IASTNode node) { + return fData.get(node); + } + + //---- Helpers to process sequential flow infos ------------------------------------- + + protected GenericSequentialFlowInfo processSequential(IASTNode parent, IASTNode[] nodes) { + GenericSequentialFlowInfo result= createSequential(parent); + process(result, nodes); + return result; + } + + protected GenericSequentialFlowInfo processSequential(IASTNode parent, Iterable<IASTNode> nodes) { + GenericSequentialFlowInfo result= createSequential(parent); + process(result, nodes); + return result; + } + + protected GenericSequentialFlowInfo processSequential(IASTNode parent, IASTNode node) { + GenericSequentialFlowInfo result= createSequential(parent); + if (node != null) + result.merge(getFlowInfo(node), fFlowContext); + return result; + } + + protected GenericSequentialFlowInfo processSequential(IASTNode parent, IASTNode node1, IASTNode node2) { + GenericSequentialFlowInfo result= createSequential(parent); + if (node1 != null) + result.merge(getFlowInfo(node1), fFlowContext); + if (node2 != null) + result.merge(getFlowInfo(node2), fFlowContext); + return result; + } + + protected GenericSequentialFlowInfo createSequential(IASTNode parent) { + GenericSequentialFlowInfo result= createSequential(); + setFlowInfo(parent, result); + return result; + } + + protected GenericSequentialFlowInfo createSequential(IASTNode[] nodes) { + GenericSequentialFlowInfo result= createSequential(); + process(result, nodes); + return result; + } + + //---- Generic merge methods -------------------------------------------------------- + + protected void process(GenericSequentialFlowInfo info, IASTNode[] nodes) { + if (nodes == null) + return; + for (IASTNode node : nodes) { + info.merge(getFlowInfo(node), fFlowContext); + } + } + + protected void process(GenericSequentialFlowInfo info, Iterable<IASTNode> nodes) { + if (nodes == null) + return; + for (IASTNode node : nodes) { + info.merge(getFlowInfo(node), fFlowContext); + } + } + + protected void process(GenericSequentialFlowInfo info, IASTNode node) { + if (node != null) + info.merge(getFlowInfo(node), fFlowContext); + } + + protected void process(GenericSequentialFlowInfo info, IASTNode node1, IASTNode node2) { + if (node1 != null) + info.merge(getFlowInfo(node1), fFlowContext); + if (node2 != null) + info.merge(getFlowInfo(node2), fFlowContext); + } + + //---- special visit methods ------------------------------------------------------- + + @Override + public int visit(IASTStatement node) { + if (skipNode(node)) + return PROCESS_SKIP; + if (node instanceof IASTBreakStatement) { + return visit((IASTBreakStatement) node); + } else if (node instanceof IASTCaseStatement) { + return visit((IASTCaseStatement) node); + } else if (node instanceof IASTCompoundStatement) { + return visit((IASTCompoundStatement) node); + } else if (node instanceof IASTContinueStatement) { + return visit((IASTContinueStatement) node); + } else if (node instanceof IASTDeclarationStatement) { + return visit((IASTDeclarationStatement) node); + } else if (node instanceof IASTDefaultStatement) { + return visit((IASTDefaultStatement) node); + } else if (node instanceof IASTDoStatement) { + return visit((IASTDoStatement) node); + } else if (node instanceof IASTExpressionStatement) { + return visit((IASTExpressionStatement) node); + } else if (node instanceof IASTForStatement) { + return visit((IASTForStatement) node); + } else if (node instanceof IASTGotoStatement) { + return visit((IASTGotoStatement) node); + } else if (node instanceof IASTIfStatement) { + return visit((IASTIfStatement) node); + } else if (node instanceof IASTLabelStatement) { + return visit((IASTLabelStatement) node); + } else if (node instanceof IASTNullStatement) { + return visit((IASTNullStatement) node); + } else if (node instanceof IASTReturnStatement) { + return visit((IASTReturnStatement) node); + } else if (node instanceof IASTSwitchStatement) { + return visit((IASTSwitchStatement) node); + } else if (node instanceof IASTWhileStatement) { + return visit((IASTWhileStatement) node); + } else if (node instanceof ICPPASTCatchHandler) { + return visit((ICPPASTCatchHandler) node); + } else if (node instanceof ICPPASTRangeBasedForStatement) { + return visit((ICPPASTRangeBasedForStatement) node); + } else if (node instanceof ICPPASTTryBlockStatement) { + return visit((ICPPASTTryBlockStatement) node); + } + return PROCESS_CONTINUE; + } + + public int visit(IASTBreakStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTCaseStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTDefaultStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTCompoundStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTContinueStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTDeclarationStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTDoStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTExpressionStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTForStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTGotoStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTIfStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTLabelStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTNullStatement node) { + // Null statements aren't of any interest. + return PROCESS_SKIP; + } + + public int visit(ICPPASTRangeBasedForStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(ICPPASTTryBlockStatement node) { + if (traverseNode(node)) { + fFlowContext.pushExceptions(node); + node.getTryBody().accept(this); + fFlowContext.popExceptions(); + for (ICPPASTCatchHandler catchHandler : node.getCatchHandlers()) { + catchHandler.accept(this); + } + } + return PROCESS_SKIP; + } + + public int visit(ICPPASTCatchHandler node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTReturnStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTSwitchStatement node) { + return PROCESS_CONTINUE; + } + + public int visit(IASTWhileStatement node) { + return PROCESS_CONTINUE; + } + + //---- Helper to process switch statement ---------------------------------------- + + protected SwitchData createSwitchData(IASTSwitchStatement node) { + SwitchData result= new SwitchData(); + IASTStatement[] statements; + IASTStatement body = node.getBody(); + if (body instanceof IASTCompoundStatement) { + statements = ((IASTCompoundStatement) body).getStatements(); + } else { + statements = new IASTStatement[] { body }; + } + if (statements.length == 0) + return result; + + int start= -1; + int end= -1; + GenericSequentialFlowInfo info= null; + + for (IASTStatement statement : statements) { + IASTFileLocation location = statement.getFileLocation(); + if (statement instanceof IASTCaseStatement || statement instanceof IASTDefaultStatement) { + if (statement instanceof IASTDefaultStatement) { + result.setHasDefaultCase(); + } + if (info == null) { + info= createSequential(); + start= location.getNodeOffset(); + } else { + if (info.isReturn() || info.isPartialReturn() || info.branches()) { + result.add(new Region(start, end - start + 1), info); + info= createSequential(); + start= location.getNodeOffset(); + } + } + } else { + if (info == null) { + info= createSequential(); + start= location.getNodeOffset(); + } else { + info.merge(getFlowInfo(statement), fFlowContext); + } + } + end= location.getNodeOffset() + location.getNodeLength() - 1; + } + result.add(new Region(start, end - start + 1), info); + return result; + } + + //---- Concrete leave methods --------------------------------------------------- + + @Override + public int leave(IASTStatement node) { + if (skipNode(node)) + return PROCESS_SKIP; + if (node instanceof IASTBreakStatement) { + return leave((IASTBreakStatement) node); + } else if (node instanceof IASTCaseStatement) { + return leave((IASTCaseStatement) node); + } else if (node instanceof IASTCompoundStatement) { + return leave((IASTCompoundStatement) node); + } else if (node instanceof IASTContinueStatement) { + return leave((IASTContinueStatement) node); + } else if (node instanceof IASTDeclarationStatement) { + return leave((IASTDeclarationStatement) node); + } else if (node instanceof IASTDefaultStatement) { + return leave((IASTDefaultStatement) node); + } else if (node instanceof IASTDoStatement) { + return leave((IASTDoStatement) node); + } else if (node instanceof IASTExpressionStatement) { + return leave((IASTExpressionStatement) node); + } else if (node instanceof IASTForStatement) { + return leave((IASTForStatement) node); + } else if (node instanceof IASTGotoStatement) { + return leave((IASTGotoStatement) node); + } else if (node instanceof IASTIfStatement) { + return leave((IASTIfStatement) node); + } else if (node instanceof IASTLabelStatement) { + return leave((IASTLabelStatement) node); + } else if (node instanceof IASTNullStatement) { + return leave((IASTNullStatement) node); + } else if (node instanceof IASTReturnStatement) { + return leave((IASTReturnStatement) node); + } else if (node instanceof IASTSwitchStatement) { + return leave((IASTSwitchStatement) node); + } else if (node instanceof IASTWhileStatement) { + return leave((IASTWhileStatement) node); + } else if (node instanceof ICPPASTCatchHandler) { + return leave((ICPPASTCatchHandler) node); + } else if (node instanceof ICPPASTRangeBasedForStatement) { + return leave((ICPPASTRangeBasedForStatement) node); + } else if (node instanceof ICPPASTTryBlockStatement) { + return leave((ICPPASTTryBlockStatement) node); + } + return PROCESS_SKIP; + } + + public int leave(IASTBreakStatement node) { + setFlowInfo(node, createBranch(null)); + return PROCESS_SKIP; + } + + public int leave(IASTCaseStatement node) { + // Nothing to do + return PROCESS_SKIP; + } + + public int leave(IASTDefaultStatement node) { + // Nothing to do + return PROCESS_SKIP; + } + + public int leave(IASTCompoundStatement node) { + BlockFlowInfo info= createBlock(); + setFlowInfo(node, info); + process(info, node.getStatements()); + return PROCESS_SKIP; + } + + public int leave(IASTContinueStatement node) { + setFlowInfo(node, createBranch(null)); + return PROCESS_SKIP; + } + + public int leave(IASTDeclarationStatement node) { + processSequential(node, node.getDeclaration()); + return PROCESS_SKIP; + } + + public int leave(IASTDoStatement node) { + DoWhileFlowInfo info= createDoWhile(); + setFlowInfo(node, info); + info.mergeAction(getFlowInfo(node.getBody()), fFlowContext); + info.mergeCondition(getFlowInfo(node.getCondition()), fFlowContext); + info.removeLabel(null); + return PROCESS_SKIP; + } + + public int leave(IASTExpressionStatement node) { + assignFlowInfo(node, node.getExpression()); + return PROCESS_SKIP; + } + + public int leave(IASTForStatement node) { + ForFlowInfo forInfo= createFor(); + setFlowInfo(node, forInfo); + forInfo.mergeInitializer(createSequential(node.getInitializerStatement()), fFlowContext); + forInfo.mergeCondition(getFlowInfo(node.getConditionExpression()), fFlowContext); + forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext); + // Increments are executed after the body. + forInfo.mergeIncrement(createSequential(node.getIterationExpression()), fFlowContext); + forInfo.removeLabel(null); + return PROCESS_SKIP; + } + + public int leave(IASTGotoStatement node) { + // TODO(sprigogin): Implement goto support + return PROCESS_SKIP; + } + + public int leave(IASTIfStatement node) { + IfFlowInfo info= createIf(); + setFlowInfo(node, info); + info.mergeCondition(getFlowInfo(node.getConditionExpression()), fFlowContext); + info.merge(getFlowInfo(node.getThenClause()), getFlowInfo(node.getElseClause()), fFlowContext); + return PROCESS_SKIP; + } + + public int leave(IASTLabelStatement node) { + FlowInfo info= assignFlowInfo(node, node.getNestedStatement()); + if (info != null) + info.removeLabel(node.getName()); + return PROCESS_SKIP; + } + + public int leave(IASTNullStatement node) { + // Leaf node. + return PROCESS_SKIP; + } + + public int leave(ICPPASTRangeBasedForStatement node) { + RangeBasedForFlowInfo forInfo= createEnhancedFor(); + setFlowInfo(node, forInfo); + forInfo.mergeDeclaration(getFlowInfo(node.getDeclaration()), fFlowContext); + forInfo.mergeInitializerClause(getFlowInfo(node.getInitializerClause()), fFlowContext); + forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext); + forInfo.removeLabel(null); + return PROCESS_SKIP; + } + + public int leave(ICPPASTTryBlockStatement node) { + TryFlowInfo info= createTry(); + setFlowInfo(node, info); + info.mergeTry(getFlowInfo(node.getTryBody()), fFlowContext); + for (ICPPASTCatchHandler catchHandler : node.getCatchHandlers()) { + info.mergeCatch(getFlowInfo(catchHandler), fFlowContext); + } + return PROCESS_SKIP; + } + + public int leave(ICPPASTCatchHandler node) { + processSequential(node, node.getDeclaration(), node.getCatchBody()); + return PROCESS_SKIP; + } + + public int leave(IASTReturnStatement node) { + if (createReturnFlowInfo(node)) { + ReturnFlowInfo info= createReturn(node); + setFlowInfo(node, info); + info.merge(getFlowInfo(node.getReturnArgument()), fFlowContext); + } else { + assignFlowInfo(node, node.getReturnArgument()); + } + return PROCESS_SKIP; + } + + public int leave(IASTSwitchStatement node) { + return leave(node, createSwitchData(node)); + } + + protected int leave(IASTSwitchStatement node, SwitchData data) { + SwitchFlowInfo switchFlowInfo= createSwitch(); + setFlowInfo(node, switchFlowInfo); + switchFlowInfo.mergeTest(getFlowInfo(node.getControllerExpression()), fFlowContext); + FlowInfo[] cases= data.getInfos(); + for (int i= 0; i < cases.length; i++) { + switchFlowInfo.mergeCase(cases[i], fFlowContext); + } + switchFlowInfo.mergeDefault(data.hasDefaultCase(), fFlowContext); + switchFlowInfo.removeLabel(null); + return PROCESS_SKIP; + } + + public int leave(IASTWhileStatement node) { + WhileFlowInfo info= createWhile(); + setFlowInfo(node, info); + info.mergeCondition(getFlowInfo(node.getCondition()), fFlowContext); + info.mergeAction(getFlowInfo(node.getBody()), fFlowContext); + info.removeLabel(null); + return PROCESS_SKIP; + } + + @Override + public int leave(IASTExpression node) { + if (skipNode(node)) + return PROCESS_SKIP; + if (node instanceof IASTArraySubscriptExpression) { + return leave((IASTArraySubscriptExpression) node); + } else if (node instanceof IASTConditionalExpression) { + return leave((IASTConditionalExpression) node); + } else if (node instanceof IASTFunctionCallExpression) { + return leave((IASTFunctionCallExpression) node); + } else if (node instanceof IASTExpressionList) { + return leave((IASTExpressionList) node); + } else if (node instanceof IASTTypeIdExpression) { + return leave((IASTTypeIdExpression) node); + } else if (node instanceof IASTBinaryExpression) { + return leave((IASTBinaryExpression) node); + } else if (node instanceof IASTLiteralExpression) { + return leave((IASTLiteralExpression) node); + } else if (node instanceof IASTIdExpression) { + return leave((IASTIdExpression) node); + } else if (node instanceof IASTCastExpression) { + return leave((IASTCastExpression) node); + } else if (node instanceof IASTUnaryExpression) { + return leave((IASTUnaryExpression) node); + } else if (node instanceof IASTFieldReference) { + return leave((IASTFieldReference) node); + } else if (node instanceof IASTTypeIdInitializerExpression) { + return leave((IASTTypeIdInitializerExpression) node); + } else if (node instanceof ICPPASTNewExpression) { + return leave((ICPPASTNewExpression) node); + } else if (node instanceof ICPPASTDeleteExpression) { + return leave((ICPPASTDeleteExpression) node); + } else if (node instanceof ICPPASTSimpleTypeConstructorExpression) { + return leave((ICPPASTSimpleTypeConstructorExpression) node); + } else if (node instanceof IASTProblemExpression) { + return leave(node); + } + return PROCESS_SKIP; + } + + public int leave(IASTArraySubscriptExpression node) { + processSequential(node, node.getArrayExpression(), node.getArgument()); + return PROCESS_SKIP; + } + + public int leave(IASTConditionalExpression node) { + ConditionalFlowInfo info= createConditional(); + setFlowInfo(node, info); + info.mergeCondition(getFlowInfo(node.getLogicalConditionExpression()), fFlowContext); + info.merge(getFlowInfo(node.getPositiveResultExpression()), + getFlowInfo(node.getNegativeResultExpression()), fFlowContext); + return PROCESS_SKIP; + } + + public int leave(IASTFunctionCallExpression node) { + processFunctionCall(node, node.getFunctionNameExpression(), node.getArguments()); + return PROCESS_SKIP; + } + + @Override + public int leave(IASTDeclaration node) { + if (skipNode(node)) + return PROCESS_SKIP; + if (node instanceof IASTFunctionDefinition) { + return leave((IASTFunctionDefinition) node); + } else if (node instanceof IASTSimpleDeclaration) { + return leave((IASTSimpleDeclaration) node); + } + return PROCESS_SKIP; + } + + public int leave(IASTFunctionDefinition node) { + GenericSequentialFlowInfo info= processSequential(node, node.getDeclSpecifier()); + process(info, node.getDeclarator()); + process(info, node.getBody()); + if (node instanceof ICPPASTFunctionWithTryBlock) { + process(info, ((ICPPASTFunctionWithTryBlock) node).getCatchHandlers()); + } + return PROCESS_SKIP; + } + + public int leave(IASTSimpleDeclaration node) { + GenericSequentialFlowInfo info= processSequential(node, node.getDeclSpecifier()); + process(info, node.getDeclarators()); + return PROCESS_SKIP; + } + + @Override + public int leave(IASTParameterDeclaration node) { + if (skipNode(node)) + return PROCESS_SKIP; + GenericSequentialFlowInfo info= processSequential(node, node.getDeclSpecifier()); + process(info, node.getDeclarator()); + return PROCESS_SKIP; + } + + @Override + public int leave(IASTDeclarator node) { + if (skipNode(node)) + return PROCESS_SKIP; + IASTNode nestedOrName = node.getNestedDeclarator(); + if (nestedOrName == null) + nestedOrName = node.getName(); + GenericSequentialFlowInfo info= processSequential(node, nestedOrName); + if (node instanceof IASTArrayDeclarator) + process(info, ((IASTArrayDeclarator) node).getArrayModifiers()); + IASTNode[] parameters = null; + if (node instanceof IASTStandardFunctionDeclarator) { + parameters = ((IASTStandardFunctionDeclarator) node).getParameters(); + } else if (node instanceof ICASTKnRFunctionDeclarator) { + parameters = ((ICASTKnRFunctionDeclarator) node).getParameterDeclarations(); + } + if (parameters != null) + process(info, parameters); + process(info, node.getInitializer()); + return PROCESS_SKIP; + } + + public int leave(IASTExpressionList node) { + IASTExpression[] expressions = node.getExpressions(); + processSequential(node, expressions); + return PROCESS_SKIP; + } + + public int leave(IASTTypeIdExpression node) { + assignFlowInfo(node, node.getTypeId()); + return PROCESS_SKIP; + } + + public int leave(IASTBinaryExpression node) { + int operator = node.getOperator(); + switch (operator) { + case IASTBinaryExpression.op_assign: + case IASTBinaryExpression.op_binaryAndAssign: + case IASTBinaryExpression.op_binaryOrAssign: + case IASTBinaryExpression.op_binaryXorAssign: + case IASTBinaryExpression.op_divideAssign: + case IASTBinaryExpression.op_minusAssign: + case IASTBinaryExpression.op_moduloAssign: + case IASTBinaryExpression.op_multiplyAssign: + case IASTBinaryExpression.op_plusAssign: + case IASTBinaryExpression.op_shiftLeftAssign: + case IASTBinaryExpression.op_shiftRightAssign: + FlowInfo lhs= getFlowInfo(node.getOperand1()); + FlowInfo rhs= getFlowInfo(node.getOperand2()); + if (lhs instanceof LocalFlowInfo) { + LocalFlowInfo llhs= (LocalFlowInfo) lhs; + llhs.setWriteAccess(fFlowContext); + if (operator != IASTBinaryExpression.op_assign) { + GenericSequentialFlowInfo tmp= createSequential(); + tmp.merge(new LocalFlowInfo(llhs, FlowInfo.READ, fFlowContext), fFlowContext); + tmp.merge(rhs, fFlowContext); + rhs= tmp; + } + } + GenericSequentialFlowInfo info= createSequential(node); + // First process right and side and then left hand side. + info.merge(rhs, fFlowContext); + info.merge(lhs, fFlowContext); + break; + + default: + IASTExpression[] operands = CPPVisitor.getOperandsOfMultiExpression(node); + processSequential(node, operands); + break; + } + return PROCESS_SKIP; + } + + public int leave(IASTLiteralExpression node) { + // Leaf node. + return PROCESS_SKIP; + } + + public int leave(IASTIdExpression node) { + assignFlowInfo(node, node.getName()); + return PROCESS_SKIP; + } + + public int leave(IASTCastExpression node) { + if (skipNode(node)) + return PROCESS_SKIP; + processSequential(node, node.getTypeId(), node.getOperand()); + return PROCESS_SKIP; + } + + public int leave(IASTUnaryExpression node) { + if (skipNode(node)) + return PROCESS_SKIP; + int operator = node.getOperator(); + + switch (operator) { + case IASTUnaryExpression.op_prefixIncr: + case IASTUnaryExpression.op_prefixDecr: + case IASTUnaryExpression.op_postFixIncr: + case IASTUnaryExpression.op_postFixDecr: { + FlowInfo info= getFlowInfo(node.getOperand()); + if (info instanceof LocalFlowInfo) { + // Normally this should be done in the parent node since the write access takes + // place later. But there seems to be no case where this influences the flow + // analysis. So it is kept here to simplify the code. + GenericSequentialFlowInfo result= createSequential(node); + result.merge(info, fFlowContext); + result.merge(new LocalFlowInfo((LocalFlowInfo)info, FlowInfo.WRITE, fFlowContext), + fFlowContext); + } else { + setFlowInfo(node, info); + } + break; + } + case IASTUnaryExpression.op_throw: { + ThrowFlowInfo info= createThrow(); + setFlowInfo(node, info); + IASTExpression expression= node.getOperand(); + info.merge(getFlowInfo(expression), fFlowContext); + break; + } + case IASTUnaryExpression.op_alignOf: + case IASTUnaryExpression.op_sizeof: + case IASTUnaryExpression.op_sizeofParameterPack: + case IASTUnaryExpression.op_typeid: + break; + + default: + assignFlowInfo(node, node.getOperand()); + break; + } + return PROCESS_SKIP; + } + + @Override + public int leave(IASTName node) { + if (skipNode(node) || node.isDeclaration() || node instanceof ICPPASTQualifiedName) + return PROCESS_SKIP; + IBinding binding= node.resolveBinding(); + if (binding instanceof IVariable) { + IVariable variable= (IVariable) binding; + if (!(variable instanceof IField)) { + int accessMode = CPPVariableReadWriteFlags.getReadWriteFlags(node); + setFlowInfo(node, new LocalFlowInfo(variable, accessMode, fFlowContext)); + } + } + return PROCESS_SKIP; + } + + public int leave(IASTFieldReference node) { + processSequential(node, node.getFieldOwner(), node.getFieldName()); + return PROCESS_SKIP; + } + + public int leave(IASTTypeIdInitializerExpression node) { + processSequential(node, node.getInitializer()); + return PROCESS_SKIP; + } + + public int leave(ICPPASTNewExpression node) { + GenericSequentialFlowInfo info= processSequential(node, node.getTypeId()); + process(info, node.getPlacementArguments()); + process(info, node.getInitializer()); + return PROCESS_SKIP; + } + + public int leave(ICPPASTDeleteExpression node) { + assignFlowInfo(node, node.getOperand()); + return PROCESS_SKIP; + } + + public int leave(ICPPASTSimpleTypeConstructorExpression node) { + processSequential(node, node.getDeclSpecifier(), node.getInitializer()); + return PROCESS_SKIP; + } + + @Override + public int leave(IASTInitializer node) { + if (skipNode(node)) + return PROCESS_SKIP; + if (node instanceof IASTEqualsInitializer) { + return leave((IASTEqualsInitializer) node); + } else if (node instanceof IASTInitializerList) { + return leave((IASTInitializerList) node); + } else if (node instanceof ICASTDesignatedInitializer) { + return leave((ICASTDesignatedInitializer) node); + } else if (node instanceof IASTInitializerList) { + return leave((ICPPASTConstructorChainInitializer) node); + } else if (node instanceof IASTInitializerList) { + return leave((ICPPASTConstructorInitializer) node); + } + return PROCESS_SKIP; + } + + public int leave(IASTEqualsInitializer node) { + assignFlowInfo(node, node.getInitializerClause()); + return PROCESS_SKIP; + } + + public int leave(IASTInitializerList node) { + processSequential(node, node.getClauses()); + return PROCESS_SKIP; + } + + public int leave(ICASTDesignatedInitializer node) { + processSequential(node, node.getDesignators()); + return PROCESS_SKIP; + } + + public int leave(ICPPASTConstructorChainInitializer node) { + processSequential(node, node.getMemberInitializerId(), node.getInitializer()); + return PROCESS_SKIP; + } + + public int leave(ICPPASTConstructorInitializer node) { + processSequential(node, node.getArguments()); + return PROCESS_SKIP; + } + + @Override + public int leave(IASTTranslationUnit node) { + if (skipNode(node)) + return PROCESS_SKIP; + processSequential(node, node.getDeclarations()); + return PROCESS_SKIP; + } + + private void processFunctionCall(IASTFunctionCallExpression node, + IASTExpression functionNameExpression, IASTInitializerClause[] arguments) { + if (skipNode(node)) + return; + FunctionCallFlowInfo info= createFunctionCallFlowInfo(); + setFlowInfo(node, info); + for (IASTInitializerClause arg : arguments) { + info.mergeArgument(getFlowInfo(arg), fFlowContext); + } + info.mergeReceiver(getFlowInfo(functionNameExpression), fFlowContext); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowContext.java new file mode 100644 index 00000000000..3c83fc36d72 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowContext.java @@ -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; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowInfo.java new file mode 100644 index 00000000000..6f59e93354c --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FlowInfo.java @@ -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; + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ForFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ForFlowInfo.java new file mode 100644 index 00000000000..e97bc87b3f5 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ForFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FunctionCallFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FunctionCallFlowInfo.java new file mode 100644 index 00000000000..d55c4cc40c1 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/FunctionCallFlowInfo.java @@ -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); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java new file mode 100644 index 00000000000..7cc2a6c76aa --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/GenericConditionalFlowInfo.java @@ -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); + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java new file mode 100644 index 00000000000..2d219c94d4c --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/GenericSequentialFlowInfo.java @@ -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); + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/IfFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/IfFlowInfo.java new file mode 100644 index 00000000000..ffeaa595197 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/IfFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java new file mode 100644 index 00000000000..42a353bf092 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java new file mode 100644 index 00000000000..885239e1e28 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java @@ -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); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java new file mode 100644 index 00000000000..8bbbb4945c6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/LocalFlowInfo.java @@ -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; + } + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/LocalVariableIndex.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/LocalVariableIndex.java new file mode 100644 index 00000000000..c33ea8ed331 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/LocalVariableIndex.java @@ -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; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/RangeBasedForFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/RangeBasedForFlowInfo.java new file mode 100644 index 00000000000..fde3c64000b --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/RangeBasedForFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java new file mode 100644 index 00000000000..323d5703972 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java @@ -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; + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/Selection.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/Selection.java new file mode 100644 index 00000000000..cc9d2b9bea1 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/Selection.java @@ -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$ + } +}
\ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java new file mode 100644 index 00000000000..be0a50f7b31 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/SwitchFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java new file mode 100644 index 00000000000..4b20b43efa9 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/ThrowFlowInfo.java @@ -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); + } +} + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/TryFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/TryFlowInfo.java new file mode 100644 index 00000000000..93ca4b12921 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/TryFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java new file mode 100644 index 00000000000..f6b4030ae7a --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/refactoring/code/flow/WhileFlowInfo.java @@ -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); + } +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/ASTNodes.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/ASTNodes.java new file mode 100644 index 00000000000..360ddfc0358 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/ASTNodes.java @@ -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(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NameInformation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NameInformation.java index cfb7ec04c2e..8859680deed 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NameInformation.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NameInformation.java @@ -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); } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java index 38ce30f0dd0..00f9ae4e6f9 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/NodeContainer.java @@ -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; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractfunction/StatementExtractor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractfunction/StatementExtractor.java index b6da4787615..88aa42d6b14 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractfunction/StatementExtractor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/refactoring/extractfunction/StatementExtractor.java @@ -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); |