Type refactoring

Hi,

A long time ago I've asked for a refactoring, that safely can change the
type of a variable according to its usages. Since Jetbrains obviously don't
show interest to implement it, I want to know from those whole already know
the OpenApi well, whether it is open enough to write a plugin myself.

- I need to get the "object" at the cursor position
- if it is no variable, I return
- then I need to find all "usages" to detect the upper-most and lowest type
in the hierarchy
- then I would like to show a dialog box showing all allowed types and let
the user select it

--
Thanks in advance,
Tom

51 comments
Comment actions Permalink

Look's like it planned for Irida already :)

TIA,
Dmitry

0
Comment actions Permalink

I would definitely think it's possible to write such a refactoring yourself. You should probably look at the source of some simple plugins to get a feel for it. And you'll want to install the PsiViewer plugin, which although a little buggy is invaluable to learn the PSI (program structure interface - sort of an abstract syntax tree).

I think you haven't described even half of the things you need to do for such a plugin, but here's how I would start:

Note that I'm not certain this is the best way to do such a thing and I have not even compiled this code yet. It's just something to get you started, cause some things can be hard to find in the openapi.

Good luck!
Bas

0
Comment actions Permalink

I'm looking forward to any results. Naturally I'm expecting a fantastic
user interface from you;-)

Bas

0
Comment actions Permalink

One more time:
"This refactoring will be present in Irida"!!!
Don't lose you time to already implemented features, look into missing :)

TIA,
Dmitry

0
Comment actions Permalink

Well, the possible classes can be detected now :) Thanks again for your
code, I'm sure, without it I would have given up because of Dmitry's statement.

Now I only need to go back to the roots of Swing to find out how to display
a simple dialog; I don't want to make the plugin 10 times bigger than
necessary because of using SmartCVS' GUI-frameworks.

Tom

0
Comment actions Permalink

Hmm, now I'm getting following assertion:

Assertion failed: Write access is allowed inside write-action only (see
com.intellij.openapi.application.Application.runWriteAction())

What's up with Application.runWriteAction(), where can I information about it?

Tom

0
Comment actions Permalink

OK, I've got it - need to use EditorAction instead of AnAction.

Tom

0
Comment actions Permalink

Thomas

Now I only need to go back to the roots of Swing to find out how to
display a simple dialog;



Time to give the UI designer a chance: try "New -> Dialog".


Alain

0
Comment actions Permalink

That's exactly what I did :)

--
Have a nice weekend!
Tom

0
Comment actions Permalink

An EditorAction will certainly work. I should of thought of that myself, but I was a bit confused by the name. It seems to be just for actions in the editor, but in fact it's probably much easier to always use EditorAction and not AnAction.

You will probably know this already, but you can also run write actions with something like this.

Bas

0
Comment actions Permalink

I guess, the mentioned issue is something different. What I mean is
http://www.intellij.net/tracker/idea/viewSCR?publicId=6017 .

No matter, I need it now, not in a couple of months. And it is a good
training for how to create a simple IDEA plugin. I'm sure, sometimes you
also like to play with "new" APIs.

Tom

0
Comment actions Permalink

:) Yes, you are right! :)

Good luck!
BTW, looking into JIRA issue and this SCR i saw, that this refactoring may refactor:
1. Type of variable/field.
1.1 Type in usage context.

2. Type of getter/setter
2.1 Type of ussage context
2.2 Type of overrided context
2.3 Type of caused implementation in case of interface implementation.

So, seems to not very simple plugin? :)

0
Comment actions Permalink

I just want to change a variable declaration. Example:

public void foo() {
final ArrayList items = new ArrayList();
bar(items);
}

public void bar(Collection collection) {
}

Here you should be able to change the type of the variable 'items', for
example, to Collection, List, AbstractList, ...

Tom

0
Comment actions Permalink

Hi,

Left or right type definition? :)
Becouse i'm thinking that changing left type is impossible, and right type is no need to be refactored.

For example:


If you change ArrayList to one of implementation of List - this is not can re Refactoring. You just change it by CtrlShitSpace after 'new' token.

If you will change List, then:
1. Find all usages of variable
2. Calculate by some magic algorithm scope of available types (in this example scope of method poo is larger that scope of method foo, and you must take scope of foo, but also list.get is using, so you can't use Collection, only List and all above)

Now, even you call this as refactor, i still think that it is not pure refactoring. So, can you explain that code you will refactor by plugin?

IFAIK, plugin, about you said - it some smart type sugegsting and scope detection by variable usage. As CtrlShiftSpace after 'new':)

Thanks!

0
Comment actions Permalink

Left or right type definition? :)


The type of the variable, hence the left expression.

 void boo() {
>   List list = new ArrayList();
>   Object o = list.get(0);
>   foo(list);
>   poo(list);
> }
> 
> void foo(Collection list) {
>   // ...
> }
> 
> void poo(Object object) {
>   // ...
> }
> ]]>


Should be able to change to

>

 void boo() {
 >   Collection list = new ArrayList();
 >   Object o = list.get(0);
 >   foo(list);
 >   poo(list);
 > }
 >
 > void foo(Collection list) {
 >   // ...
 > }
 >
 > void poo(Object object) {
 >   // ...
 > }
 > ]]>


Tom

0
Comment actions Permalink


I have the PsiReferenceExpression of 'items' in 'bar(items);'. How to get
the 'Collection'-type of the bar()-declaration?

Tom

0
Comment actions Permalink

Tom,

It can't be Collection, as i understand SRC correctly. The Collection have not method get, but it used in this sample.

I'm wrong?

Thanks!

