Local inspection tool's session
Hi all
I am developing an inspection tool that aims to detect some problems with method parameters, however I have a few questions as I'm quite new to inspections API (and IntelliJ plugin SDK in general).
1. Is the session argument passed to buildVisitor guaranteed to be the same across all calls for say a given file? As in can my tool rely on the fact that the LocalInspectionToolSession for Foo.java is going to be the same for all calls? If not, what (if any) guarantees can be made about the session?
2. As far as I am aware during a local tool inspection, subclasses must be stateless, so is there any possibility of storing state anywhere else? Perhaps state that would then be persistent across different IDEA processes, that is when the user restarts the current project.
3. If my inspection tool needs to access other files where say methods from Foo are used, e.g. suppose Bar.java would make instantiate Foo and use some of its methods, if my tool would need to know about Bar, is it the case that a local inspection is inappropriate and a global inspection tool is required?
Looking forward for some clarification!
Please sign in to leave a comment.
1. Yes. You get a LocalInspectionToolSession every time your buildVisitor() method is called. This is for every inspection pass over a file or part of a file, so multiple inspections can receive the same session object. After an edit to the source file buidVisitor() is called again with a new session.
2. Temporary state (e.g. caches) can be stored inside the psi classes, see com.intellij.openapi.util.UserDataHolder. Settings can be stored in public fields and are persisted automatically. See also the readSettings() and writeSettings() methods in com.intellij.codeInspection.InspectionProfileEntry.
3. Resolving references is cached, so finding the definition of a variable, method or class is something you should just do in a local inspection. Finding references to an element (e.g. using com.intellij.psi.search.searches.ReferencesSearch) can be very slow. If this is needed in your inspection it can be better to use a global inspection tool instead, but then you won't have editor highlights.
See also: http://www.jetbrains.org/intellij/sdk/docs/tutorials/code_inspections.html
Bas
Hi Bas
Many thanks for taking the time and responding to my questions. I know there were quite a few, so this is greatly appreciated.
Regarding my previous questions 1, I think my phrasing could've been slightly improved. I meant to ask whether any given inspection would always receive the same InspectionToolSession object each time it is called on a specific file, as opposed to if the platform would deliver the same session argument to all inspections in a given file for any call to the buildVisitor. Thankfully, you've mentioned that a new session is created each time the same inspection is called, which was also confirmed during my debugging.
Am I correct in understanding that the data stored in objects that provide the UserDataHolder interface is not persistent across different instances of IDEA? That is, if I were to store information in a PsiClass (as apparently it does extend the UserDataHolder through the PsiElement), it would all be gone when the running IDEA process would stop.
Furthermore, I am a bit confused as to what you mean by "Settings can be stored in public fields and are persisted automatically.". I was under the impression that inspection tools subclasses must be stateless (as per comments from the BaseJavaLocalInspectionTool). Or are the public fields relating to something else?
Regarding your answer to my third question, my take of it is that is ok to resolve references to PsiField, PsiMethod, PsiClass as they should be fast to compute (perhaps you could point out which class serves this functionality?), but resolving a reference to any PsiElement is not recommended since it would take a considerable amount of time.
Lastly, I was looking for a way to trigger the "Analyze | Inspect Code..." and "Analyze | Run Inspection by name..." programmatically. I haven't yet figured out a way to do it, so I was wondering which part of the code is responsible for it?
Thank you very much for taking your time and helping me!
Radu
Correct, UserDataHolder information is not persisted.
LocalInspectionTool must be stateless, because it will be called concurrently from multiple threads. Storing temporary state will cause problems. But UI settings, which are by definition only modified from the event thread, can and are supposed to be stored in the inspection class. Override the createOptionsPanel() method to create the UI. I believe this the example code from the link I provided also has an example of this.
If you are analyzing java code look for resolveXXX() methods to go from a reference or method call to a variable or method. For example a PsiMethodCallExpression has a resolveMethod() which returns a PsiMethod. Going the other way, finding all method calls from a PsiMethod is expensive.
You can probably call actionPerformed() on com.intellij.codeInspection.actions.CodeInspectionAction and com.intellij.codeInspection.actions.RunInspectionAction, to trigger the "Analyze | Inspect Code..." and "Analyze | Run Inspection by name..." programatically, but I wonder why you would want to do that?
Bas
Hi Bas,
I definitely got your point about resolving references now, thank you.
Regarding triggering the "Inspect Code..." or "Run Inspection by name..." programmatically, I guess my reasoning was that I could trigger a local inspection across a wider scope than say the current file in the editor, but I think that's what global inspections are for.
With respect to storing state, I guess what I am looking for is to associate persistent state with a PsiClass.
I came across com.intellij.openapi.vfs.newvfs.FileAttribute which I understand it is used to associate persistent data files from the VFS, although I might be wrong on this. Do you have any thoughts on this?