--
You received this message because you are subscribed to the Google Groups "lint-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lint-dev+u...@googlegroups.com.
To post to this group, send email to lint...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/lint-dev/6e61621f-1c85-41b0-805c-c46b4524461b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
public static interface Detector.UastScanner
There are several different common patterns for detecting issues:
getApplicableMethodNames()
and visitMethod(JavaContext, UCallExpression, PsiMethod)
getApplicableConstructorTypes()
and visitConstructor(JavaContext, UCallExpression, PsiMethod)
getApplicableReferenceNames()
and visitReference(JavaContext, UReferenceExpression, PsiElement)
applicableSuperClasses()
and visitClass(JavaContext, UClass)
getApplicableUastTypes()
method, and then provide a UElementHandler
from the createUastHandler(JavaContext)
where you override the various applicable handler methods. This is done rather than a general visitor from the root node to avoid having to have every single lint detector (there are hundreds) do a full tree traversal on its own.Detector.UastScanner exposes the UAST API to lint checks. UAST is short for "Universal AST" and is an abstract syntax tree library which abstracts away details about Java versus Kotlin versus other similar languages and lets the client of the library access the AST in a unified way.
UAST isn't actually a full replacement for PSI; it augments PSI. Essentially, UAST is usd for the inside of methods (e.g. method bodies), and things like field initializers. PSI continues to be used at the outer level: for packages, classes, and methods (declarations and signatures). There are also wrappers around some of these for convenience.
The Detector.UastScanner interface reflects this fact. For example, when you indicate that you want to check calls to a method named foo
, the call site node is a UAST node (in this case, UCallExpression
, but the called method itself is a PsiMethod
, since that method might be anywhere (including in a library that we don't have source for, so UAST doesn't make sense.)
PsiMethod
or PsiField
back.However, the visitor methods have all changed, generally to change to UAST types. For example, the signature Detector.JavaPsiScanner.visitMethod(JavaContext, JavaElementVisitor, PsiMethodCallExpression, PsiMethod)
should be changed to visitMethod(JavaContext, UCallExpression, PsiMethod)
.
There are a bunch of new methods on classes like JavaContext
which lets you pass in a UElement
to match the existing PsiElement
methods.
If you have code which does something specific with PSI classes, the following mapping table in alphabetical order might be helpful, since it lists the corresponding UAST classes.
PSI | UAST |
---|---|
com.intellij.psi. | org.jetbrains.uast. |
IElementType | UastBinaryOperator |
PsiAnnotation | UAnnotation |
PsiAnonymousClass | UAnonymousClass |
PsiArrayAccessExpression | UArrayAccessExpression |
PsiBinaryExpression | UBinaryExpression |
PsiCallExpression | UCallExpression |
PsiCatchSection | UCatchClause |
PsiClass | UClass |
PsiClassObjectAccessExpression | UClassLiteralExpression |
PsiConditionalExpression | UIfExpression |
PsiDeclarationStatement | UDeclarationsExpression |
PsiDoWhileStatement | UDoWhileExpression |
PsiElement | UElement |
PsiExpression | UExpression |
PsiForeachStatement | UForEachExpression |
PsiIdentifier | USimpleNameReferenceExpression |
PsiIfStatement | UIfExpression |
PsiImportStatement | UImportStatement |
PsiImportStaticStatement | UImportStatement |
PsiJavaCodeReferenceElement | UReferenceExpression |
PsiLiteral | ULiteralExpression |
PsiLocalVariable | ULocalVariable |
PsiMethod | UMethod |
PsiMethodCallExpression | UCallExpression |
PsiNameValuePair | UNamedExpression |
PsiNewExpression | UCallExpression |
PsiParameter | UParameter |
PsiParenthesizedExpression | UParenthesizedExpression |
PsiPolyadicExpression | UPolyadicExpression |
PsiPostfixExpression | UPostfixExpression or UUnaryExpression |
PsiPrefixExpression | UPrefixExpression or UUnaryExpression |
PsiReference | UReferenceExpression |
PsiReference | UResolvable |
PsiReferenceExpression | UReferenceExpression |
PsiReturnStatement | UReturnExpression |
PsiSuperExpression | USuperExpression |
PsiSwitchLabelStatement | USwitchClauseExpression |
PsiSwitchStatement | USwitchExpression |
PsiThisExpression | UThisExpression |
PsiThrowStatement | UThrowExpression |
PsiTryStatement | UTryExpression |
PsiTypeCastExpression | UBinaryExpressionWithType |
PsiWhileStatement | UWhileExpression |
getUastParent
instead of getParent
. This is to avoid method name clashes on some elements which are both UAST elements and PSI elements at the same time - such as UMethod
.PsiMethod.getBody()
. This will only give you the PSI child content, which won't work for example when dealing with Kotlin methods. Normally lint passes you the UMethod which you should be procesing instead. But if for some reason you need to look up the UAST method body from a PsiMethod, use this:UastContext context = UastUtils.getUastContext(element); UExpression body = context.getMethodBody(method);Similarly if you have a
PsiField
and you want to look up its field initializer, use this:UastContext context = UastUtils.getUastContext(element); UExpression initializer = context.getInitializerBody(field);
< call.getMethodExpression().getReferenceName(); --- > call.getMethodName()
< call.getMethodExpression().getQualifierExpression(); --- > call.getReceiver()
< PsiExpression[] args = call.getArgumentList().getExpressions(); --- > List args = call.getValueArguments();Typically you also need to go through your code and replace array access,
arg[i]
, with list access, arg.get(i)
. Or in Kotlin, just arg[i]
...UastExpressionUtils
- such as UastExpressionUtils.isAssignment(UElement)
. Take a look at all the methods there now - there are methods for checking whether a call is a constructor, whether an expression is an array initializer, etc etc.ResourceReference
. Here's an example of code which has a UExpression
and wants to know it's referencing a R.styleable resource:ResourceReference reference = ResourceReference.get(expression); if (reference == null || reference.getType() != ResourceType.STYLEABLE) { return; } ...
PsiBinaryExpression
for things like checking comparator operators or arithmetic combination of operands, you can replace this with UBinaryExpression
. But you normally shouldn't; you should use UPolyadicExpression
instead. A polyadic expression is just like a binary expression, but possibly with more than two terms. With the old parser backend, an expression like "A + B + C" would be represented by nested binary expressions (first A + B, then a parent element which combined that binary expression with C). However, this will now be provided as a UPolyadicExpression
instead. And the binary case is handled trivially without the need to special case it.The following table maps some common method names and what their corresponding names are in UAST.
createPsiVisitor | createUastVisitor |
getApplicablePsiTypes | getApplicableUastTypes |
getApplicablePsiTypes | getApplicableUastTypes |
getArgumentList | getValueArguments |
getCatchSections | getCatchClauses |
getDeclaredElements | getDeclarations |
getElseBranch | getElseExpression |
getInitializer | getUastInitializer |
getLExpression | getLeftOperand |
getOperationTokenType | getOperator |
getOwner | getUastParent |
getParent | getUastParent |
getRExpression | getRightOperand |
getReturnValue | getReturnExpression |
getText | asSourceString |
getThenBranch | getThenExpression |
getType | getExpressionType |
getTypeParameters | getTypeArguments |
resolveMethod | resolve |
getApplicableUastTypes()
and then providing a visitor where they implement the corresponding visit methods. However, from these visitors you should not be calling super.visitX. To remove this whole confusion, lint now provides a separate class, UElementHandler
. For the shared traversal, just provide this handler instead and implement the appropriate visit methods. It will throw an error if you register element types in getApplicableUastTypes() that you don't override.Detector.UastScanner
so a lot of the same concepts apply; then follow the above section. PsiCodeBlock codeBlock = method.getBody(); // method: UMethod
PsiStatement[] statements = codeBlock.getStatements();
for (PsiStatement statement : statements) {}
---
But after migrating to UExpression I found there's no equivalent, so to deal with that using UastVisitor for UExpression is a way to go?
Thanks in advance,
--
You received this message because you are subscribed to the Google Groups "lint-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lint-dev+unsubscribe@googlegroups.com.
To post to this group, send email to lint...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/lint-dev/f8e591ec-d9b6-47cf-905c-8b731a49680f%40googlegroups.com.
On Sat, Sep 9, 2017 at 5:53 PM, Shintaro Katafuchi <hot.che...@gmail.com> wrote:Thank you for your great Info and work!One quick question, as for PsiMethod.getBody() migration right now I have like the following code to traverse each statements in a method to detect something.---PsiCodeBlock codeBlock = method.getBody(); // method: UMethod
PsiStatement[] statements = codeBlock.getStatements();for (PsiStatement statement : statements) {}---
But after migrating to UExpression I found there's no equivalent, so to deal with that using UastVisitor for UExpression is a way to go?On the UMethod, you can get the body by calling getUastBody().That method returns a UExpression.That's an important point -- it's not returning a block -- because in Kotlin for example a method declaration may not have a body, it may just use an expression, like this:fun double(x: Int) = x * 2Here your getUastBody() method would return the binary expression for x * 2.But in the "normal" case when you have a block, the getUastBody() method will return a UBlockExpression. That's similar to your PsiCodeBlock. UBlockExpression has a getExpressions() method which returns the expressions in the block -- that's similar to your statements above.-- Tor
Thanks in advance,
--
You received this message because you are subscribed to the Google Groups "lint-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lint-dev+u...@googlegroups.com.
To post to this group, send email to lint...@googlegroups.com.
25.3.0 is for lint from the 2.3.0 plugin, e.g. it's missing the UAST APIs etc. It's better to use 26.0.0-beta<latest>. I'm not sure those are available as javadocs, but I'm told we include source jars on maven.google.com (where the 26.* libraries are published) so javadoc should be extractable from those (in fact when IDEs show quickdocs they often pull them from the source jars, not from the javadoc jars.)
To view this discussion on the web visit https://groups.google.com/d/msgid/lint-dev/15f38654-2011-43ff-b8f1-ad12396bcba8%40googlegroups.com.