Illegal Dalvik identifier inspection

This commit is contained in:
Yan Zhulanow
2016-01-29 21:04:26 +03:00
parent 8d87800a05
commit f2edcd99a8
8 changed files with 186 additions and 7 deletions
@@ -0,0 +1,25 @@
/*
* 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.android
import com.intellij.psi.PsiElement
import org.jetbrains.android.facet.AndroidFacet
fun PsiElement.getAndroidFacetForFile(): AndroidFacet? {
val file = containingFile ?: return null
return AndroidFacet.getInstance(file)
}
@@ -0,0 +1,89 @@
/*
* 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.android.inspection
import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInspection.*
import com.intellij.ide.DataManager
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.refactoring.rename.RenameHandlerRegistry
import org.jetbrains.kotlin.android.getAndroidFacetForFile
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
class IllegalIdentifierInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitElement(element: PsiElement) {
if (element.node.elementType != KtTokens.IDENTIFIER) return
val text = element.text
// '`' can't be escaped now
if (!text.startsWith('`') || !text.endsWith('`')) return
val unquotedName = KtPsiUtil.unquoteIdentifier(text)
// This is already an error
if (unquotedName.isEmpty()) return
if (!unquotedName.all { isValidDalvikCharacter(it) } && checkAndroidFacet(element)) {
holder.registerProblem(element,
"Identifier not allowed in Android projects",
ProblemHighlightType.GENERIC_ERROR,
RenameIdentifierFix())
}
}
fun checkAndroidFacet(element: PsiElement): Boolean {
return element.getAndroidFacetForFile() != null || ApplicationManager.getApplication().isUnitTestMode
}
// https://source.android.com/devices/tech/dalvik/dex-format.html#string-syntax
fun isValidDalvikCharacter(c: Char) = when (c) {
in 'A'..'Z' -> true
in 'a'..'z' -> true
in '0'..'9' -> true
'$', '-', '_' -> true
in '\u00a1' .. '\u1fff' -> true
in '\u2010' .. '\u2027' -> true
in '\u2030' .. '\ud7ff' -> true
in '\ue000' .. '\uffef' -> true
else -> false
}
}
}
class RenameIdentifierFix : LocalQuickFix {
override fun getName() = "Rename"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement ?: return
val file = element.containingFile ?: return
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
val editorManager = FileEditorManager.getInstance(project)
val editor = editorManager.getSelectedEditor(file.virtualFile) ?: return
val dataContext = DataManager.getInstance().getDataContext(editor.component)
val renameHandler = RenameHandlerRegistry.getInstance().getRenameHandler(dataContext)
renameHandler?.invoke(project, arrayOf(element), dataContext);
}
}
}
@@ -21,7 +21,6 @@ import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlElement;
import org.jetbrains.android.dom.AndroidAttributeValue;
import org.jetbrains.android.dom.manifest.Manifest;
@@ -35,6 +34,7 @@ import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.android.AndroidUtilKt;
import org.jetbrains.kotlin.psi.KtSimpleNameExpression;
import java.util.ArrayList;
@@ -51,12 +51,7 @@ public class KotlinAndroidGotoDeclarationHandler implements GotoDeclarationHandl
return null;
}
PsiFile file = referenceExpression.getContainingFile();
if (file == null) {
return null;
}
AndroidFacet facet = AndroidFacet.getInstance(file);
AndroidFacet facet = AndroidUtilKt.getAndroidFacetForFile(referenceExpression);
if (facet == null) {
return null;
}
+6
View File
@@ -7,6 +7,12 @@
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidPsiTreeChangePreprocessor"/>
<errorHandler implementation="org.jetbrains.kotlin.plugin.android.AndroidExtensionsReportSubmitter"/>
<gotoDeclarationHandler implementation="org.jetbrains.kotlin.android.synthetic.idea.AndroidGotoDeclarationHandler"/>
<localInspection implementationClass="org.jetbrains.kotlin.android.inspection.IllegalIdentifierInspection"
displayName="Illegal Android Identifier"
groupName="Kotlin"
enabledByDefault="true"
level="ERROR"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
@@ -0,0 +1,46 @@
<problems>
<problem>
<file>temp:///src/test.kt</file>
<line>7</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Illegal Android Identifier</problem_class>
<description>Illegal Android Identifier</description>
</problem>
<problem>
<file>temp:///src/test.kt</file>
<line>8</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Illegal Android Identifier</problem_class>
<description>Illegal Android Identifier</description>
</problem>
<problem>
<file>temp:///src/test.kt</file>
<line>9</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Illegal Android Identifier</problem_class>
<description>Illegal Android Identifier</description>
</problem>
<problem>
<file>temp:///src/test.kt</file>
<line>10</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Illegal Android Identifier</problem_class>
<description>Illegal Android Identifier</description>
</problem>
<problem>
<file>temp:///src/test.kt</file>
<line>11</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Illegal Android Identifier</problem_class>
<description>Illegal Android Identifier</description>
</problem>
</problems>
@@ -0,0 +1 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.android.inspection.IllegalIdentifierInspection
@@ -0,0 +1,11 @@
val `Kotlin` = 0
val `Котлин` = 0
val `ことりん` = 0
val `§*!`` = 0
val `0a0b0c` = 0
val `$a-b_c` = 0
val `with space` = 0 //illegal
val `a+b` = 0 //illegal
val `#` = 0 //illegal
val `!` = 0 //illegal
val `()` = 0 //illegal
@@ -106,6 +106,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/inspections"), Pattern.compile("^(inspections\\.test)$"));
}
@TestMetadata("androidIllegalIdentifiers/inspectionData/inspections.test")
public void testAndroidIllegalIdentifiers_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/androidIllegalIdentifiers/inspectionData/inspections.test");
doTest(fileName);
}
@TestMetadata("conflictingExtensionProperty/inspectionData/inspections.test")
public void testConflictingExtensionProperty_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/conflictingExtensionProperty/inspectionData/inspections.test");