From 6b30b8ce1f6c7f5885c1652ea4ec17f284fec9c0 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 31 Mar 2016 22:19:13 +0300 Subject: [PATCH] Other code review changes --- idea/src/META-INF/android-lint.xml | 5 - .../tools/klint/client/api/LintDriver.java | 6 +- .../tools/klint/detector/api/Detector.java | 2 +- .../tools/klint/detector/api/Scope.java | 2 +- .../AddJavascriptInterfaceDetector.java | 13 +- .../tools/klint/checks/AlarmDetector.java | 4 +- .../checks/AlwaysShowActionDetector.java | 9 +- .../klint/checks/AnnotationDetector.java | 11 +- .../android/tools/klint/checks/ApiDetector.kt | 17 +- .../klint/checks/AppCompatCallDetector.java | 19 +- .../checks/AppCompatResourceDetector.java | 109 ------ .../tools/klint/checks/AssertDetector.java | 19 +- .../klint/checks/BuiltinIssueRegistry.java | 4 - .../tools/klint/checks/CallSuperDetector.java | 23 +- .../checks/CipherGetInstanceDetector.java | 9 +- .../tools/klint/checks/CleanupDetector.java | 39 +-- .../tools/klint/checks/CommentDetector.java | 2 +- .../klint/checks/CustomViewDetector.java | 9 +- .../tools/klint/checks/CutPasteDetector.java | 9 +- .../klint/checks/DateFormatDetector.java | 5 +- .../tools/klint/checks/FragmentDetector.java | 26 +- .../checks/FullBackupContentDetector.java | 234 ------------- .../klint/checks/GetSignaturesDetector.java | 7 +- .../tools/klint/checks/HandlerDetector.java | 6 +- .../tools/klint/checks/IconDetector.java | 17 +- .../klint/checks/JavaPerformanceDetector.java | 4 +- .../checks/JavaScriptInterfaceDetector.java | 34 +- .../checks/LayoutConsistencyDetector.java | 2 +- .../klint/checks/LayoutInflationDetector.java | 11 +- .../tools/klint/checks/LogDetector.java | 22 +- .../checks/MergeRootFrameLayoutDetector.java | 3 +- .../klint/checks/NfcTechListDetector.java | 110 ------ .../NonInternationalizedSmsDetector.java | 11 +- .../tools/klint/checks/OverdrawDetector.java | 11 +- .../checks/OverrideConcreteDetector.java | 2 +- .../tools/klint/checks/ParcelDetector.java | 6 +- .../tools/klint/checks/PluralsDatabase.java | 329 ------------------ .../klint/checks/PrivateResourceDetector.java | 4 +- .../klint/checks/RegistrationDetector.java | 263 -------------- .../checks/RequiredAttributeDetector.java | 2 +- .../tools/klint/checks/RtlDetector.java | 7 +- .../tools/klint/checks/SQLiteDetector.java | 7 +- .../tools/klint/checks/SdCardDetector.java | 7 +- .../tools/klint/checks/SecurityDetector.java | 7 +- .../klint/checks/ServiceCastDetector.java | 7 +- .../checks/SetJavaScriptEnabledDetector.java | 7 +- .../klint/checks/SharedPrefsDetector.java | 37 +- .../klint/checks/StringFormatDetector.java | 4 +- .../checks/SupportAnnotationDetector.java | 71 +--- .../tools/klint/checks/TitleDetector.java | 114 ------ .../tools/klint/checks/ToastDetector.java | 17 +- .../klint/checks/ViewConstructorDetector.java | 6 +- .../klint/checks/ViewHolderDetector.java | 31 +- .../tools/klint/checks/ViewTypeDetector.java | 67 ++-- .../tools/klint/checks/WrongCallDetector.java | 7 +- .../klint/checks/WrongImportDetector.java | 5 +- .../AndroidInspectionExtensionsFactory.java | 16 - .../klint/AndroidLintExternalAnnotator.java | 41 +-- .../AndroidLintGlobalInspectionContext.java | 16 - .../klint/AndroidLintInspectionBase.java | 31 +- .../AndroidLintInspectionToolProvider.java | 46 --- .../klint/AndroidLintQuickFix.java | 26 +- .../inspections/klint/AndroidLintUtil.java | 16 - .../inspections/klint/AndroidModelFacade.kt | 32 -- .../klint/AndroidQuickfixContexts.java | 16 - .../inspections/klint/DomPsiParser.java | 16 +- .../inspections/klint/IntellijLintClient.java | 155 ++++----- .../klint/IntellijLintIssueRegistry.java | 10 +- .../klint/IntellijLintRequest.java | 8 +- .../inspections/klint/IntellijLintUtils.java | 253 +------------- .../klint/IntellijRegistrationDetector.java | 125 ------- .../klint/IntellijViewTypeDetector.java | 4 +- .../inspections/klint/ProblemData.java | 16 - .../android/inspections/klint/State.java | 16 - .../klint/SuppressLintIntentionAction.java | 276 --------------- .../org/jetbrains/uast/check/UastChecker.kt | 2 +- .../org/jetbrains/uast/check/UastScanner.java | 2 +- .../src/org/jetbrains/uast/UastUtils.kt | 122 ++++++- .../src/org/jetbrains/uast/UastVisitor.kt | 56 ++- .../uast/baseElements/UAnnotation.kt | 39 ++- .../jetbrains/uast/baseElements/UElement.kt | 116 +++++- .../uast/baseElements/UExpression.kt | 3 +- .../org/jetbrains/uast/baseElements/UFile.kt | 23 +- .../controlStructures/UDoWhileExpression.kt | 13 + .../controlStructures/UForEachExpression.kt | 17 + .../uast/controlStructures/UForExpression.kt | 21 ++ .../uast/controlStructures/UIfExpression.kt | 34 +- .../uast/controlStructures/ULoopExpression.kt | 6 + .../controlStructures/USwitchExpression.kt | 69 +++- .../uast/controlStructures/UTryExpression.kt | 57 ++- .../controlStructures/UWhileExpression.kt | 13 + .../org/jetbrains/uast/declarations/UClass.kt | 81 ++++- .../uast/declarations/UDeclaration.kt | 24 +- .../jetbrains/uast/declarations/UFunction.kt | 91 +++-- .../uast/declarations/UImportStatement.kt | 22 +- .../org/jetbrains/uast/declarations/UType.kt | 65 +++- .../jetbrains/uast/declarations/UVariable.kt | 51 ++- .../expressions/UArrayAccessExpression.kt | 11 + .../uast/expressions/UBinaryExpression.kt | 15 + .../expressions/UBinaryExpressionWithType.kt | 17 +- .../uast/expressions/UBlockExpression.kt | 7 + .../uast/expressions/UBreakExpression.kt | 37 ++ .../uast/expressions/UCallExpression.kt | 107 +++++- .../UCallableReferenceExpression.kt | 36 +- .../expressions/UClassLiteralExpression.kt | 9 +- .../uast/expressions/UContinueExpression.kt | 37 ++ .../expressions/UDeclarationsExpression.kt | 11 + .../uast/expressions/UExpressionList.kt | 27 +- .../uast/expressions/ULabeledExpression.kt | 11 + .../uast/expressions/ULambdaExpression.kt | 11 + .../uast/expressions/ULiteralExpression.kt | 32 +- .../uast/expressions/UNamedExpression.kt | 1 + .../expressions/UObjectLiteralExpression.kt | 7 + .../expressions/UParenthesizedExpression.kt | 7 + .../uast/expressions/UQualifiedExpression.kt | 29 ++ .../uast/expressions/UReturnExpression.kt | 38 ++ .../expressions/USimpleReferenceExpression.kt | 7 + .../uast/expressions/USuperExpression.kt | 5 + .../uast/expressions/UThisExpression.kt | 5 + .../uast/expressions/UThrowExpression.kt | 39 +++ .../uast/expressions/UUnaryExpression.kt | 4 +- .../uast/expressions/uastLiteralUtils.kt | 33 +- .../uast/internal/internalUastUtils.kt | 13 +- .../kinds/UastBinaryExpressionWithTypeKind.kt | 4 + .../uast/kinds/UastBinaryOperator.kt | 7 +- .../org/jetbrains/uast/kinds/UastCallKind.kt | 8 +- .../org/jetbrains/uast/kinds/UastClassKind.kt | 7 +- .../jetbrains/uast/kinds/UastFunctionKind.kt | 3 + .../org/jetbrains/uast/kinds/UastModifier.kt | 11 +- .../org/jetbrains/uast/kinds/UastOperator.kt | 10 +- .../uast/kinds/UastPostfixOperator.kt | 7 +- .../uast/kinds/UastPrefixOperator.kt | 5 +- .../UastQualifiedExpressionAccessType.kt | 6 +- .../uast/kinds/UastSpecialExpressionKind.kt | 19 +- .../jetbrains/uast/kinds/UastVariableKind.kt | 5 +- .../jetbrains/uast/kinds/UastVisibility.kt | 5 +- .../uast/java/JavaUastLanguagePlugin.kt | 39 +-- .../JavaUSwitchExpression.kt | 21 +- .../JavaClassInitializerUFunction.kt | 11 +- .../uast/java/declarations/JavaUAnnotation.kt | 2 +- .../uast/java/declarations/JavaUClass.kt | 19 +- .../uast/java/declarations/JavaUFunction.kt | 2 +- .../java/declarations/JavaUImportStatement.kt | 4 - .../JavaUStaticImportStatement.kt | 4 - .../uast/java/declarations/JavaUType.kt | 4 + .../java/expressions/JavaUAssertExpression.kt | 59 ++++ .../expressions/JavaUAssignmentExpression.kt | 6 +- .../java/expressions/JavaUBreakExpression.kt} | 27 +- .../JavaUCallableReferenceExpression.kt | 7 + .../expressions/JavaUContinueExpression.kt} | 15 +- .../java/expressions/JavaULambdaExpression.kt | 4 +- .../expressions/JavaULiteralExpression.kt | 3 +- .../expressions/JavaUPostfixExpression.kt | 7 +- .../java/expressions/JavaUPrefixExpression.kt | 15 +- .../expressions/JavaUQualifiedExpression.kt | 2 +- .../java/expressions/JavaUReturnExpression.kt | 29 ++ .../JavaUSynchronizedExpression.kt | 44 +++ .../JavaUThrowExpression.kt} | 19 +- .../java/expressions/javaUCallExpressions.kt | 11 +- .../java/internal/javaInternalUastUtils.kt | 8 +- .../uast/java/kinds/JavaFunctionKinds.kt | 1 + .../uast/java/kinds/JavaUastCallKinds.kt | 7 +- .../kotlin/uast/KotlinUastLanguagePlugin.kt | 27 +- .../KotlinParameterUTypeReference.kt | 2 +- .../uast/declarations/KotlinUAnnotation.kt | 3 +- .../declarations/KotlinUAnnotationList.kt | 1 + .../kotlin/uast/declarations/KotlinUClass.kt | 25 +- .../declarations/KotlinUImportStatement.kt | 5 - .../kotlin/uast/declarations/KotlinUType.kt | 10 +- .../uast/declarations/KotlinUVariable.kt | 31 +- .../uast/declarations/kotlinUFunctions.kt | 15 +- .../expressions/KotlinUBinaryExpression.kt | 4 +- .../expressions/KotlinUBreakExpression.kt | 31 ++ .../KotlinUCallableReferenceExpression.kt | 35 ++ .../expressions/KotlinUContinueExpression.kt | 31 ++ .../KotlinUFunctionCallExpression.kt | 68 +--- .../expressions/KotlinULabeledExpression.kt | 32 ++ .../expressions/KotlinULiteralExpression.kt | 13 +- .../expressions/KotlinUPrefixExpression.kt | 1 + .../expressions/KotlinUQualifiedExpression.kt | 10 +- .../expressions/KotlinUReturnExpression.kt | 29 ++ .../KotlinUSimpleReferenceExpression.kt | 5 +- .../expressions/KotlinUSwitchExpression.kt | 35 +- .../expressions/KotlinUThrowExpression.kt | 29 ++ .../uast/internal/kotlinInternalUastUtils.kt | 37 +- .../uast/kinds/KotlinBinaryOperators.kt | 3 + .../kotlin/uast/kinds/KotlinClassKinds.kt | 8 +- ...pertyAsCallAndroidUastAdditionalChecker.kt | 5 +- .../uast-kotlin/testData/ControlStructures.kt | 156 ++++++--- plugins/uast-kotlin/testData/Declarations.kt | 13 + .../uast-kotlin/testData/log/Declarations.txt | 24 +- plugins/uast-kotlin/testData/log/Simple.txt | 4 +- .../testData/render/Declarations.txt | 31 +- .../uast-kotlin/testData/render/Simple.txt | 4 +- 194 files changed, 2538 insertions(+), 3079 deletions(-) delete mode 100644 plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java delete mode 100644 plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java delete mode 100644 plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java delete mode 100644 plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java delete mode 100644 plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java delete mode 100644 plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java delete mode 100644 plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java delete mode 100644 plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java create mode 100644 plugins/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt create mode 100644 plugins/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt create mode 100644 plugins/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt create mode 100644 plugins/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt create mode 100644 plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt rename plugins/{uast-common/src/org/jetbrains/uast/kinds/UastImportKind.kt => uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt} (57%) rename plugins/{lint/uast-android/src/org/jetbrains/uast/check/AndroidUastAdditionalChecker.kt => uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt} (56%) create mode 100644 plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt create mode 100644 plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt rename plugins/uast-java/src/org/jetbrains/uast/java/{kinds/JavaSpecialExpressionKinds.kt => expressions/JavaUThrowExpression.kt} (55%) create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBreakExpression.kt create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUCallableReferenceExpression.kt create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUContinueExpression.kt create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULabeledExpression.kt create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUReturnExpression.kt create mode 100644 plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUThrowExpression.kt diff --git a/idea/src/META-INF/android-lint.xml b/idea/src/META-INF/android-lint.xml index 52523fcf0b3..6fbdff07054 100644 --- a/idea/src/META-INF/android-lint.xml +++ b/idea/src/META-INF/android-lint.xml @@ -5,7 +5,6 @@ - @@ -17,7 +16,6 @@ - @@ -43,11 +41,9 @@ - - @@ -56,7 +52,6 @@ - diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java index b58244ea01e..f85ed599246 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/client/api/LintDriver.java @@ -1044,7 +1044,7 @@ public class LintDriver { } for (File file : files) { Context context = new Context(this, project, main, file); - fireEvent(EventType.SCANNING_FILE, context); + fireEvent(LintListener.EventType.SCANNING_FILE, context); for (Detector detector : detectors) { if (detector.appliesTo(context, file)) { detector.beforeCheckFile(context); @@ -1437,7 +1437,7 @@ public class LintDriver { if (!sources.isEmpty()) { List contexts = Lists.newArrayListWithExpectedSize(sources.size()); for (File file : sources) { - JavaContext context = new JavaContext(this, project, main, file, javaParser); + JavaContext context = new JavaContext(this, project, main, file); contexts.add(context); } @@ -2377,7 +2377,7 @@ public class LintDriver { return true; } } else if (valueNode instanceof UCallExpression - && ((UCallExpression)valueNode).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) { + && ((UCallExpression)valueNode).getKind() == UastCallKind.ARRAY_INITIALIZER) { UCallExpression array = (UCallExpression) valueNode; List expressions = array.getValueArguments(); for (UExpression arrayElement : expressions) { diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java index 47ad4701343..41914c0564c 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Detector.java @@ -510,7 +510,7 @@ public abstract class Detector { return null; } - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { } diff --git a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java index 4928db54697..c3320a25694 100644 --- a/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java +++ b/plugins/lint/lint-api/src/com/android/tools/klint/detector/api/Scope.java @@ -226,7 +226,7 @@ public enum Scope { /** Scope-set used for detectors which scan all resources */ public static final EnumSet ALL_RESOURCES_SCOPE = EnumSet.of(ALL_RESOURCE_FILES); /** Scope-set used for detectors which are affected by a single Java source file */ - public static final EnumSet JAVA_FILE_SCOPE = EnumSet.of(SOURCE_FILE); + public static final EnumSet SOURCE_FILE_SCOPE = EnumSet.of(SOURCE_FILE); /** Scope-set used for detectors which are affected by a single Java class file */ public static final EnumSet CLASS_FILE_SCOPE = EnumSet.of(CLASS_FILE); /** Scope-set used for detectors which are affected by a single Gradle build file */ diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java index 52367921a96..4be34a872a7 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AddJavascriptInterfaceDetector.java @@ -34,7 +34,6 @@ import com.android.tools.klint.detector.api.Scope; import com.android.tools.klint.detector.api.Severity; import com.android.tools.klint.detector.api.Speed; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -54,21 +53,21 @@ public class AddJavascriptInterfaceDetector extends Detector implements UastScan Severity.WARNING, new Implementation( AddJavascriptInterfaceDetector.class, - Scope.JAVA_FILE_SCOPE)). + Scope.SOURCE_FILE_SCOPE)). addMoreInfo( "https://labs.mwrinfosecurity.com/blog/2013/09/24/webview-addjavascriptinterface-remote-code-execution/"); private static final String WEB_VIEW = "android.webkit.WebView"; //$NON-NLS-1$ private static final String ADD_JAVASCRIPT_INTERFACE = "addJavascriptInterface"; //$NON-NLS-1$ - // ---- Implements UastScanner ---- - @NonNull @Override public Speed getSpeed() { return Speed.FAST; } + // ---- Implements UastScanner ---- + @Nullable @Override public List getApplicableFunctionNames() { @@ -76,11 +75,13 @@ public class AddJavascriptInterfaceDetector extends Detector implements UastScan } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { + // Ignore the issue if we never build for any API less than 17. if (context.getLintContext().getMainProject().getMinSdk() >= 17) { return; } + // Ignore if the method doesn't fit our description. UFunction resolvedFunction = node.resolveOrEmpty(context); UClass containingClass = UastUtils.getContainingClassOrEmpty(resolvedFunction); if (!containingClass.isSubclassOf(WEB_VIEW)) { @@ -95,6 +96,6 @@ public class AddJavascriptInterfaceDetector extends Detector implements UastScan String message = "`WebView.addJavascriptInterface` should not be called with minSdkVersion < 17 for security reasons: " + "JavaScript can use reflection to manipulate application"; - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + context.report(ISSUE, node, context.getLocation(node), message); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java index 27a3dfdeca3..fbc3d29b70a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlarmDetector.java @@ -41,7 +41,7 @@ public class AlarmDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( AlarmDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Alarm set too soon/frequently */ public static final Issue ISSUE = Issue.create( @@ -84,7 +84,7 @@ public class AlarmDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { UFunction setRepeatingFunction = node.resolve(context); UClass containingClass = UastUtils.getContainingClassOrEmpty(setRepeatingFunction); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java index 33956b1f387..2d75b789934 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AlwaysShowActionDetector.java @@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.UElement; import org.jetbrains.uast.UQualifiedExpression; import org.jetbrains.uast.USimpleReferenceExpression; +import org.jetbrains.uast.UastUtils; import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -220,22 +221,22 @@ public class AlwaysShowActionDetector extends ResourceXmlDetector implements Uas boolean isIfRoom = description.equals("SHOW_AS_ACTION_IF_ROOM"); //$NON-NLS-1$ boolean isAlways = description.equals("SHOW_AS_ACTION_ALWAYS"); //$NON-NLS-1$ if ((isIfRoom || isAlways) - && node.getReceiver().renderString().equals("MenuItem")) { //$NON-NLS-1$ + && UastUtils.endsWithQualified(node.getReceiver(), "MenuItem")) { //$NON-NLS-1$ if (isAlways) { JavaContext lintContext = mContext.getLintContext(); if (lintContext.getDriver().isSuppressed(lintContext, ISSUE, node)) { - return false; + return super.visitQualifiedExpression(node); } if (mAlwaysFields == null) { mAlwaysFields = new ArrayList(); } - mAlwaysFields.add(UastAndroidUtils.getLocation(node)); + mAlwaysFields.add(mContext.getLocation(node)); } else { mHasIfRoomRefs = true; } } - return false; + return super.visitQualifiedExpression(node); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java index 7666136e7c1..f239e076f1a 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AnnotationDetector.java @@ -35,7 +35,6 @@ import java.io.File; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.java.JavaUastCallKinds; import org.jetbrains.uast.visitor.UastVisitor; @@ -62,7 +61,7 @@ public class AnnotationDetector extends Detector implements UastScanner { Severity.ERROR, new Implementation( AnnotationDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link AnnotationDetector} check */ public AnnotationDetector() { @@ -108,7 +107,7 @@ public class AnnotationDetector extends Detector implements UastScanner { return super.visitAnnotation(node); } } else if (valueNode instanceof UCallExpression && - ((UCallExpression) valueNode).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) { + ((UCallExpression) valueNode).getKind() == UastCallKind.ARRAY_INITIALIZER) { UCallExpression array = (UCallExpression) valueNode; if (array.getValueArgumentCount() == 0) { continue; @@ -126,7 +125,7 @@ public class AnnotationDetector extends Detector implements UastScanner { } } - return false; + return super.visitAnnotation(node); } private boolean checkId(UAnnotation node, String id) { @@ -143,7 +142,7 @@ public class AnnotationDetector extends Detector implements UastScanner { if (parent instanceof UFunction || parent instanceof UBlockExpression) { break; } else if (issue == ApiDetector.UNSUPPORTED && parent instanceof UDeclarationsExpression) { - UDeclarationsExpression declarations = (UDeclarationsExpression)parent; + UDeclarationsExpression declarations = (UDeclarationsExpression) parent; for (UVariable var : declarations.getVariables()) { if (var.getKind() != UastVariableKind.MEMBER && var.getInitializer() instanceof UQualifiedExpression) { return true; @@ -158,7 +157,7 @@ public class AnnotationDetector extends Detector implements UastScanner { // This issue doesn't have AST access: annotations are not // available for local variables or parameters - mContext.report(ISSUE, node, UastAndroidUtils.getLocation(node), String.format( + mContext.report(ISSUE, node, mContext.getLocation(node), String.format( "The `@SuppressLint` annotation cannot be used on a local " + "variable with the lint check '%1$s': move out to the " + "surrounding method", id)); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt index 7e1ade99d44..ace616590f8 100755 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ApiDetector.kt @@ -199,7 +199,8 @@ open class ApiDetector : Detector(), UastScanner { companion object { private val AOSP_BUILD = System.getenv("ANDROID_BUILD_TOP") != null - private val SDK_INT_FQNAME = "android.os.Build.VERSION.SDK_INT" + private val SDK_INT_CONTAINING = "android.os.Build.VERSION" + private val SDK_INT = "SDK_INT" tailrec fun getLocalMinSdk(scope: UElement?): Int { if (scope == null) return -1 @@ -223,7 +224,7 @@ open class ApiDetector : Detector(), UastScanner { val value = (valueNode as ULiteralExpression).value as String return SdkVersionInfo.getApiByBuildCode(value, true) } else if (valueNode is UQualifiedExpression) { - val codename = valueNode.selector.renderString() + val codename = valueNode.getSelectorAsIdentifier(); return SdkVersionInfo.getApiByBuildCode(codename, true) } else if (valueNode is USimpleReferenceExpression) { val codename = valueNode.identifier; @@ -239,8 +240,8 @@ open class ApiDetector : Detector(), UastScanner { fun isCheckedExplicitly(context: UastAndroidContext, requiredVersion: Int, node: UElement): Boolean { tailrec fun UExpression.isSdkIntReference(): Boolean = when (this) { is UParenthesizedExpression -> expression.isSdkIntReference() - is USimpleReferenceExpression -> resolve(context)?.fqName == SDK_INT_FQNAME - is UQualifiedExpression -> resolve(context)?.fqName == SDK_INT_FQNAME + is USimpleReferenceExpression -> resolve(context)?.matchesNameWithContaining(SDK_INT_CONTAINING, SDK_INT) ?: false + is UQualifiedExpression -> resolve(context)?.matchesNameWithContaining(SDK_INT_CONTAINING, SDK_INT) ?: false else -> false } @@ -305,8 +306,8 @@ open class ApiDetector : Detector(), UastScanner { else check(node.parent, node, context) } - is UExpressionSwitchClauseExpression -> { - if (checkCondition(node.caseValue) == true) + is USwitchClauseExpression -> { + if (node.caseValues?.any { checkCondition(it) == true } ?: false) true else check(node.parent, node, context) @@ -400,7 +401,7 @@ open class ApiDetector : Detector(), UastScanner { Severity.WARNING, Implementation( ApiDetector::class.java, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) /** Accessing an unsupported API */ @JvmField @@ -429,6 +430,6 @@ open class ApiDetector : Detector(), UastScanner { Severity.ERROR, Implementation( ApiDetector::class.java, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) } } \ No newline at end of file diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java index c6bd5cbe2e4..062e85d256f 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatCallDetector.java @@ -40,7 +40,6 @@ import org.jetbrains.uast.UClass; import org.jetbrains.uast.UFunction; import org.jetbrains.uast.UastUtils; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; public class AppCompatCallDetector extends Detector implements UastScanner { @@ -53,7 +52,7 @@ public class AppCompatCallDetector extends Detector implements UastScanner { Category.CORRECTNESS, 6, Severity.WARNING, new Implementation( AppCompatCallDetector.class, - Scope.JAVA_FILE_SCOPE)). + Scope.SOURCE_FILE_SCOPE)). addMoreInfo("http://developer.android.com/tools/support-library/index.html"); private static final String GET_ACTION_BAR = "getActionBar"; @@ -94,26 +93,26 @@ public class AppCompatCallDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { if (mDependsOnAppCompat && isAppBarActivityCall(context, node)) { String replace = null; - if (node.functionNameMatches(GET_ACTION_BAR)) { + if (node.matchesFunctionName(GET_ACTION_BAR)) { replace = "getSupportActionBar"; - } else if (node.functionNameMatches(START_ACTION_MODE)) { + } else if (node.matchesFunctionName(START_ACTION_MODE)) { replace = "startSupportActionMode"; - } else if (node.functionNameMatches(SET_PROGRESS_BAR_VIS)) { + } else if (node.matchesFunctionName(SET_PROGRESS_BAR_VIS)) { replace = "setSupportProgressBarVisibility"; - } else if (node.functionNameMatches(SET_PROGRESS_BAR_IN_VIS)) { + } else if (node.matchesFunctionName(SET_PROGRESS_BAR_IN_VIS)) { replace = "setSupportProgressBarIndeterminateVisibility"; - } else if (node.functionNameMatches(SET_PROGRESS_BAR_INDETERMINATE)) { + } else if (node.matchesFunctionName(SET_PROGRESS_BAR_INDETERMINATE)) { replace = "setSupportProgressBarIndeterminate"; - } else if (node.functionNameMatches(REQUEST_WINDOW_FEATURE)) { + } else if (node.matchesFunctionName(REQUEST_WINDOW_FEATURE)) { replace = "supportRequestWindowFeature"; } if (replace != null) { String message = String.format(ERROR_MESSAGE_FORMAT, replace, node.getFunctionName()); - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + context.report(ISSUE, node, context.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java deleted file mode 100644 index bd713d3a57d..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppCompatResourceDetector.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.klint.checks; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_SHOW_AS_ACTION; - -import com.android.annotations.NonNull; -import com.android.resources.ResourceFolderType; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.Project; -import com.android.tools.klint.detector.api.ResourceXmlDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.Speed; -import com.android.tools.klint.detector.api.XmlContext; - -import org.jetbrains.uast.check.UastScanner; -import org.w3c.dom.Attr; - -import java.util.Collection; -import java.util.Collections; - -/** - * Check that the right namespace is used for app compat menu items - * - * Using app:showAsAction instead of android:showAsAction leads to problems, but - * isn't caught by the API Detector since it's not in the Android namespace. - */ -public class AppCompatResourceDetector extends ResourceXmlDetector implements UastScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "AppCompatResource", //$NON-NLS-1$ - "Menu namespace", - - "When using the appcompat library, menu resources should refer to the " + - "`showAsAction` in the `app:` namespace, not the `android:` namespace.\n" + - "\n" + - "Similarly, when *not* using the appcompat library, you should be using " + - "the `android:showAsAction` attribute.", - - Category.USABILITY, - 5, - Severity.ERROR, - new Implementation( - AppCompatResourceDetector.class, - Scope.RESOURCE_FILE_SCOPE)); - - /** Constructs a new {@link com.android.tools.lint.checks.AppCompatResourceDetector} */ - public AppCompatResourceDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.MENU; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection getApplicableAttributes() { - return Collections.singletonList(ATTR_SHOW_AS_ACTION); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - Project mainProject = context.getMainProject(); - if (mainProject.isGradleProject()) { - Boolean appCompat = mainProject.dependsOn("com.android.support:appcompat-v7"); - if (ANDROID_URI.equals(attribute.getNamespaceURI())) { - if (context.getFolderVersion() >= 14) { - return; - } - if (appCompat == Boolean.TRUE) { - context.report(ISSUE, attribute, - context.getLocation(attribute), - "Should use `app:showAsAction` with the appcompat library with " - + "`xmlns:app=\"http://schemas.android.com/apk/res-auto\"`"); - } - } else { - if (appCompat == Boolean.FALSE) { - context.report(ISSUE, attribute, - context.getLocation(attribute), - "Should use `android:showAsAction` when not using the appcompat library"); - } - } - } - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java index 128fe9315ab..01835b3f725 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AssertDetector.java @@ -30,9 +30,8 @@ import java.io.File; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.java.JavaSpecialExpressionKinds; +import org.jetbrains.uast.java.JavaUAssertExpression; import org.jetbrains.uast.visitor.UastVisitor; import static org.jetbrains.uast.UastLiteralUtils.isNullLiteral; @@ -65,7 +64,7 @@ public class AssertDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( AssertDetector.class, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) .addMoreInfo( "https://code.google.com/p/android/issues/detail?id=65183"); //$NON-NLS-1$ @@ -84,8 +83,12 @@ public class AssertDetector extends Detector implements UastScanner { public UastVisitor createUastVisitor(final UastAndroidContext context) { return new UastVisitor() { @Override - public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) { - if (node.getKind() != JavaSpecialExpressionKinds.ASSERT) { + public boolean visitCallExpression(@NotNull UCallExpression node) { + if (!(node instanceof JavaUAssertExpression)) { + return true; + } + + if (node.getValueArgumentCount() < 1) { return true; } @@ -93,7 +96,7 @@ public class AssertDetector extends Detector implements UastScanner { return true; } - UExpression assertion = node.firstOrNull(); + UExpression assertion = node.getValueArguments().get(0); // Allow "assert true"; it's basically a no-op if (assertion instanceof ULiteralExpression) { ULiteralExpression literal = (ULiteralExpression) assertion; @@ -114,8 +117,8 @@ public class AssertDetector extends Detector implements UastScanner { } } String message - = "Assertions are unreliable. Use `BuildConfig.DEBUG` conditional checks instead."; - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + = "Assertions are unreliable. Use `BuildConfig.DEBUG` conditional checks instead."; + context.report(ISSUE, node, context.getLocation(node), message); return false; } }; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java index 518229c1867..20baa421d64 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java @@ -43,7 +43,6 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(ApiDetector.OVERRIDE); issues.add(ApiDetector.UNSUPPORTED); issues.add(AppCompatCallDetector.ISSUE); - issues.add(AppCompatResourceDetector.ISSUE); issues.add(AssertDetector.ISSUE); issues.add(CallSuperDetector.ISSUE); issues.add(CipherGetInstanceDetector.ISSUE); @@ -54,7 +53,6 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(CustomViewDetector.ISSUE); issues.add(CutPasteDetector.ISSUE); issues.add(DateFormatDetector.DATE_FORMAT); - issues.add(FullBackupContentDetector.ISSUE); issues.add(FragmentDetector.ISSUE); issues.add(GetSignaturesDetector.ISSUE); issues.add(HandlerDetector.ISSUE); @@ -82,7 +80,6 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(LogDetector.LONG_TAG); issues.add(LogDetector.WRONG_TAG); issues.add(MergeRootFrameLayoutDetector.ISSUE); - issues.add(NfcTechListDetector.ISSUE); issues.add(NonInternationalizedSmsDetector.ISSUE); issues.add(OverdrawDetector.ISSUE); issues.add(OverrideConcreteDetector.ISSUE); @@ -117,7 +114,6 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(SupportAnnotationDetector.RESOURCE_TYPE); issues.add(SupportAnnotationDetector.THREAD); issues.add(SupportAnnotationDetector.TYPE_DEF); - issues.add(TitleDetector.ISSUE); issues.add(ToastDetector.ISSUE); issues.add(UnusedResourceDetector.ISSUE); issues.add(UnusedResourceDetector.ISSUE_IDS); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java index a62ba8bb821..9c16fa762e9 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CallSuperDetector.java @@ -37,7 +37,6 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -51,7 +50,7 @@ public class CallSuperDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( CallSuperDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Missing call to super */ public static final Issue ISSUE = Issue.create( @@ -103,7 +102,7 @@ public class CallSuperDetector extends Detector implements UastScanner { String methodName = declaration.getName(); String message = "Overriding method should call `super." + methodName + "`"; - Location location = UastAndroidUtils.getLocation(declaration.getNameElement()); + Location location = context.getLocation(declaration.getNameElement()); context.report(ISSUE, declaration, location, message); } } @@ -148,12 +147,12 @@ public class CallSuperDetector extends Detector implements UastScanner { UFunction superMethod = directSuper; while (superMethod != null) { for (UAnnotation annotation : superMethod.getAnnotations()) { - annotation = SupportAnnotationDetector.getRelevantAnnotation(context, annotation); + annotation = SupportAnnotationDetector.getRelevantAnnotation(annotation, context); if (annotation != null) { - String signature = annotation.getFqName(); - if (CALL_SUPER_ANNOTATION.equals(signature)) { + String fqName = annotation.getFqName(); + if (CALL_SUPER_ANNOTATION.equals(fqName)) { return directSuper; - } else if (signature != null && signature.endsWith(".OverrideMustInvoke")) { + } else if (fqName != null && fqName.endsWith(".OverrideMustInvoke")) { // Handle findbugs annotation on the fly too return directSuper; } @@ -169,7 +168,8 @@ public class CallSuperDetector extends Detector implements UastScanner { /** Visits a method and determines whether the method calls its super method */ private static class SuperCallVisitor extends UastVisitor { private final UastAndroidContext mContext; - private final String mMethodFqName; + private final String mMethodContainingClassFqName; + private final String mMethodName; private boolean mCallsSuper; public static boolean callsSuper( @@ -181,9 +181,10 @@ public class CallSuperDetector extends Detector implements UastScanner { return visitor.mCallsSuper; } - private SuperCallVisitor(@NonNull UastAndroidContext context, @NonNull UFunction method) { + private SuperCallVisitor(@NonNull UastAndroidContext context, @NonNull UFunction function) { mContext = context; - mMethodFqName = UastUtils.getFqName(method); + mMethodContainingClassFqName = UastUtils.getContainingClassOrEmpty(function).getFqName(); + mMethodName = function.getName(); } @Override @@ -193,7 +194,7 @@ public class CallSuperDetector extends Detector implements UastScanner { if (receiver instanceof USuperExpression && selector instanceof UCallExpression) { UFunction resolvedFunction = ((UCallExpression) selector).resolve(mContext); - if (mMethodFqName.equals(UastUtils.getFqName(resolvedFunction))) { + if (resolvedFunction != null && resolvedFunction.matchesNameWithContaining(mMethodContainingClassFqName, mMethodName)) { mCallsSuper = true; return true; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java index 06a49cd44e2..39292c629da 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CipherGetInstanceDetector.java @@ -32,7 +32,6 @@ import java.util.Set; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; /** @@ -50,7 +49,7 @@ public class CipherGetInstanceDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( CipherGetInstanceDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); private static final String CIPHER = "javax.crypto.Cipher"; //$NON-NLS-1$ private static final String GET_INSTANCE = "getInstance"; //$NON-NLS-1$ @@ -72,7 +71,7 @@ public class CipherGetInstanceDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { UClass containingClass = UastUtils.getContainingClass(node); if (containingClass == null || !containingClass.isSubclassOf(CIPHER)) { return; @@ -107,13 +106,13 @@ public class CipherGetInstanceDetector extends Detector implements UastScanner { if (ALGORITHM_ONLY.contains(value)) { String message = "`Cipher.getInstance` should not be called without setting the" + " encryption mode and padding"; - context.report(ISSUE, call, UastAndroidUtils.getLocation(arg), message); + context.report(ISSUE, call, context.getLocation(arg), message); } else if (value.contains(ECB)) { String message = "ECB encryption mode should not be used"; if (includeValue) { message += " (was \"" + value + "\")"; } - context.report(ISSUE, call, UastAndroidUtils.getLocation(arg), message); + context.report(ISSUE, call, context.getLocation(arg), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java index 8cfd23ac323..1f35dd8b398 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CleanupDetector.java @@ -35,7 +35,6 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -47,7 +46,7 @@ public class CleanupDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( CleanupDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Problems with missing recycle calls */ public static final Issue RECYCLE_RESOURCE = Issue.create( @@ -156,8 +155,8 @@ public class CleanupDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { - if (node.functionNameMatches(BEGIN_TRANSACTION)) { + public void visitCall(UastAndroidContext context, UCallExpression node) { + if (node.matchesFunctionName(BEGIN_TRANSACTION)) { checkTransactionCommits(context, node); } else { checkResourceRecycled(context, node, node.getFunctionName()); @@ -245,7 +244,7 @@ public class CleanupDetector extends Detector implements UastScanner { FinishVisitor visitor = new FinishVisitor(context, boundVariable) { @Override protected boolean isCleanupCall(@NonNull UCallExpression call) { - if (!call.functionNameMatches(recycleName)) { + if (!call.matchesFunctionName(recycleName)) { return false; } UDeclaration resolved = call.resolve(mContext); @@ -286,7 +285,7 @@ public class CleanupDetector extends Detector implements UastScanner { } UElement locationNode = node instanceof UCallExpression ? ((UCallExpression) node).getFunctionNameElement() : node; - Location location = UastAndroidUtils.getLocation(locationNode); + Location location = context.getLocation(locationNode); context.report(RECYCLE_RESOURCE, node, location, message); } @@ -353,7 +352,7 @@ public class CleanupDetector extends Detector implements UastScanner { } String message = "This transaction should be completed with a `commit()` call"; - context.report(COMMIT_FRAGMENT, node, UastAndroidUtils.getLocation(node.getFunctionReference()), + context.report(COMMIT_FRAGMENT, node, context.getLocation(node.getFunctionReference()), message); } return false; @@ -383,15 +382,15 @@ public class CleanupDetector extends Detector implements UastScanner { private static boolean isTransactionCommitMethodCall(@NonNull UastAndroidContext context, @NonNull UCallExpression call) { - return (call.functionNameMatches(COMMIT) || call.functionNameMatches(COMMIT_ALLOWING_LOSS)) && - isMethodOnFragmentClass(context, call, + return (call.matchesFunctionName(COMMIT) || call.matchesFunctionName(COMMIT_ALLOWING_LOSS)) && + isMethodOnFragmentClass(context, call, FRAGMENT_TRANSACTION_CLS, FRAGMENT_TRANSACTION_V4_CLS); } private static boolean isShowFragmentMethodCall(@NonNull UastAndroidContext context, @NonNull UCallExpression call) { - return call.functionNameMatches(SHOW) + return call.matchesFunctionName(SHOW) && isMethodOnFragmentClass(context, call, DIALOG_FRAGMENT, DIALOG_V4_FRAGMENT); } @@ -435,8 +434,8 @@ public class CleanupDetector extends Detector implements UastScanner { private static boolean isBeginTransaction(@NonNull UastAndroidContext context, @NonNull UCallExpression node) { - assert node.functionNameMatches(BEGIN_TRANSACTION) : node.renderString(); - if (node.functionNameMatches(BEGIN_TRANSACTION)) { + assert node.matchesFunctionName(BEGIN_TRANSACTION) : node.renderString(); + if (node.matchesFunctionName(BEGIN_TRANSACTION)) { UFunction method = node.resolve(context); if (method != null) { UClass containingClass = UastUtils.getContainingClassOrEmpty(method); @@ -565,15 +564,13 @@ public class CleanupDetector extends Detector implements UastScanner { } @Override - public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) { - if (node.getKind() == UastSpecialExpressionKind.RETURN) { - UExpression value = node.firstOrNull(); - if (value instanceof USimpleReferenceExpression) { - UDeclaration resolved = ((USimpleReferenceExpression) value).resolve(mContext); - //noinspection SuspiciousMethodCalls - if (resolved != null && mVariables.contains(resolved)) { - mEscapes = true; - } + public boolean visitReturnExpression(@NotNull UReturnExpression node) { + UExpression value = node.getReturnExpression(); + if (value instanceof USimpleReferenceExpression) { + UDeclaration resolved = ((USimpleReferenceExpression) value).resolve(mContext); + //noinspection SuspiciousMethodCalls + if (resolved != null && mVariables.contains(resolved)) { + mEscapes = true; } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java index 4a381f8bd60..d20ce344b35 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CommentDetector.java @@ -38,7 +38,7 @@ public class CommentDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( CommentDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Looks for hidden code */ public static final Issue EASTER_EGG = Issue.create( diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java index 93992646060..ea907d12e11 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CustomViewDetector.java @@ -36,7 +36,6 @@ import java.util.Collections; import java.util.List; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -48,7 +47,7 @@ public class CustomViewDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( CustomViewDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Mismatched style and class names */ public static final Issue ISSUE = Issue.create( @@ -92,7 +91,7 @@ public class CustomViewDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { if (node.getParent() instanceof UExpression) { if (!context.getLintContext().isContextMethod(node)) { return; @@ -137,7 +136,7 @@ public class CustomViewDetector extends Detector implements UastScanner { + "should have the same name (various editor features rely on " + "this convention)", className, styleableName); - context.report(ISSUE, node, UastAndroidUtils.getLocation(expression), message); + context.report(ISSUE, node, context.getLocation(expression), message); } } else if (resolvedClass.isSubclassOf(CLASS_VIEWGROUP + DOT_LAYOUT_PARAMS)) { UClass outer = UastUtils.getContainingClass(resolvedClass); @@ -153,7 +152,7 @@ public class CustomViewDetector extends Detector implements UastScanner { + "class (`%3$s`) plus \"`_Layout`\", e.g. `%4$s`. " + "(Various editor features rely on this convention.)", styleableName, className, layoutClassName, expectedName); - context.report(ISSUE, node, UastAndroidUtils.getLocation(expression), message); + context.report(ISSUE, node, context.getLocation(expression), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java index 5b4465101cd..305b73475c8 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/CutPasteDetector.java @@ -38,7 +38,6 @@ import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -63,7 +62,7 @@ public class CutPasteDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( CutPasteDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); private UFunction mLastMethod; private Map mIds; @@ -88,7 +87,7 @@ public class CutPasteDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { String lhs = getLhs(node); if (lhs == null) { return; @@ -129,8 +128,8 @@ public class CutPasteDetector extends Detector implements UastScanner { if (!isReachableFrom(method, earlierCall, node)) { return; } - Location location = UastAndroidUtils.getLocation(node); - Location secondary = UastAndroidUtils.getLocation(earlierCall); + Location location = context.getLocation(node); + Location secondary = context.getLocation(earlierCall); if (location != null && secondary != null) { secondary.setMessage("First usage here"); location.setSecondary(secondary); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java index f621b15b76f..e602b67f50c 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/DateFormatDetector.java @@ -34,7 +34,6 @@ import org.jetbrains.uast.UFunction; import org.jetbrains.uast.UCallExpression; import org.jetbrains.uast.UType; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; /** @@ -44,7 +43,7 @@ public class DateFormatDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( DateFormatDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Constructing SimpleDateFormat without an explicit locale */ public static final Issue DATE_FORMAT = Issue.create( @@ -93,7 +92,7 @@ public class DateFormatDetector extends Detector implements UastScanner { @Override public void visitConstructor(UastAndroidContext context, UCallExpression functionCall, UFunction constructor) { if (!specifiesLocale(constructor)) { - Location location = UastAndroidUtils.getLocation(functionCall); + Location location = context.getLocation(functionCall); String message = "To get local formatting use `getDateInstance()`, `getDateTimeInstance()`, " + "or `getTimeInstance()`, or use `new SimpleDateFormat(String template, " + diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java index af00e446049..860cc7b53c4 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FragmentDetector.java @@ -33,10 +33,8 @@ import java.util.Arrays; import java.util.List; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.kinds.UastClassKind; /** * Checks that Fragment subclasses can be instantiated via @@ -66,7 +64,7 @@ public class FragmentDetector extends Detector implements UastScanner { Severity.FATAL, new Implementation( FragmentDetector.class, - Scope.JAVA_FILE_SCOPE) + Scope.SOURCE_FILE_SCOPE) ).addMoreInfo( "http://developer.android.com/reference/android/app/Fragment.html#Fragment()"); //$NON-NLS-1$ @@ -89,6 +87,16 @@ public class FragmentDetector extends Detector implements UastScanner { return Arrays.asList(CLASS_FRAGMENT, CLASS_V4_FRAGMENT); } + private boolean isAllParametersWithDefaultValue(UFunction function) { + List parameters = function.getValueParameters(); + for (UVariable parameter : parameters) { + if (parameter.getInitializer() == null) { + return false; + } + } + return true; + } + @Override public void visitClass(UastAndroidContext context, UClass cls) { if (cls.hasModifier(UastModifier.ABSTRACT) || cls.getKind() != UastClassKind.CLASS) { @@ -98,14 +106,14 @@ public class FragmentDetector extends Detector implements UastScanner { if (!cls.getVisibility().isPublic()) { String message = String.format("This fragment class should be public (%1$s)", cls.getFqName()); - context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message); + context.report(ISSUE, cls, context.getLocation(cls.getNameElement()), message); return; } if (UastUtils.getContainingClass(cls) != null && !cls.hasModifier(UastModifier.STATIC)) { String message = String.format( "This fragment inner class should be static (%1$s)", cls.getName()); - context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message); + context.report(ISSUE, cls, context.getLocation(cls.getNameElement()), message); return; } @@ -114,11 +122,11 @@ public class FragmentDetector extends Detector implements UastScanner { for (UFunction constructor : cls.getConstructors()) { hasConstructor = true; - if (constructor.getValueParameterCount() == 0) { + if (constructor.getValueParameterCount() == 0 || isAllParametersWithDefaultValue(constructor)) { if (constructor.getVisibility().isPublic()) { hasDefaultConstructor = true; } else { - Location location = UastAndroidUtils.getLocation(constructor.getNameElement()); + Location location = context.getLocation(constructor.getNameElement()); context.report(ISSUE, constructor, location, "The default constructor must be public"); // Also mark that we have a constructor so we don't complain again @@ -127,7 +135,7 @@ public class FragmentDetector extends Detector implements UastScanner { hasDefaultConstructor = true; } } else { - Location location = UastAndroidUtils.getLocation(constructor.getNameElement()); + Location location = context.getLocation(constructor.getNameElement()); // TODO: Use separate issue for this which isn't an error String message = "Avoid non-default constructors in fragments: " + "use a default constructor plus " @@ -141,7 +149,7 @@ public class FragmentDetector extends Detector implements UastScanner { "This fragment should provide a default constructor (a public " + "constructor with no arguments) (`%1$s`)", cls.getName()); - context.report(ISSUE, cls, UastAndroidUtils.getLocation(cls.getNameElement()), message); + context.report(ISSUE, cls, context.getLocation(cls.getNameElement()), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java deleted file mode 100644 index 3ae668bcfc8..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/FullBackupContentDetector.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.klint.checks; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.Location; -import com.android.tools.klint.detector.api.ResourceXmlDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.XmlContext; -import com.google.common.base.Joiner; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Multimap; - -import org.jetbrains.uast.check.UastScanner; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.Collection; -import java.util.List; - -/** - * Check which makes sure that a full-backup-content descriptor file is valid and logical - */ -public class FullBackupContentDetector extends ResourceXmlDetector implements UastScanner { - /** - * Validation of {@code } XML elements - */ - public static final Issue ISSUE = Issue.create( - "FullBackupContent", //$NON-NLS-1$ - "Valid Full Backup Content File", - - "Ensures that a `` file, which is pointed to by a " + - "`android:fullBackupContent attribute` in the manifest file, is valid", - - Category.CORRECTNESS, - 5, - Severity.FATAL, - new Implementation( - FullBackupContentDetector.class, - Scope.RESOURCE_FILE_SCOPE)); - - @SuppressWarnings("SpellCheckingInspection") - private static final String DOMAIN_SHARED_PREF = "sharedpref"; - private static final String DOMAIN_ROOT = "root"; - private static final String DOMAIN_FILE = "file"; - private static final String DOMAIN_DATABASE = "database"; - private static final String DOMAIN_EXTERNAL = "external"; - private static final String TAG_EXCLUDE = "exclude"; - private static final String TAG_INCLUDE = "include"; - private static final String TAG_FULL_BACKUP_CONTENT = "full-backup-content"; - private static final String ATTR_PATH = "path"; - private static final String ATTR_DOMAIN = "domain"; - - /** - * Constructs a new {@link FullBackupContentDetector} - */ - public FullBackupContentDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.XML; - } - - @Override - public void visitDocument(@NonNull XmlContext context, @NonNull Document document) { - Element root = document.getDocumentElement(); - if (root == null) { - return; - } - if (!TAG_FULL_BACKUP_CONTENT.equals(root.getTagName())) { - return; - } - - List includes = Lists.newArrayList(); - List excludes = Lists.newArrayList(); - NodeList children = root.getChildNodes(); - for (int i = 0, n = children.getLength(); i < n; i++) { - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE) { - Element element = (Element) child; - String tag = element.getTagName(); - if (TAG_INCLUDE.equals(tag)) { - includes.add(element); - } else if (TAG_EXCLUDE.equals(tag)) { - excludes.add(element); - } else { - // See FullBackup#validateInnerTagContents - context.report(ISSUE, element, context.getNameLocation(element), - String.format("Unexpected element `<%1$s>`", tag)); - } - } - } - - Multimap includePaths = ArrayListMultimap.create(includes.size(), 4); - for (Element include : includes) { - String domain = validateDomain(context, include); - String path = validatePath(context, include); - if (domain == null) { - continue; - } - includePaths.put(domain, path); - } - - for (Element exclude : excludes) { - String excludePath = validatePath(context, exclude); - if (excludePath.isEmpty()) { - continue; - } - String domain = validateDomain(context, exclude); - if (domain == null) { - continue; - } - if (includePaths.isEmpty()) { - // There is no anywhere: that means that everything - // is considered included and there's no potential prefix mismatch - continue; - } - - boolean hasPrefix = false; - Collection included = includePaths.get(domain); - if (included == null) { - continue; - } - for (String includePath : included) { - if (excludePath.startsWith(includePath)) { - if (excludePath.equals(includePath)) { - Attr pathNode = exclude.getAttributeNode(ATTR_PATH); - assert pathNode != null; - Location location = context.getValueLocation(pathNode); - // Find corresponding include path so we can link to it in the - // chained location list - for (Element include : includes) { - Attr includePathNode = include.getAttributeNode(ATTR_PATH); - String includeDomain = include.getAttribute(ATTR_DOMAIN); - if (includePathNode != null - && excludePath.equals(includePathNode.getValue()) - && domain.equals(includeDomain)) { - Location earlier = context.getLocation(includePathNode); - earlier.setMessage("Unnecessary/conflicting "); - location.setSecondary(earlier); - } - } - context.report(ISSUE, exclude, location, - String.format("Include `%1$s` is also excluded", excludePath)); - } - hasPrefix = true; - break; - } - } - if (!hasPrefix) { - Attr pathNode = exclude.getAttributeNode(ATTR_PATH); - assert pathNode != null; - context.report(ISSUE, exclude, context.getValueLocation(pathNode), - String.format("`%1$s` is not in an included path", excludePath)); - } - } - } - - @NonNull - private static String validatePath(@NonNull XmlContext context, @NonNull Element element) { - Attr pathNode = element.getAttributeNode(ATTR_PATH); - if (pathNode == null) { - return ""; - } - String value = pathNode.getValue(); - if (value.contains("//")) { - context.report(ISSUE, element, context.getValueLocation(pathNode), - "Paths are not allowed to contain `//`"); - } else if (value.contains("..")) { - context.report(ISSUE, element, context.getValueLocation(pathNode), - "Paths are not allowed to contain `..`"); - } else if (value.contains("/")) { - String domain = element.getAttribute(ATTR_DOMAIN); - if (DOMAIN_SHARED_PREF.equals(domain) || DOMAIN_DATABASE.equals(domain)) { - context.report(ISSUE, element, context.getValueLocation(pathNode), - String.format("Subdirectories are not allowed for domain `%1$s`", - domain)); - } - } - return value; - } - - @Nullable - private static String validateDomain(@NonNull XmlContext context, @NonNull Element element) { - Attr domainNode = element.getAttributeNode(ATTR_DOMAIN); - if (domainNode == null) { - context.report(ISSUE, element, context.getLocation(element), - String.format("Missing domain attribute, expected one of %1$s", - Joiner.on(", ").join(VALID_DOMAINS))); - return null; - } - String domain = domainNode.getValue(); - for (String availableDomain : VALID_DOMAINS) { - if (availableDomain.equals(domain)) { - return domain; - } - } - context.report(ISSUE, element, context.getValueLocation(domainNode), - String.format("Unexpected domain `%1$s`, expected one of %2$s", domain, - Joiner.on(", ").join(VALID_DOMAINS))); - - return domain; - } - - /** Valid domains; see FullBackup#getTokenForXmlDomain for authoritative list */ - private static final String[] VALID_DOMAINS = new String[] { - DOMAIN_ROOT, DOMAIN_FILE, DOMAIN_DATABASE, DOMAIN_SHARED_PREF, DOMAIN_EXTERNAL - }; -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java index 776f78c8f74..70cd7e974b1 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/GetSignaturesDetector.java @@ -29,7 +29,6 @@ import java.util.List; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; public class GetSignaturesDetector extends Detector implements UastScanner { @@ -47,7 +46,7 @@ public class GetSignaturesDetector extends Detector implements UastScanner { Severity.INFORMATIONAL, new Implementation( GetSignaturesDetector.class, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) .addMoreInfo("https://bluebox.com/technical/android-fake-id-vulnerability/"); private static final String PACKAGE_MANAGER_CLASS = "android.content.pm.PackageManager"; //$NON-NLS-1$ @@ -63,7 +62,7 @@ public class GetSignaturesDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { UFunction resolved = node.resolve(context); if (resolved == null || @@ -86,7 +85,7 @@ public class GetSignaturesDetector extends Detector implements UastScanner { private static void maybeReportIssue( int flagValue, UastAndroidContext context, UCallExpression node) { if ((flagValue & GET_SIGNATURES_FLAG) != 0) { - context.report(ISSUE, node, UastAndroidUtils.getLocation(node.getValueArguments().get(1)), + context.report(ISSUE, node, context.getLocation(node.getValueArguments().get(1)), "Reading app signatures from getPackageInfo: The app signatures " + "could be exploited if not validated properly; " + "see issue explanation for details."); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java index 647fcb41ed9..ed9a699bdb1 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/HandlerDetector.java @@ -31,9 +31,7 @@ import java.util.List; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.kinds.UastClassKind; /** * Checks that Handler implementations are top level classes or static. @@ -60,7 +58,7 @@ public class HandlerDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( HandlerDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); private static final String LOOPER_CLS = "android.os.Looper"; private static final String HANDLER_CLS = "android.os.Handler"; @@ -99,7 +97,7 @@ public class HandlerDetector extends Detector implements UastScanner { } UElement locationNode = node.getNameElement(); - Location location = UastAndroidUtils.getLocation(locationNode); + Location location = context.getLocation(locationNode); context.report(ISSUE, node, location, String.format( "This Handler class should be static or leaks might occur (%1$s)", node.getName())); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java index 536ad5aa843..d3df008a013 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/IconDetector.java @@ -1958,6 +1958,7 @@ public class IconDetector extends ResourceXmlDetector implements UastScanner { // ---- Implements UastScanner ---- private static final String NOTIFICATION_CLASS = "Notification"; //$NON-NLS-1$ + private static final String NOTIFICATION_COMPAT_CLASS = "NotificationCompat"; //$NON-NLS-1$ private static final String NOTIFICATION_CLASS_FQNAME = "android.app.Notification"; //$NON-NLS-1$ @@ -2006,6 +2007,8 @@ public class IconDetector extends ResourceXmlDetector implements UastScanner { return; } + List parts = UastUtils.asQualifiedIdentifiers(UastUtils.getQualifiedCallElement(node)); + String typeName = reference.getIdentifier(); if (NOTIFICATION_CLASS.equals(typeName)) { List args = node.getValueArguments(); @@ -2028,10 +2031,16 @@ public class IconDetector extends ResourceXmlDetector implements UastScanner { } } } else if (BUILDER_CLASS.equals(typeName)) { - UClass resolvedClass = reference.resolveClass(mContext); - boolean isBuilder = resolvedClass != null - && (resolvedClass.matchesFqName(NOTIFICATION_CLASS_FQNAME) - || resolvedClass.matchesFqName(NOTIFICATION_COMPAT_CLASS_FQNAME)); + boolean isBuilder = false; + if (parts != null && parts.size() == 1) { + isBuilder = true; + } else if (parts != null && parts.size() == 2) { + String clz = parts.get(0); + if (NOTIFICATION_CLASS.equals(clz) || NOTIFICATION_COMPAT_CLASS_FQNAME.equals(clz)) { + isBuilder = true; + } + } + if (isBuilder) { UFunction method = UastUtils.getContainingFunction(node); if (method != null) { diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java index f38e00d2173..55c3075dcc0 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaPerformanceDetector.java @@ -56,7 +56,7 @@ public class JavaPerformanceDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( JavaPerformanceDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Allocating objects during a paint method */ public static final Issue PAINT_ALLOC = Issue.create( @@ -227,7 +227,7 @@ public class JavaPerformanceDetector extends Detector implements UastScanner { } } - if (mFlagAllocations && !(UastUtils.isThrow(node.getParent())) && mCheckAllocations) { + if (mFlagAllocations && !(node.getParent() instanceof UThrowExpression) && mCheckAllocations) { // Make sure we're still inside the method declaration that marked // mInDraw as true, in case we've left it and we're in a static // block or something: diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java index c21a7e75bab..87229f158f2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/JavaScriptInterfaceDetector.java @@ -34,7 +34,6 @@ import java.util.Map; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -56,7 +55,7 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner Severity.ERROR, new Implementation( JavaScriptInterfaceDetector.class, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) .addMoreInfo( "http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)"); //$NON-NLS-1$ @@ -83,7 +82,7 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { if (context.getLintContext().getMainProject().getTargetSdk() < 17) { return; } @@ -146,7 +145,7 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner return; } - Location location = UastAndroidUtils.getLocation(node.getFunctionNameElement()); + Location location = context.getLocation(node.getFunctionNameElement()); String message = String.format( "None of the methods in the added interface (%1$s) have been annotated " + "with `@android.webkit.JavascriptInterface`; they will not " + @@ -216,29 +215,19 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner } @Override - public boolean visitCallExpression(@NotNull UCallExpression node) { + public void afterVisitCallExpression(@NotNull UCallExpression node) { if (node.getKind() == UastCallKind.FUNCTION_CALL && node == mTargetCall) { mFoundCall = true; } else if (node.getKind() == UastCallKind.CONSTRUCTOR_CALL) { UFunction resolved = node.resolve(mContext); if (resolved != null) { mTypes.put(node, UastUtils.getContainingClass(resolved)); - } else { - // Implicit constructor? - UType type = node.getExpressionType(); - if (type != null) { - UClass typeClass = type.resolve(mContext); - if (typeClass != null) { - mTypes.put(node, typeClass); - } - } } } - return false; } @Override - public boolean visitSimpleReferenceExpression(@NotNull USimpleReferenceExpression node) { + public void afterVisitSimpleReferenceExpression(@NotNull USimpleReferenceExpression node) { if (mTypes.get(node) == null) { UElement resolved = node.resolve(mContext); if (resolved instanceof UVariable) { @@ -248,11 +237,10 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner } } } - return false; } @Override - public boolean visitBinaryExpression(@NotNull UBinaryExpression node) { + public void afterVisitBinaryExpression(@NotNull UBinaryExpression node) { if (node.getOperator() == UastBinaryOperator.ASSIGN) { UExpression rhs = node.getRightOperand(); UClass resolvedClass = mTypes.get(rhs); @@ -267,11 +255,10 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner } } } - return false; } @Override - public boolean visitIfExpression(@NotNull UIfExpression node) { + public void afterVisitIfExpression(@NotNull UIfExpression node) { if (node.isTernary()) { UClass resolvedClass = mTypes.get(node.getThenBranch()); if (resolvedClass == null) { @@ -281,11 +268,10 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner mTypes.put(node, resolvedClass); } } - return false; } @Override - public boolean visitVariable(@NotNull UVariable node) { + public void afterVisitVariable(@NotNull UVariable node) { UExpression initializer = node.getInitializer(); if (initializer != null) { UClass resolvedClass = mTypes.get(initializer); @@ -294,16 +280,14 @@ public class JavaScriptInterfaceDetector extends Detector implements UastScanner mVariableTypes.put(node, resolvedClass); } } - return false; } @Override - public boolean visitBinaryExpressionWithType(@NotNull UBinaryExpressionWithType node) { + public void afterVisitBinaryExpressionWithType(@NotNull UBinaryExpressionWithType node) { UClass resolvedClass = mTypes.get(node); if (resolvedClass != null) { mTypes.put(node, resolvedClass); } - return false; } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java index af414bdfb68..0acd490502b 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutConsistencyDetector.java @@ -427,7 +427,7 @@ public class LayoutConsistencyDetector extends LayoutDetector implements UastSca return locations.get(0); } - // ---- Implements JavaScanner ---- + // ---- Implements UastScanner ---- @Override public boolean appliesToResourceRefs() { diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java index 55fea073cda..94707237424 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LayoutInflationDetector.java @@ -46,7 +46,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.kxml2.io.KXmlParser; @@ -75,7 +74,7 @@ public class LayoutInflationDetector extends LayoutDetector implements UastScann private static final Implementation IMPLEMENTATION = new Implementation( LayoutInflationDetector.class, Scope.SOURCE_AND_RESOURCE_FILES, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Passing in a null parent to a layout inflater */ public static final Issue ISSUE = Issue.create( @@ -153,7 +152,7 @@ public class LayoutInflationDetector extends LayoutDetector implements UastScann } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { assert ViewHolderDetector.INFLATE.equals(node.getFunctionName()); if (node instanceof USimpleReferenceExpression) { return; @@ -169,8 +168,8 @@ public class LayoutInflationDetector extends LayoutDetector implements UastScann return; } UQualifiedExpression select = (UQualifiedExpression) first; - UExpression selector = select.getSelector(); UExpression receiver = select.getReceiver(); + UExpression selector = select.getSelector(); if (receiver instanceof UQualifiedExpression && selector instanceof USimpleReferenceExpression) { UQualifiedExpression rLayout = (UQualifiedExpression) receiver; if (rLayout.selectorMatches(ResourceType.LAYOUT.getName()) && @@ -186,11 +185,11 @@ public class LayoutInflationDetector extends LayoutDetector implements UastScann if (mPendingErrors == null) { mPendingErrors = Lists.newArrayList(); } - Location location = UastAndroidUtils.getLocation(second); + Location location = context.getLocation(second); mPendingErrors.add(Pair.of(layoutName, location)); } } else if (hasLayoutParams(lintContext, layoutName)) { - context.report(ISSUE, node, UastAndroidUtils.getLocation(second), ERROR_MESSAGE); + context.report(ISSUE, node, context.getLocation(second), ERROR_MESSAGE); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java index dd6767eab02..a5e221cba61 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/LogDetector.java @@ -35,7 +35,6 @@ import java.util.List; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; /** @@ -43,7 +42,7 @@ import org.jetbrains.uast.check.UastScanner; */ public class LogDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( - LogDetector.class, Scope.JAVA_FILE_SCOPE); + LogDetector.class, Scope.SOURCE_FILE_SCOPE); /** Log call missing surrounding if */ @@ -113,7 +112,7 @@ public class LogDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { UFunction method = node.resolve(context); if (method == null) { return; @@ -137,7 +136,7 @@ public class LogDetector extends Detector implements UastScanner { "conditional: surround with `if (Log.isLoggable(...))` or " + "`if (BuildConfig.DEBUG) { ... }`", node.getFunctionName()); - context.report(CONDITIONAL, node, UastAndroidUtils.getLocation(node), message); + context.report(CONDITIONAL, node, context.getLocation(node), message); } // Check tag length @@ -156,7 +155,7 @@ public class LogDetector extends Detector implements UastScanner { String message = String.format( "The logging tag can be at most 23 characters, was %1$d (%2$s)", tag.length(), tag); - context.report(LONG_TAG, node, UastAndroidUtils.getLocation(node), message); + context.report(LONG_TAG, node, context.getLocation(node), message); } } } @@ -276,8 +275,8 @@ public class LogDetector extends Detector implements UastScanner { } if (!isOk) { - Location location = UastAndroidUtils.getLocation(logTag); - Location alternate = UastAndroidUtils.getLocation(isLoggableTag); + Location location = context.getLocation(logTag); + Location alternate = context.getLocation(isLoggableTag); if (location != null && alternate != null) { alternate.setMessage("Conflicting tag"); location.setSecondary(alternate); @@ -305,7 +304,10 @@ public class LogDetector extends Detector implements UastScanner { } String levelString = isLoggableLevel.toString(); if (isLoggableLevel instanceof UQualifiedExpression) { - levelString = ((UQualifiedExpression)isLoggableLevel).getSelector().renderString(); + String identifier = ((UQualifiedExpression)isLoggableLevel).getSelectorAsIdentifier(); + if (identifier != null) { + levelString = identifier; + } } if (levelString.isEmpty()) { return; @@ -331,8 +333,8 @@ public class LogDetector extends Detector implements UastScanner { "Mismatched logging levels: when checking `isLoggable` level `%1$s`, the " + "corresponding log call should be `Log.%2$s`, not `Log.%3$s`", levelString, expectedCall, logCallName); - Location location = UastAndroidUtils.getLocation(logCall.getFunctionNameElement()); - Location alternate = UastAndroidUtils.getLocation(isLoggableLevel); + Location location = context.getLocation(logCall.getFunctionNameElement()); + Location alternate = context.getLocation(isLoggableLevel); if (location != null && alternate != null) { alternate.setMessage("Conflicting tag"); location.setSecondary(alternate); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java index f5e3f02e2c8..bc87d42c6e0 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/MergeRootFrameLayoutDetector.java @@ -45,6 +45,7 @@ import com.android.utils.Pair; import org.jetbrains.uast.UExpression; import org.jetbrains.uast.UCallExpression; import org.jetbrains.uast.UQualifiedExpression; +import org.jetbrains.uast.UastUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Element; @@ -196,7 +197,7 @@ public class MergeRootFrameLayoutDetector extends LayoutDetector implements Uast } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { if (node.getValueArgumentCount() != 1) { return; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java deleted file mode 100644 index beb01fb8140..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NfcTechListDetector.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.klint.checks; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.ResourceXmlDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.Speed; -import com.android.tools.klint.detector.api.XmlContext; - -import org.jetbrains.uast.check.UastScanner; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.Collection; -import java.util.Collections; - -/** - * Check which makes sure NFC tech lists do not include spaces around {@code } values - * since that's not handled correctly by the inflater - */ -public class NfcTechListDetector extends ResourceXmlDetector implements UastScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "NfcTechWhitespace", //$NON-NLS-1$ - "Whitespace in NFC tech lists", - - "In a , there can be whitespace around the elements," + - "but not inside them. This is because the code which reads in the tech " + - "list is currently very strict and will include the whitespace as part " + - "of the name.\n" + - "\n" + - "In other words, use name, not name .", - - Category.CORRECTNESS, - 5, - Severity.FATAL, - new Implementation( - NfcTechListDetector.class, - Scope.RESOURCE_FILE_SCOPE)) - .addMoreInfo( - "https://code.google.com/p/android/issues/detail?id=65351"); //$NON-NLS-1$ - - /** Constructs a new {@link NfcTechListDetector} */ - public NfcTechListDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.XML; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - @Nullable - public Collection getApplicableElements() { - return Collections.singletonList("tech"); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - Node parentNode = element.getParentNode(); - if (parentNode == null || parentNode.getNodeType() != Node.ELEMENT_NODE || - !"tech-list".equals(parentNode.getNodeName())) { - return; - } - - NodeList children = element.getChildNodes(); - if (children.getLength() != 1) { - return; - } - Node child = children.item(0); - if (child.getNodeType() != Node.TEXT_NODE) { - // TODO: Warn if you have comment nodes etc too? Will probably also break inflater. - return; - } - - String text = child.getNodeValue(); - if (!text.equals(text.trim())) { - String message = "There should not be any whitespace inside `` elements"; - context.report(ISSUE, element, context.getLocation(child), message); - } - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java index 4f613d675a2..69d3b8c6cbb 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/NonInternationalizedSmsDetector.java @@ -33,7 +33,6 @@ import org.jetbrains.uast.UExpression; import org.jetbrains.uast.UCallExpression; import org.jetbrains.uast.ULiteralExpression; import org.jetbrains.uast.USimpleReferenceExpression; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -53,7 +52,7 @@ public class NonInternationalizedSmsDetector extends Detector implements UastSca Severity.WARNING, new Implementation( NonInternationalizedSmsDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link NonInternationalizedSmsDetector} check */ @@ -77,15 +76,11 @@ public class NonInternationalizedSmsDetector extends Detector implements UastSca } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { String functionName = node.getFunctionName(); assert "sendTextMessage".equals(functionName) || //$NON-NLS-1$ "sendMultipartTextMessage".equals(functionName); //$NON-NLS-1$ - if (node instanceof USimpleReferenceExpression) { - // "sendTextMessage"/"sendMultipartTextMessage" in the code with no operand - return; - } List args = node.getValueArguments(); if (args.size() == 5) { @@ -95,7 +90,7 @@ public class NonInternationalizedSmsDetector extends Detector implements UastSca String number = (String) ((ULiteralExpression) destinationAddress).getValue(); if (number != null && !number.startsWith("+")) { //$NON-NLS-1$ - context.report(ISSUE, node, UastAndroidUtils.getLocation(destinationAddress), + context.report(ISSUE, node, context.getLocation(destinationAddress), "To make sure the SMS can be sent by all users, please start the SMS number " + "with a + and a country code or restrict the code invocation to people in the country " + "you are targeting."); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java index a7ad37ab5a3..95d52bc4815 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverdrawDetector.java @@ -486,16 +486,7 @@ public class OverdrawDetector extends LayoutDetector implements UastScanner { String name = node.getName(); if (mActivities != null && mActivities.contains(mClassFqn) || name.endsWith(ACTIVITY) || node.isSubclassOf(CLASS_ACTIVITY)) { - String packageName = ""; - if (node.getParent() instanceof UFile) { - UFile file = (UFile) node.getParent(); - if (file.getPackageFqName() == null) { - return true; - } - packageName = file.getPackageFqName(); - } - mClassFqn = (!packageName.isEmpty() ? (packageName + '.') : "") + name; - + mClassFqn = node.getFqName(); return false; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java index cfa04973cec..a2347bf66cf 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/OverrideConcreteDetector.java @@ -55,7 +55,7 @@ public class OverrideConcreteDetector extends Detector implements UastScanner { Severity.FATAL, new Implementation( OverrideConcreteDetector.class, - Scope.JAVA_FILE_SCOPE) + Scope.SOURCE_FILE_SCOPE) ); // This check is currently hardcoded for the specific case of the diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java index d830ecfabef..b97dbfa7672 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ParcelDetector.java @@ -27,10 +27,8 @@ import com.android.tools.klint.detector.api.Speed; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.kinds.UastClassKind; import org.jetbrains.uast.visitor.UastVisitor; /** @@ -53,7 +51,7 @@ public class ParcelDetector extends Detector implements UastScanner { Severity.ERROR, new Implementation( ParcelDetector.class, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) .addMoreInfo("http://developer.android.com/reference/android/os/Parcelable.html"); /** Constructs a new {@link ParcelDetector} check */ @@ -100,7 +98,7 @@ public class ParcelDetector extends Detector implements UastScanner { return true; } } - Location location = UastAndroidUtils.getLocation(node.getNameElement()); + Location location = mContext.getLocation(node.getNameElement()); mContext.report(ISSUE, node, location, "This class implements `Parcelable` but does not " + "provide a `CREATOR` field"); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java deleted file mode 100644 index f80394c8700..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PluralsDatabase.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.tools.klint.checks; - -import static com.android.tools.klint.checks.PluralsDatabase.Quantity.few; -import static com.android.tools.klint.checks.PluralsDatabase.Quantity.many; -import static com.android.tools.klint.checks.PluralsDatabase.Quantity.one; -import static com.android.tools.klint.checks.PluralsDatabase.Quantity.two; -import static com.android.tools.klint.checks.PluralsDatabase.Quantity.zero; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.klint.detector.api.LintUtils; -import com.google.common.collect.Maps; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; - -/** - * Database used by the {@link PluralsDetector} to get information - * about plural forms for a given language - */ -public class PluralsDatabase { - private static final EnumSet NONE = EnumSet.noneOf(Quantity.class); - - private static final PluralsDatabase sInstance = new PluralsDatabase(); - private Map> mPlurals = Maps.newHashMap(); - - /** Bit set if this language uses quantity zero */ - @SuppressWarnings("PointlessBitwiseExpression") - static final int FLAG_ZERO = 1 << 0; - /** Bit set if this language uses quantity one */ - static final int FLAG_ONE = 1 << 1; - /** Bit set if this language uses quantity two */ - static final int FLAG_TWO = 1 << 2; - /** Bit set if this language uses quantity few */ - static final int FLAG_FEW = 1 << 3; - /** Bit set if this language uses quantity many */ - static final int FLAG_MANY = 1 << 4; - /** Bit set if this language has multiple values that match quantity zero */ - static final int FLAG_MULTIPLE_ZERO = 1 << 5; - /** Bit set if this language has multiple values that match quantity one */ - static final int FLAG_MULTIPLE_ONE = 1 << 6; - /** Bit set if this language has multiple values that match quantity two */ - static final int FLAG_MULTIPLE_TWO = 1 << 7; - - @NonNull - public static PluralsDatabase get() { - return sInstance; - } - - private static int getFlags(@NonNull String language) { - int index = getLanguageIndex(language); - if (index != -1) { - return FLAGS[index]; - } - return 0; - } - - private static int getLanguageIndex(@NonNull String language) { - int index = Arrays.binarySearch(LANGUAGE_CODES, language); - if (index >= 0) { - assert LANGUAGE_CODES[index].equals(language); - return index; - } else { - return -1; - } - } - - @Nullable - public EnumSet getRelevant(@NonNull String language) { - EnumSet set = mPlurals.get(language); - if (set == null) { - int index = getLanguageIndex(language); - if (index == -1) { - mPlurals.put(language, NONE); - return null; - } - - // Process each item and look for relevance - int flag = FLAGS[index]; - - set = EnumSet.noneOf(Quantity.class); - if ((flag & FLAG_ZERO) != 0) { - set.add(zero); - } - if ((flag & FLAG_ONE) != 0) { - set.add(one); - } - if ((flag & FLAG_TWO) != 0) { - set.add(two); - } - if ((flag & FLAG_FEW) != 0) { - set.add(few); - } - if ((flag & FLAG_MANY) != 0) { - set.add(many); - } - - mPlurals.put(language, set); - } - return set == NONE ? null : set; - } - - @SuppressWarnings("MethodMayBeStatic") - public boolean hasMultipleValuesForQuantity( - @NonNull String language, - @NonNull Quantity quantity) { - if (quantity == one) { - return (getFlags(language) & FLAG_MULTIPLE_ONE) != 0; - } else if (quantity == two) { - return (getFlags(language) & FLAG_MULTIPLE_TWO) != 0; - } else { - return quantity == zero && (getFlags(language) & FLAG_MULTIPLE_ZERO) != 0; - } - } - - @SuppressWarnings("MethodMayBeStatic") - @Nullable - public String findIntegerExamples(@NonNull String language, @NonNull Quantity quantity) { - if (quantity == one) { - return getExampleForQuantityOne(language); - } else if (quantity == two) { - return getExampleForQuantityTwo(language); - } else if (quantity == zero) { - return getExampleForQuantityZero(language); - } else { - return null; - } - } - - public enum Quantity { - // deliberately lower case to match attribute names - few, many, one, two, zero, other; - - @Nullable - public static Quantity get(@NonNull String name) { - for (Quantity quantity : values()) { - if (name.equals(quantity.name())) { - return quantity; - } - } - - return null; - } - - public static String formatSet(@NonNull EnumSet set) { - List list = new ArrayList(set.size()); - for (Quantity quantity : set) { - list.add('`' + quantity.name() + '`'); - } - return LintUtils.formatList(list, Integer.MAX_VALUE); - } - } - - // GENERATED DATA. - // This data is generated by the #testDatabaseAccurate method in PluralsDatabaseTest - // which will generate the following if it can find an ICU plurals database file - // in the unit test data folder. - - /** Set of language codes relevant to plurals data */ - private static final String[] LANGUAGE_CODES = new String[] { - "af", "ak", "am", "ar", "az", "be", "bg", "bh", "bm", "bn", - "bo", "br", "bs", "ca", "cs", "cy", "da", "de", "dv", "dz", - "ee", "el", "en", "eo", "es", "et", "eu", "fa", "ff", "fi", - "fo", "fr", "fy", "ga", "gd", "gl", "gu", "gv", "ha", "he", - "hi", "hr", "hu", "hy", "id", "ig", "ii", "in", "is", "it", - "iu", "iw", "ja", "ji", "jv", "ka", "kk", "kl", "km", "kn", - "ko", "ks", "ku", "kw", "ky", "lb", "lg", "ln", "lo", "lt", - "lv", "mg", "mk", "ml", "mn", "mr", "ms", "mt", "my", "nb", - "nd", "ne", "nl", "nn", "no", "nr", "ny", "om", "or", "os", - "pa", "pl", "ps", "pt", "rm", "ro", "ru", "se", "sg", "si", - "sk", "sl", "sn", "so", "sq", "sr", "ss", "st", "sv", "sw", - "ta", "te", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", - "ug", "uk", "ur", "uz", "ve", "vi", "vo", "wa", "wo", "xh", - "yi", "yo", "zh", "zu" - }; - - /** - * Relevant flags for each language (corresponding to each language listed - * in the same position in {@link #LANGUAGE_CODES}). - */ - private static final int[] FLAGS = new int[] { - 0x0002, 0x0042, 0x0042, 0x001f, 0x0002, 0x005a, 0x0002, 0x0042, - 0x0000, 0x0042, 0x0000, 0x00de, 0x004a, 0x0002, 0x000a, 0x001f, - 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, - 0x0002, 0x0002, 0x0002, 0x0042, 0x0042, 0x0002, 0x0002, 0x0042, - 0x0002, 0x001e, 0x00ce, 0x0002, 0x0042, 0x00ce, 0x0002, 0x0016, - 0x0042, 0x004a, 0x0002, 0x0042, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0042, 0x0002, 0x0006, 0x0016, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0002, 0x0002, 0x0000, 0x0042, 0x0000, 0x0002, 0x0002, 0x0006, - 0x0002, 0x0002, 0x0002, 0x0042, 0x0000, 0x004a, 0x0063, 0x0042, - 0x0042, 0x0002, 0x0002, 0x0042, 0x0000, 0x001a, 0x0000, 0x0002, - 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, - 0x0002, 0x0002, 0x0042, 0x001a, 0x0002, 0x0002, 0x0002, 0x000a, - 0x005a, 0x0006, 0x0000, 0x0042, 0x000a, 0x00ce, 0x0002, 0x0002, - 0x0002, 0x004a, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, - 0x0000, 0x0042, 0x0002, 0x0042, 0x0002, 0x0000, 0x0002, 0x0002, - 0x0002, 0x005a, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0042, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0042 - }; - - @Nullable - private static String getExampleForQuantityZero(@NonNull String language) { - int index = getLanguageIndex(language); - switch (index) { - // set14 - case 70: // lv - return "0, 10~20, 30, 40, 50, 60, 100, 1000, 10000, 100000, 1000000, \u2026"; - case -1: - default: - return null; - } - } - - @Nullable - private static String getExampleForQuantityOne(@NonNull String language) { - int index = getLanguageIndex(language); - switch (index) { - // set1 - case 2: // am - case 9: // bn - case 27: // fa - case 36: // gu - case 40: // hi - case 59: // kn - case 75: // mr - case 133: // zu - return "0, 1"; - // set11 - case 48: // is - return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026"; - // set12 - case 72: // mk - return "1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, \u2026"; - // set13 - case 115: // tl - return "0~3, 5, 7, 8, 10~13, 15, 17, 18, 20, 21, 100, 1000, 10000, 100000, 1000000, \u2026"; - // set14 - case 70: // lv - return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026"; - // set2 - case 28: // ff - case 31: // fr - case 43: // hy - return "0, 1"; - // set20 - case 12: // bs - case 41: // hr - case 105: // sr - return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026"; - // set21 - case 34: // gd - return "1, 11"; - // set22 - case 101: // sl - return "1, 101, 201, 301, 401, 501, 601, 701, 1001, \u2026"; - // set26 - case 5: // be - return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026"; - // set27 - case 69: // lt - return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026"; - // set29 - case 96: // ru - case 121: // uk - return "1, 21, 31, 41, 51, 61, 71, 81, 101, 1001, \u2026"; - // set30 - case 11: // br - return "1, 21, 31, 41, 51, 61, 81, 101, 1001, \u2026"; - // set32 - case 37: // gv - return "1, 11, 21, 31, 41, 51, 61, 71, 101, 1001, \u2026"; - // set5 - case 99: // si - return "0, 1"; - // set6 - case 1: // ak - case 7: // bh - case 67: // ln - case 71: // mg - case 90: // pa - case 113: // ti - case 127: // wa - return "0, 1"; - case -1: - default: - return null; - } - } - - @Nullable - private static String getExampleForQuantityTwo(@NonNull String language) { - int index = getLanguageIndex(language); - switch (index) { - // set21 - case 34: // gd - return "2, 12"; - // set22 - case 101: // sl - return "2, 102, 202, 302, 402, 502, 602, 702, 1002, \u2026"; - // set30 - case 11: // br - return "2, 22, 32, 42, 52, 62, 82, 102, 1002, \u2026"; - // set32 - case 37: // gv - return "2, 12, 22, 32, 42, 52, 62, 72, 102, 1002, \u2026"; - case -1: - default: - return null; - } - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java index a8ca82cd828..77344401c20 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/PrivateResourceDetector.java @@ -81,7 +81,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Uast private static final Implementation IMPLEMENTATION = new Implementation( PrivateResourceDetector.class, Scope.SOURCE_AND_RESOURCE_FILES, - Scope.JAVA_FILE_SCOPE, + Scope.SOURCE_FILE_SCOPE, Scope.RESOURCE_FILE_SCOPE); /** The main issue discovered by this detector */ @@ -259,7 +259,7 @@ public class PrivateResourceDetector extends ResourceXmlDetector implements Uast } @Override - public void beforeCheckProject(@NonNull Context context) { + public void beforeCheckFile(@NonNull Context context) { File file = context.file; boolean isXmlFile = LintUtils.isXmlFile(file); if (!isXmlFile && !LintUtils.isBitmapFile(file)) { diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java deleted file mode 100644 index e1670621b1e..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RegistrationDetector.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.klint.checks; - -import static com.android.SdkConstants.ANDROID_APP_ACTIVITY; -import static com.android.SdkConstants.ANDROID_APP_SERVICE; -import static com.android.SdkConstants.ANDROID_CONTENT_BROADCAST_RECEIVER; -import static com.android.SdkConstants.ANDROID_CONTENT_CONTENT_PROVIDER; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.TAG_ACTIVITY; -import static com.android.SdkConstants.TAG_PROVIDER; -import static com.android.SdkConstants.TAG_RECEIVER; -import static com.android.SdkConstants.TAG_SERVICE; - -import com.android.annotations.NonNull; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.ClassContext; -import com.android.tools.klint.detector.api.Detector.ClassScanner; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.LayoutDetector; -import com.android.tools.klint.detector.api.Location; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.Speed; -import com.android.tools.klint.detector.api.XmlContext; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.ClassNode; -import org.w3c.dom.Element; - -import java.util.Arrays; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Map.Entry; - -/** - * Checks for missing manifest registrations for activities, services etc - * and also makes sure that they are registered with the correct tag - *

- * TODO: Rewrite as Java visitor! - */ -public class RegistrationDetector extends LayoutDetector implements ClassScanner { - /** Unregistered activities and services */ - public static final Issue ISSUE = Issue.create( - "Registered", //$NON-NLS-1$ - "Class is not registered in the manifest", - - "Activities, services and content providers should be registered in the " + - "`AndroidManifest.xml` file using ``, `` and `` tags.\n" + - "\n" + - "If your activity is simply a parent class intended to be subclassed by other " + - "\"real\" activities, make it an abstract class.", - - Category.CORRECTNESS, - 6, - Severity.WARNING, - new Implementation( - RegistrationDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE))) - .addMoreInfo( - "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$ - - protected Multimap mManifestRegistrations; - - /** Constructs a new {@link RegistrationDetector} */ - public RegistrationDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - // ---- Implements XmlScanner ---- - - @Override - public Collection getApplicableElements() { - return Arrays.asList(sTags); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - String fqcn = getFqcn(context, element); - String tag = element.getTagName(); - String frameworkClass = tagToClass(tag); - if (frameworkClass != null) { - String signature = ClassContext.getInternalName(fqcn); - if (mManifestRegistrations == null) { - mManifestRegistrations = ArrayListMultimap.create(4, 8); - } - mManifestRegistrations.put(frameworkClass, signature); - if (signature.indexOf('$') != -1) { - // The internal name contains a $ which means it's an inner class. - // The conversion from fqcn to internal name is a bit ambiguous: - // "a.b.C.D" usually means "inner class D in class C in package a.b". - // However, it can (see issue 31592) also mean class D in package "a.b.C". - // Place *both* of these possibilities in the registered map, since this - // is only used to check that an activity is registered, not the other way - // (so it's okay to have entries there that do not correspond to real classes). - signature = signature.replace('$', '/'); - mManifestRegistrations.put(frameworkClass, signature); - } - } - } - - /** - * Returns the fully qualified class name for a manifest entry element that - * specifies a name attribute - * - * @param context the query context providing the project - * @param element the element - * @return the fully qualified class name - */ - @NonNull - private static String getFqcn(@NonNull XmlContext context, @NonNull Element element) { - String className = element.getAttributeNS(ANDROID_URI, ATTR_NAME); - if (className.startsWith(".")) { //$NON-NLS-1$ - return context.getMainProject().getPackage() + className; - } else if (className.indexOf('.') == -1) { - // According to the manifest element documentation, this is not - // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html ) - // but it appears in manifest files and appears to be supported by the runtime - // so handle this in code as well: - return context.getMainProject().getPackage() + '.' + className; - } // else: the class name is already a fully qualified class name - - return className; - } - - // ---- Implements ClassScanner ---- - - @Override - public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) { - // Abstract classes do not need to be registered - if ((classNode.access & Opcodes.ACC_ABSTRACT) != 0) { - return; - } - String curr = classNode.name; - - int lastIndex = curr.lastIndexOf('$'); - if (lastIndex != -1 && lastIndex < curr.length() - 1) { - if (Character.isDigit(curr.charAt(lastIndex+1))) { - // Anonymous inner class, doesn't need to be registered - return; - } - } - - while (curr != null) { - for (String s : sClasses) { - if (curr.equals(s)) { - Collection registered = mManifestRegistrations != null ? - mManifestRegistrations.get(curr) : null; - if (registered == null || !registered.contains(classNode.name)) { - report(context, classNode, curr); - } - - } - } - - curr = context.getDriver().getSuperClass(curr); - } - } - - private void report(ClassContext context, ClassNode classNode, String curr) { - String tag = classToTag(curr); - String className = ClassContext.createSignature(classNode.name, null, null); - - String wrongClass = null; // The framework class this class actually extends - if (mManifestRegistrations != null) { - Collection> entries = - mManifestRegistrations.entries(); - for (Entry entry : entries) { - if (entry.getValue().equals(classNode.name)) { - wrongClass = entry.getKey(); - break; - } - } - } - if (wrongClass != null) { - Location location = context.getLocation(classNode); - context.report( - ISSUE, - location, - String.format( - "`%1$s` is a `<%2$s>` but is registered in the manifest as a `<%3$s>`", - className, tag, classToTag(wrongClass))); - } else if (!TAG_RECEIVER.equals(tag)) { // don't need to be registered - if (context.getMainProject().isGradleProject()) { - // Disabled for now; we need to formalize the difference between - // the *manifest* package and the variant package, since in some contexts - // (such as manifest registrations) we should be using the manifest package, - // not the gradle package - return; - } - Location location = context.getLocation(classNode); - context.report( - ISSUE, - location, - String.format( - "The `<%1$s> %2$s` is not registered in the manifest", - tag, className)); - } - } - - /** The manifest tags we care about */ - private static final String[] sTags = new String[] { - TAG_ACTIVITY, - TAG_SERVICE, - TAG_RECEIVER, - TAG_PROVIDER, - // Keep synchronized with {@link #sClasses} - }; - - /** The corresponding framework classes that the tags in {@link #sTags} should extend */ - private static final String[] sClasses = new String[] { - ANDROID_APP_ACTIVITY, - ANDROID_APP_SERVICE, - ANDROID_CONTENT_BROADCAST_RECEIVER, - ANDROID_CONTENT_CONTENT_PROVIDER, - // Keep synchronized with {@link #sTags} - }; - - /** Looks up the corresponding framework class a given manifest tag's class should extend */ - private static String tagToClass(String tag) { - for (int i = 0, n = sTags.length; i < n; i++) { - if (sTags[i].equals(tag)) { - return sClasses[i]; - } - } - - return null; - } - - /** Looks up the tag a given framework class should be registered with */ - protected static String classToTag(String className) { - for (int i = 0, n = sClasses.length; i < n; i++) { - if (sClasses[i].equals(className)) { - return sTags[i]; - } - } - - return null; - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java index 08f10995213..df31c354861 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RequiredAttributeDetector.java @@ -538,7 +538,7 @@ public class RequiredAttributeDetector extends LayoutDetector implements UastSca } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { // Handle // View#inflate(Context context, int resource, ViewGroup root) // LayoutInflater#inflate(int resource, ViewGroup root) diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java index 0a169c82bb8..a43819f3681 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/RtlDetector.java @@ -73,7 +73,6 @@ import com.android.tools.klint.detector.api.XmlContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.EmptyUastVisitor; @@ -99,7 +98,7 @@ public class RtlDetector extends LayoutDetector implements UastScanner { RtlDetector.class, EnumSet.of(Scope.RESOURCE_FILE, Scope.SOURCE_FILE, Scope.MANIFEST), Scope.RESOURCE_FILE_SCOPE, - Scope.JAVA_FILE_SCOPE, + Scope.SOURCE_FILE_SCOPE, Scope.MANIFEST_SCOPE ); @@ -607,7 +606,7 @@ public class RtlDetector extends LayoutDetector implements UastScanner { // Can't resolve types (for example while editing code with errors): // rely on heuristics like import statements and class qualifiers if (parent instanceof UQualifiedExpression && - !(GRAVITY_CLASS.equals(((UQualifiedExpression) parent).getReceiver().renderString()))) { + !(UastUtils.matchesQualified(((UQualifiedExpression) parent).getReceiver(), GRAVITY_CLASS))) { return false; } if (parent instanceof USimpleReferenceExpression) { @@ -624,7 +623,7 @@ public class RtlDetector extends LayoutDetector implements UastScanner { + "behavior in right-to-left locales", (isLeft ? GRAVITY_VALUE_START : GRAVITY_VALUE_END).toUpperCase(Locale.US), (isLeft ? GRAVITY_VALUE_LEFT : GRAVITY_VALUE_RIGHT).toUpperCase(Locale.US)); - Location location = UastAndroidUtils.getLocation(node); + Location location = mContext.getLocation(node); mContext.report(USE_START, node, location, message); return true; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java index 70dca758002..f30a9b0b415 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SQLiteDetector.java @@ -29,7 +29,6 @@ import java.util.Collections; import java.util.List; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -38,7 +37,7 @@ import org.jetbrains.uast.check.UastScanner; */ public class SQLiteDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( - SQLiteDetector.class, Scope.JAVA_FILE_SCOPE); + SQLiteDetector.class, Scope.SOURCE_FILE_SCOPE); /** Using STRING instead of TEXT for columns */ public static final Issue ISSUE = Issue.create( @@ -77,7 +76,7 @@ public class SQLiteDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { UFunction resolvedFunction = node.resolve(context); UClass containingClass = UastUtils.getContainingClass(resolvedFunction); if (resolvedFunction == null || containingClass == null) { @@ -100,7 +99,7 @@ public class SQLiteDetector extends Detector implements UastScanner { + "(STRING is a numeric type and its value can be adjusted; for example," + "strings that look like integers can drop leading zeroes. See issue " + "explanation for details.)"; - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + context.report(ISSUE, node, context.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java index 9dbe622b9da..07c2ccb77b2 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SdCardDetector.java @@ -31,7 +31,6 @@ import java.io.File; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.ULiteralExpression; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -57,7 +56,7 @@ public class SdCardDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( SdCardDetector.class, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) .addMoreInfo( "http://developer.android.com/guide/topics/data/data-storage.html#filesExternal"); //$NON-NLS-1$ @@ -110,13 +109,13 @@ public class SdCardDetector extends Detector implements UastScanner { || s.startsWith("file:///sdcard/")) { //$NON-NLS-1$ String message = "Do not hardcode \"/sdcard/\"; " + "use `Environment.getExternalStorageDirectory().getPath()` instead"; - Location location = UastAndroidUtils.getLocation(node); + Location location = mContext.getLocation(node); mContext.report(ISSUE, node, location, message); } else if (s.startsWith("/data/data/") //$NON-NLS-1$ || s.startsWith("/data/user/")) { //$NON-NLS-1$ String message = "Do not hardcode \"`/data/`\"; " + "use `Context.getFilesDir().getPath()` instead"; - Location location = UastAndroidUtils.getLocation(node); + Location location = mContext.getLocation(node); mContext.report(ISSUE, node, location, message); } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java index ddd71b9bf31..9092ed96e73 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SecurityDetector.java @@ -52,7 +52,6 @@ import com.android.tools.klint.detector.api.XmlContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.UCallExpression; import org.jetbrains.uast.USimpleReferenceExpression; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -75,7 +74,7 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, U private static final Implementation IMPLEMENTATION_JAVA = new Implementation( SecurityDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Exported services */ public static final Issue EXPORTED_SERVICE = Issue.create( @@ -366,12 +365,12 @@ public class SecurityDetector extends Detector implements Detector.XmlScanner, U @Override public boolean visitSimpleReferenceExpression(@NotNull USimpleReferenceExpression node) { if ("MODE_WORLD_WRITEABLE".equals(node.getIdentifier())) { //$NON-NLS-1$ - Location location = UastAndroidUtils.getLocation(node); + Location location = mContext.getLocation(node); mContext.report(WORLD_WRITEABLE, node, location, "Using `MODE_WORLD_WRITEABLE` when creating files can be " + "risky, review carefully"); } else if ("MODE_WORLD_READABLE".equals(node.getIdentifier())) { //$NON-NLS-1$ - Location location = UastAndroidUtils.getLocation(node); + Location location = mContext.getLocation(node); mContext.report(WORLD_READABLE, node, location, "Using `MODE_WORLD_READABLE` when creating files can be " + "risky, review carefully"); diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java index 9b92117ad33..719c28c5471 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ServiceCastDetector.java @@ -33,7 +33,6 @@ import java.util.List; import java.util.Map; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -55,7 +54,7 @@ public class ServiceCastDetector extends Detector implements UastScanner { Severity.FATAL, new Implementation( ServiceCastDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link ServiceCastDetector} check */ public ServiceCastDetector() { @@ -75,7 +74,7 @@ public class ServiceCastDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { UExpression receiver = UastUtils.getQualifiedCallElement(node); UElement parent = receiver.getParent(); if (!(parent instanceof UBinaryExpressionWithType) @@ -103,7 +102,7 @@ public class ServiceCastDetector extends Detector implements UastScanner { String message = String.format( "Suspicious cast to `%1$s` for a `%2$s`: expected `%3$s`", stripPackage(castType), name, stripPackage(expectedClass)); - context.report(ISSUE, node, UastAndroidUtils.getLocation(cast), message); + context.report(ISSUE, node, context.getLocation(cast), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java index a8f62ddcf7f..0b1b5e96bc5 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SetJavaScriptEnabledDetector.java @@ -27,7 +27,6 @@ import java.util.Collections; import java.util.List; import org.jetbrains.uast.UCallExpression; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -47,7 +46,7 @@ public class SetJavaScriptEnabledDetector extends Detector implements UastScanne Severity.WARNING, new Implementation( SetJavaScriptEnabledDetector.class, - Scope.JAVA_FILE_SCOPE)) + Scope.SOURCE_FILE_SCOPE)) .addMoreInfo( "http://developer.android.com/guide/practices/security.html"); //$NON-NLS-1$ @@ -58,14 +57,14 @@ public class SetJavaScriptEnabledDetector extends Detector implements UastScanne // ---- Implements UastScanner ---- @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { if (node.getValueArgumentCount() != 1) { return; } Object value = node.getValueArguments().get(0).evaluate(); if (value instanceof Boolean && (Boolean) value) { - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), + context.report(ISSUE, node, context.getLocation(node), "Using `setJavaScriptEnabled` can introduce XSS vulnerabilities " + "into you application, review carefully."); } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java index 1ae46cca7a8..8c764ee9b47 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SharedPrefsDetector.java @@ -34,9 +34,8 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.java.JavaSpecialExpressionKinds; +import org.jetbrains.uast.java.JavaUAssertExpression; import org.jetbrains.uast.visitor.UastVisitor; /** @@ -57,7 +56,7 @@ public class SharedPrefsDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( SharedPrefsDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); public static final String ANDROID_CONTENT_SHARED_PREFERENCES = "android.content.SharedPreferences"; //$NON-NLS-1$ @@ -82,7 +81,7 @@ public class SharedPrefsDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { assert "edit".equals(node.getFunctionName()); boolean verifiedType = false; @@ -149,7 +148,7 @@ public class SharedPrefsDetector extends Detector implements UastScanner { CommitFinder finder = new CommitFinder(context, node, allowCommitBeforeTarget); method.accept(finder); if (!finder.isCommitCalled()) { - context.report(ISSUE, method, UastAndroidUtils.getLocation(node), + context.report(ISSUE, method, context.getLocation(node), "`SharedPreferences.edit()` without a corresponding `commit()` or `apply()` call"); } } @@ -220,8 +219,7 @@ public class SharedPrefsDetector extends Detector implements UastScanner { returnValueIgnored = true; } else if (qualifiedElement instanceof UIfExpression) { returnValueIgnored = ((UIfExpression) qualifiedElement).getCondition() != node; - } else if (qualifiedElement instanceof USpecialExpressionList && - ((USpecialExpressionList)qualifiedElement).getKind() == UastSpecialExpressionKind.RETURN) { + } else if (qualifiedElement instanceof UReturnExpression) { returnValueIgnored = false; } else if (qualifiedElement instanceof UVariable) { returnValueIgnored = false; @@ -231,11 +229,16 @@ public class SharedPrefsDetector extends Detector implements UastScanner { returnValueIgnored = ((UWhileExpression) qualifiedElement).getCondition() != node; } else if (qualifiedElement instanceof UDoWhileExpression) { returnValueIgnored = ((UDoWhileExpression) qualifiedElement).getCondition() != node; - } else if (qualifiedElement instanceof UExpressionSwitchClauseExpression) { - returnValueIgnored = ((UExpressionSwitchClauseExpression) qualifiedElement).getCaseValue() != node; - } else if (qualifiedElement instanceof USpecialExpressionList && - ((USpecialExpressionList)qualifiedElement).getKind() == JavaSpecialExpressionKinds.ASSERT) { - returnValueIgnored = !((USpecialExpressionList) qualifiedElement).getExpressions().contains(node); + } else if (qualifiedElement instanceof USwitchClauseExpression) { + List caseValues = ((USwitchClauseExpression) qualifiedElement).getCaseValues(); + for (UExpression caseValue : caseValues) { + if (caseValue == node) { + returnValueIgnored = false; + break; + } + } + } else if (qualifiedElement instanceof JavaUAssertExpression) { + returnValueIgnored = !((JavaUAssertExpression) qualifiedElement).getCondition().equals(node); } else { returnValueIgnored = true; } @@ -243,7 +246,7 @@ public class SharedPrefsDetector extends Detector implements UastScanner { String message = "Consider using `apply()` instead; `commit` writes " + "its data to persistent storage immediately, whereas " + "`apply` will handle it in the background"; - mContext.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + mContext.report(ISSUE, node, mContext.getLocation(node), message); } } } @@ -254,11 +257,9 @@ public class SharedPrefsDetector extends Detector implements UastScanner { } @Override - public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) { - if (node.getKind() == UastSpecialExpressionKind.RETURN) { - if (node.getExpressions().contains(mTarget)) { - mFound = true; - } + public boolean visitReturnExpression(@NotNull UReturnExpression node) { + if (mTarget.equals(node.getExpressionType())) { + mFound = true; } return false; diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java index 7415d5130c7..5c979565007 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/StringFormatDetector.java @@ -102,7 +102,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements UastSca private static final Implementation IMPLEMENTATION_XML_AND_JAVA = new Implementation( StringFormatDetector.class, EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.SOURCE_FILE), - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Whether formatting strings are invalid */ @@ -987,7 +987,7 @@ public class StringFormatDetector extends ResourceXmlDetector implements UastSca } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { if (mFormatStrings == null && !context.getLintContext().getClient().supportsProjectResources()) { return; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java index a052377f592..747c0c1e311 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/SupportAnnotationDetector.java @@ -53,7 +53,6 @@ import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.java.JavaUFunction; import org.jetbrains.uast.java.JavaUastCallKinds; -import org.jetbrains.uast.kinds.UastOperator; import org.jetbrains.uast.visitor.UastVisitor; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -79,7 +78,7 @@ import java.util.Set; public class SupportAnnotationDetector extends Detector implements UastScanner { public static final Implementation IMPLEMENTATION - = new Implementation(SupportAnnotationDetector.class, Scope.JAVA_FILE_SCOPE); + = new Implementation(SupportAnnotationDetector.class, Scope.SOURCE_FILE_SCOPE); /** Method result should be used */ public static final Issue RANGE = Issue.create( @@ -753,7 +752,7 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { UQualifiedExpression select = (UQualifiedExpression) node.getReceiver(); if (select.getReceiver() instanceof UQualifiedExpression) { // android.R.... UQualifiedExpression innerSelect = (UQualifiedExpression) select.getReceiver(); - if (innerSelect.getSelector().renderString().equals(R_CLASS)) { + if (UastUtils.matchesQualified(innerSelect.getSelector(), R_CLASS)) { String typeName = select.getSelector().renderString(); ResourceType type = ResourceType.getEnum(typeName); return type != null ? Collections.singletonList(type) : null; @@ -770,7 +769,7 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { } // Arbitrary packages -- android.R.type.name, foo.bar.R.type.name - if (node.getSelector().renderString().equals(R_CLASS)) { + if (UastUtils.matchesQualified(node.getSelector(), R_CLASS)) { UElement parent = node.getParent(); if (parent instanceof UQualifiedExpression) { UElement grandParent = parent.getParent(); @@ -788,8 +787,7 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { } } else if (argument instanceof UCallExpression) { UDeclaration resolved = ((UCallExpression)argument).resolve(context); - //noinspection ConstantConditions - if (resolved instanceof UAnnotated) { + if (resolved != null) { List annotations = ((UAnnotated) resolved).getAnnotations(); for (UAnnotation annotation : annotations) { String signature = annotation.getFqName(); @@ -972,7 +970,7 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { assert s != null; actual = s.length(); } else if (argument instanceof UCallExpression - && ((UCallExpression)argument).getKind() == JavaUastCallKinds.ARRAY_INITIALIZER) { + && ((UCallExpression)argument).getKind() == UastCallKind.ARRAY_INITIALIZER) { UCallExpression initializer = (UCallExpression) argument; actual = initializer.getValueArgumentCount(); } else { @@ -1059,7 +1057,7 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { if (!flag) { reportTypeDef(context, annotation, argument, errorNode); } - } else if (argument instanceof UIfExpression && ((UIfExpression) argument).isTernary()) { + } else if (argument instanceof UIfExpression) { UIfExpression expression = (UIfExpression) argument; if (expression.getThenBranch() != null) { checkTypeDefConstant(context, annotation, expression.getThenBranch(), errorNode, flag); @@ -1077,7 +1075,6 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { "Flag not allowed here"); } } else if (argument instanceof UBinaryExpression) { - // If it's ?: then check both the if and else clauses UBinaryExpression expression = (UBinaryExpression) argument; if (flag) { checkTypeDefConstant(context, annotation, expression.getLeftOperand(), errorNode, true); @@ -1102,13 +1099,18 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { private static void checkTypeDefConstant(@NonNull UastAndroidContext context, @NonNull UAnnotation annotation, @NonNull UElement argument, @Nullable UElement errorNode, boolean flag, Object value) { - List> valueArguments = annotation.getValues(); - for (Pair o : valueArguments) { - if (o.getSecond().equals(value)) { - return; + for (UNamedExpression namedExpression : annotation.getValueArguments()) { + UExpression expression = namedExpression.getExpression(); + if (expression instanceof UCallExpression && + ((UCallExpression) expression).getKind() == UastCallKind.ARRAY_INITIALIZER) { + for (UExpression arg : ((UCallExpression) expression).getValueArguments()) { + if (value.equals(arg.evaluate())) { + return; + } + } } } - reportTypeDef(context, argument, errorNode, flag, valueArguments); + reportTypeDef(context, argument, errorNode, flag, annotation.getValues()); } private static void reportTypeDef(@NonNull UastAndroidContext context, @@ -1242,47 +1244,6 @@ public class SupportAnnotationDetector extends Detector implements UastScanner { return null; } - @Nullable - static UAnnotation getRelevantAnnotation( - @NonNull UastAndroidContext context, - @NonNull UAnnotation annotation - ) { - String signature = annotation.getFqName(); - if (signature != null && signature.startsWith(SUPPORT_ANNOTATIONS_PREFIX)) { - // Bail on the nullness annotations early since they're the most commonly - // defined ones. They're not analyzed in lint yet. - if (signature.endsWith(".Nullable") || signature.endsWith(".NonNull")) { - return null; - } - - - return annotation; - } - - if (signature != null && signature.startsWith("java.")) { - // @Override, @SuppressWarnings etc. Ignore - return null; - } - - // Special case @IntDef and @StringDef: These are used on annotations - // themselves. For example, you create a new annotation named @foo.bar.Baz, - // annotate it with @IntDef, and then use @foo.bar.Baz in your signatures. - // Here we want to map from @foo.bar.Baz to the corresponding int def. - // Don't need to compute this if performing @IntDef or @StringDef lookup - UClass type = annotation.resolve(context); - if (type != null) { - for (UAnnotation inner : type.getAnnotations()) { - if (INT_DEF_ANNOTATION.equals(inner.getFqName()) - || STRING_DEF_ANNOTATION.equals(inner.getFqName()) - || PERMISSION_ANNOTATION.equals(inner.getFqName())) { - return inner; - } - } - } - - return null; - } - // ---- Implements UastScanner ---- @Override diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java deleted file mode 100644 index a409c084206..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/TitleDetector.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.tools.klint.checks; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_TITLE; -import static com.android.SdkConstants.ATTR_VISIBLE; -import static com.android.SdkConstants.TAG_ITEM; -import static com.android.SdkConstants.VALUE_FALSE; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceFolderType; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.ResourceXmlDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.Speed; -import com.android.tools.klint.detector.api.XmlContext; - -import org.jetbrains.uast.check.UastScanner; -import org.w3c.dom.Element; - -import java.util.Collection; -import java.util.Collections; - -/** - * Check which makes sure menu items specify a title - */ -public class TitleDetector extends ResourceXmlDetector implements UastScanner { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "MenuTitle", //$NON-NLS-1$ - "Missing menu title", - - "From the action bar documentation:\n" + - // u2014: em dash - "\"It's important that you always define android:title for each menu item \u2014 " + - "even if you don't declare that the title appear with the action item \u2014 for " + - "three reasons:\n" + - "\n" + - "* If there's not enough room in the action bar for the action item, the menu " + - "item appears in the overflow menu and only the title appears.\n" + - "* Screen readers for sight-impaired users read the menu item's title.\n" + - "* If the action item appears with only the icon, a user can long-press the item " + - "to reveal a tool-tip that displays the action item's title.\n" + - "The android:icon is always optional, but recommended.", - - Category.USABILITY, - 5, - Severity.ERROR, - new Implementation( - TitleDetector.class, - Scope.RESOURCE_FILE_SCOPE)) - .addMoreInfo( - "http://developer.android.com/guide/topics/ui/actionbar.html"); //$NON-NLS-1$ - - /** Constructs a new {@link TitleDetector} */ - public TitleDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.MENU; - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - @Nullable - public Collection getApplicableElements() { - return Collections.singletonList(TAG_ITEM); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (element.hasAttributeNS(ANDROID_URI, ATTR_TITLE)) { - return; - } - - // TODO: Find out if this is necessary on older versions too. - // I swear I saw it mentioned. - if (context.getMainProject().getTargetSdk() < 11) { - return; - } - - if (VALUE_FALSE.equals(element.getAttributeNS(ANDROID_URI, ATTR_VISIBLE))) { - return; - } - - String message = "Menu items should specify a `title`"; - context.report(ISSUE, element, context.getLocation(element), message); - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java index 45e2aa81c45..7b9602a3d52 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ToastDetector.java @@ -31,7 +31,6 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -51,7 +50,7 @@ public class ToastDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( ToastDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link ToastDetector} check */ @@ -72,7 +71,7 @@ public class ToastDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { assert "makeText".equals(node.getFunctionName()); UElement qualifiedExpression = node.getParent(); @@ -80,8 +79,7 @@ public class ToastDetector extends Detector implements UastScanner { return; } - String operand = ((UQualifiedExpression)qualifiedExpression).getReceiver().renderString(); - if (!(operand.equals("Toast") || operand.endsWith(".Toast"))) { + if (!UastUtils.endsWithQualified(((UQualifiedExpression) qualifiedExpression).getReceiver(), "Toast")) { return; } @@ -107,7 +105,7 @@ public class ToastDetector extends Detector implements UastScanner { ShowFinder finder = new ShowFinder(nodeWithPossibleQualifier); method.accept(finder); if (!finder.isShowCalled()) { - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), + context.report(ISSUE, node, context.getLocation(node), "Toast created but not shown: did you forget to call `show()` ?"); } } @@ -128,7 +126,7 @@ public class ToastDetector extends Detector implements UastScanner { public boolean visitCallExpression(@NotNull UCallExpression node) { if (node == mTarget) { mSeenTarget = true; - } else if (mSeenTarget && node.functionNameMatches("show")) { //$NON-NLS-1$ + } else if (mSeenTarget && node.matchesFunctionName("show")) { //$NON-NLS-1$ // TODO: Do more flow analysis to see whether we're really calling show // on the right type of object? mFound = true; @@ -147,9 +145,8 @@ public class ToastDetector extends Detector implements UastScanner { } @Override - public boolean visitSpecialExpressionList(@NotNull USpecialExpressionList node) { - if (node.getKind() == UastSpecialExpressionKind.RETURN && node.firstOrNull() == mTarget) { - // If you just do "return Toast.makeText(...) don't warn + public boolean visitReturnExpression(@NotNull UReturnExpression node) { + if (mTarget.equals(node.getReturnExpression())) { mFound = true; } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java index bf954c10168..74ea0e86961 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewConstructorDetector.java @@ -35,10 +35,8 @@ import java.util.Collections; import java.util.List; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.kinds.UastClassKind; /** * Looks for custom views that do not define the view constructors needed by UI builders @@ -65,7 +63,7 @@ public class ViewConstructorDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( ViewConstructorDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link ViewConstructorDetector} check */ public ViewConstructorDetector() { @@ -140,7 +138,7 @@ public class ViewConstructorDetector extends Detector implements UastScanner { + "`(Context)` or `(Context,AttributeSet)` " + "or `(Context,AttributeSet,int)`", node.getFqName()); - Location location = UastAndroidUtils.getLocation(node.getNameElement()); + Location location = context.getLocation(node.getNameElement()); context.report(ISSUE, node, location, message /*data*/); } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java index 39cbca1a7eb..6439689c12e 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewHolderDetector.java @@ -34,7 +34,6 @@ import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -45,7 +44,7 @@ public class ViewHolderDetector extends Detector implements UastScanner { private static final Implementation IMPLEMENTATION = new Implementation( ViewHolderDetector.class, - Scope.JAVA_FILE_SCOPE); + Scope.SOURCE_FILE_SCOPE); /** Using a view inflater unconditionally in an AdapterView */ public static final Issue ISSUE = Issue.create( @@ -109,32 +108,8 @@ public class ViewHolderDetector extends Detector implements UastScanner { if (GET_VIEW.equals(node.getName())) { List parameters = node.getValueParameters(); if (parameters.size() == 3) { - Iterator iterator = parameters.iterator(); - if (!iterator.hasNext()) { - return false; - } - - UVariable first = iterator.next(); - if (!first.getType().isInt()) { - return false; - } - - if (!iterator.hasNext()) { - return false; - } - - UVariable second = iterator.next(); - if (!second.getType().matchesFqName(CLASS_VIEW)) { - return false; - } - - if (!iterator.hasNext()) { - return false; - } - - UVariable third = iterator.next(); //noinspection RedundantIfStatement - if (!third.getType().matchesFqName(CLASS_VIEWGROUP)) { + if (!parameters.get(2).getType().matchesFqName(CLASS_VIEWGROUP)) { return false; } @@ -202,7 +177,7 @@ public class ViewHolderDetector extends Detector implements UastScanner { + "Should use View Holder pattern (use recycled view passed " + "into this method as the second parameter) for smoother " + "scrolling"; - mContext.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + mContext.report(ISSUE, node, mContext.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java index 40c7a22835b..7f13c246df6 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ViewTypeDetector.java @@ -54,7 +54,6 @@ import com.google.common.collect.Sets; import org.jetbrains.uast.*; import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastScanner; import org.w3c.dom.Attr; import org.w3c.dom.Document; @@ -98,7 +97,7 @@ public class ViewTypeDetector extends ResourceXmlDetector implements UastScanner new Implementation( ViewTypeDetector.class, EnumSet.of(Scope.ALL_RESOURCE_FILES, Scope.ALL_SOURCE_FILES), - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Flag used to do no work if we're running in incremental mode in a .java file without * a client supporting project resources */ @@ -168,7 +167,7 @@ public class ViewTypeDetector extends ResourceXmlDetector implements UastScanner } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { JavaContext lintContext = context.getLintContext(); LintClient client = lintContext.getClient(); if (mIgnore == Boolean.TRUE) { @@ -193,42 +192,42 @@ public class ViewTypeDetector extends ResourceXmlDetector implements UastScanner // TODO: Do flow analysis as in the StringFormatDetector in order // to handle variable references too if (first instanceof UQualifiedExpression) { - String resource = first.renderString(); - if (resource.startsWith("R.id.")) { //$NON-NLS-1$ - String id = ((UQualifiedExpression) first).getSelector().renderString(); + if (UastUtils.startsWithQualified(first, "R.id")) { //$NON-NLS-1$ + String id = ((UQualifiedExpression) first).getSelectorAsIdentifier(); - if (client.supportsProjectResources()) { - AbstractResourceRepository resources = client - .getProjectResources(lintContext.getMainProject(), true); - if (resources == null) { - return; - } + if (id != null) { + if (client.supportsProjectResources()) { + AbstractResourceRepository resources = client + .getProjectResources(lintContext.getMainProject(), true); + if (resources == null) { + return; + } - List items = resources.getResourceItem(ResourceType.ID, - id); - if (items != null && !items.isEmpty()) { - Set compatible = Sets.newHashSet(); - for (ResourceItem item : items) { - Collection tags = getViewTags(lintContext, item); - if (tags != null) { - compatible.addAll(tags); + List items = resources.getResourceItem(ResourceType.ID, id); + if (items != null && !items.isEmpty()) { + Set compatible = Sets.newHashSet(); + for (ResourceItem item : items) { + Collection tags = getViewTags(lintContext, item); + if (tags != null) { + compatible.addAll(tags); + } + } + if (!compatible.isEmpty()) { + ArrayList layoutTypes = Lists.newArrayList(compatible); + checkCompatible(lintContext, castType, null, layoutTypes, cast); } } - if (!compatible.isEmpty()) { - ArrayList layoutTypes = Lists.newArrayList(compatible); + } else { + Object types = mIdToViewTag.get(id); + if (types instanceof String) { + String layoutType = (String) types; + checkCompatible(lintContext, castType, layoutType, null, cast); + } else if (types instanceof List) { + @SuppressWarnings("unchecked") + List layoutTypes = (List) types; checkCompatible(lintContext, castType, null, layoutTypes, cast); } } - } else { - Object types = mIdToViewTag.get(id); - if (types instanceof String) { - String layoutType = (String) types; - checkCompatible(lintContext, castType, layoutType, null, cast); - } else if (types instanceof List) { - @SuppressWarnings("unchecked") - List layoutTypes = (List) types; - checkCompatible(lintContext, castType, null, layoutTypes, cast); - } } } } @@ -240,6 +239,8 @@ public class ViewTypeDetector extends ResourceXmlDetector implements UastScanner return (UBinaryExpressionWithType) expression; } else if (expression instanceof UQualifiedExpression) { return findContainingTypeCast(expression.getParent()); + } else if (expression instanceof UParenthesizedExpression) { + return findContainingTypeCast(((UParenthesizedExpression) expression).getExpression()); } else { return null; } @@ -337,7 +338,7 @@ public class ViewTypeDetector extends ResourceXmlDetector implements UastScanner String message = String.format( "Unexpected cast to `%1$s`: layout tag was `%2$s`", castType, layoutType); - context.report(ISSUE, node, UastAndroidUtils.getLocation(node), message); + context.report(ISSUE, node, context.getLocation(node), message); } } } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java index 29f38397482..09675c315f8 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongCallDetector.java @@ -37,7 +37,6 @@ import java.util.Arrays; import java.util.List; import org.jetbrains.uast.*; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; @@ -58,7 +57,7 @@ public class WrongCallDetector extends Detector implements UastScanner { Severity.FATAL, new Implementation( WrongCallDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link WrongCallDetector} */ public WrongCallDetector() { @@ -83,7 +82,7 @@ public class WrongCallDetector extends Detector implements UastScanner { } @Override - public void visitFunctionCall(UastAndroidContext context, UCallExpression node) { + public void visitCall(UastAndroidContext context, UCallExpression node) { // Call is only allowed if it is both only called on the super class (invoke special) // as well as within the same overriding method (e.g. you can't call super.onLayout // from the onMeasure method) @@ -130,7 +129,7 @@ public class WrongCallDetector extends Detector implements UastScanner { // Keep in sync with {@link #getOldValue} and {@link #getNewValue} below! "Suspicious method call; should probably call \"`%1$s`\" rather than \"`%2$s`\"", suggestion, name); - context.report(ISSUE, node, UastAndroidUtils.getLocation(node.getFunctionReference()), message); + context.report(ISSUE, node, context.getLocation(node.getFunctionReference()), message); } /** diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java index 115dbed5469..e9a4226052c 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/WrongImportDetector.java @@ -28,7 +28,6 @@ import com.android.tools.klint.detector.api.Speed; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.UImportStatement; -import org.jetbrains.uast.check.UastAndroidUtils; import org.jetbrains.uast.check.UastAndroidContext; import org.jetbrains.uast.check.UastScanner; import org.jetbrains.uast.visitor.UastVisitor; @@ -62,7 +61,7 @@ public class WrongImportDetector extends Detector implements UastScanner { Severity.WARNING, new Implementation( WrongImportDetector.class, - Scope.JAVA_FILE_SCOPE)); + Scope.SOURCE_FILE_SCOPE)); /** Constructs a new {@link WrongImportDetector} check */ public WrongImportDetector() { @@ -92,7 +91,7 @@ public class WrongImportDetector extends Detector implements UastScanner { public boolean visitImportStatement(@NotNull UImportStatement node) { String fqn = node.getFqNameToImport(); if (fqn != null && fqn.equals("android.R")) { //$NON-NLS-1$ - Location location = UastAndroidUtils.getLocation(node); + Location location = mContext.getLocation(node); mContext.report(ISSUE, node, location, "Don't include `android.R` here; use a fully qualified name for " + "each usage instead"); diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java index b5ac6f03bd2..f7da14066d3 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidInspectionExtensionsFactory.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.intellij.codeInspection.HTMLComposer; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java index 6f2ff8ed248..fc4d8d698bd 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintExternalAnnotator.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.SdkConstants; @@ -139,14 +123,14 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator pair = + final Pair pair = AndroidLintUtil.getHighlighLevelAndInspection(project, issue, file); if (pair == null) { continue; } - final org.jetbrains.android.inspections.klint.AndroidLintInspectionBase inspection = pair.getFirst(); + final AndroidLintInspectionBase inspection = pair.getFirst(); HighlightDisplayLevel displayLevel = pair.getSecond(); if (inspection != null) { @@ -259,7 +242,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator" + DaemonBundle.message("inspection.extended.description") - + " " + getShowMoreShortCut(); + + "href=\"#lint/" + issue.getId() + "\"" + + (UIUtil.isUnderDarcula() ? " color=\"7AB4C9\" " : "") + + ">" + DaemonBundle.message("inspection.extended.description") + + " " + getShowMoreShortCut(); String tooltip = XmlStringUtil.wrapInHtml(RAW.convertTo(message, HTML) + link); try { @@ -333,7 +316,7 @@ public class AndroidLintExternalAnnotator extends ExternalAnnotator() { @Override public String compute() { diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java index ff4c4001953..2984e790220 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintGlobalInspectionContext.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.tools.klint.client.api.LintDriver; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java index d85ba973410..24a070651dd 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionBase.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.annotations.concurrency.GuardedBy; @@ -215,8 +199,7 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { if (AndroidLintExternalAnnotator.INCLUDE_IDEA_SUPPRESS_ACTIONS) { final List result = new ArrayList(); result.add(suppressLintQuickFix); - result.addAll(Arrays.asList( - BatchSuppressManager.SERVICE.getInstance().createBatchSuppressActions(HighlightDisplayKey.find(getShortName())))); + result.addAll(Arrays.asList(BatchSuppressManager.SERVICE.getInstance().createBatchSuppressActions(HighlightDisplayKey.find(getShortName())))); result.addAll(Arrays.asList(new XmlSuppressableInspectionTool.SuppressTagStatic(getShortName()), new XmlSuppressableInspectionTool.SuppressForFile(getShortName()))); return result.toArray(new SuppressQuickFix[result.size()]); @@ -250,13 +233,7 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { } @Override - public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement myElement = descriptor.getPsiElement(); - PsiFile file = PsiTreeUtil.getParentOfType(myElement, PsiFile.class, false); - if (file != null) { - new SuppressLintIntentionAction(myIssue.getId(), myElement).invoke(project, null, file); - } - } + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {} } @TestOnly @@ -454,9 +431,9 @@ public abstract class AndroidLintInspectionBase extends GlobalInspectionTool { } /** - * A {@link ProblemDescriptor} for image and directory files. This is + * A {@link com.intellij.codeInspection.ProblemDescriptor} for image and directory files. This is * necessary because the {@link InspectionManager}'s createProblemDescriptor methods - * all use {@link ProblemDescriptorBase} where in the constructor + * all use {@link com.intellij.codeInspection.ProblemDescriptorBase} where in the constructor * it insists that the start and end {@link PsiElement} instances must have a valid * text range, which does not apply for images. *

diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java index 6ec61151641..12ae6e4de2e 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintInspectionToolProvider.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.tools.klint.checks.*; @@ -67,12 +51,6 @@ public class AndroidLintInspectionToolProvider { } } - public static class AndroidKLintFullBackupContentInspection extends AndroidLintInspectionBase { - public AndroidKLintFullBackupContentInspection() { - super(AndroidBundle.message("android.lint.inspections.full.backup.content"), FullBackupContentDetector.ISSUE); - } - } - public static class AndroidKLintGetInstanceInspection extends AndroidLintInspectionBase { public AndroidKLintGetInstanceInspection() { super(AndroidBundle.message("android.lint.inspections.get.instance"), CipherGetInstanceDetector.ISSUE); @@ -139,12 +117,6 @@ public class AndroidLintInspectionToolProvider { } } - public static class AndroidKLintAppCompatResourceInspection extends AndroidLintInspectionBase { - public AndroidKLintAppCompatResourceInspection() { - super(AndroidBundle.message("android.lint.inspections.app.compat.resource"), AppCompatResourceDetector.ISSUE); - } - } - public static class AndroidKLintAssertInspection extends AndroidLintInspectionBase { public AndroidKLintAssertInspection() { super(AndroidBundle.message("android.lint.inspections.assert"), AssertDetector.ISSUE); @@ -271,12 +243,6 @@ public class AndroidLintInspectionToolProvider { } } - public static class AndroidKLintNfcTechWhitespaceInspection extends AndroidLintInspectionBase { - public AndroidKLintNfcTechWhitespaceInspection() { - super(AndroidBundle.message("android.lint.inspections.nfc.tech.whitespace"), NfcTechListDetector.ISSUE); - } - } - public static class AndroidKLintExportedServiceInspection extends AndroidLintInspectionBase { public AndroidKLintExportedServiceInspection() { super(AndroidBundle.message("android.lint.inspections.exported.service"), SecurityDetector.EXPORTED_SERVICE); @@ -441,12 +407,6 @@ public class AndroidLintInspectionToolProvider { } } - public static class AndroidKLintMenuTitleInspection extends AndroidLintInspectionBase { - public AndroidKLintMenuTitleInspection() { - super(AndroidBundle.message("android.lint.inspections.menu.title"), TitleDetector.ISSUE); - } - } - public static class AndroidKLintOverrideAbstractInspection extends AndroidLintInspectionBase { public AndroidKLintOverrideAbstractInspection() { super(AndroidBundle.message("android.lint.inspections.override.abstract"), OverrideConcreteDetector.ISSUE); @@ -459,12 +419,6 @@ public class AndroidLintInspectionToolProvider { } } - public static class AndroidKLintRegisteredInspection extends AndroidLintInspectionBase { - public AndroidKLintRegisteredInspection() { - super(AndroidBundle.message("android.lint.inspections.registered"), RegistrationDetector.ISSUE); - } - } - public static class AndroidKLintRequiredSizeInspection extends AndroidLintInspectionBase { public AndroidKLintRequiredSizeInspection() { super(AndroidBundle.message("android.lint.inspections.required.size"), RequiredAttributeDetector.ISSUE); diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java index 29c44f17e74..d5690b77072 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintQuickFix.java @@ -1,32 +1,16 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; public interface AndroidLintQuickFix { - AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0]; + AndroidLintQuickFix[] EMPTY_ARRAY = new AndroidLintQuickFix[0]; - void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context); + void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context); - boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.ContextType contextType); + boolean isApplicable(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.ContextType contextType); - @NotNull - String getName(); + @NotNull + String getName(); } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java index b95935ff428..4682520d9e7 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidLintUtil.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.tools.klint.detector.api.Issue; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt index 7bf38c07e49..b397078d556 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidModelFacade.kt @@ -1,35 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint import com.android.builder.model.AndroidProject diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java index c1e5827318a..a83fd52060a 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/AndroidQuickfixContexts.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.intellij.openapi.editor.Editor; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java index edbbe97d166..1c0ca05feec 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/DomPsiParser.java @@ -57,13 +57,13 @@ class DomPsiParser extends XmlParser { @Override public int getNodeStartOffset(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); + TextRange textRange = DomPsiConverter.getTextRange(node); return textRange.getStartOffset(); } @Override public int getNodeEndOffset(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); + TextRange textRange = DomPsiConverter.getTextRange(node); return textRange.getEndOffset(); } @@ -92,7 +92,7 @@ class DomPsiParser extends XmlParser { XmlFile xmlFile = (XmlFile)psiFile; try { - return org.jetbrains.android.inspections.klint.DomPsiConverter.convert(xmlFile); + return DomPsiConverter.convert(xmlFile); } catch (Throwable t) { myClient.log(t, "Failed converting PSI parse tree to DOM for file %1$s", context.file.getPath()); @@ -103,7 +103,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getLocation(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); + TextRange textRange = DomPsiConverter.getTextRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(context.file, start, end); @@ -112,7 +112,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getLocation(@NonNull XmlContext context, @NonNull Node node, int startDelta, int endDelta) { - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(node); + TextRange textRange = DomPsiConverter.getTextRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset() + startDelta); Position end = new DefaultPosition(-1, -1, textRange.getStartOffset() + endDelta); return Location.create(context.file, start, end); @@ -121,7 +121,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getNameLocation(@NonNull XmlContext context, @NonNull Node node) { - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextNameRange(node); + TextRange textRange = DomPsiConverter.getTextNameRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(context.file, start, end); @@ -130,7 +130,7 @@ class DomPsiParser extends XmlParser { @NonNull @Override public Location getValueLocation(@NonNull XmlContext context, @NonNull Attr node) { - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextValueRange(node); + TextRange textRange = DomPsiConverter.getTextValueRange(node); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(context.file, start, end); @@ -163,7 +163,7 @@ class DomPsiParser extends XmlParser { } }); } - TextRange textRange = org.jetbrains.android.inspections.klint.DomPsiConverter.getTextRange(myNode); + TextRange textRange = DomPsiConverter.getTextRange(myNode); Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); return Location.create(myFile, start, end); diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java index c211eb38b80..e27d8260e5a 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintClient.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.annotations.NonNull; @@ -30,6 +14,13 @@ import com.android.tools.idea.rendering.LocalResourceRepository; import com.android.tools.idea.sdk.IdeSdks; import com.android.tools.klint.client.api.*; import com.android.tools.klint.checks.ApiLookup; +import com.android.tools.klint.detector.api.Context; +import com.android.tools.klint.detector.api.DefaultPosition; +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.Location; +import com.android.tools.klint.detector.api.Position; +import com.android.tools.klint.detector.api.Severity; +import com.android.tools.klint.detector.api.TextFormat; import com.intellij.analysis.AnalysisScope; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; @@ -77,10 +68,10 @@ import java.util.Map; import static com.android.tools.klint.detector.api.TextFormat.RAW; /** - * Implementation of the {@linkplain com.android.tools.klint.client.api.LintClient} API for executing lint within the IDE: + * Implementation of the {@linkplain LintClient} API for executing lint within the IDE: * reading files, reporting issues, logging errors, etc. */ -public class IntellijLintClient extends com.android.tools.klint.client.api.LintClient implements Disposable { +public class IntellijLintClient extends LintClient implements Disposable { protected static final Logger LOG = Logger.getInstance("#org.jetbrains.android.inspections.IntellijLintClient"); @NonNull protected Project myProject; @@ -96,9 +87,9 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC /** Creates a lint client for batch inspections */ public static IntellijLintClient forBatch(@NotNull Project project, - @NotNull Map>> problemMap, + @NotNull Map>> problemMap, @NotNull AnalysisScope scope, - @NotNull List issues) { + @NotNull List issues) { return new BatchLintClient(project, problemMap, scope, issues); } @@ -139,7 +130,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC } @Override - public com.android.tools.klint.client.api.Configuration getConfiguration(@NonNull com.android.tools.klint.detector.api.Project project) { + public Configuration getConfiguration(@NonNull com.android.tools.klint.detector.api.Project project) { if (project.isGradleProject() && project.isAndroidProject() && !project.isLibrary()) { AndroidProject model = project.getGradleProjectModel(); if (model != null) { @@ -147,30 +138,30 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC LintOptions lintOptions = model.getLintOptions(); final Map overrides = lintOptions.getSeverityOverrides(); if (overrides != null && !overrides.isEmpty()) { - return new com.android.tools.klint.client.api.DefaultConfiguration(this, project, null) { + return new DefaultConfiguration(this, project, null) { @NonNull @Override - public com.android.tools.klint.detector.api.Severity getSeverity(@NonNull com.android.tools.klint.detector.api.Issue issue) { + public Severity getSeverity(@NonNull Issue issue) { Integer severity = overrides.get(issue.getId()); if (severity != null) { switch (severity.intValue()) { case LintOptions.SEVERITY_FATAL: - return com.android.tools.klint.detector.api.Severity.FATAL; + return Severity.FATAL; case LintOptions.SEVERITY_ERROR: - return com.android.tools.klint.detector.api.Severity.ERROR; + return Severity.ERROR; case LintOptions.SEVERITY_WARNING: - return com.android.tools.klint.detector.api.Severity.WARNING; + return Severity.WARNING; case LintOptions.SEVERITY_INFORMATIONAL: - return com.android.tools.klint.detector.api.Severity.INFORMATIONAL; + return Severity.INFORMATIONAL; case LintOptions.SEVERITY_IGNORE: default: - return com.android.tools.klint.detector.api.Severity.IGNORE; + return Severity.IGNORE; } } // This is a LIST lookup. I should make this faster! if (!getIssues().contains(issue)) { - return com.android.tools.klint.detector.api.Severity.IGNORE; + return Severity.IGNORE; } return super.getSeverity(issue); @@ -182,25 +173,25 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC } } } - return new com.android.tools.klint.client.api.DefaultConfiguration(this, project, null) { + return new DefaultConfiguration(this, project, null) { @Override - public boolean isEnabled(@NonNull com.android.tools.klint.detector.api.Issue issue) { + public boolean isEnabled(@NonNull Issue issue) { return getIssues().contains(issue) && super.isEnabled(issue); } }; } @Override - public void report(@NonNull com.android.tools.klint.detector.api.Context context, - @NonNull com.android.tools.klint.detector.api.Issue issue, - @NonNull com.android.tools.klint.detector.api.Severity severity, - @Nullable com.android.tools.klint.detector.api.Location location, + public void report(@NonNull Context context, + @NonNull Issue issue, + @NonNull Severity severity, + @Nullable Location location, @NonNull String message, - @NonNull com.android.tools.klint.detector.api.TextFormat format) { + @NonNull TextFormat format) { assert false : message; } - @NonNull protected List getIssues() { + @NonNull protected List getIssues() { return Collections.emptyList(); } @@ -214,9 +205,9 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC * linked location, and so on.This is necessary since IntelliJ problems don't have secondary locations; instead, we create one * problem for each location associated with the lint error. */ - protected void reportSecondary(@NonNull com.android.tools.klint.detector.api.Context context, @NonNull com.android.tools.klint.detector.api.Issue issue, @NonNull com.android.tools.klint.detector.api.Severity severity, @NonNull com.android.tools.klint.detector.api.Location location, - @NonNull String message, @NonNull com.android.tools.klint.detector.api.TextFormat format) { - com.android.tools.klint.detector.api.Location secondary = location.getSecondary(); + protected void reportSecondary(@NonNull Context context, @NonNull Issue issue, @NonNull Severity severity, @NonNull Location location, + @NonNull String message, @NonNull TextFormat format) { + Location secondary = location.getSecondary(); if (secondary != null) { if (secondary.getMessage() != null) { message = message + " (" + secondary.getMessage() + ")"; @@ -226,14 +217,14 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC } @Override - public void log(@NonNull com.android.tools.klint.detector.api.Severity severity, @Nullable Throwable exception, @Nullable String format, @Nullable Object... args) { - if (severity == com.android.tools.klint.detector.api.Severity.ERROR || severity == com.android.tools.klint.detector.api.Severity.FATAL) { + public void log(@NonNull Severity severity, @Nullable Throwable exception, @Nullable String format, @Nullable Object... args) { + if (severity == Severity.ERROR || severity == Severity.FATAL) { if (format != null) { LOG.error(String.format(format, args), exception); } else if (exception != null) { LOG.error(exception); } - } else if (severity == com.android.tools.klint.detector.api.Severity.WARNING) { + } else if (severity == Severity.WARNING) { if (format != null) { LOG.warn(String.format(format, args), exception); } else if (exception != null) { @@ -249,7 +240,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC } @Override - public com.android.tools.klint.client.api.XmlParser getXmlParser() { + public XmlParser getXmlParser() { return new DomPsiParser(this); } @@ -453,35 +444,35 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC @NonNull @Override - protected List getIssues() { + protected List getIssues() { return myState.getIssues(); } @Override - public void report(@NonNull com.android.tools.klint.detector.api.Context context, - @NonNull com.android.tools.klint.detector.api.Issue issue, - @NonNull com.android.tools.klint.detector.api.Severity severity, - @Nullable com.android.tools.klint.detector.api.Location location, + public void report(@NonNull Context context, + @NonNull Issue issue, + @NonNull Severity severity, + @Nullable Location location, @NonNull String message, - @NonNull com.android.tools.klint.detector.api.TextFormat format) { + @NonNull TextFormat format) { if (location != null) { final File file = location.getFile(); final VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); if (myState.getMainFile().equals(vFile)) { - final com.android.tools.klint.detector.api.Position start = location.getStart(); - final com.android.tools.klint.detector.api.Position end = location.getEnd(); + final Position start = location.getStart(); + final Position end = location.getEnd(); final TextRange textRange = start != null && end != null && start.getOffset() <= end.getOffset() ? new TextRange(start.getOffset(), end.getOffset()) : TextRange.EMPTY_RANGE; - com.android.tools.klint.detector.api.Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; + Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; message = format.convertTo(message, RAW); - myState.getProblems().add(new org.jetbrains.android.inspections.klint.ProblemData(issue, message, textRange, configuredSeverity)); + myState.getProblems().add(new ProblemData(issue, message, textRange, configuredSeverity)); } - com.android.tools.klint.detector.api.Location secondary = location.getSecondary(); + Location secondary = location.getSecondary(); if (secondary != null && myState.getMainFile().equals(LocalFileSystem.getInstance().findFileByIoFile(secondary.getFile()))) { reportSecondary(context, issue, severity, location, message, format); } @@ -560,7 +551,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC public List getResourceFolders(@NonNull com.android.tools.klint.detector.api.Project project) { AndroidFacet facet = AndroidFacet.getInstance(myState.getModule()); if (facet != null) { - return org.jetbrains.android.inspections.klint.IntellijLintUtils.getResourceDirectories(facet); + return IntellijLintUtils.getResourceDirectories(facet); } return super.getResourceFolders(project); } @@ -568,14 +559,14 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC /** Lint client used for batch operations */ private static class BatchLintClient extends IntellijLintClient { - private final Map>> myProblemMap; + private final Map>> myProblemMap; private final AnalysisScope myScope; - private final List myIssues; + private final List myIssues; public BatchLintClient(@NotNull Project project, - @NotNull Map>> problemMap, + @NotNull Map>> problemMap, @NotNull AnalysisScope scope, - @NotNull List issues) { + @NotNull List issues) { super(project); myProblemMap = problemMap; myScope = scope; @@ -591,17 +582,17 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC @NonNull @Override - protected List getIssues() { + protected List getIssues() { return myIssues; } @Override - public void report(@NonNull com.android.tools.klint.detector.api.Context context, - @NonNull com.android.tools.klint.detector.api.Issue issue, - @NonNull com.android.tools.klint.detector.api.Severity severity, - @Nullable com.android.tools.klint.detector.api.Location location, + public void report(@NonNull Context context, + @NonNull Issue issue, + @NonNull Severity severity, + @Nullable Location location, @NonNull String message, - @NonNull com.android.tools.klint.detector.api.TextFormat format) { + @NonNull TextFormat format) { VirtualFile vFile = null; File file = null; @@ -642,31 +633,31 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC if (inScope) { file = new File(PathUtil.getCanonicalPath(file.getPath())); - Map> file2ProblemList = myProblemMap.get(issue); + Map> file2ProblemList = myProblemMap.get(issue); if (file2ProblemList == null) { - file2ProblemList = new HashMap>(); + file2ProblemList = new HashMap>(); myProblemMap.put(issue, file2ProblemList); } - List problemList = file2ProblemList.get(file); + List problemList = file2ProblemList.get(file); if (problemList == null) { - problemList = new ArrayList(); + problemList = new ArrayList(); file2ProblemList.put(file, problemList); } TextRange textRange = TextRange.EMPTY_RANGE; if (location != null) { - final com.android.tools.klint.detector.api.Position start = location.getStart(); - final com.android.tools.klint.detector.api.Position end = location.getEnd(); + final Position start = location.getStart(); + final Position end = location.getEnd(); if (start != null && end != null && start.getOffset() <= end.getOffset()) { textRange = new TextRange(start.getOffset(), end.getOffset()); } } - com.android.tools.klint.detector.api.Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; + Severity configuredSeverity = severity != issue.getDefaultSeverity() ? severity : null; message = format.convertTo(message, RAW); - problemList.add(new org.jetbrains.android.inspections.klint.ProblemData(issue, message, textRange, configuredSeverity)); + problemList.add(new ProblemData(issue, message, textRange, configuredSeverity)); if (location != null && location.getSecondary() != null) { reportSecondary(context, issue, severity, location, message, format); @@ -697,7 +688,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC if (module != null) { AndroidFacet facet = AndroidFacet.getInstance(module); if (facet != null) { - return org.jetbrains.android.inspections.klint.IntellijLintUtils.getResourceDirectories(facet); + return IntellijLintUtils.getResourceDirectories(facet); } } return super.getResourceFolders(project); @@ -736,7 +727,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC @NonNull @Override - public com.android.tools.klint.detector.api.Location.Handle createResourceItemHandle(@NonNull ResourceItem item) { + public Location.Handle createResourceItemHandle(@NonNull ResourceItem item) { XmlTag tag = LocalResourceRepository.getItemTag(myProject, item); if (tag != null) { ResourceFile source = item.getSource(); @@ -762,7 +753,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC return super.getResourceVisibilityProvider(); } - private static class LocationHandle implements com.android.tools.klint.detector.api.Location.Handle, Computable { + private static class LocationHandle implements Location.Handle, Computable { private final File myFile; private final XmlElement myNode; private Object myClientData; @@ -774,7 +765,7 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC @NonNull @Override - public com.android.tools.klint.detector.api.Location resolve() { + public Location resolve() { if (!ApplicationManager.getApplication().isReadAccessAllowed()) { return ApplicationManager.getApplication().runReadAction(this); } @@ -791,13 +782,13 @@ public class IntellijLintClient extends com.android.tools.klint.client.api.LintC } } - com.android.tools.klint.detector.api.Position start = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getStartOffset()); - com.android.tools.klint.detector.api.Position end = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getEndOffset()); - return com.android.tools.klint.detector.api.Location.create(myFile, start, end); + Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); + Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); + return Location.create(myFile, start, end); } @Override - public com.android.tools.klint.detector.api.Location compute() { + public Location compute() { return resolve(); } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java index 943ce94b445..0f415867b71 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintIssueRegistry.java @@ -49,16 +49,12 @@ public class IntellijLintIssueRegistry extends BuiltinIssueRegistry { Implementation implementation = issue.getImplementation(); EnumSet scope = implementation.getScope(); Class detectorClass = implementation.getDetectorClass(); - if (detectorClass == RegistrationDetector.class) { - issue.setImplementation(IntellijRegistrationDetector.IMPLEMENTATION); - } else if (detectorClass == ViewTypeDetector.class) { - issue.setImplementation(IntellijViewTypeDetector.IMPLEMENTATION); - } else if (detectorClass == SupportAnnotationDetector.class) { + if (detectorClass == SupportAnnotationDetector.class) { // Handled by the ResourceTypeInspection continue; } else if (scope.contains(Scope.CLASS_FILE) || - scope.contains(Scope.ALL_CLASS_FILES) || - scope.contains(Scope.JAVA_LIBRARIES)) { + scope.contains(Scope.ALL_CLASS_FILES) || + scope.contains(Scope.JAVA_LIBRARIES)) { //noinspection ConstantConditions assert !IntellijLintProject.SUPPORT_CLASS_FILES; // When enabled, adjust this to include class detector based issues diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java index 859e7f97f99..497db06767d 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintRequest.java @@ -73,8 +73,8 @@ public class IntellijLintRequest extends LintRequest { mScope = Scope.infer(projects); //noinspection ConstantConditions - if (!org.jetbrains.android.inspections.klint.IntellijLintProject.SUPPORT_CLASS_FILES && (mScope.contains(Scope.CLASS_FILE) || mScope.contains(Scope.ALL_CLASS_FILES) - || mScope.contains(Scope.JAVA_LIBRARIES))) { + if (!IntellijLintProject.SUPPORT_CLASS_FILES && (mScope.contains(Scope.CLASS_FILE) || mScope.contains(Scope.ALL_CLASS_FILES) + || mScope.contains(Scope.JAVA_LIBRARIES))) { mScope = EnumSet.copyOf(mScope); // make mutable // Can't run class file based checks mScope.remove(Scope.CLASS_FILE); @@ -93,7 +93,7 @@ public class IntellijLintRequest extends LintRequest { if (mProjects == null) { if (myIncremental && myFileList != null && myFileList.size() == 1 && myModules.size() == 1) { Pair pair = - org.jetbrains.android.inspections.klint.IntellijLintProject.createForSingleFile(mLintClient, myFileList.get(0), myModules.get(0)); + IntellijLintProject.createForSingleFile(mLintClient, myFileList.get(0), myModules.get(0)); mProjects = pair.first != null ? Collections.singletonList(pair.first) : Collections.emptyList(); myMainProject = pair.second; @@ -102,7 +102,7 @@ public class IntellijLintRequest extends LintRequest { // and add projects for the gradle libraries and set error reporting to // false on those //mProjects = computeProjects() - mProjects = org.jetbrains.android.inspections.klint.IntellijLintProject.create(mLintClient, myFileList, myModules.toArray(new Module[myModules.size()])); + mProjects = IntellijLintProject.create(mLintClient, myFileList, myModules.toArray(new Module[myModules.size()])); } else { mProjects = super.getProjects(); } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java index 896764b2a31..bdcf145c93d 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijLintUtils.java @@ -20,6 +20,12 @@ import com.android.annotations.NonNull; import com.android.annotations.Nullable; import com.android.builder.model.SourceProvider; import com.android.tools.klint.client.api.LintRequest; +import com.android.tools.klint.detector.api.ClassContext; +import com.android.tools.klint.detector.api.Context; +import com.android.tools.klint.detector.api.DefaultPosition; +import com.android.tools.klint.detector.api.Issue; +import com.android.tools.klint.detector.api.Location; +import com.android.tools.klint.detector.api.Position; import com.google.common.base.Splitter; import com.intellij.debugger.engine.JVMNameUtil; import com.intellij.facet.Facet; @@ -36,9 +42,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.uast.UClass; import org.jetbrains.uast.UElement; @@ -49,9 +53,6 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static com.android.SdkConstants.CONSTRUCTOR_NAME; -import static com.android.SdkConstants.SUPPRESS_ALL; - /** * Common utilities for handling lint within IntelliJ * TODO: Merge with {@link AndroidLintUtil} @@ -60,11 +61,6 @@ public class IntellijLintUtils { private IntellijLintUtils() { } - @NonNls - public static final String SUPPRESS_LINT_FQCN = "android.annotation.SuppressLint"; - @NonNls - public static final String SUPPRESS_WARNINGS_FQCN = "java.lang.SuppressWarnings"; - private static final ProjectSystemId GRADLE_ID = new ProjectSystemId("GRADLE"); /** @@ -75,7 +71,7 @@ public class IntellijLintUtils { * @return the location of the given element */ @NonNull - public static com.android.tools.klint.detector.api.Location getLocation(@NonNull File file, @NonNull PsiElement element) { + public static Location getLocation(@NonNull File file, @NonNull PsiElement element) { //noinspection ConstantConditions assert element.getContainingFile().getVirtualFile() == null || FileUtil.filesEqual(VfsUtilCore.virtualToIoFile(element.getContainingFile().getVirtualFile()), file); @@ -90,19 +86,19 @@ public class IntellijLintUtils { } TextRange textRange = element.getTextRange(); - com.android.tools.klint.detector.api.Position start = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getStartOffset()); - com.android.tools.klint.detector.api.Position end = new com.android.tools.klint.detector.api.DefaultPosition(-1, -1, textRange.getEndOffset()); - return com.android.tools.klint.detector.api.Location.create(file, start, end); + Position start = new DefaultPosition(-1, -1, textRange.getStartOffset()); + Position end = new DefaultPosition(-1, -1, textRange.getEndOffset()); + return Location.create(file, start, end); } /** - * Returns the {@link PsiFile} associated with a given lint {@link com.android.tools.klint.detector.api.Context} + * Returns the {@link PsiFile} associated with a given lint {@link Context} * * @param context the context to look up the file for * @return the corresponding {@link PsiFile}, or null */ @Nullable - public static PsiFile getPsiFile(@NonNull com.android.tools.klint.detector.api.Context context) { + public static PsiFile getPsiFile(@NonNull Context context) { VirtualFile file = VfsUtil.findFileByIoFile(context.file, false); if (file == null) { return null; @@ -115,106 +111,17 @@ public class IntellijLintUtils { return PsiManager.getInstance(project).findFile(file); } - public static boolean isSuppressed(@NonNull UElement element, @NonNull UFile file, @NonNull com.android.tools.klint.detector.api.Issue issue) { - //TODO - return true; - } - - /** - * Returns true if the given issue is suppressed at the given element within the given file - * - * @param element the element to check - * @param file the file containing the element - * @param issue the issue to check - * @return true if the given issue is suppressed - */ - public static boolean isSuppressed(@NonNull PsiElement element, @NonNull PsiFile file, @NonNull com.android.tools.klint.detector.api.Issue issue) { - // Search upwards for suppress lint and suppress warnings annotations - // Search upwards for target api annotations - while (element != null && element != file) { // otherwise it will keep going into directories! - if (element instanceof PsiModifierListOwner) { - PsiModifierListOwner owner = (PsiModifierListOwner)element; - PsiModifierList modifierList = owner.getModifierList(); - if (modifierList != null) { - for (PsiAnnotation annotation : modifierList.getAnnotations()) { - String fqcn = annotation.getQualifiedName(); - if (fqcn.equals(SUPPRESS_LINT_FQCN) || fqcn.equals(SUPPRESS_WARNINGS_FQCN)) { - PsiAnnotationParameterList parameterList = annotation.getParameterList(); - for (PsiNameValuePair pair : parameterList.getAttributes()) { - PsiAnnotationMemberValue v = pair.getValue(); - String text = v.getText().trim(); // UGH! Find better way to access value! - if (text.isEmpty()) { - continue; - } - if (v instanceof PsiLiteral) { - PsiLiteral literal = (PsiLiteral)v; - Object value = literal.getValue(); - if (value instanceof String) { - text = (String) value; - } - } else if (v instanceof PsiArrayInitializerMemberValue) { - PsiArrayInitializerMemberValue mv = (PsiArrayInitializerMemberValue)v; - for (PsiAnnotationMemberValue mmv : mv.getInitializers()) { - if (mmv instanceof PsiLiteral) { - PsiLiteral literal = (PsiLiteral) mmv; - Object value = literal.getValue(); - if (value instanceof String) { - text = (String) value; - break; - } - } - } - } - - if (text != null) { - for (String id : Splitter.on(',').trimResults().split(text)) { - if (id.equals(issue.getId()) || id.equals(SUPPRESS_ALL)) { - return true; - } - } - } - } - } - } - } - } - element = element.getParent(); - } - + public static boolean isSuppressed(@NonNull UElement element, @NonNull UFile file, @NonNull Issue issue) { return false; } - /** Returns the internal method name */ - @NonNull - public static String getInternalMethodName(@NonNull PsiMethod method) { - if (method.isConstructor()) { - return SdkConstants.CONSTRUCTOR_NAME; - } - else { - return method.getName(); - } - } - - @Nullable - public static PsiElement getCallName(@NonNull PsiCallExpression expression) { - PsiElement firstChild = expression.getFirstChild(); - if (firstChild != null) { - PsiElement lastChild = firstChild.getLastChild(); - if (lastChild instanceof PsiIdentifier) { - return lastChild; - } - } - return null; - } - - /** * Computes the internal class name of the given class. * For example, for PsiClass foo.bar.Foo.Bar it returns foo/bar/Foo$Bar. * * @param psiClass the class to look up the internal name for * @return the internal class name - * @see com.android.tools.klint.detector.api.ClassContext#getInternalName(String) + * @see ClassContext#getInternalName(String) */ @Nullable public static String getInternalName(@NonNull PsiClass psiClass) { @@ -232,13 +139,13 @@ public class IntellijLintUtils { if (sig == null) { String qualifiedName = psiClass.getQualifiedName(); if (qualifiedName != null) { - return com.android.tools.klint.detector.api.ClassContext.getInternalName(qualifiedName); + return ClassContext.getInternalName(qualifiedName); } return null; } else if (sig.indexOf('.') != -1) { // Workaround -- ClassUtil doesn't treat this correctly! // .replace('.', '/'); - sig = com.android.tools.klint.detector.api.ClassContext.getInternalName(sig); + sig = ClassContext.getInternalName(sig); } return sig; } @@ -251,134 +158,6 @@ public class IntellijLintUtils { } } - /** - * Computes the internal class name of the given class type. - * For example, for PsiClassType foo.bar.Foo.Bar it returns foo/bar/Foo$Bar. - * - * @param psiClassType the class type to look up the internal name for - * @return the internal class name - * @see com.android.tools.klint.detector.api.ClassContext#getInternalName(String) - */ - @Nullable - public static String getInternalName(@NonNull PsiClassType psiClassType) { - PsiClass resolved = psiClassType.resolve(); - if (resolved != null) { - return getInternalName(resolved); - } - - String className = psiClassType.getClassName(); - if (className != null) { - return com.android.tools.klint.detector.api.ClassContext.getInternalName(className); - } - - return null; - } - - /** - * Computes the internal JVM description of the given method. This is in the same - * format as the ASM desc fields for methods; meaning that a method named foo which for example takes an - * int and a String and returns a void will have description {@code foo(ILjava/lang/String;):V}. - * - * @param method the method to look up the description for - * @param includeName whether the name should be included - * @param includeReturn whether the return type should be included - * @return the internal JVM description for this method - */ - @Nullable - public static String getInternalDescription(@NonNull PsiMethod method, boolean includeName, boolean includeReturn) { - assert !includeName; // not yet tested - assert !includeReturn; // not yet tested - - StringBuilder signature = new StringBuilder(); - - if (includeName) { - if (method.isConstructor()) { - final PsiClass declaringClass = method.getContainingClass(); - if (declaringClass != null) { - final PsiClass outerClass = declaringClass.getContainingClass(); - if (outerClass != null) { - // declaring class is an inner class - if (!declaringClass.hasModifierProperty(PsiModifier.STATIC)) { - if (!appendJvmTypeName(signature, outerClass)) { - return null; - } - } - } - } - signature.append(CONSTRUCTOR_NAME); - } else { - signature.append(method.getName()); - } - } - - signature.append('('); - - for (PsiParameter psiParameter : method.getParameterList().getParameters()) { - if (!appendJvmSignature(signature, psiParameter.getType())) { - return null; - } - } - signature.append(')'); - if (includeReturn) { - if (!method.isConstructor()) { - if (!appendJvmSignature(signature, method.getReturnType())) { - return null; - } - } - else { - signature.append('V'); - } - } - return signature.toString(); - } - - private static boolean appendJvmTypeName(@NonNull StringBuilder signature, @NonNull PsiClass outerClass) { - String className = getInternalName(outerClass); - if (className == null) { - return false; - } - signature.append('L').append(className.replace('.', '/')).append(';'); - return true; - } - - private static boolean appendJvmSignature(@NonNull StringBuilder buffer, @Nullable PsiType type) { - if (type == null) { - return false; - } - final PsiType psiType = TypeConversionUtil.erasure(type); - if (psiType instanceof PsiArrayType) { - buffer.append('['); - appendJvmSignature(buffer, ((PsiArrayType)psiType).getComponentType()); - } - else if (psiType instanceof PsiClassType) { - PsiClass resolved = ((PsiClassType)psiType).resolve(); - if (resolved == null) { - return false; - } - if (!appendJvmTypeName(buffer, resolved)) { - return false; - } - } - else if (psiType instanceof PsiPrimitiveType) { - buffer.append(JVMNameUtil.getPrimitiveSignature(psiType.getCanonicalText())); - } - else { - return false; - } - return true; - } - - public static boolean isProjectReady(AndroidFacet facet) { - try { - return AndroidFacet.class.getMethod("getIdeaAndroidProject", AndroidFacet.class).invoke(facet) != null; - } catch (Exception e) { - try { - return AndroidFacet.class.getMethod("getAndroidModel", AndroidFacet.class).invoke(facet) != null; - } catch (Exception ignored) {} - } - return false; - } - public static AndroidModelFacade getModelFacade(AndroidFacet facet) { return new AndroidModelFacade(facet); } diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java deleted file mode 100644 index b82edc0173e..00000000000 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijRegistrationDetector.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.android.inspections.klint; - -import com.android.tools.klint.checks.RegistrationDetector; -import com.android.tools.klint.detector.api.ClassContext; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Location; -import com.android.tools.klint.detector.api.Scope; -import com.intellij.openapi.application.ApplicationManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.uast.UClass; -import org.jetbrains.uast.UFile; -import org.jetbrains.uast.UastModifier; -import org.jetbrains.uast.UastUtils; -import org.jetbrains.uast.check.UastAndroidContext; -import org.jetbrains.uast.check.UastAndroidUtils; -import org.jetbrains.uast.check.UastScanner; -import org.jetbrains.uast.kinds.UastClassKind; -import org.jetbrains.uast.visitor.UastVisitor; - -import java.util.Collection; -import java.util.EnumSet; - -import static com.android.SdkConstants.*; - -/** - * Intellij-specific version of the {@link RegistrationDetector} which uses the PSI structure - * to check classes. - *

- *

    - *
  • Unit tests, and compare to the bytecode based results
  • - *
- */ -public class IntellijRegistrationDetector extends RegistrationDetector implements UastScanner { - static final Implementation IMPLEMENTATION = new Implementation( - IntellijRegistrationDetector.class, - EnumSet.of(Scope.MANIFEST, Scope.SOURCE_FILE)); - - @Override - public UastVisitor createUastVisitor(final UastAndroidContext context) { - return new UastVisitor() { - @Override - public boolean visitFile(@NotNull UFile file) { - check(context, file); - return true; - } - }; - } - - private void check(final UastAndroidContext context, final UFile file) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - for (UClass clz : file.getClasses()) { - check(context, clz); - } - } - }); - } - - private void check(UastAndroidContext context, UClass clz) { - for (UClass current = clz.getSuperClass(context); current != null; current = current.getSuperClass(context)) { - // Ignore abstract classes - if (clz.hasModifier(UastModifier.ABSTRACT) || clz.getKind() != UastClassKind.CLASS) { - continue; - } - String fqcn = current.getFqName(); - if (fqcn == null) { - continue; - } - if (CLASS_ACTIVITY.equals(fqcn) - || CLASS_SERVICE.equals(fqcn) - || CLASS_CONTENTPROVIDER.equals(fqcn)) { - - String internalName = clz.getInternalName(); - if (internalName == null) { - internalName = IntellijLintUtils.getInternalName(clz); - } - if (internalName == null) { - continue; - } - String frameworkClass = ClassContext.getInternalName(fqcn); - Collection registered = mManifestRegistrations != null ? mManifestRegistrations.get(frameworkClass) : null; - if (registered == null || !registered.contains(internalName)) { - report(context, clz, frameworkClass); - } - break; - } - } - - for (UClass innerClass : clz.getNestedClasses()) { - check(context, innerClass); - } - } - - private static void report(UastAndroidContext context, UClass clz, String internalName) { - // Unlike the superclass, we don't have to check that the tags are compatible; - // IDEA already checks that as part of its XML validation - - if (IntellijLintUtils.isSuppressed(clz, UastUtils.getContainingFile(clz), ISSUE)) { - return; - } - String tag = classToTag(internalName); - Location location = UastAndroidUtils.getLocation(clz); - String fqcn = clz.getFqName(); - if (fqcn == null) { - fqcn = clz.getName(); - } - context.report(ISSUE, clz, location, String.format("The <%1$s> %2$s is not registered in the manifest", tag, fqcn)); - } -} diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java index 4c54a1910bb..6bb95d42253 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/IntellijViewTypeDetector.java @@ -30,8 +30,8 @@ import java.util.Collections; public class IntellijViewTypeDetector extends ViewTypeDetector { static final Implementation IMPLEMENTATION = new Implementation( - IntellijViewTypeDetector.class, - Scope.JAVA_FILE_SCOPE); + IntellijViewTypeDetector.class, + Scope.SOURCE_FILE_SCOPE); @Nullable @Override diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java index ff2aedda337..33abc54e177 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ProblemData.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.tools.klint.detector.api.Issue; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java index 29cc5c66b0b..b122b494b16 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/State.java @@ -1,19 +1,3 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.jetbrains.android.inspections.klint; import com.android.tools.klint.detector.api.Issue; diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java deleted file mode 100644 index ed85df873aa..00000000000 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/SuppressLintIntentionAction.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2010-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.android.inspections.klint; - -import com.google.common.base.Joiner; -import com.google.common.base.Splitter; -import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.codeInsight.FileModificationService; -import com.intellij.codeInsight.intention.AddAnnotationFix; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.icons.AllIcons; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Iconable; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.xml.XmlAttribute; -import com.intellij.psi.xml.XmlFile; -import com.intellij.psi.xml.XmlTag; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.android.util.AndroidBundle; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static com.android.SdkConstants.*; - -/** Intention for adding a {@code @SuppressLint} annotation on the given element for the given id */ -public class SuppressLintIntentionAction implements IntentionAction, Iconable { - private static final String NO_INSPECTION_PREFIX = "//noinspection "; - private final String myId; - private final PsiElement myElement; - - SuppressLintIntentionAction(String id, PsiElement element) { - myId = id; - myElement = element; - } - - @Override - public Icon getIcon(@IconFlags int flags) { - return AllIcons.Actions.Cancel; - } - - @NotNull - @Override - public String getText() { - String id = getLintId(myId); - final PsiFile file = PsiTreeUtil.getParentOfType(myElement, PsiFile.class); - if (file == null) { - return ""; - } else if (file instanceof XmlFile) { - return AndroidBundle.message("android.lint.fix.suppress.lint.api.attr", id); - } else if (file instanceof PsiJavaFile) { - return AndroidBundle.message("android.lint.fix.suppress.lint.api.annotation", id); - } else { - return ""; - } - } - - @NotNull - @Override - public String getFamilyName() { - return getText(); - } - - @Override - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - return true; - } - - @Override - public void invoke(@NotNull Project project, @Nullable Editor editor, @NotNull PsiFile file) throws IncorrectOperationException { - if (file instanceof XmlFile) { - final XmlTag element = PsiTreeUtil.getParentOfType(myElement, XmlTag.class); - if (element == null) { - return; - } - - if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) { - return; - } - String lintId = getLintId(myId); - addSuppressAttribute(project, (XmlFile) file, element, lintId); - } else if (file instanceof PsiJavaFile) { - final PsiModifierListOwner container = - PsiTreeUtil.getParentOfType(myElement, PsiModifierListOwner.class); - if (container == null) { - return; - } - - if (!FileModificationService.getInstance().preparePsiElementForWrite(container)) { - return; - } - final PsiModifierList modifierList = container.getModifierList(); - if (modifierList != null) { - - String lintId = getLintId(myId); - addSuppressAnnotation(project, container, container, lintId); - } - } - } - - /** - * TODO: There is probably an existing utility method somewhere in IntelliJ for this; - * find it and inline. Possible candidate: {@link com.intellij.xml.XmlNamespaceHelper#insertNamespaceDeclaration}. - * See also code in {@link com.intellij.codeInsight.completion.XmlAttributeInsertHandler} for additional useful - * code such as code to pick a unique prefix, look up the prefix from the schema provider etc (which presumably would - * consult {@link org.jetbrains.android.AndroidXmlSchemaProvider}). - * - */ - @NotNull - public static String ensureNamespaceImported(@NotNull Project project, @NotNull XmlFile file, @NotNull String namespaceUri) { - ApplicationManager.getApplication().assertWriteAccessAllowed(); - - final XmlTag rootTag = file.getRootTag(); - - assert rootTag != null; - final XmlElementFactory elementFactory = XmlElementFactory.getInstance(project); - - String prefix = rootTag.getPrefixByNamespace(namespaceUri); - if (prefix != null) { - return prefix; - } - - if (TOOLS_URI.equals(namespaceUri)) { - prefix = TOOLS_PREFIX; - } else if (ANDROID_URI.equals(namespaceUri)) { - prefix = ANDROID_NS_NAME; - } else { - prefix = APP_PREFIX; - } - if (rootTag.getAttribute(XMLNS_PREFIX + prefix) != null) { - String base = prefix; - for (int i = 2; ; i++) { - prefix = base + Integer.toString(i); - if (rootTag.getAttribute(XMLNS_PREFIX + prefix) == null) { - break; - } - } - } - String name = XMLNS_PREFIX + prefix; - final XmlAttribute xmlnsAttr = elementFactory.createXmlAttribute(name, namespaceUri); - final XmlAttribute[] attributes = rootTag.getAttributes(); - XmlAttribute next = attributes.length > 0 ? attributes[0] : null; - for (XmlAttribute attribute : attributes) { - String attributeName = attribute.getName(); - if (!attributeName.startsWith(XMLNS_PREFIX) || attributeName.compareTo(name) > 0) { - next = attribute; - break; - } - } - if (next != null) { - rootTag.addBefore(xmlnsAttr, next); - } - else { - rootTag.add(xmlnsAttr); - } - - return prefix; - } - - static String getLintId(String intentionId) { - String lintId = intentionId; - if (lintId.startsWith("AndroidLint")) { - lintId = lintId.substring("AndroidLint".length()); - } - - return lintId; - } - - @Override - public boolean startInWriteAction() { - return true; - } - - private static void addSuppressAttribute(final Project project, - final XmlFile file, - final XmlTag element, - final String id) throws IncorrectOperationException { - XmlAttribute attribute = element.getAttribute(ATTR_IGNORE, TOOLS_URI); - String value; - if (attribute == null) { - value = id; - } else { - List ids = new ArrayList(); - for (String existing : Splitter.on(',').trimResults().split(attribute.getValue())) { - if (!existing.equals(id)) { - ids.add(existing); - } - } - ids.add(id); - Collections.sort(ids); - value = Joiner.on(',').join(ids); - } - ensureNamespaceImported(project, file, TOOLS_URI); - element.setAttribute(ATTR_IGNORE, TOOLS_URI, value); - } - - // Based on the equivalent code in com.intellij.codeInsight.daemon.impl.actions.SuppressFix - // to add @SuppressWarnings annotations - - private static void addSuppressAnnotation(final Project project, - final PsiElement container, - final PsiModifierListOwner modifierOwner, - final String id) throws IncorrectOperationException { - PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierOwner, FQCN_SUPPRESS_LINT); - final PsiAnnotation newAnnotation = createNewAnnotation(project, container, annotation, id); - if (newAnnotation != null) { - if (annotation != null && annotation.isPhysical()) { - annotation.replace(newAnnotation); - } - else { - final PsiNameValuePair[] attributes = newAnnotation.getParameterList().getAttributes(); - //noinspection ConstantConditions - new AddAnnotationFix(FQCN_SUPPRESS_LINT, modifierOwner, attributes).invoke(project, null /*editor*/, - container.getContainingFile()); - } - } - } - - @Nullable - private static PsiAnnotation createNewAnnotation(@NotNull final Project project, - @NotNull final PsiElement container, - @Nullable final PsiAnnotation annotation, - @NotNull final String id) { - if (annotation != null) { - final String currentSuppressedId = "\"" + id + "\""; - String annotationText = annotation.getText(); - if (!annotationText.contains("{")) { - final PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes(); - if (attributes.length == 1) { - final String suppressedWarnings = attributes[0].getText(); - if (suppressedWarnings.contains(currentSuppressedId)) return null; - return JavaPsiFacade.getInstance(project).getElementFactory().createAnnotationFromText( - "@" + FQCN_SUPPRESS_LINT + "({" + suppressedWarnings + ", " + currentSuppressedId + "})", container); - - } - } - else { - final int curlyBraceIndex = annotationText.lastIndexOf("}"); - if (curlyBraceIndex > 0) { - final String oldSuppressWarning = annotationText.substring(0, curlyBraceIndex); - if (oldSuppressWarning.contains(currentSuppressedId)) return null; - return JavaPsiFacade.getInstance(project).getElementFactory().createAnnotationFromText( - oldSuppressWarning + ", " + currentSuppressedId + "})", container); - } - } - } - else { - return JavaPsiFacade.getInstance(project).getElementFactory() - .createAnnotationFromText("@" + FQCN_SUPPRESS_LINT + "(\"" + id + "\")", container); - } - return null; - } -} diff --git a/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt index 546c3656c94..52b6c140dab 100644 --- a/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt +++ b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastChecker.kt @@ -75,7 +75,7 @@ object UastChecker { override fun visitCallExpression(node: UCallExpression): Boolean { if (applicableFunctionNames.isNotEmpty()) { if (node.kind == FUNCTION_CALL && node.functionName in applicableFunctionNames) { - scanner.visitFunctionCall(context, node) + scanner.visitCall(context, node) } } diff --git a/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java index 061b9bb73de..1940452596d 100644 --- a/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java +++ b/plugins/lint/uast-android/src/org/jetbrains/uast/check/UastScanner.java @@ -30,7 +30,7 @@ public interface UastScanner { List getApplicableSuperClasses(); List getApplicableConstructorTypes(); - void visitFunctionCall(UastAndroidContext context, UCallExpression node); + void visitCall(UastAndroidContext context, UCallExpression node); void visitClass(UastAndroidContext context, UClass node); void visitConstructor(UastAndroidContext context, UCallExpression functionCall, UFunction constructor); diff --git a/plugins/uast-common/src/org/jetbrains/uast/UastUtils.kt b/plugins/uast-common/src/org/jetbrains/uast/UastUtils.kt index 8082c69c6e6..74eaa1d3f9e 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/UastUtils.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/UastUtils.kt @@ -16,15 +16,28 @@ @file:JvmName("UastUtils") package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastClassKind import org.jetbrains.uast.visitor.UastVisitor +internal val ERROR_NAME = "" + +/** + * Returns the containing class of an element. + * + * @return the containing [UClass] element, + * or null if the receiver is null, or it is a top-level declaration. + */ tailrec fun UElement?.getContainingClass(): UClass? { val parent = this?.parent ?: return null if (parent is UClass) return parent return parent.getContainingClass() } +/** + * Returns the containing file of an element. + * + * @return the containing [UFile] element, + * or null if the receiver is null, or the element is not inside a [UFile] (it is abmormal). + */ tailrec fun UElement?.getContainingFile(): UFile? { val parent = this?.parent ?: return null if (parent is UFile) return parent @@ -33,20 +46,45 @@ tailrec fun UElement?.getContainingFile(): UFile? { fun UElement?.getContainingClassOrEmpty() = getContainingClass() ?: UClassNotResolved +/** + * Returns the containing function of an element. + * + * @return the containing [UFunction] element, + * or null if the receiver is null, or the element is not inside a [UFunction]. + */ tailrec fun UElement?.getContainingFunction(): UFunction? { val parent = this?.parent ?: return null if (parent is UFunction) return parent return parent.getContainingFunction() } +/** + * Returns the containing declaration of an element. + * + * @return the containing [UDeclaration] element, + * or null if the receiver is null, or the element is a top-level declaration. + */ tailrec fun UElement?.getContainingDeclaration(): UDeclaration? { val parent = this?.parent ?: return null if (parent is UDeclaration) return parent return parent.getContainingDeclaration() } +/** + * Checks if the element is a top-level declaration. + * + * @return true if the element is a top-level declaration, false otherwise. + */ fun UDeclaration.isTopLevel() = parent is UFile +/** + * Builds the log message for the [UElement.logString] function. + * + * @param firstLine the message line (the interface name, some optional information). + * @param nested nested UElements. Could be `List`, [UElement] or `null`. + * @throws IllegalStateException if the [nested] argument is invalid. + * @return the rendered log string. + */ fun UElement.log(firstLine: String, vararg nested: Any?): String { return (if (firstLine.isBlank()) "" else firstLine + "\n") + nested.joinToString("\n") { when (it) { @@ -74,12 +112,27 @@ fun UClass.findFunctions(name: String) = declarations.filter { it is UFunction & fun UCallExpression.getReceiver(): UExpression? = (this.parent as? UQualifiedExpression)?.receiver +/** + * Resolves the receiver element if it implements [UResolvable]. + * + * @return the resolved element, or null if the element was not resolved, or if the receiver element is not an [UResolvable]. + */ fun UElement.resolveIfCan(context: UastContext): UDeclaration? = (this as? UResolvable)?.resolve(context) -fun UElement.isThrow() = this is USpecialExpressionList && this.kind == UastSpecialExpressionKind.THROW - +/** + * Find an annotation with the required qualified name. + * + * @param fqName the qualified name to search + * @return [UAnnotation] element if the annotation with the specified [fqName] was found, null otherwise. + */ fun UAnnotated.findAnnotation(fqName: String) = annotations.firstOrNull { it.fqName == fqName } +/** + * Get all class declarations (including supertypes). + * + * @param context the Uast context + * @return the list of declarations for the receiver class + */ fun UClass.getAllDeclarations(context: UastContext): List = mutableListOf().apply { this += declarations for (superType in superTypes) { @@ -107,14 +160,6 @@ fun UCallExpression.getQualifiedCallElement(): UExpression { return findParent(parent as? UExpression) ?: this } -val UDeclaration.fqName: String - get() { - val containingFqName = this.getContainingDeclaration()?.fqName - ?: this.getContainingFile()?.packageFqName - val containingFqNameWithDot = containingFqName?.let { it + "." } ?: "" - return containingFqNameWithDot + this.name - } - inline fun UElement.getParentOfType(): T? = getParentOfType(T::class.java) fun UElement.getParentOfType(clazz: Class): T? { @@ -133,10 +178,9 @@ fun UElement.getParentOfType(clazz: Class): T? { fun UClass.findStaticMemberOfType(name: String, type: Class): T? { for (companion in companions) { - val classKind = companion.kind as? UastClassKind.UastCompanionObject ?: continue - if (!classKind.default) continue - - val member = companion.declarations.firstOrNull { it.name == name && type.isInstance(it) } + val member = companion.declarations.firstOrNull { + it.name == name && type.isInstance(it) && it is UModifierOwner && it.hasModifier(UastModifier.STATIC) + } @Suppress("UNCHECKED_CAST") if (member != null) return member as T } @@ -146,4 +190,52 @@ fun UClass.findStaticMemberOfType(name: String, type: Class): T? { it.name == name && it is UModifierOwner && it.hasModifier(UastModifier.STATIC) && type.isInstance(it) } as T +} + +fun UExpression.asQualifiedIdentifiers(): List? { + var error = false + val list = mutableListOf() + fun addIdentifiers(expr: UQualifiedExpression) { + val receiver = expr.receiver + val selector = expr.selector as? USimpleReferenceExpression ?: run { error = true; return } + when (receiver) { + is UQualifiedExpression -> addIdentifiers(receiver) + is USimpleReferenceExpression -> list += receiver.identifier + else -> { + error = true + return + } + } + list += selector.identifier + } + when (this) { + is UQualifiedExpression -> addIdentifiers(this) + is USimpleReferenceExpression -> listOf(identifier) + else -> return null + } + return if (error) null else list +} + +fun UExpression.matchesQualified(fqName: String): Boolean { + val identifiers = this.asQualifiedIdentifiers() ?: return false + val passedIdentifiers = fqName.split('.') + return identifiers == passedIdentifiers +} + +fun UExpression.startsWithQualified(fqName: String): Boolean { + val identifiers = this.asQualifiedIdentifiers() ?: return false + val passedIdentifiers = fqName.split('.') + identifiers.forEachIndexed { i, identifier -> + if (identifier != passedIdentifiers[i]) return false + } + return true +} + +fun UExpression.endsWithQualified(fqName: String): Boolean { + val identifiers = this.asQualifiedIdentifiers() ?: return false + val passedIdentifiers = fqName.split('.') + identifiers.forEachIndexed { i, identifier -> + if (identifier != passedIdentifiers[i]) return false + } + return true } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/UastVisitor.kt b/plugins/uast-common/src/org/jetbrains/uast/UastVisitor.kt index 15d777422dc..5578578d882 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/UastVisitor.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/UastVisitor.kt @@ -46,7 +46,6 @@ abstract class UastVisitor { open fun visitPrefixExpression(node: UPrefixExpression) = visitElement(node) open fun visitPostfixExpression(node: UPostfixExpression) = visitElement(node) open fun visitSpecialExpressionList(node: USpecialExpressionList) = visitElement(node) - open fun visitExpressionList(node: UExpressionList) = visitElement(node) open fun visitIfExpression(node: UIfExpression) = visitElement(node) open fun visitSwitchExpression(node: USwitchExpression) = visitElement(node) open fun visitSwitchClauseExpression(node: USwitchClauseExpression) = visitElement(node) @@ -58,11 +57,66 @@ abstract class UastVisitor { open fun visitLiteralExpression(node: ULiteralExpression) = visitElement(node) open fun visitThisExpression(node: UThisExpression) = visitElement(node) open fun visitSuperExpression(node: USuperExpression) = visitElement(node) + open fun visitReturnExpression(node: UReturnExpression) = visitElement(node) + open fun visitBreakExpression(node: UBreakExpression) = visitElement(node) + open fun visitContinueExpression(node: UContinueExpression) = visitElement(node) + open fun visitThrowExpression(node: UThrowExpression) = visitElement(node) open fun visitArrayAccessExpression(node: UArrayAccessExpression) = visitElement(node) open fun visitCallableReferenceExpression(node: UCallableReferenceExpression) = visitElement(node) open fun visitClassLiteralExpression(node: UClassLiteralExpression) = visitElement(node) open fun visitLambdaExpression(node: ULambdaExpression) = visitElement(node) open fun visitObjectLiteralExpression(node: UObjectLiteralExpression) = visitElement(node) + + // After + + open fun afterVisitElement(node: UElement) {} + + open fun afterVisitFile(node: UFile) {} + open fun afterVisitImportStatement(node: UImportStatement) {} + open fun afterVisitAnnotation(node: UAnnotation) {} + open fun afterVisitCatchClause(node: UCatchClause) {} + open fun afterVisitType(node: UType) {} + + // Declarations + open fun afterVisitClass(node: UClass) {} + open fun afterVisitFunction(node: UFunction) {} + open fun afterVisitVariable(node: UVariable) {} + + + // Expressions + open fun afterVisitLabeledExpression(node: ULabeledExpression) {} + open fun afterVisitDeclarationsExpression(node: UDeclarationsExpression) {} + open fun afterVisitBlockExpression(node: UBlockExpression) {} + open fun afterVisitQualifiedExpression(node: UQualifiedExpression) {} + open fun afterVisitSimpleReferenceExpression(node: USimpleReferenceExpression) {} + open fun afterVisitCallExpression(node: UCallExpression) {} + open fun afterVisitBinaryExpression(node: UBinaryExpression) {} + open fun afterVisitBinaryExpressionWithType(node: UBinaryExpressionWithType) {} + open fun afterVisitParenthesizedExpression(node: UParenthesizedExpression) {} + open fun afterVisitUnaryExpression(node: UUnaryExpression) {} + open fun afterVisitPrefixExpression(node: UPrefixExpression) {} + open fun afterVisitPostfixExpression(node: UPostfixExpression) {} + open fun afterVisitSpecialExpressionList(node: USpecialExpressionList) {} + open fun afterVisitIfExpression(node: UIfExpression) {} + open fun afterVisitSwitchExpression(node: USwitchExpression) {} + open fun afterVisitSwitchClauseExpression(node: USwitchClauseExpression) {} + open fun afterVisitWhileExpression(node: UWhileExpression) {} + open fun afterVisitDoWhileExpression(node: UDoWhileExpression) {} + open fun afterVisitForExpression(node: UForExpression) {} + open fun afterVisitForEachExpression(node: UForEachExpression) {} + open fun afterVisitTryExpression(node: UTryExpression) {} + open fun afterVisitLiteralExpression(node: ULiteralExpression) {} + open fun afterVisitThisExpression(node: UThisExpression) {} + open fun afterVisitSuperExpression(node: USuperExpression) {} + open fun afterVisitReturnExpression(node: UReturnExpression) {} + open fun afterVisitBreakExpression(node: UBreakExpression) {} + open fun afterVisitContinueExpression(node: UContinueExpression) {} + open fun afterVisitThrowExpression(node: UThrowExpression) {} + open fun afterVisitArrayAccessExpression(node: UArrayAccessExpression) {} + open fun afterVisitCallableReferenceExpression(node: UCallableReferenceExpression) {} + open fun afterVisitClassLiteralExpression(node: UClassLiteralExpression) {} + open fun afterVisitLambdaExpression(node: ULambdaExpression) {} + open fun afterVisitObjectLiteralExpression(node: UObjectLiteralExpression) {} } object EmptyUastVisitor : UastVisitor() \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UAnnotation.kt b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UAnnotation.kt index e4de5de6dca..1a5f7cee61d 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UAnnotation.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UAnnotation.kt @@ -17,24 +17,59 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + Represents an applied JVM annotation. + */ interface UAnnotation : UElement, UNamed, UFqNamed { + /** + * Returns the list of named value arguments. + */ val valueArguments: List + /** + * Returns the class of this annotation. + * + * @return annotation class or null if the class was not resolved + */ fun resolve(context: UastContext): UClass? + + /** + * Returns the evaluated value of the argument with the specified name. + * + * @param name name of the annotation value parameter + * @return the argument value + */ fun getValue(name: String) = valueArguments.firstOrNull { it.name == name }?.expression?.evaluate() + /** + * Returns the list of Pair(name, evaluatedValue) for all applied annotation arguments. + * + * @return the list of name-value pairs. + */ fun getValues() = valueArguments.map { Pair(it.name, it.expression.evaluate()) } override fun logString() = log("UAnnotation ($name)") - override fun renderString() = if (valueArguments.isEmpty()) "@$name" else "@$name(" + - valueArguments.joinToString { it.renderString() } + ")" + + override fun renderString(): String { + return if (valueArguments.isEmpty()) + "@$name" + else + "@$name(" + valueArguments.joinToString { it.renderString() } + ")" + } override fun accept(visitor: UastVisitor) { if (visitor.visitAnnotation(this)) return valueArguments.acceptList(visitor) + visitor.afterVisitAnnotation(this) } } +/** + * Represents an annotated element. + */ interface UAnnotated : UElement { + /** + * Returns the list of annotations applied to the current element. + */ val annotations: List } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt index ef79af1bd4a..044777452a4 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UElement.kt @@ -17,39 +17,153 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * The common interface for all Uast elements. + */ interface UElement { + /** + * Returns the element parent. + */ val parent: UElement? + /** + * Returns true if this element is valid, false otherwise. + */ open val isValid: Boolean get() = true - + + /** + * Returns the log string. + * + * Output example (should be something like this): + * UWhileExpression + * UBinaryExpression (>) + * USimpleReferenceExpression (i) + * ULiteralExpression (5) + * UBlockExpression + * UCallExpression (println) + * ULiteralExpression (ABC) + * UPostfixExpression (--) + * USimpleReferenceExpression(i) + * + * @return the expression tree for this element. + * @see [UIfExpression] for example. + */ fun logString(): String + + /** + * Returns the string in pseudo-code. + * + * Output example (should be something like this): + * while (i > 5) { + * println("Hello, world") + * i-- + * } + * + * @return the rendered text. + * @see [UIfExpression] for example. + */ fun renderString(): String = logString() + /** + * Passes the element to the specified visitor. + * + * @param visitor the visitor to pass the element to. + */ fun accept(visitor: UastVisitor) { visitor.visitElement(this) } } +/** + * An interface for the [UElement] which has a name. + */ interface UNamed { + /** + * Returns the name of this element. + */ val name: String + /** + * Checks if the element name is equal to the passed name. + * + * @param name the name to check against + * @return true if the element name is equal to [name], false otherwise. + */ fun matchesName(name: String) = this.name == name } +/** + * An interface for the [UElement] which has a qualified name. + */ interface UFqNamed : UNamed { + /** + * Returns the qualified name of this element, or null if the qualified name was not resolved. + */ val fqName: String? + /** + * Checks if the element qualified name is equal to the passed name. + * + * @param fqName qualified name to check against + * @return true if the element name is not null and is equal to [name], false otherwise. + */ fun matchesFqName(fqName: String) = this.fqName == fqName } +/** + * An interface for the [UElement] which has Uast modifiers. + */ interface UModifierOwner { + /** + * Checks if the element has the passed modifier. + * + * @param modifier modifier to check + * @return true if the element has [modifier], false otherwise. + */ fun hasModifier(modifier: UastModifier): Boolean } +/** + * An interface for the [UElement] which has a visibility (class, function, variable). + */ +interface UVisibilityOwner { + /** + * Returns the element visibility. + */ + val visibility: UastVisibility +} + +/** + * An inteface for the [UElement] which can be resolved to the its declaration. + */ interface UResolvable { + /** + * Returns the declaration element. + * + * @param context the Uast context + * @return the resolved declaration, or null if the declaration was not resolved. + */ fun resolve(context: UastContext): UDeclaration? + + /** + * Returns the declaration element or an empty Uast declaration element if the declaration was not resolved. + * An empty declaration element should be a singleton. + * [resolveOrEmpty] should not create new elements each time the declaration was not resolved. + * + * @param context the Uast context + * @return the resolved declaration, of an empty error Uast declaration element if the declaration was not resolved. + * + * @see [UDeclarationNotResolved] + */ fun resolveOrEmpty(context: UastContext): UDeclaration = resolve(context) ?: UDeclarationNotResolved + /** + * Returns the declaration [UClass] element of null if the declaration was not resolved. + * + * @param context the Uast context + * @return the resolved [UClass] element, or null if the class was not resolved, + * or if the resolved [UElement] is not an instance of [UClass]. + */ fun resolveClass(context: UastContext): UClass? = resolve(context) as? UClass } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt index e869c61d95d..c2cf5bbb251 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UExpression.kt @@ -24,6 +24,7 @@ interface UExpression : UElement { override fun accept(visitor: UastVisitor) { visitor.visitElement(this) + visitor.afterVisitElement(this) } } @@ -36,6 +37,6 @@ interface NoModifiers : UModifierOwner { override fun hasModifier(modifier: UastModifier) = false } -class EmptyExpression(override val parent: UElement) : UExpression { +class EmptyUExpression(override val parent: UElement) : UExpression { override fun logString() = "EmptyExpression" } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UFile.kt b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UFile.kt index 9c6085eaaf7..30bd6467c60 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/baseElements/UFile.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/baseElements/UFile.kt @@ -17,11 +17,31 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a source file. + * File is the topmost element of the UElement hierarchy. + * Files should not be nested, thus the [parent] property should always return null. + */ interface UFile: UElement { + /** + * Returns the qualified package name of this file. + * Could be an empty string if the package is "default", or null if the package directive is not present in this file. + */ val packageFqName: String? + + /** + * Returns the list of import statements. + */ val importStatements: List + + /** + * Returns the list of declarations in this file (classes, properties, functions, etc). + */ val declarations: List + /** + * Returns list of classes containing in this file. + */ val classes: List get() = declarations.filterIsInstance() @@ -32,12 +52,13 @@ interface UFile: UElement { if (visitor.visitFile(this)) return declarations.acceptList(visitor) importStatements.acceptList(visitor) + visitor.afterVisitFile(this) } override fun logString() = "UFile (package = $packageFqName)\n" + declarations.joinToString("\n") { it.logString().withMargin } override fun renderString() = buildString { - if (packageFqName != null) { + if (!packageFqName.isNullOrBlank()) { appendln("package $packageFqName") appendln() } diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt index 31124fd2b71..bb4509bf8bb 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UDoWhileExpression.kt @@ -17,13 +17,26 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represent a + * + * `do { + * // body + * } while (expr)` + * + * loop expression. + */ interface UDoWhileExpression : ULoopExpression { + /** + * Returns the loop post-condition. + */ val condition: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitDoWhileExpression(this)) return condition.accept(visitor) body.accept(visitor) + visitor.afterVisitDoWhileExpression(this) } override fun renderString() = buildString { diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt index 23979f1066d..051b98aa6a3 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForEachExpression.kt @@ -17,8 +17,24 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a + * + * `for (element : collectionOfElements) { + * // body + * }` + * + * loop expression. + */ interface UForEachExpression : ULoopExpression { + /** + * Returns the loop variable. + */ val variable: UVariable + + /** + * Returns the iterated value (collection, sequence, iterable etc.) + */ val iteratedValue: UExpression override fun accept(visitor: UastVisitor) { @@ -26,6 +42,7 @@ interface UForEachExpression : ULoopExpression { variable.accept(visitor) iteratedValue.accept(visitor) body.accept(visitor) + visitor.afterVisitForEachExpression(this) } override fun renderString() = buildString { diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt index 497d96e8850..044dcba18c7 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UForExpression.kt @@ -17,9 +17,29 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a + * + * `for (initDeclarations; loopCondition; update) { + * // body + * }` + * + * loop expression. + */ interface UForExpression : ULoopExpression { + /** + * Returns the [UExpression] containing variable declarations, or null if the are no variables declared. + */ val declaration: UExpression? + + /** + * Returns the loop condition, or null if the condition is empty. + */ val condition: UExpression? + + /** + * Returns the loop update expression(s). + */ val update: UExpression? override fun accept(visitor: UastVisitor) { @@ -28,6 +48,7 @@ interface UForExpression : ULoopExpression { condition?.accept(visitor) update?.accept(visitor) body.accept(visitor) + visitor.afterVisitForExpression(this) } override fun renderString() = buildString { diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt index 2be73c1b1bb..fdffd3b42d5 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UIfExpression.kt @@ -17,10 +17,40 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents + * + * `if (condition) { + * // do if true + * } else { + * // do if false + * }` + * + * and + * + * `condition : trueExpression ? falseExpression` + * + * condition expressions. + */ interface UIfExpression : UExpression { + /** + * Returns the condition expression. + */ val condition: UExpression + + /** + * Returns the expression which is executed if the condition is true, or null if the expression is empty. + */ val thenBranch: UExpression? + + /** + * Returns the expression which is executed if the condition is false, or null if the expression is empty. + */ val elseBranch: UExpression? + + /** + * Returns true if the expression is ternary (condition ? trueExpression : falseExpression). + */ val isTernary: Boolean override fun accept(visitor: UastVisitor) { @@ -28,9 +58,11 @@ interface UIfExpression : UExpression { condition.accept(visitor) thenBranch?.accept(visitor) elseBranch?.accept(visitor) + visitor.afterVisitIfExpression(this) } override fun logString() = log("UIfExpression", condition, thenBranch, elseBranch) + override fun renderString() = buildString { if (isTernary) { append("(" + condition.renderString() + ")") @@ -42,7 +74,7 @@ interface UIfExpression : UExpression { append("if (${condition.renderString()}) ") thenBranch?.let { append(it.renderString()) } val elseBranch = elseBranch - if (elseBranch != null && elseBranch !is EmptyExpression) { + if (elseBranch != null && elseBranch !is EmptyUExpression) { if (thenBranch !is UBlockExpression) append(" ") append("else ") append(elseBranch.renderString()) diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt index 9c5028250a5..2eb6b03b47b 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/ULoopExpression.kt @@ -15,6 +15,12 @@ */ package org.jetbrains.uast +/** + * Represents the loop expression. + */ interface ULoopExpression : UExpression { + /** + * Returns the loop body [UExpression]. + */ val body: UExpression } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt index 99b0001a52b..80c0871baea 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/USwitchExpression.kt @@ -17,14 +17,35 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a + * + * ` switch (expression) { + * case value1 -> expr1 + * case value2 -> expr2 + * ... + * else -> exprElse + * } + * + * conditional expression. + */ interface USwitchExpression : UExpression { + /* + Returns the expression on which the `switch` expression is performed. + */ val expression: UExpression? + + /* + Returns the switch body. + Body should contain [USwitchClauseExpression] expressions. + */ val body: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitSwitchExpression(this)) return expression?.accept(visitor) body.accept(visitor) + visitor.afterVisitSwitchExpression(this) } override fun logString() = log("USwitchExpression", expression, body) @@ -35,23 +56,47 @@ interface USwitchExpression : UExpression { } } -interface USwitchClauseExpression : UExpression - -interface UExpressionSwitchClauseExpression : USwitchClauseExpression { - val caseValue: UExpression +/** + * Represents a [USwitchExpression] clause. + * [USwitchClauseExpression] does not contain the clause body, + * and the actual body expression should be the next element in the parent expression list. + */ +interface USwitchClauseExpression : UExpression { + /** + * Returns the list of values for this clause, or null if the are no values for this close + * (for example, for the `else` clause). + */ + val caseValues: List? override fun accept(visitor: UastVisitor) { if (visitor.visitSwitchClauseExpression(this)) return - caseValue.accept(visitor) + caseValues?.acceptList(visitor) + visitor.afterVisitSwitchClauseExpression(this) } - override fun renderString() = caseValue.renderString() + " -> " - override fun logString() = log("UExpressionSwitchClauseExpression", caseValue) + override fun renderString() = (caseValues?.joinToString { it.renderString() } ?: "else") + " -> " + override fun logString() = log("USwitchClauseExpression", caseValues) } -interface UDefaultSwitchClauseExpression : USwitchClauseExpression { - override fun logString() = "UDefaultSwitchClause" - override fun renderString() = "else -> " -} +/** + * Represents a [USwitchExpression] clause with the body. + * [USwitchClauseExpressionWithBody], comparing with [USwitchClauseExpression], contains the body expression. + * + * Implementing this interface *is the right way* to support `switch` clauses in your language. + */ +interface USwitchClauseExpressionWithBody : USwitchClauseExpression { + /** + * Returns the body expression for this clause. + */ + val body: UExpression -class SimpleUDefaultSwitchClauseExpression(override val parent: UElement) : UDefaultSwitchClauseExpression \ No newline at end of file + override fun accept(visitor: UastVisitor) { + if (visitor.visitSwitchClauseExpression(this)) return + caseValues?.acceptList(visitor) + body.accept(visitor) + visitor.afterVisitSwitchClauseExpression(this) + } + + override fun renderString() = (caseValues?.joinToString { it.renderString() } ?: "else") + " -> " + body.renderString() + override fun logString() = log("USwitchClauseExpressionWithBody", caseValues, body) +} \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt index 919560f6152..c6c9a491bd1 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UTryExpression.kt @@ -17,10 +17,45 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents + * + * `try { + * // tryClause body + * } catch (e: Type1, Type2 ... TypeN) { + * // catchClause1 body + * } ... { + * finally { + * //finallyBody + * }` + * + * and + * + * `try (resource1, ..., resourceN) { + * // tryClause body + * }` + * + * expressions. + */ interface UTryExpression : UExpression { + /** + * Returns the list of try resources, or null if this expression is not a `try-with-resources` expression. + */ val resources: List? + + /** + * Returns the `try` clause expression. + */ val tryClause: UExpression + + /** + * Returns the `catch` clauses [UCatchClause] expression list. + */ val catchClauses: List + + /** + * Returns the `finally` clause expression, or null if the `finally` clause is absent. + */ val finallyClause: UExpression? override fun accept(visitor: UastVisitor) { @@ -29,13 +64,14 @@ interface UTryExpression : UExpression { tryClause.accept(visitor) catchClauses.acceptList(visitor) finallyClause?.accept(visitor) + visitor.afterVisitTryExpression(this) } override fun renderString() = buildString { append("try ") - appendln(tryClause.renderString()) - catchClauses.forEach { appendln(it.renderString()) } - finallyClause?.let { append("finally ").append(it.renderString()) } + appendln(tryClause.renderString().trim('\n')) + catchClauses.forEach { appendln(it.renderString().trim('\n')) } + finallyClause?.let { append("finally ").append(it.renderString().trim('\n')) } } override fun logString() = "UTryExpression\n" + @@ -44,9 +80,23 @@ interface UTryExpression : UExpression { (finallyClause?.let { it.logString().withMargin } ?: "" ) } +/** + * Represents the `catch` clause in [UTryExpression]. + */ interface UCatchClause : UElement { + /** + * Returns the `catch` clause body expression. + */ val body: UExpression + + /** + * Returns the exception parameter variables for this `catch` clause. + */ val parameters: List + + /** + * Returns the exception types for this `catch` clause. + */ val types: List override fun accept(visitor: UastVisitor) { @@ -54,6 +104,7 @@ interface UCatchClause : UElement { body.accept(visitor) parameters.acceptList(visitor) types.acceptList(visitor) + visitor.afterVisitCatchClause(this) } override fun logString() = log("UCatchClause", body) diff --git a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt index 8ed670e799b..4a354dc9308 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/controlStructures/UWhileExpression.kt @@ -17,13 +17,26 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a + * + * `while (condition) { + * // body + * }` + * + * expression. + */ interface UWhileExpression : ULoopExpression { + /** + * Returns the loop condition. + */ val condition: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitWhileExpression(this)) return condition.accept(visitor) body.accept(visitor) + visitor.afterVisitWhileExpression(this) } override fun renderString() = buildString { diff --git a/plugins/uast-common/src/org/jetbrains/uast/declarations/UClass.kt b/plugins/uast-common/src/org/jetbrains/uast/declarations/UClass.kt index b0bd5f12022..3506e40bbf4 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/declarations/UClass.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/declarations/UClass.kt @@ -15,41 +15,94 @@ */ package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastClassKind import org.jetbrains.uast.visitor.UastVisitor -interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated { - /* The simple class name is only for the debug purposes. Do not check against it in the production code */ +/** + * Represents a JVM class (ordinary class, interface, annotation, singleton, etc.). + */ +interface UClass : UDeclaration, UFqNamed, UModifierOwner, UVisibilityOwner, UAnnotated { + /** + * Returns the class simple (non-qualified) name. + * The simple class name is only for the debug purposes. Do not check against it in the production code. + */ override val name: String + /** + * Returns true if the class is anonymous ([name] in this case should return a placeholder like ""). + */ val isAnonymous: Boolean - val visibility: UastVisibility + + /** + * Returns the class kind (ordinary class, interface, annotation, etc.). + */ val kind: UastClassKind + + /** + * Returns the default type for this class. + */ val defaultType: UType + /** + * Returns the class companions. + */ val companions: List + /** + * Returns the class JVM name, or null if the JVM name is unknown. + */ open val internalName: String? get() = null + /** + * Returns the all supertypes of this class. + */ val superTypes: List + + /** + * Returns class declarations (nested classes, constructors, functions, variables, etc.). + * An empty (default) constructor also should be present. + */ val declarations: List + /** + * Returns nested classes declared in this class. + */ val nestedClasses: List get() = declarations.filterIsInstance() + /** + * Returns functions declared in this class. + */ val functions: List get() = declarations.filterIsInstance() + /** + * Returns properties declared in this class. + */ val properties: List get() = declarations.filterIsInstance() + /** + * Returns constructors declared in this class. + */ @Suppress("UNCHECKED_CAST") val constructors: List get() = declarations.filter { it is UFunction && it.kind == UastFunctionKind.CONSTRUCTOR } as List + /** + * Checks if the class is subclass of another. + * + * @param fqName qualified name of the class to check against + * @return true if the class is the *subclass* of the class with the specified [fqName], + * false if the class qualified name is [fqName] or if the class is not a subclass of the class with the specified [fqName] + */ fun isSubclassOf(fqName: String) : Boolean + /** + * Get the direct superclass of this class. + * + * @return null if the class has not a superclass (java.lang.Object). + */ fun getSuperClass(context: UastContext): UClass? override fun accept(visitor: UastVisitor) { @@ -57,24 +110,24 @@ interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated { nameElement?.accept(visitor) declarations.acceptList(visitor) annotations.acceptList(visitor) + visitor.afterVisitClass(this) } - override fun renderString(): String { - val modifiers = listOf(UastModifier.ABSTRACT, UastModifier.FINAL, UastModifier.STATIC) - .filter { hasModifier(it) }.joinToString(" ") { it.name }.let { if (it.isBlank()) it else "$it " } - - val name = if (isAnonymous) "" else " $name" + override fun renderString() = buildString { + appendWithSpace(visibility.name) + appendWithSpace(renderModifiers()) + appendWithSpace(kind.text) + appendWithSpace(name) val declarations = if (declarations.isEmpty()) "" else buildString { appendln("{") - append(declarations.joinToString("\n") { it.renderString() }.withMargin) + append(declarations.joinToString("\n\n") { it.renderString().trim('\n') }.withMargin) append("\n}") } - - return "${visibility.name} " + modifiers + kind.text + name + " " + declarations + append(declarations) } - override fun logString() = "UClass ($name, kind = ${kind.text})\n" + declarations.logString() + override fun logString() = log("UClass ($name, kind = ${kind.text})", declarations) } object UClassNotResolved : UClass { @@ -88,7 +141,7 @@ object UClassNotResolved : UClass { override val defaultType = UastErrorType override val nameElement = null override val parent = null - override val name = "" + override val name = ERROR_NAME override val fqName = null override val internalName = null diff --git a/plugins/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt b/plugins/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt index 45f5e593454..31f8a295bde 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/declarations/UDeclaration.kt @@ -17,16 +17,38 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a declaration. + */ interface UDeclaration : UElement, UNamed { + /** + * Returns an element for the name node, or null if the node does not exist in the underlying AST (Psi). + */ val nameElement: UElement? + /** + * Checks if the function name is [name], and the function containing class qualified name is [containingClassFqName]. + * + * @param containingClassFqName the required containing class qualified name. + * @param name the function name to check against. + * @return true if the call is a function call, the function name is [name], + * and the qualified name of the function direct containing class is [containingClassFqName], + * false otherwise. + */ + open fun matchesNameWithContaining(containingClassFqName: String, name: String): Boolean { + if (!matchesName(name)) return false + val containingClass = parent as? UClass ?: return false + return containingClass.matchesFqName(containingClassFqName) + } + override fun accept(visitor: UastVisitor) { visitor.visitElement(this) + visitor.afterVisitElement(this) } } object UDeclarationNotResolved : UDeclaration { - override val name = "" + override val name = ERROR_NAME override val nameElement = null override val parent = null diff --git a/plugins/uast-common/src/org/jetbrains/uast/declarations/UFunction.kt b/plugins/uast-common/src/org/jetbrains/uast/declarations/UFunction.kt index aae00f8aea2..9cf8d7f720a 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/declarations/UFunction.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/declarations/UFunction.kt @@ -17,58 +17,109 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor -interface UFunction : UDeclaration, UModifierOwner, UAnnotated { +/** + * Represents a function. + * Function could be a JVM method, a property accessor, a constructor, etc. + */ +interface UFunction : UDeclaration, UModifierOwner, UVisibilityOwner, UAnnotated { + /** + * Returns the function kind. + */ val kind: UastFunctionKind - val valueParameters: List - val valueParameterCount: Int - val typeParameters: List - val typeParameterCount: Int - val returnType: UType? - val body: UExpression - val visibility: UastVisibility + /** + * Returns the function value parameters. + */ + val valueParameters: List + + /** + * Returns the function value parameters count. + * Retrieving the parameter count could be faster than getting the [valueParameters.size], + * because there is no need to create actual [UVariable] instances. + */ + val valueParameterCount: Int + + /** + * Returns the function type parameters. + */ + val typeParameters: List + + /** + * Returns the function type parameter count. + */ + val typeParameterCount: Int + + /** + * Returns the function return type, or null if the function does not have a return type + * (e.g. it is a constructor). + */ + val returnType: UType? + + /** + * Returns the function body expression. + */ + val body: UExpression? + + /** + * Returns the function JVM descriptor (for example, "(ILjava/lang/String;)[I"), or null if the descriptor is unknown. + */ open val bytecodeDescriptor: String? get() = null + + /** + * Get the list of all super functions for this function. + */ fun getSuperFunctions(context: UastContext): List override fun accept(visitor: UastVisitor) { if (visitor.visitFunction(this)) return nameElement?.accept(visitor) valueParameters.acceptList(visitor) - body.accept(visitor) + body?.accept(visitor) annotations.acceptList(visitor) typeParameters.acceptList(visitor) returnType?.accept(visitor) + visitor.afterVisitFunction(this) } - override fun renderString(): String { - val typeParameters = if (typeParameterCount == 0) "" else "<" + typeParameters.joinToString { it.renderString() } + "> " - val valueParameters = valueParameters.joinToString { it.renderString() } - val returnType = returnType?.let { ": " + it.renderString() } ?: "" - val body = when (body) { + override fun renderString(): String = buildString { + appendWithSpace(visibility.name) + appendWithSpace(renderModifiers()) + append("fun ") + if (typeParameterCount > 0) { + append('<').append(typeParameters.joinToString { it.renderString() }).append("> ") + } + append(name) + append('(') + append(valueParameters.joinToString() { it.renderString() }) + append(')') + returnType?.let { append(": " + it.renderString()) } + + val body = body + val bodyRendered = when (body) { + null -> "" is UBlockExpression -> " " + body.renderString() else -> " = " + body.renderString() } - return "${visibility.name} fun " + typeParameters + name + "(" + valueParameters + ")" + returnType + body + append(bodyRendered) } - override fun logString() = "UFunction ($name, kind = ${kind.text}, " + - "paramCount = $valueParameterCount)\n" + body.logString().withMargin + override fun logString() = log("UFunction ($name, kind = ${kind.text}, paramCount = $valueParameterCount)", body) } object UFunctionNotResolved : UFunction { - override val kind = UastFunctionKind("") + override val kind = UastFunctionKind(ERROR_NAME) override val valueParameters = emptyList() override val valueParameterCount = 0 override val typeParameters = emptyList() override val typeParameterCount = 0 override val returnType = null - override val body = EmptyExpression(this) + override val body = null override val visibility = UastVisibility.PRIVATE override val nameElement = null override val parent = null - override val name = "" + override val name = ERROR_NAME override fun hasModifier(modifier: UastModifier) = false override fun getSuperFunctions(context: UastContext) = emptyList() diff --git a/plugins/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt b/plugins/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt index beb39d81a81..74420f5003a 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/declarations/UImportStatement.kt @@ -15,19 +15,33 @@ */ package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastImportKind import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents an import statement. + */ interface UImportStatement : UElement { + /** + * Returns the qualified name to import. + */ val fqNameToImport: String? - val isStarImport: Boolean - val kind: UastImportKind - /* Returns null if isStarImport = true */ + /** + * Returns true is the import is a "star" import (on-demand, all-under). + */ + val isStarImport: Boolean + + /** + * Resolve the import statement to the declaration. + * + * @param context the Uast context + * @return the declaration element, or null if the declaration was not resolved. + */ fun resolve(context: UastContext): UDeclaration? override fun accept(visitor: UastVisitor) { visitor.visitImportStatement(this) + visitor.afterVisitImportStatement(this) } override fun logString() = "UImport ($fqNameToImport)" diff --git a/plugins/uast-common/src/org/jetbrains/uast/declarations/UType.kt b/plugins/uast-common/src/org/jetbrains/uast/declarations/UType.kt index 2198f551c21..3f12e8259ac 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/declarations/UType.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/declarations/UType.kt @@ -17,50 +17,105 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents the type. + * The abstraction is quite simple. Intersection types, union types, platform types are yet to be supported. + */ interface UType : UElement, UNamed, UFqNamed, UAnnotated, UResolvable { - override fun matchesName(name: String) = this.name == name || this.name.endsWith(".$name") - - /* The simple type name is only for the debug purposes. Do not check against it in the production code */ + /** + * Returns the simple (non-qualified) type name. + * The simple type name is only for the debug purposes. Do not check against it in the production code. + */ override val name: String - /* Semantics: returns true if the type is either a boxed or an unboxed, false otherwise */ + /** + * Returns true if the type is either a boxed or an unboxed [Integer], false otherwise. + */ val isInt: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Short], false otherwise. + */ + val isShort: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Long], false otherwise. + */ val isLong: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Float], false otherwise. + */ val isFloat: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Double], false otherwise. + */ val isDouble: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Character], false otherwise. + */ val isChar: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Boolean], false otherwise. + */ val isBoolean: Boolean + + /** + * Returns true if the type is either a boxed or an unboxed [Byte], false otherwise. + */ val isByte: Boolean override fun logString() = "UType ($name)" override fun renderString() = name + /** + * Returns the [UClass] declaration element for this type. + * + * @param context the Uast context + * @return the [UClass] declaration element, or null if the class was not resolved. + */ override fun resolve(context: UastContext): UClass? + override fun resolveOrEmpty(context: UastContext) = resolve(context) ?: UClassNotResolved override fun accept(visitor: UastVisitor) { if (visitor.visitType(this)) return annotations.acceptList(visitor) + visitor.afterVisitType(this) } } +/** + * Represents a type reference. + */ interface UTypeReference : UDeclaration, UResolvable { override fun renderString() = "" override fun logString() = log("UTypeReference") + /** + * Returns the [UClass] declaration for this type reference. + * + * @param context the Uast context + * @return the [UClass] declaration element, or null if the class was not resolved. + */ override fun resolve(context: UastContext): UClass? + override fun resolveOrEmpty(context: UastContext) = resolve(context) ?: UClassNotResolved } object UastErrorType : UType, NoAnnotations { override val isInt = false override val isLong = false + override val isShort = false override val isFloat = false override val isDouble = false override val isChar = false override val isByte = false override val parent = null - override val name = "" + override val name = ERROR_NAME override val fqName = null override val isBoolean = false override fun resolve(context: UastContext) = null diff --git a/plugins/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt b/plugins/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt index 2b5346b3da7..c361535ae8b 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/declarations/UVariable.kt @@ -17,16 +17,26 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor -interface UVariable : UDeclaration, UModifierOwner, UAnnotated { +interface UVariable : UDeclaration, UModifierOwner, UVisibilityOwner, UAnnotated { + /** + * Return the variable initializer (or the default value for value parameter), or null if the variable is not initialized. + */ val initializer: UExpression? + + /** + * Return the variable kind. + */ val kind: UastVariableKind + + /** + * Return the variable type. + */ val type: UType - val visibility: UastVisibility - open val getters: List? - get() = null - - open val setters: List? + /** + * Return the list of accessors if the variable is a property, or null otherwise. + */ + open val accessors: List? get() = null override fun accept(visitor: UastVisitor) { @@ -35,13 +45,28 @@ interface UVariable : UDeclaration, UModifierOwner, UAnnotated { initializer?.accept(visitor) annotations.acceptList(visitor) type.accept(visitor) + visitor.afterVisitVariable(this) } - override fun renderString(): String { - val initializer = if (initializer != null && initializer !is EmptyExpression) " = ${initializer!!.renderString()}" else "" - val prefix = if (kind == UastVariableKind.VALUE_PARAMETER) "" else "var " - val emptyLine = if (kind == UastVariableKind.MEMBER) "\n" else "" - return "$prefix$name: " + type.name + initializer + emptyLine + override fun renderString(): String = buildString { + if (kind != UastVariableKind.VALUE_PARAMETER) appendWithSpace(visibility.name) + appendWithSpace(renderModifiers()) + if (kind != UastVariableKind.VALUE_PARAMETER) append("var ") + append(name) + append(": ") + append(type.name) + if (initializer != null && initializer !is EmptyUExpression) { + append(" = ") + append(initializer!!.renderString()) + } + + accessors?.let { + appendln() + it.forEachIndexed { i, accessor -> + this@buildString.append(accessor.renderString().withMargin) + if ((i + 1) < it.size) appendln() + } + } } override fun logString() = "UVariable ($name, kind = ${kind.name})\n" + @@ -50,11 +75,11 @@ interface UVariable : UDeclaration, UModifierOwner, UAnnotated { object UVariableNotResolved : UVariable { override val initializer = null - override val kind = UastVariableKind.MEMBER + override val kind = UastVariableKind(ERROR_NAME) override val type = UastErrorType override val nameElement = null override val parent = null - override val name = "" + override val name = ERROR_NAME override val visibility = UastVisibility.LOCAL override fun hasModifier(modifier: UastModifier) = false diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt index d5529328e71..d422327d3df 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UArrayAccessExpression.kt @@ -17,14 +17,25 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents `receiver[index0, ..., indexN]` expression. + */ interface UArrayAccessExpression : UExpression { + /** + * Returns the receiver expression. + */ val receiver: UExpression + + /** + * Returns the list of index expressions. + */ val indices: List override fun accept(visitor: UastVisitor) { if (visitor.visitArrayAccessExpression(this)) return receiver.accept(visitor) indices.acceptList(visitor) + visitor.afterVisitArrayAccessExpression(this) } override fun logString() = log("UArrayAccessExpression", receiver, indices) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt index 94a9601941b..88bbc193dae 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpression.kt @@ -17,15 +17,30 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a binary expression (value1 op value2), eg. `2 + "A"`. + */ interface UBinaryExpression : UExpression { + /** + * Returns the left operand. + */ val leftOperand: UExpression + + /** + * Returns the binary operator. + */ val operator: UastBinaryOperator + + /** + * Returns the right operand. + */ val rightOperand: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitBinaryExpression(this)) return leftOperand.accept(visitor) rightOperand.accept(visitor) + visitor.afterVisitBinaryExpression(this) } override fun logString() = diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt index 3561cac92ac..8d9fd5be3a7 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBinaryExpressionWithType.kt @@ -17,17 +17,32 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a binary expression with type (value op type), e.g. ("A" instanceof String). + */ interface UBinaryExpressionWithType : UExpression { + /** + * Returns the operand expression. + */ val operand: UExpression + + /** + * Returns the operation kind. + */ val operationKind: UastBinaryExpressionWithTypeKind + + /** + * Returns the type. + */ val type: UType override fun logString() = log("UBinaryExpressionWithType (${getExpressionType()?.name}, ${operationKind.name})", operand) - override fun renderString() = "(${operand.renderString()}) ${operationKind.name} ${getExpressionType()?.name}" + override fun renderString() = "${operand.renderString()} ${operationKind.name} ${type.name}" override fun accept(visitor: UastVisitor) { if (visitor.visitBinaryExpressionWithType(this)) return operand.accept(visitor) type.accept(visitor) + visitor.afterVisitBinaryExpressionWithType(this) } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt index 478ce438373..0e24b3ba099 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBlockExpression.kt @@ -17,12 +17,19 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents the code block expression: `{ /* code */ }`. + */ interface UBlockExpression : UExpression { + /** + * Returns the list of block expressions. + */ val expressions: List override fun accept(visitor: UastVisitor) { if (visitor.visitBlockExpression(this)) return expressions.acceptList(visitor) + visitor.afterVisitBlockExpression(this) } override fun logString() = log("UBlockExpression", expressions) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt new file mode 100644 index 00000000000..c68ae13d789 --- /dev/null +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UBreakExpression.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `break` expression. + */ +interface UBreakExpression : UExpression { + /** + * Returns the expression label, or null if the label is not specified. + */ + val label: String? + + override fun accept(visitor: UastVisitor) { + visitor.visitBreakExpression(this) + visitor.afterVisitBreakExpression(this) + } + + override fun logString() = "UBreakExpression (" + (label ?: "") + ")" + override fun renderString() = label?.let { "break@$it" } ?: "break" +} \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt index c18fa7be054..b550bab0014 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallExpression.kt @@ -17,25 +17,99 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a call expression (function call, constructor call, array initializer). + */ interface UCallExpression : UExpression, UResolvable { - val functionReference: USimpleReferenceExpression? - - val classReference: USimpleReferenceExpression? - - val functionName: String? - val functionNameElement: UElement? - - fun functionNameMatches(name: String) = functionName == name - - val valueArgumentCount: Int - val valueArguments: List - - val typeArgumentCount: Int - val typeArguments: List - + /** + * Returns the call kind. + */ val kind: UastCallKind + /** + * Returns the function reference expression if the call is a function call, null otherwise. + */ + val functionReference: USimpleReferenceExpression? + + /** + * Returns the class reference if the call is a constructor call, null otherwise. + */ + val classReference: USimpleReferenceExpression? + + /** + * Returns the function name if the call is a function call, null otherwise. + * + * [functionName] should only be used in debug messages. + * Use [matchesFunctionName] to check against the name. + */ + val functionName: String? + + /** + * Returns an element for the function name node, or null if the node does not exist in the underlying AST (Psi). + */ + val functionNameElement: UElement? + + /** + * Checks if the function name is [name]. + * + * @param name the name to check against. + * @return true if the call is a function call, and the function name is [name], false otherwise. + */ + open fun matchesFunctionName(name: String) = functionName == name + + /** + * Checks if the function name is [name], and the function containing class qualified name is [containingClassFqName]. + * + * @param containingClassFqName the required containing class qualified name. + * @param name the function name to check against. + * @return true if the call is a function call, the function name is [name], + * and the qualified name of the function direct containing class is [containingClassFqName], + * false otherwise. + */ + open fun matchesFunctionNameWithContaining(containingClassFqName: String, name: String): Boolean { + if (!matchesFunctionName(name)) return false + val containingClass = parent as? UClass ?: return false + return containingClass.matchesFqName(containingClassFqName) + } + + /** + * Returns the value argument count. + * + * Retrieving the argument count could be faster than getting the [valueArguments.size], + * because there is no need to create actual [UExpression] instances. + */ + val valueArgumentCount: Int + + /** + * Returns the list of value arguments. + */ + val valueArguments: List + + /** + * Returns the type argument count. + */ + val typeArgumentCount: Int + + /** + * Returns the function type arguments. + */ + val typeArguments: List + + /** + * Resolve the call to the [UFunction] element. + * + * @param context the Uast context + * @return the [UFunction] element, or null if the reference was not resolved. + */ override fun resolve(context: UastContext): UFunction? + + /** + * Try to resolve the call to the [UFunction] element. + * + * @param context the Uast context + * @return the [UFunction] element, of [UFunctionNotResolved] if the reference was not resolved, + * or the call is not a function call. + */ override fun resolveOrEmpty(context: UastContext): UFunction = resolve(context) ?: UFunctionNotResolved override fun accept(visitor: UastVisitor) { @@ -45,11 +119,12 @@ interface UCallExpression : UExpression, UResolvable { functionNameElement?.accept(visitor) valueArguments.acceptList(visitor) typeArguments.acceptList(visitor) + visitor.afterVisitCallExpression(this) } override fun logString() = log("UFunctionCallExpression ($kind, argCount = $valueArgumentCount)", functionReference, valueArguments) override fun renderString(): String { - val ref = functionName ?: functionReference?.renderString() ?: classReference?.renderString() ?: "" + val ref = functionName ?: classReference?.renderString() ?: functionReference?.renderString() ?: "" return ref + "(" + valueArguments.joinToString { it.renderString() } + ")" } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt index 010c47a9876..27c23ce73aa 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UCallableReferenceExpression.kt @@ -17,14 +17,42 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor -interface UCallableReferenceExpression : UExpression { - val qualifierType: UType +/** + * Represents a callable reference expression, e.g. `Clazz::functionName`. + */ +interface UCallableReferenceExpression : UExpression, UResolvable { + /** + * Returns the qualifier expression. + * Can be null if the [qualifierType] is known. + */ + val qualifierExpression: UExpression? + + /** + * Returns the qualifier type. + * Can be null if the qualifier is an expression. + */ + val qualifierType: UType? + + /** + * Returns the callable name. + */ + val callableName: String override fun accept(visitor: UastVisitor) { if (visitor.visitCallableReferenceExpression(this)) return - qualifierType.accept(visitor) + qualifierExpression?.accept(visitor) + qualifierType?.accept(visitor) + visitor.afterVisitCallableReferenceExpression(this) } override fun logString() = "UCallableReferenceExpression" - override fun renderString() = "::" + qualifierType.name + override fun renderString() = buildString { + qualifierExpression?.let { + append(it.renderString()) + } ?: qualifierType?.let { + append(it.name) + } + append("::") + append(callableName) + } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt index 46c39fdda32..807b0d4a06d 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UClassLiteralExpression.kt @@ -17,13 +17,20 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents the class literal expression, e.g. `Clazz.class`. + */ interface UClassLiteralExpression : UExpression { override fun logString() = "UClassLiteralExpression" - override fun renderString() = getExpressionType()?.name ?: "::class" + override fun renderString() = type.name + "::class" + /** + * Returns the type for this class literal expression. + */ val type: UType override fun accept(visitor: UastVisitor) { visitor.visitClassLiteralExpression(this) + visitor.afterVisitClassLiteralExpression(this) } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt new file mode 100644 index 00000000000..a560e632d61 --- /dev/null +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UContinueExpression.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `continue` expression. + */ +interface UContinueExpression : UExpression { + /** + * Returns the expression label, or null if the label is not specified. + */ + val label: String? + + override fun accept(visitor: UastVisitor) { + visitor.visitContinueExpression(this) + visitor.afterVisitContinueExpression(this) + } + + override fun logString() = "UContinueExpression (" + (label ?: "") + ")" + override fun renderString() = label?.let { "continue@$it" } ?: "continue" +} \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt index e43066a7882..b9d09823eb9 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UDeclarationsExpression.kt @@ -17,15 +17,26 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a list of declarations. + * Example in Java: `int a = 4, b = 3`. + */ interface UDeclarationsExpression : UExpression { + /** + * Returns the list of declarations inside this [UDeclarationsExpression]. + */ val declarations: List + /** + * Returns the list of variables inside this [UDeclarationsExpression]. + */ val variables: List get() = declarations.filterIsInstance() override fun accept(visitor: UastVisitor) { if (visitor.visitDeclarationsExpression(this)) return declarations.acceptList(visitor) + visitor.afterVisitDeclarationsExpression(this) } override fun renderString() = declarations.joinToString("\n") { it.renderString() } diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt index e220b8fa6e2..220dec6f324 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UExpressionList.kt @@ -17,21 +17,26 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor -interface UExpressionList : UExpression { +/** + * Represents a generic list of expressions. + */ +interface USpecialExpressionList : UExpression { + /** + * Returns the list of expressions. + */ val expressions: List - override fun accept(visitor: UastVisitor) { - if (visitor.visitExpressionList(this)) return - expressions.acceptList(visitor) - } - - override fun logString() = log("UExpressionList", expressions) - override fun renderString() = log("", expressions) -} - -interface USpecialExpressionList : UExpressionList { + /** + * Returns the list kind. + */ val kind: UastSpecialExpressionKind + override fun accept(visitor: UastVisitor) { + if (visitor.visitSpecialExpressionList(this)) return + expressions.acceptList(visitor) + visitor.afterVisitSpecialExpressionList(this) + } + fun firstOrNull(): UExpression? = expressions.firstOrNull() override fun logString() = log("USpecialExpressionList (${kind.name})", expressions) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt index fcf35b620e1..156fce62ef2 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/ULabeledExpression.kt @@ -17,13 +17,24 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents an expression with the label specified. + */ interface ULabeledExpression : UExpression { + /** + * Returns the expression label. + */ val label: String + + /** + * Returns the expression itself. + */ val expression: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitLabeledExpression(this)) return expression.accept(visitor) + visitor.afterVisitLabeledExpression(this) } override fun evaluate() = expression.evaluate() diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt index 9766c51dfc4..e35a4222b15 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/ULambdaExpression.kt @@ -17,14 +17,25 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents the lambda expression. + */ interface ULambdaExpression : UExpression { + /** + * Returns the list of lambda value parameters. + */ val valueParameters: List + + /** + * Returns the lambda body expression. + */ val body: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitLambdaExpression(this)) return valueParameters.acceptList(visitor) body.accept(visitor) + visitor.afterVisitLambdaExpression(this) } override fun logString() = log("ULambdaExpression", valueParameters, body) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt index 2ac3cfa58e8..8e4f5048005 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/ULiteralExpression.kt @@ -17,23 +17,51 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a literal expression. + */ interface ULiteralExpression : UExpression { + /** + * Returns the literal expression value. + * This is basically a String, Number or null if the literal is a `null` literal. + */ val value: Any? + /** + * Returns true if the literal is a `null`-literal, false otherwise. + */ val isNull: Boolean + /** + * Returns true if the literal is a [String] literal, false otherwise. + */ val isString: Boolean get() = evaluate() is String + /** + * Returns true if the literal is a [Boolean] literal, false otherwise. + */ val isBoolean: Boolean get() = evaluate() is Boolean - fun asString() = value?.toString() ?: "" + /** + * Returns the string representation of the literal expression. + * + * @return the string representation, or "null" if the literal is a "null"-literal. + */ + fun asString(): String { + val value = value + return if (value == null) + "null" + else + value.toString() + } override fun accept(visitor: UastVisitor) { visitor.visitLiteralExpression(this) + visitor.afterVisitLiteralExpression(this) } override fun logString() = "ULiteralExpression (${asString()})" - override fun renderString() = asString() + override fun renderString() = if (value is String) "\"$value\"" else asString() } diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt index 0b9c2a6e871..03743129934 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UNamedExpression.kt @@ -26,6 +26,7 @@ class UNamedExpression( override fun accept(visitor: UastVisitor) { if (visitor.visitElement(this)) return expression.accept(visitor) + visitor.afterVisitElement(this) } override fun logString() = log("UNamedExpression ($name)", expression) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt index 5e153d3ea7b..b7806d4a974 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UObjectLiteralExpression.kt @@ -17,12 +17,19 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents an object literal expression, e.g. `new Runnable() {}` in Java. + */ interface UObjectLiteralExpression : UExpression { + /** + * Returns the class declaration. + */ val declaration: UClass override fun accept(visitor: UastVisitor) { if (visitor.visitObjectLiteralExpression(this)) return declaration.accept(visitor) + visitor.afterVisitObjectLiteralExpression(this) } override fun logString() = log("UObjectLiteralExpression", declaration) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt index a03ac041278..47a96f996bb 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UParenthesizedExpression.kt @@ -17,12 +17,19 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a parenthesized expression, e.g. `(23 + 3)`. + */ interface UParenthesizedExpression : UExpression { + /** + * Returns an expression inside the parenthesis. + */ val expression: UExpression override fun accept(visitor: UastVisitor) { if (visitor.visitParenthesizedExpression(this)) return expression.accept(visitor) + visitor.afterVisitParenthesizedExpression(this) } override fun evaluate() = expression.evaluate() diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UQualifiedExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UQualifiedExpression.kt index 93972c70759..ee842be7b41 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UQualifiedExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UQualifiedExpression.kt @@ -17,19 +17,48 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents the qualified expression (receiver.selector). + */ interface UQualifiedExpression : UExpression, UResolvable { + /** + * Returns the expression receiver. + */ val receiver: UExpression + + /** + * Returns the expression selector. + */ val selector: UExpression + + /** + * Returns the access type (simple, safe access, etc.). + */ val accessType: UastQualifiedExpressionAccessType + /** + * Checks if the selector is a simple reference expression, and if its identifier is [identifier]. + * + * @param identifier the identifier to check agains + * @return true if the selector is a simple reference expression, and if its identifier is [identifier], + * false otherwise + */ fun selectorMatches(identifier: String) = (selector as? USimpleReferenceExpression)?.identifier == identifier + /** + * Returns the selector identifier if the selector is a simple reference expression. + * + * @return the selector identifier if the selector is a simple reference expression, null otherwise. + */ + fun getSelectorAsIdentifier(): String? = (selector as? USimpleReferenceExpression)?.identifier + override fun renderString() = receiver.renderString() + accessType.name + selector.renderString() override fun accept(visitor: UastVisitor) { if (visitor.visitQualifiedExpression(this)) return receiver.accept(visitor) selector.accept(visitor) + visitor.afterVisitQualifiedExpression(this) } override fun logString() = log("UQualifiedExpression", receiver, selector) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt new file mode 100644 index 00000000000..87dd8148d94 --- /dev/null +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UReturnExpression.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `return` expression. + */ +interface UReturnExpression : UExpression { + /** + * Returns the `return` value. + */ + val returnExpression: UExpression? + + override fun accept(visitor: UastVisitor) { + if (visitor.visitReturnExpression(this)) return + returnExpression?.accept(visitor) + visitor.afterVisitReturnExpression(this) + } + + override fun renderString() = returnExpression.let { if (it == null) "return" else "return " + it.renderString() } + override fun logString() = log("UReturnExpression", returnExpression) +} \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/USimpleReferenceExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/USimpleReferenceExpression.kt index 4cf4ab38dd5..32ec7a9748d 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/USimpleReferenceExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/USimpleReferenceExpression.kt @@ -17,11 +17,18 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a simple reference expression (a non-qualified identifier). + */ interface USimpleReferenceExpression : UExpression, UResolvable { + /** + * Returns the identifier name. + */ val identifier: String override fun accept(visitor: UastVisitor) { visitor.visitSimpleReferenceExpression(this) + visitor.afterVisitSimpleReferenceExpression(this) } override fun logString() = "USimpleReferenceExpression ($identifier)" diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt index 2edf91a02a4..867d510a35e 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/USuperExpression.kt @@ -17,11 +17,16 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a `super` expression. + * Qualified `super` is not supported at the moment. + */ interface USuperExpression : UExpression { override fun logString() = "USuperExpression" override fun renderString() = "super" override fun accept(visitor: UastVisitor) { visitor.visitSuperExpression(this) + visitor.afterVisitSuperExpression(this) } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt index bb82dc8ee58..cd16221e92d 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UThisExpression.kt @@ -17,11 +17,16 @@ package org.jetbrains.uast import org.jetbrains.uast.visitor.UastVisitor +/** + * Represents a `this` expression. + * Qualified `this` is not supported at the moment. + */ interface UThisExpression : UExpression { override fun logString() = "UThisExpression" override fun renderString() = "this" override fun accept(visitor: UastVisitor) { visitor.visitThisExpression(this) + visitor.afterVisitThisExpression(this) } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt new file mode 100644 index 00000000000..bd78321e2d3 --- /dev/null +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UThrowExpression.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast + +import org.jetbrains.uast.visitor.UastVisitor + +/** + * Represents a `throw` expression. + */ +interface UThrowExpression : UExpression { + /** + * Returns ths thrown expression. + */ + val thrownExpression: UExpression + + override fun accept(visitor: UastVisitor) { + if (visitor.visitThrowExpression(this)) return + thrownExpression.accept(visitor) + visitor.afterVisitThrowExpression(this) + } + + override fun renderString() = "throw " + thrownExpression.renderString() + + override fun logString() = log("UThrowExpression", thrownExpression) +} \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt index 3e3411a95bf..512f9383ff4 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/UUnaryExpression.kt @@ -15,7 +15,6 @@ */ package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastOperator import org.jetbrains.uast.visitor.UastVisitor interface UUnaryExpression : UExpression { @@ -25,6 +24,7 @@ interface UUnaryExpression : UExpression { override fun accept(visitor: UastVisitor) { if (visitor.visitUnaryExpression(this)) return operand.accept(visitor) + visitor.afterVisitUnaryExpression(this) } } @@ -34,6 +34,7 @@ interface UPrefixExpression : UUnaryExpression { override fun accept(visitor: UastVisitor) { if (visitor.visitPrefixExpression(this)) return operand.accept(visitor) + visitor.afterVisitPrefixExpression(this) } override fun logString() = log("UPrefixExpression (${operator.text})", operand) @@ -46,6 +47,7 @@ interface UPostfixExpression : UUnaryExpression { override fun accept(visitor: UastVisitor) { if (visitor.visitPostfixExpression(this)) return operand.accept(visitor) + visitor.afterVisitPostfixExpression(this) } override fun logString() = log("UPostfixExpression (${operator.text})", operand) diff --git a/plugins/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt b/plugins/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt index 087be8bcc76..557809aca6c 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/expressions/uastLiteralUtils.kt @@ -16,15 +16,40 @@ @file:JvmName("UastLiteralUtils") package org.jetbrains.uast +/** + * Checks if the [UElement] is a null literal. + * + * @return true if the receiver is a null literal, false otherwise. + */ fun UElement.isNullLiteral(): Boolean = this is ULiteralExpression && this.isNull +/** + * Checks if the [UElement] is a [String] literal. + * + * @return true if the receiver is a [String] literal, false otherwise. + */ fun UElement.isStringLiteral(): Boolean = this is ULiteralExpression && this.isString +/** + * Returns the [String] literal value. + * + * @return literal text if the receiver is a valid [String] literal, null otherwise. + */ fun UElement.getValueIfStringLiteral(): String? = if (isStringLiteral()) (this as ULiteralExpression).value as String else null +/** + * Checks if the [UElement] is a [Number] literal (Integer, Long, Float, Double, etc.). + * + * @return true if the receiver is a [Number] literal, false otherwise. + */ fun UElement.isNumberLiteral(): Boolean = this is ULiteralExpression && this.value is Number +/** + * Checks if the [UElement] is an integral literal (is an [Integer], [Long], [Short], [Char] or [Byte]). + * + * @return true if the receiver is an integral literal, false otherwise. + */ fun UElement.isIntegralLiteral(): Boolean = this is ULiteralExpression && when (value) { is Int -> true is Long -> true @@ -34,8 +59,12 @@ fun UElement.isIntegralLiteral(): Boolean = this is ULiteralExpression && when ( else -> false } -fun UElement.isBooleanLiteral(): Boolean = this is ULiteralExpression && this.isBoolean - +/** + * Returns the integral value of the literal. + * + * @return long representation of the literal expression value, + * 0 if the receiver literal expression is not a integral one. + */ fun ULiteralExpression.getLongValue(): Long = value.let { when (it) { is Long -> it diff --git a/plugins/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt b/plugins/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt index 6a585432c06..60d8a0823e9 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/internal/internalUastUtils.kt @@ -15,4 +15,15 @@ */ package org.jetbrains.uast -internal fun List.logString() = joinToString("\n") { it.logString().withMargin } \ No newline at end of file +internal fun List.logString() = joinToString("\n") { it.logString().withMargin } + +internal fun UModifierOwner.renderModifiers() = UastModifier.VALUES + .filter { hasModifier(it) } + .joinToString(" ") { it.name } + +internal fun StringBuilder.appendWithSpace(s: String) { + if (s.isNotEmpty()) { + append(s) + append(' ') + } +} \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt index 93643a3c1fb..dc73b665dde 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryExpressionWithTypeKind.kt @@ -16,6 +16,10 @@ @file:JvmName("UastBinaryExpressionWithTypeUtils") package org.jetbrains.uast +/** + * Kinds of [UBinaryExpressionWithType]. + * Examples: type casts, instance checks. + */ open class UastBinaryExpressionWithTypeKind(val name: String) { open class TypeCast(name: String) : UastBinaryExpressionWithTypeKind(name) open class InstanceCheck(name: String) : UastBinaryExpressionWithTypeKind(name) diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt index 8f0bef29f0d..7e24a472970 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastBinaryOperator.kt @@ -15,8 +15,9 @@ */ package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastOperator - +/** + * Kinds of operators in [UBinaryExpression]. + */ open class UastBinaryOperator(override val text: String): UastOperator { class LogicalOperator(text: String): UastBinaryOperator(text) class ComparationOperator(text: String): UastBinaryOperator(text) @@ -105,7 +106,7 @@ open class UastBinaryOperator(override val text: String): UastOperator { @JvmField val DIVIDE_ASSIGN = AssignOperator("/=") - + @JvmField val REMAINDER_ASSIGN = AssignOperator("%=") diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt index 43f4e295f9d..c99e9b9004b 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastCallKind.kt @@ -15,13 +15,19 @@ */ package org.jetbrains.uast -class UastCallKind(val name: String) { +/** + * Kinds of [UCallExpression]. + */ +open class UastCallKind(val name: String) { companion object { @JvmField val FUNCTION_CALL = UastCallKind("function_call") @JvmField val CONSTRUCTOR_CALL = UastCallKind("constructor_call") + + @JvmField + val ARRAY_INITIALIZER = UastCallKind("array_initializer") } override fun toString(): String{ diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt index 6f6319c6533..d8d9bb44db4 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastClassKind.kt @@ -14,11 +14,12 @@ * limitations under the License. */ -package org.jetbrains.uast.kinds +package org.jetbrains.uast +/** + * Kinds of [UClass]. + */ open class UastClassKind(val text: String) { - class UastCompanionObject(val default: Boolean) : UastClassKind("companion object") - companion object { @JvmField val CLASS = UastClassKind("class") diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastFunctionKind.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastFunctionKind.kt index a5584de91bb..6e229ca2f05 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastFunctionKind.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastFunctionKind.kt @@ -15,6 +15,9 @@ */ package org.jetbrains.uast +/** + * Kinds of [UFunction]. + */ open class UastFunctionKind(val text: String) { class UastInitializerKind(val name: String) : UastFunctionKind("INITIALIZER ($name)") class UastVariableAccessor(val name: String) : UastFunctionKind(name) diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastModifier.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastModifier.kt index ad63533cea1..973a1fa63d9 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastModifier.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastModifier.kt @@ -15,7 +15,12 @@ */ package org.jetbrains.uast -class UastModifier(val name: String) { +/** + * Uast declaration modifiers. + * + * @see UModifierOwner + */ +open class UastModifier(val name: String) { companion object { @JvmField val ABSTRACT = UastModifier("abstract") @@ -24,9 +29,13 @@ class UastModifier(val name: String) { @JvmField val FINAL = UastModifier("final") @JvmField + val IMMUTABLE = UastModifier("immutable") + @JvmField val VARARG = UastModifier("vararg") @JvmField val OVERRIDE = UastModifier("override") + + val VALUES = listOf(ABSTRACT, STATIC, FINAL, IMMUTABLE, VARARG, OVERRIDE) } override fun toString(): String{ diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt index 72022aa9087..968bd4656e8 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastOperator.kt @@ -13,8 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.uast.kinds +package org.jetbrains.uast +/** + * Uast operator base inteface. + * + * @see [UastPrefixOperator], [UastPostfixOperator], [UastBinaryOperator] + */ interface UastOperator { + /** + * Returns the operator text to render in [UElement.renderString]. + */ val text: String } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt index c2ef7e1913b..1fbd2af5773 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPostfixOperator.kt @@ -15,9 +15,10 @@ */ package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastOperator - -class UastPostfixOperator(override val text: String): UastOperator { +/** + * [UPostfixExpression] operators. + */ +open class UastPostfixOperator(override val text: String): UastOperator { companion object { @JvmField val INC = UastPostfixOperator("++") diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt index 3bdc50cc2c3..e4f021998ce 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastPrefixOperator.kt @@ -15,8 +15,9 @@ */ package org.jetbrains.uast -import org.jetbrains.uast.kinds.UastOperator - +/** + * [UPrefixExpression] operators. + */ class UastPrefixOperator(override val text: String): UastOperator { companion object { @JvmField diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt index 4e4d01d94b7..b2b3968a029 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastQualifiedExpressionAccessType.kt @@ -15,7 +15,11 @@ */ package org.jetbrains.uast -class UastQualifiedExpressionAccessType(val name: String) { +/** + * Access types of [UQualifiedExpression]. + * Additional type examples: Kotlin safe call (?.). + */ +open class UastQualifiedExpressionAccessType(val name: String) { companion object { @JvmField val SIMPLE = UastQualifiedExpressionAccessType(".") diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt index 369d3f8e443..162eebac8a9 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastSpecialExpressionKind.kt @@ -15,21 +15,10 @@ */ package org.jetbrains.uast -class UastSpecialExpressionKind(val name: String) { - companion object { - @JvmField - val RETURN = UastSpecialExpressionKind("return") - - @JvmField - val THROW = UastSpecialExpressionKind("throw") - - @JvmField - val BREAK = UastSpecialExpressionKind("break") - - @JvmField - val CONTINUE = UastSpecialExpressionKind("continue") - } - +/** + * Kinds of [USpecialExpressionList]. + */ +open class UastSpecialExpressionKind(val name: String) { override fun toString(): String{ return "UastSpecialExpressionKind(name='$name')" } diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVariableKind.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVariableKind.kt index 823bc4e86fb..fb997b903c5 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVariableKind.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVariableKind.kt @@ -15,7 +15,10 @@ */ package org.jetbrains.uast -class UastVariableKind(val name: String) { +/** + * Kinds of [UVariable]. + */ +open class UastVariableKind(val name: String) { companion object { @JvmField val LOCAL_VARIABLE = UastVariableKind("local") diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt index 188f5ee83ac..4bbe5d69f1e 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt +++ b/plugins/uast-common/src/org/jetbrains/uast/kinds/UastVisibility.kt @@ -15,7 +15,10 @@ */ package org.jetbrains.uast -class UastVisibility(val name: String) { +/** + * Uast visibility list. + */ +open class UastVisibility(val name: String) { companion object { @JvmField val PUBLIC = UastVisibility("public") diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt b/plugins/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt index c6c8af85642..2c732164324 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/JavaUastLanguagePlugin.kt @@ -18,6 +18,7 @@ package org.jetbrains.uast.java import com.intellij.psi.* import org.jetbrains.uast.* +import org.jetbrains.uast.java.expressions.JavaUSynchronizedExpression object JavaUastLanguagePlugin : UastLanguagePlugin { override val converter: UastConverter = JavaConverter @@ -45,8 +46,6 @@ internal object JavaConverter : UastConverter { return convertPsiElement(element, parentUElement) } - fun convertAnnotation(annotation: PsiAnnotation) = convertWithParent(annotation) as UAnnotation - private fun convertPsiElement(element: PsiElement?, parent: UElement) = when (element) { is PsiJavaFile -> JavaUFile(element) is PsiClass -> JavaUClass(element, parent) @@ -115,7 +114,7 @@ internal object JavaConverter : UastConverter { val referenceNameElement = expression.element ?: expression return JavaUCompositeQualifiedExpression(parent).apply { - receiver = expression.qualifier?.let { convert(it, this) } as? UExpression ?: EmptyExpression(parent) + receiver = expression.qualifier?.let { convert(it, this) } as? UExpression ?: EmptyUExpression(parent) selector = JavaUSimpleReferenceExpression(referenceNameElement, referenceName, this) } } @@ -185,42 +184,30 @@ internal object JavaConverter : UastConverter { is PsiSwitchStatement -> JavaUSwitchExpression(statement, parent) is PsiSwitchLabelStatement -> { if (statement.isDefaultCase) - SimpleUDefaultSwitchClauseExpression(parent) - else JavaUExpressionSwitchClauseExpression(statement, parent) + DefaultUSwitchClauseExpression(parent) + else JavaUCaseSwitchClauseExpression(statement, parent) } is PsiWhileStatement -> JavaUWhileExpression(statement, parent) is PsiDoWhileStatement -> JavaUDoWhileExpression(statement, parent) is PsiForStatement -> JavaUForExpression(statement, parent) is PsiForeachStatement -> JavaUForEachExpression(statement, parent) - is PsiBreakStatement -> JavaUSpecialExpressionList.Empty(statement, UastSpecialExpressionKind.BREAK, parent) - is PsiContinueStatement -> JavaUSpecialExpressionList.Empty(statement, UastSpecialExpressionKind.CONTINUE, parent) - is PsiReturnStatement -> JavaUSpecialExpressionList(statement, UastSpecialExpressionKind.RETURN, parent).apply { - expressions = singletonListOrEmpty(convertOrNull(statement.returnValue, this)) - } - is PsiAssertStatement -> JavaUSpecialExpressionList(statement, JavaSpecialExpressionKinds.ASSERT, parent).apply { - expressions = listOf( - convertOrEmpty(statement.assertCondition, this), - convertOrEmpty(statement.assertDescription, this)) - } - is PsiThrowStatement -> JavaUSpecialExpressionList(statement, UastSpecialExpressionKind.THROW, parent).apply { - expressions = singletonListOrEmpty(convertOrNull(statement.exception, this)) - } - is PsiSynchronizedStatement -> JavaUSpecialExpressionList(statement, JavaSpecialExpressionKinds.SYNCHRONIZED, parent).apply { - expressions = listOf( - convertOrEmpty(statement.lockExpression, this), - convertOrEmpty(statement.body, this)) - } + is PsiBreakStatement -> JavaUBreakExpression(statement, parent) + is PsiContinueStatement -> JavaUContinueExpression(statement, parent) + is PsiReturnStatement -> JavaUReturnExpression(statement, parent) + is PsiAssertStatement -> JavaUAssertExpression(statement, parent) + is PsiThrowStatement -> JavaUThrowExpression(statement, parent) + is PsiSynchronizedStatement -> JavaUSynchronizedExpression(statement, parent) is PsiTryStatement -> JavaUTryExpression(statement, parent) else -> UnknownJavaExpression(statement, parent) } internal fun convertOrEmpty(statement: PsiStatement?, parent: UElement): UExpression { - return if (statement != null) convert(statement, parent) else EmptyExpression(parent) + return if (statement != null) convert(statement, parent) else EmptyUExpression(parent) } internal fun convertOrEmpty(expression: PsiExpression?, parent: UElement): UExpression { - return if (expression != null) convert(expression, parent) else EmptyExpression(parent) + return if (expression != null) convert(expression, parent) else EmptyUExpression(parent) } internal fun convertOrNull(expression: PsiExpression?, parent: UElement): UExpression? { @@ -228,7 +215,7 @@ internal object JavaConverter : UastConverter { } internal fun convertOrEmpty(block: PsiCodeBlock?, parent: UElement): UExpression { - return if (block != null) convert(block, parent) else EmptyExpression(parent) + return if (block != null) convert(block, parent) else EmptyUExpression(parent) } private fun convertDeclarations(elements: Array, parent: UElement): SimpleUDeclarationsExpression { diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt index 7c3a6792905..01412b30e44 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/controlStructures/JavaUSwitchExpression.kt @@ -17,9 +17,7 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiSwitchLabelStatement import com.intellij.psi.PsiSwitchStatement -import org.jetbrains.uast.UElement -import org.jetbrains.uast.UExpressionSwitchClauseExpression -import org.jetbrains.uast.USwitchExpression +import org.jetbrains.uast.* import org.jetbrains.uast.psi.PsiElementBacked class JavaUSwitchExpression( @@ -30,9 +28,20 @@ class JavaUSwitchExpression( override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) } } -class JavaUExpressionSwitchClauseExpression( +class JavaUCaseSwitchClauseExpression( override val psi: PsiSwitchLabelStatement, override val parent: UElement -) : JavaAbstractUElement(), UExpressionSwitchClauseExpression, PsiElementBacked { - override val caseValue by lz { JavaConverter.convertOrEmpty(psi.caseValue, this) } +) : JavaAbstractUElement(), USwitchClauseExpression, PsiElementBacked { + override val caseValues by lz { + val value = psi.caseValue ?: return@lz null + listOf(JavaConverter.convert(value, this)) + } +} + +class DefaultUSwitchClauseExpression(override val parent: UElement) : USwitchClauseExpression { + override val caseValues: List? + get() = null + + override fun logString() = "DefaultUSwitchClauseExpression" + override fun renderString() = "else -> " } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaClassInitializerUFunction.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaClassInitializerUFunction.kt index 3303fc47b6c..cf0db6ed937 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaClassInitializerUFunction.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaClassInitializerUFunction.kt @@ -16,15 +16,21 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiClassInitializer +import com.intellij.psi.PsiModifier import org.jetbrains.uast.* import org.jetbrains.uast.psi.PsiElementBacked class JavaClassInitializerUFunction( override val psi: PsiClassInitializer, override val parent: UElement -) : JavaAbstractUElement(), UFunction, PsiElementBacked, NoAnnotations, NoModifiers { +) : JavaAbstractUElement(), UFunction, PsiElementBacked, NoAnnotations { override val kind: UastFunctionKind.UastInitializerKind - get() = JavaFunctionKinds.STATIC_INITIALIZER + get() { + return if (psi.hasModifierProperty(PsiModifier.STATIC)) + JavaFunctionKinds.STATIC_INITIALIZER + else + JavaFunctionKinds.INSTANCE_INITIALIZER + } override val valueParameters: List get() = emptyList() @@ -53,4 +59,5 @@ class JavaClassInitializerUFunction( get() = "" override fun getSuperFunctions(context: UastContext) = emptyList() + override fun hasModifier(modifier: UastModifier) = psi.hasModifier(modifier) } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt index 1a10b4054cb..375c1a98d1a 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUAnnotation.kt @@ -24,7 +24,7 @@ import org.jetbrains.uast.psi.PsiElementBacked class JavaUAnnotation( override val psi: PsiAnnotation, - override val parent: UElement? + override val parent: UElement ) : JavaAbstractUElement(), UAnnotation, PsiElementBacked { override val name: String get() = psi.nameReferenceElement?.referenceName.orAnonymous() diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt index 4fb404684b2..d88e0cb24e1 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUClass.kt @@ -21,13 +21,11 @@ import com.intellij.psi.util.ClassUtil import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTypesUtil import org.jetbrains.uast.* -import org.jetbrains.uast.kinds.UastClassKind import org.jetbrains.uast.psi.PsiElementBacked -import java.util.* class JavaUClass( override val psi: PsiClass, - override val parent: UElement?, + override val parent: UElement, val newExpression: PsiNewExpression? = null ) : JavaAbstractUElement(), UClass, PsiElementBacked { override val name: String @@ -79,27 +77,19 @@ class JavaUClass( override val declarations by lz { val declarations = arrayListOf() psi.fields.mapTo(declarations) { JavaConverter.convert(it, this) } - psi.constructors.mapTo(declarations) { JavaConverter.convert(it, this) } if (psi is PsiAnonymousClass && newExpression != null) { declarations += JavaUAnonymousClassConstructor(psi, newExpression, this) } - psi.methods.filter { !it.isConstructor }.mapTo(declarations) { JavaConverter.convert(it, this) } - psi.interfaces.mapTo(declarations) { JavaConverter.convert(it, this) } + psi.methods.mapTo(declarations) { JavaConverter.convert(it, this) } psi.innerClasses.mapTo(declarations) { JavaConverter.convert(it, this) } psi.initializers.mapTo(declarations) { JavaConverter.convert(it, this) } declarations } override fun isSubclassOf(fqName: String): Boolean { - tailrec fun isSubClassOf(clazz: PsiClass?, name: String): Boolean = when { - clazz == null -> false - clazz.qualifiedName == name -> true - else -> isSubClassOf(clazz.superClass, name) - } - - return isSubClassOf(psi, fqName) + return psi.supers.any { base -> psi.isInheritor(base, false) } } private companion object { @@ -179,7 +169,8 @@ private class JavaUAnonymousClassConstructor( override val returnType: UType? get() = null - override val body = EmptyExpression(this) + override val body: UExpression? + get() = null override val visibility: UastVisibility get() = UastVisibility.LOCAL diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFunction.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFunction.kt index a6580b10589..6b09774e7e8 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFunction.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUFunction.kt @@ -57,7 +57,7 @@ class JavaUFunction( override val visibility: UastVisibility get() = psi.getVisibility() - override val body by lz { JavaConverter.convertOrEmpty(psi.body, this) } + override val body by lz { psi.body?.let { JavaConverter.convert(it, this) } } override val bytecodeDescriptor by lz { getDescriptor(psi) } diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt index 2780f7f418e..0676d06ce79 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUImportStatement.kt @@ -20,7 +20,6 @@ import org.jetbrains.uast.UDeclaration import org.jetbrains.uast.UElement import org.jetbrains.uast.UImportStatement import org.jetbrains.uast.UastContext -import org.jetbrains.uast.kinds.UastImportKind import org.jetbrains.uast.psi.PsiElementBacked class JavaUImportStatement( @@ -30,9 +29,6 @@ class JavaUImportStatement( override val fqNameToImport: String? get() = psi.qualifiedName - override val kind: UastImportKind - get() = UastImportKind.CLASS - override val isStarImport: Boolean get() = psi.isOnDemand diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUStaticImportStatement.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUStaticImportStatement.kt index e697f6c7f91..8dda062ba19 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUStaticImportStatement.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUStaticImportStatement.kt @@ -20,7 +20,6 @@ import org.jetbrains.uast.UDeclaration import org.jetbrains.uast.UElement import org.jetbrains.uast.UImportStatement import org.jetbrains.uast.UastContext -import org.jetbrains.uast.kinds.UastImportKind import org.jetbrains.uast.psi.PsiElementBacked class JavaUStaticImportStatement( @@ -30,9 +29,6 @@ class JavaUStaticImportStatement( override val fqNameToImport: String? get() = psi.referenceName - override val kind: UastImportKind - get() = UastImportKind.MEMBER - override val isStarImport: Boolean get() = psi.isOnDemand diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUType.kt b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUType.kt index 830dc346dfd..163fc740869 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUType.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/declarations/JavaUType.kt @@ -44,6 +44,9 @@ class JavaUType( override val isLong: Boolean get() = check("long", "java.lang.Long") + override val isShort: Boolean + get() = check("short", "java.lang.Short") + override val isFloat: Boolean get() = check("float", "java.lang.Float") @@ -59,6 +62,7 @@ class JavaUType( override val isByte: Boolean get() = check("byte", "java.lang.Byte") + @Suppress("NOTHING_TO_INLINE") private inline fun check(unboxedType: String, boxedType: String): Boolean = name == unboxedType || (psi as? PsiClassType)?.resolve()?.qualifiedName == boxedType diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt new file mode 100644 index 00000000000..fe8764f3f82 --- /dev/null +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssertExpression.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiAssertStatement +import org.jetbrains.uast.* +import org.jetbrains.uast.psi.PsiElementBacked + +class JavaUAssertExpression( + override val psi: PsiAssertStatement, + override val parent: UElement +) : JavaAbstractUElement(), UCallExpression, PsiElementBacked { + val condition: UExpression by lz { JavaConverter.convertOrEmpty(psi.assertCondition, this) } + val message: UExpression? by lz { JavaConverter.convertOrNull(psi.assertDescription, this) } + + override val functionReference: USimpleReferenceExpression? + get() = null + + override val classReference: USimpleReferenceExpression? + get() = null + + override val functionName: String? + get() = "" + + override val functionNameElement: UElement? + get() = null + + override val valueArgumentCount: Int + get() = if (message != null) 2 else 1 + + override val valueArguments by lz { + val message = this.message + if (message != null) listOf(condition, message) else listOf(condition) + } + + override val typeArgumentCount: Int + get() = 0 + override val typeArguments: List + get() = emptyList() + + override val kind: UastCallKind + get() = JavaUastCallKinds.ASSERT + + override fun resolve(context: UastContext) = null +} \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt index 48fe06ed8a9..cc17bf2de65 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUAssignmentExpression.kt @@ -18,7 +18,6 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiAssignmentExpression import org.jetbrains.uast.UBinaryExpression import org.jetbrains.uast.UElement -import org.jetbrains.uast.UastBinaryOperator import org.jetbrains.uast.psi.PsiElementBacked class JavaUAssignmentExpression( @@ -26,9 +25,6 @@ class JavaUAssignmentExpression( override val parent: UElement ) : JavaAbstractUElement(), UBinaryExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { override val leftOperand by lz { JavaConverter.convert(psi.lExpression, this) } - - override val operator: UastBinaryOperator - get() = UastBinaryOperator.ASSIGN - override val rightOperand by lz { JavaConverter.convertOrEmpty(psi.rExpression, this) } + override val operator by lz { psi.operationTokenType.getOperatorType() } } \ No newline at end of file diff --git a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastImportKind.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt similarity index 57% rename from plugins/uast-common/src/org/jetbrains/uast/kinds/UastImportKind.kt rename to plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt index 7385b34df14..936b560d767 100644 --- a/plugins/uast-common/src/org/jetbrains/uast/kinds/UastImportKind.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUBreakExpression.kt @@ -14,22 +14,17 @@ * limitations under the License. */ -package org.jetbrains.uast.kinds +package org.jetbrains.uast.java -class UastImportKind(val text: String) { +import com.intellij.psi.PsiBreakStatement +import org.jetbrains.uast.UBreakExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.psi.PsiElementBacked - companion object { - @JvmField - val CLASS = UastImportKind("class") - - @JvmField - val MEMBER = UastImportKind("member") - - @JvmField - val UNKNOWN = UastImportKind("unknown") - } - - override fun toString(): String{ - return "UastImportKind(text='$text')" - } +class JavaUBreakExpression( + override val psi: PsiBreakStatement, + override val parent: UElement +) : JavaAbstractUElement(), UBreakExpression, PsiElementBacked { + override val label: String? + get() = psi.labelIdentifier?.text } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt index e8fd2acee7a..08d844d05e7 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUCallableReferenceExpression.kt @@ -17,12 +17,19 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiMethodReferenceExpression import org.jetbrains.uast.UCallableReferenceExpression +import org.jetbrains.uast.UDeclaration import org.jetbrains.uast.UElement +import org.jetbrains.uast.UastContext import org.jetbrains.uast.psi.PsiElementBacked class JavaUCallableReferenceExpression( override val psi: PsiMethodReferenceExpression, override val parent: UElement ) : JavaAbstractUElement(), UCallableReferenceExpression, PsiElementBacked, JavaUElementWithType { + override val qualifierExpression by lz { JavaConverter.convertOrNull(psi.qualifierExpression, this) } override val qualifierType by lz { JavaConverter.convert(psi.qualifierType?.type, this) } + override val callableName: String + get() = psi.referenceName.orAnonymous() + + override fun resolve(context: UastContext) = context.convert(psi.resolve()) as? UDeclaration } \ No newline at end of file diff --git a/plugins/lint/uast-android/src/org/jetbrains/uast/check/AndroidUastAdditionalChecker.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt similarity index 56% rename from plugins/lint/uast-android/src/org/jetbrains/uast/check/AndroidUastAdditionalChecker.kt rename to plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt index ca372b1a8f4..28278517478 100644 --- a/plugins/lint/uast-android/src/org/jetbrains/uast/check/AndroidUastAdditionalChecker.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUContinueExpression.kt @@ -14,8 +14,17 @@ * limitations under the License. */ -package org.jetbrains.uast.check +package org.jetbrains.uast.java -import org.jetbrains.uast.UastAdditionalChecker +import com.intellij.psi.PsiContinueStatement +import org.jetbrains.uast.UContinueExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.psi.PsiElementBacked -interface AndroidUastAdditionalChecker : UastAdditionalChecker \ No newline at end of file +class JavaUContinueExpression( + override val psi: PsiContinueStatement, + override val parent: UElement +) : JavaAbstractUElement(), UContinueExpression, PsiElementBacked { + override val label: String? + get() = psi.labelIdentifier?.text +} \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt index 03dd5fe50eb..27a7c6f88ca 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULambdaExpression.kt @@ -18,7 +18,7 @@ package org.jetbrains.uast.java import com.intellij.psi.PsiCodeBlock import com.intellij.psi.PsiExpression import com.intellij.psi.PsiLambdaExpression -import org.jetbrains.uast.EmptyExpression +import org.jetbrains.uast.EmptyUExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.ULambdaExpression import org.jetbrains.uast.psi.PsiElementBacked @@ -34,7 +34,7 @@ class JavaULambdaExpression( when (b) { is PsiCodeBlock -> JavaConverter.convert(b, this) is PsiExpression -> JavaConverter.convert(b, this) - else -> EmptyExpression(this) + else -> EmptyUExpression(this) } } } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt index 0dfce0edbaa..f96679fdca2 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaULiteralExpression.kt @@ -15,6 +15,7 @@ */ package org.jetbrains.uast.java +import com.intellij.psi.PsiKeyword import com.intellij.psi.PsiLiteralExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.ULiteralExpression @@ -29,5 +30,5 @@ class JavaULiteralExpression( override val value by lz { evaluate() } override val isNull: Boolean - get() = asString() == "null" + get() = asString() == PsiKeyword.NULL } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt index 5f8a75ff541..dca782c2e76 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPostfixExpression.kt @@ -15,6 +15,7 @@ */ package org.jetbrains.uast.java +import com.intellij.psi.JavaTokenType import com.intellij.psi.PsiPostfixExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UPostfixExpression @@ -27,9 +28,9 @@ class JavaUPostfixExpression( ) : JavaAbstractUElement(), UPostfixExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } - override val operator = when (psi.operationSign.text) { - "++" -> UastPostfixOperator.INC - "--" -> UastPostfixOperator.DEC + override val operator = when (psi.operationSign) { + JavaTokenType.PLUSPLUS -> UastPostfixOperator.INC + JavaTokenType.MINUSMINUS -> UastPostfixOperator.DEC else -> UastPostfixOperator.UNKNOWN } } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt index 103688b478e..d9831ab5f58 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUPrefixExpression.kt @@ -15,6 +15,7 @@ */ package org.jetbrains.uast.java +import com.intellij.psi.JavaTokenType import com.intellij.psi.PsiPrefixExpression import org.jetbrains.uast.UElement import org.jetbrains.uast.UPrefixExpression @@ -27,13 +28,13 @@ class JavaUPrefixExpression( ) : JavaAbstractUElement(), UPrefixExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { override val operand by lz { JavaConverter.convertOrEmpty(psi.operand, this) } - override val operator = when (psi.operationSign.text) { - "+" -> UastPrefixOperator.UNARY_PLUS - "-" -> UastPrefixOperator.UNARY_MINUS - "++" -> UastPrefixOperator.INC - "--" -> UastPrefixOperator.DEC - "!" -> UastPrefixOperator.LOGICAL_NOT - "~" -> UastPrefixOperator.BITWISE_NOT + override val operator = when (psi.operationSign) { + JavaTokenType.PLUS -> UastPrefixOperator.UNARY_PLUS + JavaTokenType.MINUS -> UastPrefixOperator.UNARY_MINUS + JavaTokenType.PLUSPLUS -> UastPrefixOperator.INC + JavaTokenType.MINUSMINUS-> UastPrefixOperator.DEC + JavaTokenType.EXCL -> UastPrefixOperator.LOGICAL_NOT + JavaTokenType.TILDE -> UastPrefixOperator.BITWISE_NOT else -> UastPrefixOperator.UNKNOWN } } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedExpression.kt index 9f3fdb4c6bc..bd3fccd26a9 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedExpression.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUQualifiedExpression.kt @@ -24,7 +24,7 @@ class JavaUQualifiedExpression( override val parent: UElement ) : JavaAbstractUElement(), UQualifiedExpression, PsiElementBacked, JavaUElementWithType { override val receiver by lz { JavaConverter.convertOrEmpty(psi.qualifierExpression, this) } - override val selector by lz { JavaConverter.convert(psi.referenceNameElement, this) as? UExpression ?: EmptyExpression(this) } + override val selector by lz { JavaConverter.convert(psi.referenceNameElement, this) as? UExpression ?: EmptyUExpression(this) } override val accessType: UastQualifiedExpressionAccessType get() = UastQualifiedExpressionAccessType.SIMPLE diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt new file mode 100644 index 00000000000..485ed9677ab --- /dev/null +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUReturnExpression.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java + +import com.intellij.psi.PsiReturnStatement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UReturnExpression +import org.jetbrains.uast.psi.PsiElementBacked + +class JavaUReturnExpression( + override val psi: PsiReturnStatement, + override val parent: UElement +) : JavaAbstractUElement(), UReturnExpression, PsiElementBacked { + override val returnExpression by lz { JavaConverter.convertOrNull(psi.returnValue, this) } +} \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt new file mode 100644 index 00000000000..2120aa0bef8 --- /dev/null +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUSynchronizedExpression.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.java.expressions + +import com.intellij.psi.PsiSynchronizedStatement +import org.jetbrains.uast.UBlockExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.acceptList +import org.jetbrains.uast.java.JavaAbstractUElement +import org.jetbrains.uast.java.JavaConverter +import org.jetbrains.uast.java.JavaUElementWithType +import org.jetbrains.uast.java.lz +import org.jetbrains.uast.psi.PsiElementBacked +import org.jetbrains.uast.visitor.UastVisitor + +class JavaUSynchronizedExpression( + override val psi: PsiSynchronizedStatement, + override val parent: UElement +) : JavaAbstractUElement(), UBlockExpression, PsiElementBacked, JavaUElementWithType { + override val expressions by lz { psi.body?.statements?.map { JavaConverter.convert(it, this) } ?: listOf() } + val lockExpression by lz { JavaConverter.convertOrEmpty(psi.lockExpression, this) } + + override fun accept(visitor: UastVisitor) { + if (visitor.visitBlockExpression(this)) return + psi.lockExpression + expressions.acceptList(visitor) + lockExpression.accept(visitor) + visitor.afterVisitBlockExpression(this) + } +} \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaSpecialExpressionKinds.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThrowExpression.kt similarity index 55% rename from plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaSpecialExpressionKinds.kt rename to plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThrowExpression.kt index 78eede2ba8b..85d70a4a9c1 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaSpecialExpressionKinds.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/JavaUThrowExpression.kt @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,14 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.jetbrains.uast.java -import org.jetbrains.uast.UastSpecialExpressionKind +import com.intellij.psi.PsiThrowStatement +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UThrowExpression +import org.jetbrains.uast.psi.PsiElementBacked -object JavaSpecialExpressionKinds { - @JvmField - val ASSERT = UastSpecialExpressionKind("assert") - - @JvmField - val SYNCHRONIZED = UastSpecialExpressionKind("synchronized") +class JavaUThrowExpression( + override val psi: PsiThrowStatement, + override val parent: UElement +) : JavaAbstractUElement(), UThrowExpression, PsiElementBacked { + override val thrownExpression by lz { JavaConverter.convertOrEmpty(psi.exception, this) } } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt index 31d03b14ac7..d2482ba57d8 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt @@ -57,8 +57,8 @@ class JavaConstructorUCallExpression( ) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType { override val kind by lz { when { - psi.arrayInitializer != null -> JavaUastCallKinds.ARRAY_INITIALIZER - psi.arrayDimensions.isNotEmpty() -> JavaUastCallKinds.ARRAY_DIMENSIONS + psi.arrayInitializer != null -> JavaUastCallKinds.NEW_ARRAY_WITH_INITIALIZER + psi.arrayDimensions.isNotEmpty() -> JavaUastCallKinds.NEW_ARRAY_DIMENTIONS else -> UastCallKind.CONSTRUCTOR_CALL } } @@ -104,7 +104,7 @@ class JavaConstructorUCallExpression( get() { val initializer = psi.arrayInitializer return if (initializer != null) - "" + "" else if (psi.arrayDimensions.isNotEmpty()) "" else null @@ -141,7 +141,7 @@ class JavaArrayInitializerUCallExpression( get() = emptyList() override val kind: UastCallKind - get() = JavaUastCallKinds.ARRAY_INITIALIZER + get() = UastCallKind.ARRAY_INITIALIZER override fun resolve(context: UastContext) = null } @@ -150,7 +150,8 @@ class JavaAnnotationArrayInitializerUCallExpression( override val psi: PsiArrayInitializerMemberValue, override val parent: UElement ) : JavaAbstractUElement(), UCallExpression, PsiElementBacked, JavaUElementWithType, JavaEvaluatableUElement { - override val kind = JavaUastCallKinds.ARRAY_INITIALIZER + override val kind: UastCallKind + get() = UastCallKind.ARRAY_INITIALIZER override val functionReference: USimpleReferenceExpression? get() = null diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt b/plugins/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt index c0c91e6573d..8a35f8f9624 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/internal/javaInternalUastUtils.kt @@ -33,6 +33,12 @@ internal fun PsiModifierListOwner.hasModifier(modifier: UastModifier): Boolean { if (modifier == UastModifier.VARARG && this is PsiParameter) { return this.isVarArgs } + if (modifier == UastModifier.IMMUTABLE && this is PsiVariable) { + return this.hasModifierProperty(PsiModifier.FINAL) + } + if (modifier == UastModifier.FINAL && this is PsiVariable) { + return false; + } val javaModifier = MODIFIER_MAP[modifier] ?: return false return hasModifierProperty(javaModifier) } @@ -58,7 +64,6 @@ internal fun IElementType.getOperatorType() = when (this) { JavaTokenType.PERC -> UastBinaryOperator.MOD JavaTokenType.OR -> UastBinaryOperator.BITWISE_OR JavaTokenType.AND -> UastBinaryOperator.BITWISE_AND - JavaTokenType.TILDE -> UastBinaryOperator.BITWISE_XOR JavaTokenType.EQEQ -> UastBinaryOperator.IDENTITY_EQUALS JavaTokenType.NE -> UastBinaryOperator.IDENTITY_NOT_EQUALS JavaTokenType.GT -> UastBinaryOperator.GREATER @@ -72,6 +77,7 @@ internal fun IElementType.getOperatorType() = when (this) { JavaTokenType.MINUSEQ -> UastBinaryOperator.MINUS_ASSIGN JavaTokenType.ASTERISKEQ -> UastBinaryOperator.MULTIPLY_ASSIGN JavaTokenType.DIVEQ -> UastBinaryOperator.DIVIDE_ASSIGN + JavaTokenType.PERCEQ -> UastBinaryOperator.REMAINDER_ASSIGN JavaTokenType.ANDEQ -> UastBinaryOperator.AND_ASSIGN JavaTokenType.XOREQ -> UastBinaryOperator.XOR_ASSIGN JavaTokenType.OREQ -> UastBinaryOperator.OR_ASSIGN diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaFunctionKinds.kt b/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaFunctionKinds.kt index 0ac3da8526a..e668b8f6c55 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaFunctionKinds.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaFunctionKinds.kt @@ -19,5 +19,6 @@ package org.jetbrains.uast.java import org.jetbrains.uast.UastFunctionKind object JavaFunctionKinds { + val INSTANCE_INITIALIZER = UastFunctionKind.UastInitializerKind("") val STATIC_INITIALIZER = UastFunctionKind.UastInitializerKind("") } \ No newline at end of file diff --git a/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt b/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt index 0df6bd91778..24a0dc850b4 100644 --- a/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt +++ b/plugins/uast-java/src/org/jetbrains/uast/java/kinds/JavaUastCallKinds.kt @@ -19,8 +19,11 @@ import org.jetbrains.uast.UastCallKind object JavaUastCallKinds { @JvmField - val ARRAY_INITIALIZER = UastCallKind("array_initializer") + val NEW_ARRAY_WITH_INITIALIZER = UastCallKind("new_array_with_initializer") @JvmField - val ARRAY_DIMENSIONS = UastCallKind("array_dimensions") + val NEW_ARRAY_DIMENTIONS = UastCallKind("new_array_dimensions") + + @JvmField + val ASSERT = UastCallKind("assert") } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt index 79eb1e479c8..47bef979715 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/KotlinUastLanguagePlugin.kt @@ -25,6 +25,8 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.uast.expressions.KotlinUBreakExpression +import org.jetbrains.kotlin.uast.expressions.KotlinUContinueExpression import org.jetbrains.kotlin.uast.kinds.KotlinSpecialExpressionKinds import org.jetbrains.kotlin.uast.lint.PropertyAsCallAndroidUastAdditionalChecker import org.jetbrains.uast.* @@ -102,7 +104,10 @@ internal object KotlinConverter : UastConverter { internal fun convert(entry: KtStringTemplateEntry, parent: UElement): UExpression = when (entry) { is KtStringTemplateEntryWithExpression -> convertOrEmpty(entry.expression, parent) - else -> KotlinStringULiteralExpression(entry, parent) + is KtEscapeStringTemplateEntry -> KotlinStringULiteralExpression(entry, parent, entry.unescapedValue) + else -> { + KotlinStringULiteralExpression(entry, parent) + } } internal fun convert(expression: KtExpression, parent: UElement): UExpression = when (expression) { @@ -111,7 +116,9 @@ internal object KotlinConverter : UastConverter { is KtClass -> convertDeclaration(expression, parent) is KtStringTemplateExpression -> { - if (expression.entries.size == 1) + if (expression.entries.isEmpty()) + KotlinStringULiteralExpression(expression, parent) + else if (expression.entries.size == 1) convert(expression.entries[0], parent) else convertStringTemplateExpression(expression, parent, expression.entries.size - 1) @@ -128,6 +135,7 @@ internal object KotlinConverter : UastConverter { } declarations = listOf(tempAssignment) + destructuringAssignments } + is KtLabeledExpression -> KotlinULabeledExpression(expression, parent) is KtClassLiteralExpression -> KotlinUClassLiteralExpression(expression, parent) is KtObjectLiteralExpression -> KotlinUObjectLiteralExpression(expression, parent) is KtStringTemplateEntry -> convertOrEmpty(expression.expression, parent) @@ -141,20 +149,17 @@ internal object KotlinConverter : UastConverter { is KtPostfixExpression -> KotlinUPostfixExpression(expression, parent) is KtThisExpression -> KotlinUThisExpression(expression, parent) is KtSuperExpression -> KotlinUSuperExpression(expression, parent) + is KtCallableReferenceExpression -> KotlinUCallableReferenceExpression(expression, parent) is KtIsExpression -> KotlinUTypeCheckExpression(expression, parent) is KtIfExpression -> KotlinUIfExpression(expression, parent) is KtWhileExpression -> KotlinUWhileExpression(expression, parent) is KtDoWhileExpression -> KotlinUDoWhileExpression(expression, parent) is KtForExpression -> KotlinUForEachExpression(expression, parent) is KtWhenExpression -> KotlinUSwitchExpression(expression, parent) - is KtBreakExpression -> KotlinUSpecialExpressionList.Empty(expression, UastSpecialExpressionKind.BREAK, parent) - is KtContinueExpression -> KotlinUSpecialExpressionList.Empty(expression, UastSpecialExpressionKind.CONTINUE, parent) - is KtReturnExpression -> KotlinUSpecialExpressionList(expression, UastSpecialExpressionKind.RETURN, parent).apply { - expressions = listOf(convertOrEmpty(expression.returnedExpression, this)) - } - is KtThrowExpression -> KotlinUSpecialExpressionList(expression, UastSpecialExpressionKind.THROW, parent).apply { - expressions = singletonListOrEmpty(convertOrNull(expression.thrownExpression, this)) - } + is KtBreakExpression -> KotlinUBreakExpression(expression, parent) + is KtContinueExpression -> KotlinUContinueExpression(expression, parent) + is KtReturnExpression -> KotlinUReturnExpression(expression, parent) + is KtThrowExpression -> KotlinUThrowExpression(expression, parent) is KtBlockExpression -> KotlinUBlockExpression(expression, parent) is KtConstantExpression -> KotlinULiteralExpression(expression, parent) is KtTryExpression -> KotlinUTryExpression(expression, parent) @@ -190,7 +195,7 @@ internal object KotlinConverter : UastConverter { } internal fun convertOrEmpty(expression: KtExpression?, parent: UElement): UExpression { - return if (expression != null) convert(expression, parent) else EmptyExpression(parent) + return if (expression != null) convert(expression, parent) else EmptyUExpression(parent) } internal fun convertOrNull(expression: KtExpression?, parent: UElement): UExpression? { diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinParameterUTypeReference.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinParameterUTypeReference.kt index 5307977ccc7..b9eeea5637c 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinParameterUTypeReference.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinParameterUTypeReference.kt @@ -31,7 +31,7 @@ class KotlinParameterUTypeReference( override fun resolve(context: UastContext): UClass? { val descriptor = psi.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE_PARAMETER, psi] ?.typeConstructor?.declarationDescriptor as? ClassDescriptor ?: return null - return context.convert(descriptor.toSource(psi)) as? UClass + return context.convert(descriptor.toSource()) as? UClass } override val nameElement: UElement? diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotation.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotation.kt index aa80571a4e3..d4875f2d7c5 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotation.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotation.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.uast import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtUserType import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -55,7 +54,7 @@ class KotlinUAnnotation( override fun resolve(context: UastContext): UClass? { val classDescriptor = resolveToDescriptor() ?: return null - val source = DescriptorToSourceUtilsIde.getAnyDeclaration(psi.project, classDescriptor) ?: return null + val source = classDescriptor.toSource() ?: return null return context.convert(source) as? UClass } } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotationList.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotationList.kt index 60eac7ef61c..89cc63d28c4 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotationList.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUAnnotationList.kt @@ -35,5 +35,6 @@ class KotlinUAnnotationList( override fun accept(visitor: UastVisitor) { if (visitor.visitElement(this)) return annotations.acceptList(visitor) + visitor.afterVisitElement(this) } } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUClass.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUClass.kt index 96c4def32b2..884f4b1c5b8 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUClass.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUClass.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.load.java.JvmAbi -import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtObjectDeclaration @@ -35,7 +34,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.typeUtil.supertypes import org.jetbrains.uast.* -import org.jetbrains.uast.kinds.UastClassKind import org.jetbrains.uast.psi.PsiElementBacked class KotlinUClass( @@ -47,13 +45,8 @@ class KotlinUClass( get() = psi.name.orAnonymous() override val nameElement by lz { - fun objectKeyword() = try { // !! in getObjectKeyword() - if (psi is KtObjectDeclaration && psi.isObjectLiteral()) psi.getObjectKeyword() else null - } catch (e: NullPointerException) { - null - } - - val namePsiElement = psi.nameIdentifier ?: objectKeyword() + val namePsiElement = psi.nameIdentifier + ?: if (psi is KtObjectDeclaration && psi.isObjectLiteral()) psi.getObjectKeyword() else null KotlinConverter.asSimpleReference(namePsiElement, this) } @@ -63,12 +56,7 @@ class KotlinUClass( override val kind by lz { when { psi.isAnnotation() -> UastClassKind.ANNOTATION - (psi as? KtObjectDeclaration)?.isCompanion() ?: false -> { - if (psi.name == SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT.toString()) - KotlinClassKinds.DEFAULT_COMPANION_OBJECT - else - KotlinClassKinds.NAMED_COMPANION_OBJECT - } + (psi as? KtObjectDeclaration)?.isCompanion() ?: false -> KotlinClassKinds.COMPANION_OBJECT psi is KtObjectDeclaration -> UastClassKind.OBJECT (psi as? KtClass)?.isInterface() ?: false -> UastClassKind.INTERFACE (psi as? KtClass)?.isEnum() ?: false -> UastClassKind.ENUM @@ -95,7 +83,7 @@ class KotlinUClass( override fun getSuperClass(context: UastContext): UClass? { val descriptor = resolveToDescriptor() ?: return null if (KotlinBuiltIns.isAny(descriptor)) return null - val source = descriptor.getSuperClassOrAny().toSource(psi.project) ?: return null + val source = descriptor.getSuperClassOrAny().toSource() ?: return null return context.convert(source) as? UClass } @@ -103,10 +91,7 @@ class KotlinUClass( override val declarations by lz { val primaryConstructor = if (psi is KtObjectDeclaration && psi.isObjectLiteral()) { - val obj = psi - object : KotlinObjectLiteralConstructorUFunction(obj, this) { - - } + KotlinObjectLiteralConstructorUFunction(psi, this) } else { psi.getPrimaryConstructor()?.let { KotlinConverter.convert(it, this) } ?: run { if (psi.getSecondaryConstructors().isEmpty()) diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUImportStatement.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUImportStatement.kt index 0ba93bd5dac..8f2c2173f75 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUImportStatement.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUImportStatement.kt @@ -20,7 +20,6 @@ import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.uast.UElement import org.jetbrains.uast.UImportStatement import org.jetbrains.uast.UastContext -import org.jetbrains.uast.kinds.UastImportKind import org.jetbrains.uast.psi.PsiElementBacked class KotlinUImportStatement( @@ -29,10 +28,6 @@ class KotlinUImportStatement( ) : KotlinAbstractUElement(), UImportStatement, PsiElementBacked { override val fqNameToImport = psi.importedFqName?.asString() - //TODO support member imports - override val kind: UastImportKind - get() = UastImportKind.UNKNOWN - override val isStarImport: Boolean get() = psi.isAllUnder diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUType.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUType.kt index 0bf301590d4..f8e2b943c23 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUType.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUType.kt @@ -39,17 +39,21 @@ class KotlinUType( override val fqName: String? get() = type.constructor.declarationDescriptor?.fqNameSafe?.asString() - override val isInt = name == "Int" - override fun resolve(context: UastContext): UClass? { val descriptor = type.constructor.declarationDescriptor ?: return null - val sourceElement = descriptor.toSource(project) ?: return null + val sourceElement = descriptor.toSource() ?: return null return context.convert(sourceElement) as? UClass } override val isBoolean: Boolean get() = checkType(KotlinBuiltIns.FQ_NAMES._boolean) + override val isInt: Boolean + get() = checkType(KotlinBuiltIns.FQ_NAMES._int) + + override val isShort: Boolean + get() = checkType(KotlinBuiltIns.FQ_NAMES._short) + override val isLong: Boolean get() = checkType(KotlinBuiltIns.FQ_NAMES._long) diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUVariable.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUVariable.kt index 0f85189bf2d..8f4c171e027 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUVariable.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/KotlinUVariable.kt @@ -42,12 +42,8 @@ open class KotlinUVariable( KotlinConverter.convert(type, psi.project, this) } - override val getters: List? by lz { - (psi as? KtProperty)?.accessors?.filter { it.isGetter }?.map { VariableAccessorFunction(it, this) } - } - - override val setters: List? by lz { - (psi as? KtProperty)?.accessors?.filter { it.isSetter }?.map { VariableAccessorFunction(it, this) } + override val accessors: List? by lz { + (psi as? KtProperty)?.accessors?.map { VariableAccessorFunction(it, this) } } override val kind: UastVariableKind @@ -82,32 +78,33 @@ open class KotlinUVariable( override val typeParameters: List get() = emptyList() + override val typeParameterCount: Int get() = 0 override val returnType: UType? get() = if (psi.isSetter) null else parent.type - override val body by lz { KotlinConverter.convertOrEmpty(psi.bodyExpression, this) } + override val body by lz { KotlinConverter.convertOrNull(psi.bodyExpression, this) } override val visibility: UastVisibility get() = psi.getVisibility() - override fun getSuperFunctions(context: UastContext): List { - throw UnsupportedOperationException() - } + override fun getSuperFunctions(context: UastContext) = emptyList() + + override val nameElement by lz { KotlinDumbUElement(psi.namePlaceholder, this) } - override val nameElement: UElement? - get() = throw UnsupportedOperationException() override val name: String - get() = throw UnsupportedOperationException() + get() = when { + psi.isSetter -> "" + psi.isGetter -> "" + else -> "" + } - override fun hasModifier(modifier: UastModifier): Boolean { - throw UnsupportedOperationException() - } + override fun hasModifier(modifier: UastModifier) = false override val annotations: List - get() = throw UnsupportedOperationException() + get() = emptyList() } } diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/kotlinUFunctions.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/kotlinUFunctions.kt index d8483cb876b..d530917c1f7 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/kotlinUFunctions.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/declarations/kotlinUFunctions.kt @@ -43,10 +43,11 @@ abstract class KotlinAbstractUFunction : KotlinAbstractUElement(), UFunction, Ps override val valueParameters by lz { psi.valueParameters.map { KotlinConverter.convert(it, this) } } override fun getSuperFunctions(context: UastContext): List { + if (this.isTopLevel()) return emptyList() val bindingContext = psi.analyze(BodyResolveMode.PARTIAL) val clazz = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, psi] as? FunctionDescriptor ?: return emptyList() return clazz.overriddenDescriptors.map { - context.convert(it.toSource(psi)) as? UFunction + context.convert(it.toSource()) as? UFunction }.filterNotNull() } @@ -61,7 +62,7 @@ abstract class KotlinAbstractUFunction : KotlinAbstractUElement(), UFunction, Ps override fun hasModifier(modifier: UastModifier) = psi.hasModifier(modifier) override val annotations by lz { psi.getUastAnnotations(this) } - override val body by lz { KotlinConverter.convertOrEmpty(psi.bodyExpression, this) } + override val body by lz { KotlinConverter.convertOrNull(psi.bodyExpression, this) } override val visibility by lz { psi.getVisibility() } } @@ -126,7 +127,7 @@ class KotlinAnonymousInitializerUFunction( override val returnType: UType? get() = null - override val body by lz { KotlinConverter.convertOrEmpty(psi.body, this) } + override val body by lz { KotlinConverter.convertOrNull(psi.body, this) } override val visibility: UastVisibility get() = UastVisibility.PRIVATE @@ -168,7 +169,8 @@ open class KotlinDefaultPrimaryConstructorUFunction( override val returnType: UType? get() = null - override val body: UExpression = EmptyExpression(this) + override val body: UExpression? + get() = null override val visibility: UastVisibility get() = parent.visibility @@ -207,7 +209,7 @@ open class KotlinObjectLiteralConstructorUFunction( get() = KotlinConverter.convert(param.type, psi.project, this) override val nameElement: UElement? get() = null - override val parent: UElement? + override val parent: UElement get() = this@KotlinObjectLiteralConstructorUFunction override val name: String get() = param.name.asString() @@ -237,7 +239,8 @@ open class KotlinObjectLiteralConstructorUFunction( override val returnType: UType? get() = null - override val body: UExpression = EmptyExpression(this) + override val body: UExpression? + get() = null override val visibility: UastVisibility get() = parent.visibility diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpression.kt index 67ff90efd0d..e5ac4305f49 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBinaryExpression.kt @@ -38,9 +38,6 @@ class KotlinUBinaryExpression( KtTokens.PERC -> UastBinaryOperator.MOD KtTokens.OROR -> UastBinaryOperator.LOGICAL_OR KtTokens.ANDAND -> UastBinaryOperator.LOGICAL_AND - //KtTokens.OR -> UastBinaryOperator.BITWISE_OR - //KtTokens.AND -> UastBinaryOperator.BITWISE_AND - //KtTokens.XOR -> UastBinaryOperator.BITWISE_XOR KtTokens.EQEQ -> UastBinaryOperator.EQUALS KtTokens.EXCLEQ -> UastBinaryOperator.NOT_EQUALS KtTokens.EQEQEQ -> UastBinaryOperator.IDENTITY_EQUALS @@ -56,6 +53,7 @@ class KotlinUBinaryExpression( KtTokens.PERCEQ -> UastBinaryOperator.REMAINDER_ASSIGN KtTokens.IN_KEYWORD -> KotlinBinaryOperators.IN KtTokens.NOT_IN -> KotlinBinaryOperators.NOT_IN + KtTokens.RANGE -> KotlinBinaryOperators.RANGE_TO else -> UastBinaryOperator.UNKNOWN } } diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBreakExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBreakExpression.kt new file mode 100644 index 00000000000..f768700758f --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUBreakExpression.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast.expressions + +import org.jetbrains.kotlin.psi.KtBreakExpression +import org.jetbrains.kotlin.uast.KotlinAbstractUElement +import org.jetbrains.uast.UBreakExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinUBreakExpression( + override val psi: KtBreakExpression, + override val parent: UElement +) : KotlinAbstractUElement(), UBreakExpression, PsiElementBacked { + override val label: String? + get() = psi.getLabelName() +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUCallableReferenceExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUCallableReferenceExpression.kt new file mode 100644 index 00000000000..49ee5b7fbe9 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUCallableReferenceExpression.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast + +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.uast.* +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinUCallableReferenceExpression( + override val psi: KtCallableReferenceExpression, + override val parent: UElement +) : KotlinAbstractUElement(), UCallableReferenceExpression, PsiElementBacked, KotlinUElementWithType { + override val qualifierExpression = null + override val qualifierType by lz { KotlinConverter.convert(psi.typeReference, this) } + override val callableName: String + get() = psi.callableReference.getReferencedName() + + override fun resolve(context: UastContext): UDeclaration? { + throw UnsupportedOperationException() + } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUContinueExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUContinueExpression.kt new file mode 100644 index 00000000000..010c36b64cb --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUContinueExpression.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast.expressions + +import org.jetbrains.kotlin.psi.KtContinueExpression +import org.jetbrains.kotlin.uast.KotlinAbstractUElement +import org.jetbrains.uast.UContinueExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinUContinueExpression( + override val psi: KtContinueExpression, + override val parent: UElement +) : KotlinAbstractUElement(), UContinueExpression, PsiElementBacked { + override val label: String? + get() = psi.getLabelName() +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt index 55b5e8c6213..9cdb46ebdc8 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUFunctionCallExpression.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.uast import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall @@ -31,8 +30,8 @@ class KotlinUFunctionCallExpression( override val psi: KtCallExpression, override val parent: UElement ) : KotlinAbstractUElement(), UCallExpression, PsiElementBacked, KotlinUElementWithType { - override val functionName: String? - get() = (psi.calleeExpression as? KtSimpleNameExpression)?.getReferencedName() + override val functionName: String? by lz { resolveCall()?.resultingDescriptor?.name?.asString().orAnonymous() } + override fun matchesFunctionName(name: String) = functionName == name override val functionNameElement by lz { psi.calleeExpression?.let { KotlinConverter.convert(it, this) } } @@ -66,7 +65,7 @@ class KotlinUFunctionCallExpression( override fun resolve(context: UastContext): UFunction? { val resolvedCall = resolveCall() val descriptor = resolvedCall?.resultingDescriptor ?: return null - val source = descriptor.toSource(psi) ?: return null + val source = descriptor.toSource() ?: return null if (descriptor is ConstructorDescriptor && descriptor.isPrimary && source is KtClassOrObject && source.getPrimaryConstructor() == null @@ -95,65 +94,4 @@ class KotlinUComponentFunctionCallExpression( override val functionNameElement = null override val kind = UastCallKind.FUNCTION_CALL override fun resolve(context: UastContext) = null -} - -class KotlinUSuperConstructorCallExpression( - override val psi: KtObjectDeclaration, - override val parent: UElement, - val resolvedCall: () -> ResolvedCall<*>? -) : UCallExpression, PsiElementBacked { - override val functionReference: USimpleReferenceExpression? - get() = null - - override val classReference: USimpleReferenceExpression by lz { - val referenceParent = this - object : USimpleReferenceExpression, PsiElementBacked { - override val parent: UElement? - get() = referenceParent - - override val identifier: String - get() = resolvedCall()?.resultingDescriptor?.name?.asString().orAnonymous() - - override fun resolve(context: UastContext): UDeclaration? { - val descriptor = resolvedCall()?.resultingDescriptor ?: return null - val source = DescriptorToSourceUtilsIde.getAnyDeclaration(psi.project, descriptor) ?: return null - return context.convert(source) as? UDeclaration - } - - override val psi: PsiElement - get() = this@KotlinUSuperConstructorCallExpression.psi - } - } - - override val functionName: String? - get() = "" - - override val functionNameElement by lz { - try { - KotlinDumbUElement(psi.getObjectKeyword(), this) - } catch (e: NullPointerException) { - null - } - } - - override val valueArgumentCount: Int - get() = resolvedCall()?.valueArguments?.size ?: 0 - - override val valueArguments by lz { - resolvedCall()?.call?.valueArguments?.map { KotlinConverter.convertOrEmpty(it.getArgumentExpression(), this) } ?: emptyList() - } - - override val typeArgumentCount: Int - get() = 0 - override val typeArguments: List - get() = emptyList() - - override val kind: UastCallKind - get() = UastCallKind.CONSTRUCTOR_CALL - - override fun resolve(context: UastContext): UFunction? { - val descriptor = resolvedCall()?.resultingDescriptor ?: return null - val source = DescriptorToSourceUtilsIde.getAnyDeclaration(psi.project, descriptor) ?: return null - return context.convert(source) as? UFunction - } } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULabeledExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULabeledExpression.kt new file mode 100644 index 00000000000..f8924b11969 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULabeledExpression.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast + +import org.jetbrains.kotlin.psi.KtLabeledExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.ULabeledExpression +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinULabeledExpression( + override val psi: KtLabeledExpression, + override val parent: UElement +) : KotlinAbstractUElement(), ULabeledExpression, PsiElementBacked { + override val label: String + get() = psi.getLabelName().orAnonymous("label") + + override val expression by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULiteralExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULiteralExpression.kt index b13592ee099..2661dc1a65f 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULiteralExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinULiteralExpression.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.uast +import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression import org.jetbrains.kotlin.psi.KtConstantExpression @@ -30,20 +31,18 @@ class KotlinULiteralExpression( override val isNull: Boolean get() = psi.isNullExpression() - override fun asString(): String = psi.text - override val value by lz { evaluate() } } class KotlinStringULiteralExpression( override val psi: PsiElement, - override val parent: UElement -) : KotlinAbstractUElement(), ULiteralExpression, PsiElementBacked, KotlinUElementWithType, KotlinEvaluatableUElement { + override val parent: UElement, + val text: String? = null +) : KotlinAbstractUElement(), ULiteralExpression, PsiElementBacked, KotlinUElementWithType{ override val isNull = false - override fun asString(): String = '"' + psi.text + '"' override val value: String - get() = psi.text + get() = text ?: StringUtil.unescapeStringCharacters(psi.text) - override fun evaluate() = psi.text + override fun evaluate() = value } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUPrefixExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUPrefixExpression.kt index 229c534ec6a..ec07a395d19 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUPrefixExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUPrefixExpression.kt @@ -30,6 +30,7 @@ class KotlinUPrefixExpression( override val operand by lz { KotlinConverter.convertOrEmpty(psi.baseExpression, this) } override val operator = when (psi.operationToken) { + KtTokens.EXCL -> UastPrefixOperator.LOGICAL_NOT KtTokens.PLUS -> UastPrefixOperator.UNARY_PLUS KtTokens.MINUS -> UastPrefixOperator.UNARY_MINUS KtTokens.PLUSPLUS -> UastPrefixOperator.INC diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUQualifiedExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUQualifiedExpression.kt index aa971b34da9..086ab84496e 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUQualifiedExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUQualifiedExpression.kt @@ -16,8 +16,11 @@ package org.jetbrains.kotlin.uast +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.uast.* import org.jetbrains.uast.psi.PsiElementBacked @@ -43,5 +46,10 @@ class KotlinUComponentQualifiedExpression( internal set override val accessType = UastQualifiedExpressionAccessType.SIMPLE - override fun resolve(context: UastContext) = null + override fun resolve(context: UastContext): UDeclaration? { + val bindingContext = psi.analyze(BodyResolveMode.PARTIAL) + val descriptor = bindingContext[BindingContext.COMPONENT_RESOLVED_CALL, psi]?.resultingDescriptor ?: return null + val source = descriptor.toSource() ?: return null + return context.convert(source) as? UDeclaration + } } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUReturnExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUReturnExpression.kt new file mode 100644 index 00000000000..14e23e7e90e --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUReturnExpression.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast + +import org.jetbrains.kotlin.psi.KtReturnExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UReturnExpression +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinUReturnExpression( + override val psi: KtReturnExpression, + override val parent: UElement +) : KotlinAbstractUElement(), UReturnExpression, PsiElementBacked, KotlinUElementWithType { + override val returnExpression by lz { KotlinConverter.convertOrNull(psi.returnedExpression, this) } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt index 3b6adbdb6a8..0b95883fcb9 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSimpleReferenceExpression.kt @@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall @@ -42,7 +41,7 @@ open class KotlinUSimpleReferenceExpression( resolvedCall.resultingDescriptor } - val source = DescriptorToSourceUtilsIde.getAnyDeclaration(psi.project, resultingDescriptor) ?: return null + val source = resultingDescriptor.toSource() ?: return null return context.convert(source) as? UDeclaration } } @@ -63,7 +62,7 @@ class KotlinClassViaConstructorUSimpleReferenceExpression( val resolvedCall = psi.getResolvedCall(psi.analyze(BodyResolveMode.PARTIAL)) val resultingDescriptor = resolvedCall?.resultingDescriptor as? ConstructorDescriptor ?: return null val clazz = resultingDescriptor.containingDeclaration - val source = clazz.toSource(psi) ?: return null + val source = clazz.toSource() ?: return null return context.convert(source) as? UClass } } diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt index 112792c82f5..1004c563971 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUSwitchExpression.kt @@ -28,7 +28,6 @@ class KotlinUSwitchExpression( ) : KotlinAbstractUElement(), USwitchExpression, PsiElementBacked, KotlinUElementWithType { override val expression by lz { KotlinConverter.convertOrNull(psi.subjectExpression, this) } - //TODO to entries override val body: UExpression by lz { object : KotlinUSpecialExpressionList(psi, KotlinSpecialExpressionKinds.WHEN, this) { override fun renderString() = expressions.joinToString("\n") { it.renderString().withMargin } @@ -48,8 +47,8 @@ class KotlinUSwitchExpression( class KotlinUSwitchEntry( override val psi: KtWhenEntry, override val parent: UExpression -) : KotlinAbstractUElement(), UExpression, PsiElementBacked { - val conditions by lz { +) : KotlinAbstractUElement(), USwitchClauseExpressionWithBody, PsiElementBacked { + override val caseValues by lz { psi.conditions.map { when (it) { is KtWhenConditionInRange -> KotlinCustomUBinaryExpression(it, this).apply { leftOperand = KotlinStringUSimpleReferenceExpression("it", this) @@ -68,11 +67,11 @@ class KotlinUSwitchEntry( type = KotlinConverter.convert(it.typeReference, this) } is KtWhenConditionWithExpression -> KotlinConverter.convertOrEmpty(it.expression, this) - else -> EmptyExpression(this) + else -> EmptyUExpression(this) }} } - val expression: UExpression by lz { + override val body: UExpression by lz { object : KotlinUSpecialExpressionList(psi, KotlinSpecialExpressionKinds.WHEN_ENTRY, this) { override fun renderString() = buildString { appendln("{") @@ -85,25 +84,13 @@ class KotlinUSwitchEntry( is KtBlockExpression -> exprPsi.statements.map { KotlinConverter.convert(it, this) } else -> listOf(KotlinConverter.convertOrEmpty(exprPsi, this)) } - expressions = userExpressions + KotlinUSpecialExpressionList.Empty( - exprPsi ?: this@KotlinUSwitchEntry.psi, UastSpecialExpressionKind.BREAK, parent) + parent + expressions = userExpressions + object : UBreakExpression { + override val label: String? + get() = null + override val parent: UElement? + get() = this@KotlinUSwitchEntry + } } } - - override fun renderString() = buildString { - if (conditions.isEmpty()) { - append("else") - } else { - append(conditions.joinToString { it.renderString() }) - } - append(" -> ") - append(expression.renderString()) - } - - override fun logString() = log("KotlinUSwitchEntry", expression) - - override fun accept(visitor: UastVisitor) { - if (visitor.visitElement(this)) return - expression.accept(visitor) - } } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUThrowExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUThrowExpression.kt new file mode 100644 index 00000000000..097f79bafe9 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/expressions/KotlinUThrowExpression.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.uast + +import org.jetbrains.kotlin.psi.KtThrowExpression +import org.jetbrains.uast.UElement +import org.jetbrains.uast.UThrowExpression +import org.jetbrains.uast.psi.PsiElementBacked + +class KotlinUThrowExpression( + override val psi: KtThrowExpression, + override val parent: UElement +) : KotlinAbstractUElement(), UThrowExpression, PsiElementBacked, KotlinUElementWithType { + override val thrownExpression by lz { KotlinConverter.convertOrEmpty(psi.thrownExpression, this) } +} \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/internal/kotlinInternalUastUtils.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/internal/kotlinInternalUastUtils.kt index c0ac0dc73ed..470f9a679bc 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/internal/kotlinInternalUastUtils.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/internal/kotlinInternalUastUtils.kt @@ -17,15 +17,12 @@ package org.jetbrains.kotlin.uast import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType -import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.uast.kinds.KotlinUastVisibilities @@ -49,15 +46,18 @@ internal fun KtModifierListOwner.hasModifier(modifier: UastModifier): Boolean { if (this is KtObjectDeclaration && this.isObjectLiteral()) { return false } - return !hasModifier(KtTokens.INNER_KEYWORD) - } - - if (modifier == UastModifier.FINAL) { - if (hasModifier(KtTokens.FINAL_KEYWORD)) return true - if (this is KtProperty && !this.isVar) return true + if (this is KtClassOrObject && !hasModifier(KtTokens.INNER_KEYWORD)) { + return true + } + if (this is KtDeclaration && parent is KtObjectDeclaration) { + return true + } return false } + if (modifier == UastModifier.IMMUTABLE && this is KtVariableDeclaration && !this.isVar) return true + if (modifier == UastModifier.FINAL && hasModifier(KtTokens.FINAL_KEYWORD)) return true + val javaModifier = MODIFIER_MAP[modifier] ?: return false return hasModifier(javaModifier) } @@ -69,15 +69,7 @@ internal fun runReadAction(action: () -> T): T { internal fun KtElement?.resolveCallToUDeclaration(context: UastContext): UDeclaration? { if (this == null) return null val resolvedCall = this.getResolvedCall(analyze(BodyResolveMode.PARTIAL)) ?: return null - val source = (resolvedCall.resultingDescriptor).toSource(project) ?: return null - return context.convert(source) as? UDeclaration -} - -internal fun KtElement?.resolveElementToUDeclaration(context: UastContext): UDeclaration? { - if (this == null) return null - val bindingContext = analyze(BodyResolveMode.PARTIAL) - val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this] ?: return null - val source = descriptor.toSource(project) ?: return null + val source = (resolvedCall.resultingDescriptor).toSource() ?: return null return context.convert(source) as? UDeclaration } @@ -90,11 +82,8 @@ internal fun KtAnnotated.getUastAnnotations(parent: UElement) = annotationEntrie internal fun singletonListOrEmpty(element: T?) = if (element != null) listOf(element) else emptyList() -@Suppress("NOTHING_TO_INLINE") -internal inline fun DeclarationDescriptor.toSource(element: PsiElement) = toSource(element.project) - -internal fun DeclarationDescriptor.toSource(project: Project) = try { - DescriptorToSourceUtilsIde.getAnyDeclaration(project, this) +internal fun DeclarationDescriptor.toSource() = try { + DescriptorToSourceUtils.descriptorToDeclaration(this) } catch (e: Exception) { null } diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinBinaryOperators.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinBinaryOperators.kt index 2e3447f424c..8098c315dcd 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinBinaryOperators.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinBinaryOperators.kt @@ -23,4 +23,7 @@ object KotlinBinaryOperators { val IN = UastBinaryOperator("in") @JvmField val NOT_IN = UastBinaryOperator("!in") + + @JvmField + val RANGE_TO = UastBinaryOperator("..") } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinClassKinds.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinClassKinds.kt index a5c62519e99..b474b5dd726 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinClassKinds.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/kinds/KotlinClassKinds.kt @@ -16,12 +16,10 @@ package org.jetbrains.kotlin.uast -import org.jetbrains.uast.kinds.UastClassKind +import org.jetbrains.uast.UastClassKind + object KotlinClassKinds { @JvmField - val DEFAULT_COMPANION_OBJECT = UastClassKind.UastCompanionObject(true) - - @JvmField - val NAMED_COMPANION_OBJECT = UastClassKind.UastCompanionObject(false) + val COMPANION_OBJECT = UastClassKind("companion object") } \ No newline at end of file diff --git a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt index 4a75107e47c..cf4f3c9494a 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/kotlin/uast/lint/PropertyAsCallAndroidUastAdditionalChecker.kt @@ -25,10 +25,9 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.uast.* import org.jetbrains.uast.* -import org.jetbrains.uast.check.AndroidUastAdditionalChecker import org.jetbrains.uast.psi.PsiElementBacked -class PropertyAsCallAndroidUastAdditionalChecker : AndroidUastAdditionalChecker { +class PropertyAsCallAndroidUastAdditionalChecker : UastAdditionalChecker { override fun invoke(element: UElement, callback: UastCallback, context: UastContext) { val expr = element as? KotlinUSimpleReferenceExpression ?: return if (expr is KotlinNameUSimpleReferenceExpression) return @@ -78,7 +77,7 @@ class PropertyAsCallAndroidUastAdditionalChecker : AndroidUastAdditionalChecker override val kind = UastCallKind.FUNCTION_CALL override fun resolve(context: UastContext): UFunction? { - val source = accessorDescriptor.toSource(psi.project) + val source = accessorDescriptor.toSource() if (source != null) { (context.convert(source) as? UFunction)?.let { return it } } diff --git a/plugins/uast-kotlin/testData/ControlStructures.kt b/plugins/uast-kotlin/testData/ControlStructures.kt index eaff37c7ecf..c460d37cdcb 100644 --- a/plugins/uast-kotlin/testData/ControlStructures.kt +++ b/plugins/uast-kotlin/testData/ControlStructures.kt @@ -1,59 +1,107 @@ class ControlStructures { + val prop = 3 + + fun nullFun(): String? = null + fun test(): Boolean { - if (5 > 3) { - println("5 > 3") - } + "" +// " " +// "Z Z" +// +// val qwe = 2 +// " $qwe " +// "5\n 2" +// "\t\t\t" - for (c in "ABC") { - println(c) - } - - for (c: Char in "DEF") { - println(c.toByte()) - } - - var i = 5 - while (i > 0) { - i-- - if (i == 3) break - if (i == 2) { - continue - } - } - - i = 5 - do { - i -= 1 - } while (i > 0) - - "ABC".forEach { println(it.toString()[0]) } - - "ABC".zip("DEF").forEach { println(it.first + " " + it.second) } - - val (a, b) = "ABC".zip("DEF") - - val value = if (5 > 3) "a" else "b" - val list = listOf("A") - val list2 = listOf("A") - - val type = when (value) { - in list -> "inlist" - !in list2 -> "notinlist2" - is String -> "string" - is CharSequence -> "cs" - else -> "unknown" - } - - val x = when { - value == "b" -> "B" - 5 % 2 == 0 -> { - println("A") - "Q" - } - false -> "!" - else -> "A" - } - - return false +// if (5 > 3) { +// println("5 > 3") +// } +// +// for (c in "ABC") { +// println(c) +// } +// +// for (c: Char in "DEF") { +// println(c.toByte()) +// } +// +// var i = 5 +// while (i > 0) { +// i-- +// if (i == 3) break +// if (i == 2) { +// continue +// } +// } +// +// "" is String +// ("" as Any) as String? +// +// super.equals(this) +// this.equals(this) +// +// this@ControlStructures.equals(this) +// +// ControlStructures::test +// ControlStructures::prop +// ControlStructures::class.java +// +// outer@ for (outerVal in 1..2) { +// inner@ for (innerVal in 3..4) { +// continue@outer +// } +// break@outer +// } +// +// nullFun()?.let { println(it) } +// +// i = 5 +// do { +// i -= 1 +// } while (i > 0) +// +// "ABC".forEach { println(it.toString()[0]) } +// +// "ABC".zip("DEF").forEach { println(it.first + " " + it.second) } +// +// val arr = arrayOf("A", "B", "C") +// println(arr[2]) +// +// val (a, b) = "ABC".zip("DEF") +// +// val value = if (5 > 3) "a" else "b" +// val list = listOf("A") +// val list2 = listOf("A") +// +// val type = when (value) { +// in list -> "inlist" +// !in list2 -> "notinlist2" +// is String -> "string" +// is CharSequence -> "cs" +// else -> "unknown" +// } +// +// val x = when { +// value == "b" -> "B" +// 5 % 2 == 0 -> { +// println("A") +// "Q" +// } +// false -> "!" +// else -> "A" +// } +// +// try { +// 5 + 1 +// throw Exception() +// } catch (e: Exception) { +// e.printStackTrace() +// } catch (thr: Throwable) { +// System.out.println("error!") +// } finally { +// System.out.println("finally") +// } +// +// return false } } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/Declarations.kt b/plugins/uast-kotlin/testData/Declarations.kt index bb423f89146..05c2edf6dfa 100644 --- a/plugins/uast-kotlin/testData/Declarations.kt +++ b/plugins/uast-kotlin/testData/Declarations.kt @@ -1,5 +1,10 @@ class Declarations { val a: String = "a" + var b: String + get() = "A" + set(v) { println(v) } + val c: String + class NestedClass { val b: String = "b" } @@ -7,6 +12,14 @@ class Declarations { val c: CharSequence = a } + companion object { + val CONST_VAL = 1 + } + + companion object A { + fun b(): Boolean = true + } + fun func(a: Int, b: String): Int { return (a + 1) * b.length } diff --git a/plugins/uast-kotlin/testData/log/Declarations.txt b/plugins/uast-kotlin/testData/log/Declarations.txt index b2240295f9a..5bb903f36b7 100644 --- a/plugins/uast-kotlin/testData/log/Declarations.txt +++ b/plugins/uast-kotlin/testData/log/Declarations.txt @@ -1,22 +1,36 @@ -UFile (package = null) +UFile (package = ) UClass (Declarations, kind = class) - UFunction (, kind = CONSTRUCTOR, paramCount = 0) + UFunction (, kind = constructor, paramCount = 0) EmptyExpression UVariable (a, kind = member) ULiteralExpression ("a") + UVariable (b, kind = member) + EmptyExpression + UVariable (c, kind = member) + EmptyExpression UClass (NestedClass, kind = class) - UFunction (, kind = CONSTRUCTOR, paramCount = 0) + UFunction (, kind = constructor, paramCount = 0) EmptyExpression UVariable (b, kind = member) ULiteralExpression ("b") UClass (InnerClass, kind = class) - UFunction (, kind = CONSTRUCTOR, paramCount = 0) + UFunction (, kind = constructor, paramCount = 0) EmptyExpression UVariable (c, kind = member) USimpleReferenceExpression (a) + UClass (Companion, kind = companion object) + UFunction (, kind = constructor, paramCount = 0) + EmptyExpression + UVariable (CONST_VAL, kind = member) + ULiteralExpression (1) + UClass (A, kind = companion object) + UFunction (, kind = constructor, paramCount = 0) + EmptyExpression + UFunction (b, kind = function, paramCount = 0) + ULiteralExpression (true) UFunction (func, kind = function, paramCount = 2) UBlockExpression - USpecialExpressionList (return) + UReturnExpression UBinaryExpression (*) UParenthesizedExpression UBinaryExpression (+) diff --git a/plugins/uast-kotlin/testData/log/Simple.txt b/plugins/uast-kotlin/testData/log/Simple.txt index c2c6f6a0ddf..262fc426347 100644 --- a/plugins/uast-kotlin/testData/log/Simple.txt +++ b/plugins/uast-kotlin/testData/log/Simple.txt @@ -1,6 +1,6 @@ -UFile (package = null) +UFile (package = ) UClass (Simple, kind = class) - UFunction (, kind = CONSTRUCTOR, paramCount = 0) + UFunction (, kind = constructor, paramCount = 0) EmptyExpression UVariable (a, kind = member) UBinaryExpression (+) diff --git a/plugins/uast-kotlin/testData/render/Declarations.txt b/plugins/uast-kotlin/testData/render/Declarations.txt index 88c905cfb49..95af5700823 100644 --- a/plugins/uast-kotlin/testData/render/Declarations.txt +++ b/plugins/uast-kotlin/testData/render/Declarations.txt @@ -1,19 +1,42 @@ public static class Declarations { public fun () = EmptyExpression - var a: String = "a" + + public immutable var a: String = "a" + + public var b: String + public fun (): String = "A" + public fun (v: String) { + println(v) + } + + + public immutable var c: String public static class NestedClass { public fun () = EmptyExpression - var b: String = "b" + public immutable var b: String = "b" } + public class InnerClass { public fun () = EmptyExpression - var c: CharSequence = a + public immutable var c: CharSequence = a } + + public static companion object Companion { + public fun () = EmptyExpression + + public immutable var CONST_VAL: Int = 1 + } + + public static companion object A { + public fun () = EmptyExpression + + public fun b(): Boolean = true + } + public fun func(a: Int, b: String): Int { return (a + 1) * b.length } - } diff --git a/plugins/uast-kotlin/testData/render/Simple.txt b/plugins/uast-kotlin/testData/render/Simple.txt index b8256ae00b3..4bf7171b32f 100644 --- a/plugins/uast-kotlin/testData/render/Simple.txt +++ b/plugins/uast-kotlin/testData/render/Simple.txt @@ -1,8 +1,8 @@ public static class Simple { public fun () = EmptyExpression - var a: String = "text" + "other" + "text" + public immutable var a: String = "text" + "other" + "text" - var b: List = listOf("A") + public immutable var b: List = listOf("A") public fun test(): Unit { System.out.println(5.0f / 2)