0
Comment actions Permalink

Did you have PsiViewer enabled in IDEA?
AFIAK you will get only PsiIdentifier in this method parameters list. But you have PsiMethodCallExpression, then you can found PsiMethod and get from it PsiParameterList the PsiTypeElement of parameter type.

Thanks!

0
Comment actions Permalink

You are right, your example contains a get(int), which I've overseen.

Tom

0
Comment actions Permalink

Did you have PsiViewer enabled in IDEA?


Yes, I have, it helps a lot to browse the structure.

AFIAK you will get only PsiIdentifier in this method parameters list. But you have PsiMethodCallExpression, then you can found PsiMethod and get from it PsiParameterList the PsiTypeElement of parameter type.


Do I need to get it from the n-th parameter or does the Psi-object itself
contains the necessary type information?

Tom

0
Comment actions Permalink

I don't have IDEA in front of me right now, but calling resolve() on a PsiReferenceExpression returns the object the reference refers to, a PsiMethod in this case. You can query that for further info. Does that help?

Bas

0
Comment actions Permalink

Sorry, it does not work. In this example:

resolve() returns a PsiLocalVariable, that's .type() returns List, not
Collection :(

Can somebody from Jetbrains please comment on this issue? Thanks in advance.

Tom

0
Comment actions Permalink

Ah sorry, didn't quite understand which reference you had there. Ok, when you have the reference to "coll", you then want the reference to the "doSomethingElse" method in this case. I would do it like this:

 0) {
		final PsiElement parentsParent = parent.getParent();
		if (parentsParent instanceof PsiMethodCallExpression) {
			final PsiMethodCallExpression expression = (PsiMethodCallExpression)parentsParent;
			final PsiReferenceExpression methodReference = expression.getMethodExpression();
			final PsiMethod method = (PsiMethod)methodReference.resolve();
			final PsiParameterList parameterList = method.getParameterList();
			final PsiParameter[] parameters = parameterList.getParameters();
			PsiType type = parameters[index].getType();
		}
	}
}
]]>

Note again that this code was never compiled and may not fulfill its intended purpose.

Bas

0
Comment actions Permalink

Thanks again, that did it :)

Tom

0
Comment actions Permalink

I now wanna check, in what PsiTypes, the method 'size()' is defined. I have
the PsiMethodCallExpression, which can return the PsiMethod, which can
return the containing PsiClass. In other words, I have a PsiMethod and want
to get all super-types and implemented interfaces, which contain this method
as well.

I've skimmed the IntentionPowerPack-sources but could not find something
similar.

BTW, what is the difference between PsiType, PsiClass and PsiClassType? How
to convert from a PsiClass to a PsiType?
The documentation provided by Jetbrains is very week (or I did not find it);
just got the non-description-version of the OpenAPI-Javadoc.

--
Thanks in advance,
Tom

0
Comment actions Permalink

Something like following:

PsiMethodCallExpression call = ...;
PsiMethod method = call.resolveMethod();
if (method != null) {
PsiMethod originalMethod = method.findDeepestSuperMethod();
}

BTW, what is the difference between PsiType, PsiClass and
PsiClassType? How
to convert from a PsiClass to a PsiType?


PsiType may denote types other than reference types like primitive ones (int,
long, etc.)
PsiClassType denotes reference types.

PsiClass->PsiType conversion should be done as follows:
PsiManager.getInstance(project).getElementFactory().createType(psiClass)

PsiClassType->PsiClass conversion should be done as follows:
psiClassType.resolve()
Whether substution of generic parameters is important for you use resolveGenerics()
method instead.

0
Comment actions Permalink

First, thanks for the detailled answer.

Something like following:

PsiMethodCallExpression call = ...;
PsiMethod method = call.resolveMethod();
if (method != null) {
PsiMethod originalMethod = method.findDeepestSuperMethod();
}


Unfortunately this does not work for me. I have a couple of possible
variable Psi(Class)Types, which I want to check, whether they contain the
same method. In other words, I need all Psi(Class)Types between (including)
method.getContainingClass() and originalMethod.getContainingClass().

Tom

0
Comment actions Permalink

Hi Thomas,

 coll = new ArrayList();
> 	
> System.out.println(coll.size());]]>

I now wanna check, in what PsiTypes, the method
'size()' is defined. I have
the PsiMethodCallExpression, which can return the
PsiMethod, which can
return the containing PsiClass. In other words, I
have a PsiMethod and want
to get all super-types and implemented interfaces,
which contain this method
as well.


You might want to take a look at the com.intellij.psi.util.PsiSuperMethodUtil class. It has some utility methods which may be useful to you.


I've skimmed the IntentionPowerPack-sources but could
not find something
similar.

BTW, what is the difference between PsiType, PsiClass
and PsiClassType? How
to convert from a PsiClass to a PsiType?


I don't know too much about it but PsiClassType is a subclass of PsiType with some extra methods for classes. PsiType also describes primitives. As far as I can tell PsiType and PsiClassType are light weight classes for type checking and PsiClass provides meta information about classes.

To convert from a PsiClass to a PsiType:

The documentation provided by Jetbrains is very week
(or I did not find it);
just got the non-description-version of the
OpenAPI-Javadoc.


Psi documentation is getter better actually:-) It still has a long way to go, but since it has been officially opened documentation has started to appear here and there where there used to be nothing. Existing plugin sources continue to be the best source of information though. Some stuff can also be found on the www.intellij.org wiki, but that's mostly out of date and not structured very well. Alain Ravet had the idea some time ago of building an openapi wiki, but the response was low.

Bas

0

Please sign in to leave a comment.