Uast: Rewrite visitor
This commit is contained in:
@@ -1454,7 +1454,7 @@ public class LintDriver {
|
||||
UastScanner scanner = (UastScanner) check;
|
||||
UastVisitor customVisitor = scanner.createUastVisitor(context);
|
||||
if (customVisitor != null) {
|
||||
UastChecker.INSTANCE.checkWithCustomHandler(
|
||||
UastChecker.INSTANCE.check(
|
||||
ideaProject, context.file, context, customVisitor);
|
||||
} else {
|
||||
UastChecker.INSTANCE.check(
|
||||
@@ -1515,7 +1515,7 @@ public class LintDriver {
|
||||
for (UastScanner detector : detectors) {
|
||||
UastVisitor customHandler = detector.createUastVisitor(context);
|
||||
if (customHandler != null) {
|
||||
checker.checkWithCustomHandler(intellijProject, file, context, customHandler);
|
||||
checker.check(intellijProject, file, context, customHandler);
|
||||
} else {
|
||||
checker.check(intellijProject, file, detector, context);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public class CallSuperDetector extends Detector implements UastScanner {
|
||||
@NonNull UFunction methodDeclaration,
|
||||
@NonNull UFunction superMethod) {
|
||||
SuperCallVisitor visitor = new SuperCallVisitor(context, superMethod);
|
||||
visitor.process(methodDeclaration);
|
||||
methodDeclaration.accept(visitor);
|
||||
return visitor.mCallsSuper;
|
||||
}
|
||||
|
||||
@@ -203,10 +203,8 @@ public class CallSuperDetector extends Detector implements UastScanner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull UElement element) {
|
||||
if (!mCallsSuper) {
|
||||
super.process(element);
|
||||
}
|
||||
public boolean visitElement(@NotNull UElement node) {
|
||||
return mCallsSuper || super.visitElement(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ public class CleanupDetector extends Detector implements UastScanner {
|
||||
}
|
||||
};
|
||||
|
||||
visitor.process(method);
|
||||
method.accept(visitor);
|
||||
if (visitor.isCleanedUp() || visitor.variableEscapes()) {
|
||||
return;
|
||||
}
|
||||
@@ -346,7 +346,7 @@ public class CleanupDetector extends Detector implements UastScanner {
|
||||
}
|
||||
};
|
||||
|
||||
commitVisitor.handle(method);
|
||||
method.accept(commitVisitor);
|
||||
if (commitVisitor.isCleanedUp() || commitVisitor.variableEscapes()) {
|
||||
return true;
|
||||
}
|
||||
@@ -476,10 +476,8 @@ public class CleanupDetector extends Detector implements UastScanner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull UElement element) {
|
||||
if (!mContainsCleanup) {
|
||||
super.process(element);
|
||||
}
|
||||
public boolean visitElement(@NotNull UElement node) {
|
||||
return mContainsCleanup || super.visitElement(node);
|
||||
}
|
||||
|
||||
protected abstract boolean isCleanupCall(@NonNull UCallExpression call);
|
||||
|
||||
@@ -179,7 +179,7 @@ public class CutPasteDetector extends Detector implements UastScanner {
|
||||
@NonNull UCallExpression from,
|
||||
@NonNull UCallExpression to) {
|
||||
ReachableVisitor visitor = new ReachableVisitor(from, to);
|
||||
visitor.process(method);
|
||||
method.accept(visitor);
|
||||
return visitor.isReachable();
|
||||
}
|
||||
|
||||
@@ -214,16 +214,16 @@ public class CutPasteDetector extends Detector implements UastScanner {
|
||||
UExpression condition = node.getCondition();
|
||||
UExpression body = node.getThenBranch();
|
||||
UElement elseBody = node.getElseBranch();
|
||||
process(condition);
|
||||
condition.accept(this);
|
||||
|
||||
if (body != null) {
|
||||
boolean wasReachable = mReachable;
|
||||
process(body);
|
||||
body.accept(this);
|
||||
mReachable = wasReachable;
|
||||
}
|
||||
if (elseBody != null) {
|
||||
boolean wasReachable = mReachable;
|
||||
process(elseBody);
|
||||
elseBody.accept(this);
|
||||
mReachable = wasReachable;
|
||||
}
|
||||
|
||||
@@ -231,10 +231,8 @@ public class CutPasteDetector extends Detector implements UastScanner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull UElement element) {
|
||||
if (!mSeenEnd) {
|
||||
super.process(element);
|
||||
}
|
||||
public boolean visitElement(@NotNull UElement node) {
|
||||
return mSeenEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1985,7 +1985,7 @@ public class IconDetector extends ResourceXmlDetector implements UastScanner {
|
||||
public boolean visitFunction(@NotNull UFunction node) {
|
||||
if (node.matchesName(ON_CREATE_OPTIONS_MENU)) {
|
||||
// Gather any R.menu references found in this method
|
||||
new MenuFinder().process(node);
|
||||
node.accept(new MenuFinder());
|
||||
}
|
||||
|
||||
return super.visitFunction(node);
|
||||
@@ -2036,7 +2036,7 @@ public class IconDetector extends ResourceXmlDetector implements UastScanner {
|
||||
UFunction method = UastUtils.getContainingFunction(node);
|
||||
if (method != null) {
|
||||
SetIconFinder finder = new SetIconFinder();
|
||||
finder.process(method);
|
||||
method.accept(finder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -324,7 +324,7 @@ public class JavaPerformanceDetector extends Detector implements UastScanner {
|
||||
UExpression thenBranch = ifNode.getThenBranch();
|
||||
if (thenBranch != null) {
|
||||
AssignmentTracker visitor = new AssignmentTracker(assignments);
|
||||
visitor.process(thenBranch);
|
||||
thenBranch.accept(visitor);
|
||||
}
|
||||
if (!assignments.isEmpty()) {
|
||||
List<String> references = new ArrayList<String>();
|
||||
|
||||
+3
-5
@@ -109,7 +109,7 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner
|
||||
UFunction method = UastUtils.getContainingFunction(node);
|
||||
if (method != null) {
|
||||
ConcreteTypeVisitor v = new ConcreteTypeVisitor(context, node);
|
||||
v.process(method);
|
||||
method.accept(v);
|
||||
resolved = v.getType();
|
||||
if (resolved == null) {
|
||||
return;
|
||||
@@ -211,10 +211,8 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull UElement element) {
|
||||
if (!mFoundCall) {
|
||||
super.process(element);
|
||||
}
|
||||
public boolean visitElement(@NotNull UElement node) {
|
||||
return mFoundCall || super.visitElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -590,13 +590,13 @@ public abstract class PermissionRequirement {
|
||||
|
||||
if (node != null) {
|
||||
final AtomicReference<UExpression> reference = new AtomicReference<UExpression>();
|
||||
new UastVisitor() {
|
||||
node.accept(new UastVisitor() {
|
||||
@Override
|
||||
public boolean visitVariable(@NotNull UVariable node) {
|
||||
reference.set(node.getInitializer());
|
||||
return true;
|
||||
}
|
||||
}.process(node);
|
||||
});
|
||||
UExpression expression = reference.get();
|
||||
if (expression != null) {
|
||||
return parse(annotation, expression);
|
||||
|
||||
@@ -389,10 +389,10 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, U
|
||||
public boolean visitCallExpression(@NotNull UCallExpression node) {
|
||||
String name = node.getFunctionName();
|
||||
if ("openFileOutput".equals(name) || "getSharedPreferences".equals(name)) {
|
||||
identifierHandler.processChildren(node);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ public class SharedPrefsDetector extends Detector implements UastScanner {
|
||||
}
|
||||
|
||||
CommitFinder finder = new CommitFinder(context, node, allowCommitBeforeTarget);
|
||||
finder.process(method);
|
||||
method.accept(finder);
|
||||
if (!finder.isCommitCalled()) {
|
||||
context.report(ISSUE, method, UastAndroidUtils.getLocation(node),
|
||||
"`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call");
|
||||
|
||||
+5
-7
@@ -1043,7 +1043,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements UastSca
|
||||
|
||||
JavaContext lintContext = context.getLintContext();
|
||||
UastStringTracker tracker = new UastStringTracker(lintContext, method, call, 0);
|
||||
tracker.process(method);
|
||||
method.accept(tracker);
|
||||
String name = tracker.getFormatStringName();
|
||||
if (name == null) {
|
||||
return;
|
||||
@@ -1314,7 +1314,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements UastSca
|
||||
@NonNull UElement call) {
|
||||
assert call instanceof UCallExpression;
|
||||
UastStringTracker tracker = new UastStringTracker(null, method, call, 0);
|
||||
tracker.process(method);
|
||||
method.accept(tracker);
|
||||
|
||||
return tracker.getFormatStringName();
|
||||
}
|
||||
@@ -1327,7 +1327,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements UastSca
|
||||
int argIndex) {
|
||||
assert call instanceof UCallExpression;
|
||||
UastStringTracker tracker = new UastStringTracker(null, method, call, argIndex);
|
||||
tracker.process(method);
|
||||
method.accept(tracker);
|
||||
|
||||
return tracker.getFormatStringName();
|
||||
}
|
||||
@@ -1481,10 +1481,8 @@ public class StringFormatDetector extends ResourceXmlDetector implements UastSca
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull UElement element) {
|
||||
if (!mDone) {
|
||||
super.process(element);
|
||||
}
|
||||
public boolean visitElement(@NotNull UElement node) {
|
||||
return mDone || super.visitElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-5
@@ -388,7 +388,7 @@ public class SupportAnnotationDetector extends Detector implements UastScanner {
|
||||
UFunction methodNode = UastUtils.getContainingFunction(node);
|
||||
if (methodNode != null) {
|
||||
CheckPermissionVisitor visitor = new CheckPermissionVisitor(node);
|
||||
visitor.process(methodNode);
|
||||
methodNode.accept(visitor);
|
||||
handlesMissingPermission = visitor.checksPermission();
|
||||
}
|
||||
}
|
||||
@@ -466,10 +466,8 @@ public class SupportAnnotationDetector extends Detector implements UastScanner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(@NotNull UElement element) {
|
||||
if (!mDone) {
|
||||
super.process(element);
|
||||
}
|
||||
public boolean visitElement(@NotNull UElement node) {
|
||||
return mDone;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -105,7 +105,7 @@ public class ToastDetector extends Detector implements UastScanner {
|
||||
|
||||
UExpression nodeWithPossibleQualifier = UastUtils.getQualifiedCallElement(node);
|
||||
ShowFinder finder = new ShowFinder(nodeWithPossibleQualifier);
|
||||
finder.process(method);
|
||||
method.accept(finder);
|
||||
if (!finder.isShowCalled()) {
|
||||
context.report(ISSUE, node, UastAndroidUtils.getLocation(node),
|
||||
"Toast created but not shown: did you forget to call `show()` ?");
|
||||
|
||||
@@ -95,7 +95,7 @@ public class ViewHolderDetector extends Detector implements UastScanner {
|
||||
public boolean visitFunction(@NotNull UFunction node) {
|
||||
if (isViewAdapterMethod(node)) {
|
||||
InflationVisitor visitor = new InflationVisitor(mContext);
|
||||
visitor.process(node);
|
||||
node.accept(visitor);
|
||||
visitor.finish();
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -38,26 +38,10 @@ interface UastAndroidContext : UastContext {
|
||||
}
|
||||
|
||||
object UastChecker {
|
||||
fun checkWithCustomHandler(
|
||||
project: Project,
|
||||
file: File,
|
||||
context: UastAndroidContext,
|
||||
visitor: UastVisitor) {
|
||||
check(project, file, context, UastCallback { visitor.handle(it) })
|
||||
}
|
||||
|
||||
fun check(project: Project, file: File, context: UastAndroidContext, callback: UastCallback) {
|
||||
fun check(project: Project, file: File, context: UastAndroidContext, visitor: UastVisitor) {
|
||||
val vfile = VirtualFileManager.getInstance().findFileByUrl("file://" + file.absolutePath) ?: return
|
||||
|
||||
val plugins = context.languagePlugins
|
||||
val additionalCheckers = plugins.fold(mutableListOf<UastAdditionalChecker>()) { list, plugin ->
|
||||
for (checker in plugin.additionalCheckers) {
|
||||
if (checker is AndroidUastAdditionalChecker) list += checker
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
val handlerWrapper = CallbackWrapper(callback, additionalCheckers, context)
|
||||
|
||||
ApplicationManager.getApplication().runReadAction {
|
||||
val psiFile = PsiManager.getInstance(project).findFile(vfile)
|
||||
@@ -66,12 +50,12 @@ object UastChecker {
|
||||
when (psiFile) {
|
||||
is PsiJavaFile -> {
|
||||
val ufile = JavaUastLanguagePlugin.converter.convertWithParent(psiFile)
|
||||
ufile?.handleTraverse(handlerWrapper)
|
||||
ufile?.accept(visitor)
|
||||
}
|
||||
else -> for (plugin in plugins) {
|
||||
val ufile = plugin.converter.convertWithParent(psiFile)
|
||||
if (ufile != null) {
|
||||
ufile.handleTraverse(handlerWrapper)
|
||||
ufile.accept(visitor)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -80,19 +64,6 @@ object UastChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private class CallbackWrapper(
|
||||
val original: UastCallback,
|
||||
val additionalCheckers: List<UastAdditionalChecker>,
|
||||
val context: UastAndroidContext
|
||||
) : UastCallback {
|
||||
override fun invoke(element: UElement) {
|
||||
original(element)
|
||||
for (checker in additionalCheckers) {
|
||||
checker(element, this, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun check(project: Project, file: File, scanner: UastScanner, context: UastAndroidContext) {
|
||||
val applicableFunctionNames = scanner.applicableFunctionNames ?: emptyList()
|
||||
val applicableSuperClasses = scanner.applicableSuperClasses ?: emptyList()
|
||||
@@ -100,57 +71,64 @@ object UastChecker {
|
||||
|
||||
val appliesToResourcesRefs = scanner.appliesToResourceRefs()
|
||||
|
||||
var callback: UastCallback?
|
||||
callback = UastCallback { element ->
|
||||
when (element) {
|
||||
is UCallExpression -> {
|
||||
if (applicableFunctionNames.isNotEmpty()) {
|
||||
if (element.kind == FUNCTION_CALL && element.functionName in applicableFunctionNames) {
|
||||
scanner.visitFunctionCall(context, element)
|
||||
}
|
||||
val visitor = object : UastVisitor() {
|
||||
override fun visitCallExpression(node: UCallExpression): Boolean {
|
||||
if (applicableFunctionNames.isNotEmpty()) {
|
||||
if (node.kind == FUNCTION_CALL && node.functionName in applicableFunctionNames) {
|
||||
scanner.visitFunctionCall(context, node)
|
||||
}
|
||||
}
|
||||
|
||||
if (applicableConstructorTypes.isNotEmpty()) {
|
||||
if (element.kind == CONSTRUCTOR_CALL) {
|
||||
element.resolve(context)?.let { constructor ->
|
||||
if (constructor.getContainingClass()?.fqName in applicableConstructorTypes) {
|
||||
scanner.visitConstructor(context, element, constructor)
|
||||
}
|
||||
if (applicableConstructorTypes.isNotEmpty()) {
|
||||
if (node.kind == CONSTRUCTOR_CALL) {
|
||||
node.resolve(context)?.let { constructor ->
|
||||
if (constructor.getContainingClass()?.fqName in applicableConstructorTypes) {
|
||||
scanner.visitConstructor(context, node, constructor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is UClass -> if (applicableSuperClasses.isNotEmpty()) {
|
||||
if (applicableSuperClasses.any { element.isSubclassOf(it) }) {
|
||||
scanner.visitClass(context, element)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun visitClass(node: UClass): Boolean {
|
||||
if (applicableSuperClasses.isNotEmpty()) {
|
||||
if (applicableSuperClasses.any { node.isSubclassOf(it) }) {
|
||||
scanner.visitClass(context, node)
|
||||
}
|
||||
}
|
||||
is UQualifiedExpression -> {
|
||||
if (appliesToResourcesRefs && element.receiver is UQualifiedExpression) {
|
||||
val parentQualifiedExpr = element.receiver as UQualifiedExpression
|
||||
val resourceName = element.selector
|
||||
val resourceType = parentQualifiedExpr.selector
|
||||
val receiver = parentQualifiedExpr.receiver
|
||||
|
||||
val receiverIsResourceClass = when (receiver) {
|
||||
is USimpleReferenceExpression -> receiver.identifier == "R"
|
||||
is UQualifiedExpression -> receiver.selectorMatches("R")
|
||||
else -> false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (resourceName is USimpleReferenceExpression && resourceType is USimpleReferenceExpression
|
||||
&& receiverIsResourceClass && receiver is UResolvable) {
|
||||
val resolvedReceiver = receiver.resolve(context)
|
||||
val isFramework = (resolvedReceiver as? UClass)?.matchesFqName("android.R") ?: false
|
||||
override fun visitQualifiedExpression(node: UQualifiedExpression): Boolean {
|
||||
if (appliesToResourcesRefs && node.receiver is UQualifiedExpression) {
|
||||
val parentQualifiedExpr = node.receiver as UQualifiedExpression
|
||||
val resourceName = node.selector
|
||||
val resourceType = parentQualifiedExpr.selector
|
||||
val receiver = parentQualifiedExpr.receiver
|
||||
|
||||
scanner.visitResourceReference(context, element, resourceType.identifier, resourceName.identifier, isFramework)
|
||||
}
|
||||
val receiverIsResourceClass = when (receiver) {
|
||||
is USimpleReferenceExpression -> receiver.identifier == "R"
|
||||
is UQualifiedExpression -> receiver.selectorMatches("R")
|
||||
else -> false
|
||||
}
|
||||
|
||||
if (resourceName is USimpleReferenceExpression && resourceType is USimpleReferenceExpression
|
||||
&& receiverIsResourceClass && receiver is UResolvable) {
|
||||
val resolvedReceiver = receiver.resolve(context)
|
||||
val isFramework = (resolvedReceiver as? UClass)?.matchesFqName("android.R") ?: false
|
||||
|
||||
scanner.visitResourceReference(context, node, resourceType.identifier, resourceName.identifier, isFramework)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
check(project, file, context, callback)
|
||||
check(project, file, context, visitor)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user