Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dennis Ushakov
2014-12-29 12:06:55 +03:00
14 changed files with 245 additions and 36 deletions
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2014 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 com.intellij.ant;
import org.apache.tools.ant.types.RegularExpression;
/**
* A pattern which is used to skip NotNull assertion instrumentation on classes that have at least one annotation matching this pattern.
*
* Example usage:
*
* <javac2 ...>
* <skip pattern="com/acme/Instrumented"/>
* </javac2>
*/
public class ClassFilterAnnotationRegexp extends RegularExpression {
}
@@ -25,10 +25,8 @@ import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.types.Path;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
import org.jetbrains.org.objectweb.asm.ClassWriter;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.apache.tools.ant.util.regexp.Regexp;
import org.jetbrains.org.objectweb.asm.*;
import java.io.*;
import java.net.MalformedURLException;
@@ -40,6 +38,7 @@ public class Javac2 extends Javac {
private ArrayList myFormFiles;
private List myNestedFormPathList;
private boolean instrumentNotNull = true;
private List<Regexp> myClassFilterAnnotationRegexpList = new ArrayList<Regexp>(0);
public Javac2() {
}
@@ -75,6 +74,16 @@ public class Javac2 extends Javac {
this.instrumentNotNull = instrumentNotNull;
}
/**
* Allows to specify patterns of annotation class names to skip NotNull instrumentation on classes which have at least one
* annotation matching at least one of the given patterns
*
* @param regexp the regular expression for JVM internal name (slash-separated) of annotations
*/
public void add(final ClassFilterAnnotationRegexp regexp) {
myClassFilterAnnotationRegexpList.add(regexp.getRegexp(getProject()));
}
/**
* The overridden setter method that warns about unsupported option.
*
@@ -425,8 +434,8 @@ public class Javac2 extends Javac {
ClassReader reader = new ClassReader(inputStream);
int version = getClassFileVersion(reader);
if (version >= Opcodes.V1_5) {
if (version >= Opcodes.V1_5 && !shouldBeSkippedByAnnotationPattern(reader)) {
ClassWriter writer = new InstrumenterClassWriter(getAsmClassWriterFlags(version), finder);
if (NotNullVerifyingInstrumenter.processClassFile(reader, writer)) {
@@ -471,6 +480,30 @@ public class Javac2 extends Javac {
return classfileVersion[0];
}
private boolean shouldBeSkippedByAnnotationPattern(ClassReader reader) {
if (myClassFilterAnnotationRegexpList.isEmpty()) {
return false;
}
final boolean[] result = new boolean[]{false};
reader.accept(new ClassVisitor(Opcodes.ASM5) {
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (!result[0]) {
String internalName = Type.getType(desc).getInternalName();
for (Regexp regexp : myClassFilterAnnotationRegexpList) {
if (regexp.matches(internalName)) {
result[0] = true;
break;
}
}
}
return null;
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return result[0];
}
private void fireError(final String message) {
if (failOnError) {
throw new BuildException(message, getLocation());
@@ -43,13 +43,13 @@ public class ReformatCodeActionInEditorTest extends LightCodeInsightFixtureTestC
String before = null;
if (options.isProcessOnlyChangedText()) {
myFixture.configureByFile(getTestDataPath() + getTestName(true) + "_revision.java");
myFixture.configureByFile(getTestName(true) + "_revision.java");
PsiFile file = myFixture.getFile();
Document document = myFixture.getDocument(file);
before = document.getText();
}
myFixture.configureByFile(getTestDataPath() + getTestName(true) + "_before.java");
myFixture.configureByFile(getTestName(true) + "_before.java");
if (before != null) {
myFixture.getFile().putUserData(FormatChangedTextUtil.TEST_REVISION_CONTENT, before);
@@ -176,6 +176,7 @@ class JavaPredefinedConfigurations {
createSearchTemplateInfo(SSRBundle.message("predefined.configuration.javadoc.tags"),"/** @'Tag+ '_TagValue* */", METADATA_TYPE),
createSearchTemplateInfo(SSRBundle.message("predefined.configuration.xdoclet.metadata"),"/** @'Tag \n '_Property+\n*/", METADATA_TYPE),
createSearchTemplateInfo(SSRBundle.message("predefined.configuration.annotations"), "@'_Annotation", METADATA_TYPE),
createSearchTemplateInfo(SSRBundle.message("predefined.configuration.annotated.class"),
"@'_Annotation( )\n" +
"class 'Class {}", METADATA_TYPE),
@@ -755,11 +755,7 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
}
if (list != null && list.getTypeParameterElements().length > 0) {
boolean result = typeparams != null &&
myMatchingVisitor.matchInAnyOrder(
list.getTypeParameterElements(),
typeparams
);
boolean result = typeparams != null && myMatchingVisitor.matchSequentially(list.getTypeParameterElements(), typeparams);
if (!result) return false;
el = ((PsiJavaCodeReferenceElement)el).getReferenceNameElement();
@@ -851,8 +847,9 @@ public class JavaMatchingVisitor extends JavaElementVisitor {
else {
PsiElement element2 = ((PsiJavaReference)el2).resolve();
if (element2 != null) {
return text.equals(((PsiClass)element2).getQualifiedName());
if (element2 instanceof PsiClass) {
final PsiClass aClass = (PsiClass)element2;
return text.equals(aClass.getQualifiedName()) || text.equals(aClass.getName());
}
else {
return MatchUtils.compareWithNoDifferenceToPackage(text, text2);
@@ -8,6 +8,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.structuralsearch.*;
import com.intellij.structuralsearch.impl.matcher.CompiledPattern;
import com.intellij.structuralsearch.impl.matcher.JavaCompiledPattern;
@@ -211,7 +212,7 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
}
currentReference = (PsiReferenceExpression)qualifier;
}
if (!hasNoNestedSubstitutionHandlers) {
if (!hasNoNestedSubstitutionHandlers && PsiTreeUtil.getChildOfType(reference, PsiAnnotation.class) == null) {
createAndSetSubstitutionHandlerFromReference(
reference,
resolve != null ? ((PsiClass)resolve).getQualifiedName() : reference.getText(),
@@ -284,14 +285,26 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
return;
}
}
else if (firstChild instanceof PsiModifierList) {
final PsiModifierList modifierList = (PsiModifierList)firstChild;
final PsiAnnotation[] annotations = modifierList.getAnnotations();
if (annotations.length != 1) {
throw new UnsupportedPatternException("Pattern is malformed");
}
for (String modifier : PsiModifier.MODIFIERS) {
if (modifierList.hasExplicitModifier(modifier)) {
throw new UnsupportedPatternException("Pattern is malformed");
}
}
myCompilingVisitor.setHandler(psiDeclarationStatement, new AnnotationHandler());
final MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(psiDeclarationStatement);
handler.setFilter(AnnotationFilter.getInstance());
return;
}
final MatchingHandler handler = new DeclarationStatementHandler();
myCompilingVisitor.getContext().getPattern().setHandler(psiDeclarationStatement, handler);
PsiElement previousNonWhiteSpace = psiDeclarationStatement.getPrevSibling();
while (previousNonWhiteSpace instanceof PsiWhiteSpace) {
previousNonWhiteSpace = previousNonWhiteSpace.getPrevSibling();
}
final PsiElement previousNonWhiteSpace = PsiTreeUtil.skipSiblingsBackward(psiDeclarationStatement, PsiWhiteSpace.class);
if (previousNonWhiteSpace instanceof PsiComment) {
((DeclarationStatementHandler)handler)
@@ -459,6 +472,7 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
}
private static void handleReferenceText(String refname, CompileContext compileContext) {
System.out.println("JavaCompilingVisitor" + ".handleReferenceText(" + refname + ", " + compileContext + ")");
if (refname == null) return;
if (compileContext.getPattern().isTypedVar(refname)) {
@@ -0,0 +1,40 @@
/*
* Copyright 2000-2014 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 com.intellij.structuralsearch.impl.matcher.filters;
import com.intellij.dupLocator.util.NodeFilter;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiElement;
/**
* @author Bas
*/
public class AnnotationFilter implements NodeFilter {
private static class NodeFilterHolder {
private static final NodeFilter instance = new AnnotationFilter();
}
public static NodeFilter getInstance() {
return NodeFilterHolder.instance;
}
private AnnotationFilter() {}
public boolean accepts(PsiElement element) {
return element instanceof PsiAnnotation;
}
}
@@ -13,11 +13,9 @@ import com.intellij.psi.javadoc.PsiDocComment;
* To change this template use Options | File Templates.
*/
public class JavaDocFilter implements NodeFilter {
protected boolean result;
public boolean accepts(PsiElement element) {
return element instanceof PsiDocCommentOwner ||
element instanceof PsiDocComment;
return element instanceof PsiDocCommentOwner || element instanceof PsiDocComment;
}
private static class NodeFilterHolder {
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2014 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 com.intellij.structuralsearch.impl.matcher.handlers;
import com.intellij.psi.PsiElement;
import com.intellij.structuralsearch.impl.matcher.MatchContext;
import com.intellij.structuralsearch.impl.matcher.filters.AnnotationFilter;
/**
* @author Bas
*/
public class AnnotationHandler extends MatchingHandler {
public AnnotationHandler() {
setFilter(AnnotationFilter.getInstance());
}
public boolean match(PsiElement patternNode, PsiElement matchedNode, MatchContext context) {
if (!super.match(patternNode,matchedNode,context)) {
return false;
}
final PsiElement element = patternNode.getFirstChild().getFirstChild();
return context.getMatcher().match(element, matchedNode);
}
}
@@ -35,9 +35,25 @@ public interface DocumentationProvider {
*/
ExtensionPointName<DocumentationProvider> EP_NAME = ExtensionPointName.create("com.intellij.documentationProvider");
/**
* Returns the text to show in the Ctrl-hover popup for the specified element.
*
* @param element the element for which the documentation is requested (for example, if the mouse is over
* a method reference, this will be the method to which the reference is resolved).
* @param originalElement the element under the mouse cursor
* @return the documentation to show, or null if the provider can't provide any documentation for this element.
*/
@Nullable
String getQuickNavigateInfo(PsiElement element, PsiElement originalElement);
/**
* Returns the list of possible URLs to show as external documentation for the specified element.
* @param element the element for which the documentation is requested (for example, if the mouse is over
* a method reference, this will be the method to which the reference is resolved).
* @param originalElement the element under the mouse cursor
* @return the list of URLs to open in the browser. If the list contains a single URL, it will be opened.
* If the list contains multiple URls, the user will be prompted to choose one of them.
*/
@Nullable
List<String> getUrlFor(PsiElement element, PsiElement originalElement);
@@ -45,10 +61,11 @@ public interface DocumentationProvider {
* Callback for asking the doc provider for the complete documentation.
* <p/>
* Underlying implementation may be time-consuming, that's why this method is expected not to be called from EDT.
*
* @param element target element which documentation is being requested
* @param originalElement element initially picked up from the current context
* @return target element's documentation (if any)
*
* @param element the element for which the documentation is requested (for example, if the mouse is over
* a method reference, this will be the method to which the reference is resolved).
* @param originalElement the element under the mouse cursor
* @return target element's documentation (if any)
*/
@Nullable
String generateDoc(PsiElement element, @Nullable PsiElement originalElement);
@@ -521,10 +521,6 @@ public class SubstitutionHandler extends MatchingHandler {
this.target = target;
}
public MatchingHandler getMatchHandler() {
return matchHandler;
}
public void setMatchHandler(MatchingHandler matchHandler) {
this.matchHandler = matchHandler;
}
@@ -117,7 +117,8 @@ predefined.configuration.usage.of.derived.type.in.cast=usage of derived type in
predefined.configuration.annotated.methods=annotated methods
predefined.configuration.not.annotated.methods=not annotated methods
predefined.configuration.annotation.declarations=annotation declarations
predefined.configuration.annotated.class=annotated class
predefined.configuration.annotations=annotations
predefined.configuration.annotated.class=annotated classes
predefined.configuration.entity.ejb=entity ejb
predefined.configuration.generic.methods=generic methods
predefined.configuration.cloneable.implementations=Cloneable implementations
@@ -1320,8 +1320,8 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
String s82_8 = "'T<'_Subst+>";
assertEquals(
"typed symbol",
findMatchesCount(s81_4,s82_8),
6
8,
findMatchesCount(s81_4,s82_8)
);
String s81_5 = "class A { HashMap<String, Integer> variable = new HashMap<String, Integer>(\"aaa\");}";
@@ -1336,6 +1336,16 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
findMatchesCount(s81_5, "new 'Type<>('_Param)"),
0
);
assertEquals(
"order of parameters matters",
0,
findMatchesCount(s81_5, "HashMap<Integer, String>")
);
assertEquals(
"order of parameters matters 2",
2,
findMatchesCount(s81_5, "HashMap<String, Integer>")
);
String source1 = "class Comparator<T> { private Comparator<String> c; private Comparator d; }";
String target1 = "java.util.Comparator 'a;";
@@ -2408,6 +2418,24 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
assertEquals("Find anno parameter value",0,findMatchesCount(s11,s12_5));
assertEquals("Find anno parameter value",4,findMatchesCount(s11,s12_6));
assertEquals("Find anno parameter value",4,findMatchesCount(s11,s12_7));
String source1 = "class A {" +
" void m() {" +
" new @B Object();" +
" }" +
"}";
assertEquals("Find annotated new expression", 1, findMatchesCount(source1, "new Object()"));
assertEquals("Find annotated new expression", 1, findMatchesCount(source1, "new @B Object()"));
assertEquals("Find annotated new expression", 0, findMatchesCount(source1, "new @C Object()"));
String source2 = "@X\n" +
"class A {\n" +
" @Y int value;" +
" @Y int void m(@Z int i) {\n" +
" return 1;\n" +
" }\n" +
"}\n";
assertEquals("Find all annotations", 4, findMatchesCount(source2, "@'_Annotation"));
}
public void testBoxingAndUnboxing() {
@@ -2938,6 +2966,7 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
" Runnable r = System.out::println;" +
" Runnable s = this::hashCode;" +
" Runnable t = this::new;" +
" Runnable u = @AA A::new;" +
" static {" +
" System.out.println();" +
" }" +
@@ -2950,7 +2979,10 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
assertEquals("should find method reference 2", 2, findMatchesCount(source, pattern2));
String pattern3 = "'_a::'_b";
assertEquals("should find all method references", 3, findMatchesCount(source, pattern3));
assertEquals("should find all method references", 4, findMatchesCount(source, pattern3));
String pattern4 = "@AA A::new";
assertEquals("should find annotated method references", 1, findMatchesCount(source, pattern4));
}
public void testNoUnexpectedException() {
@@ -32,6 +32,7 @@ import com.intellij.vcs.log.graph.GraphCommit;
import com.intellij.vcs.log.graph.impl.facade.PermanentGraphImpl;
import com.intellij.vcs.log.impl.HashImpl;
import com.intellij.vcs.log.impl.LogDataImpl;
import com.intellij.vcs.log.util.StopWatch;
import git4idea.*;
import git4idea.branch.GitBranchUtil;
import git4idea.config.GitVersionSpecialty;
@@ -135,8 +136,10 @@ public class GitLogProvider implements VcsLogProvider {
}
}
StopWatch sw = StopWatch.start("sorting commits in " + root.getName());
List<VcsCommitMetadata> sortedCommits = VcsLogSorter.sortByDateTopoOrder(allDetails);
sortedCommits = sortedCommits.subList(0, Math.min(sortedCommits.size(), requirements.getCommitCount()));
sw.report();
if (LOG.isDebugEnabled()) {
validateDataAndReportError(root, allRefs, sortedCommits, data, branches, currentTagNames, commitsFromTags);
@@ -152,6 +155,7 @@ public class GitLogProvider implements VcsLogProvider {
final Set<VcsRef> manuallyReadBranches,
@Nullable final Set<String> currentTagNames,
@Nullable final DetailedLogData commitsFromTags) {
StopWatch sw = StopWatch.start("validating data in " + root.getName());
final Set<Hash> refs = ContainerUtil.map2Set(allRefs, new Function<VcsRef, Hash>() {
@Override
public Hash fun(VcsRef ref) {
@@ -181,6 +185,7 @@ public class GitLogProvider implements VcsLogProvider {
return 0;
}
}, refs);
sw.report();
}
@SuppressWarnings("StringConcatenationInsideStringBufferAppend")
@@ -264,8 +269,10 @@ public class GitLogProvider implements VcsLogProvider {
@NotNull
private Set<String> readCurrentTagNames(@NotNull VirtualFile root) throws VcsException {
StopWatch sw = StopWatch.start("reading tags in " + root.getName());
Set<String> tags = newHashSet();
GitTag.listAsStrings(myProject, root, tags, null);
sw.report();
return tags;
}
@@ -289,9 +296,11 @@ public class GitLogProvider implements VcsLogProvider {
@NotNull
private DetailedLogData loadSomeCommitsOnTaggedBranches(@NotNull VirtualFile root, int commitCount,
@NotNull Collection<String> unmatchedTags) throws VcsException {
StopWatch sw = StopWatch.start("loading commits on tagged branch in " + root.getName());
List<String> params = new ArrayList<String>();
params.add("--max-count=" + commitCount);
params.addAll(unmatchedTags);
sw.report();
return GitHistoryUtils.loadMetadata(myProject, root, true, ArrayUtil.toStringArray(params));
}
@@ -337,6 +346,7 @@ public class GitLogProvider implements VcsLogProvider {
@NotNull
private Set<VcsRef> readBranches(@NotNull GitRepository repository) {
StopWatch sw = StopWatch.start("readBranches in " + repository.getRoot().getName());
VirtualFile root = repository.getRoot();
repository.update();
Collection<GitLocalBranch> localBranches = repository.getBranches().getLocalBranches();
@@ -354,6 +364,7 @@ public class GitLogProvider implements VcsLogProvider {
if (currentRevision != null) { // null => fresh repository
refs.add(myVcsObjectsFactory.createRef(HashImpl.build(currentRevision), "HEAD", GitRefManager.HEAD, root));
}
sw.report();
return refs;
}