diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index 540e972759..b970880aa0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -147,7 +147,16 @@ public class GUI implements CPDListener { @Override public boolean canIgnoreAnnotations() { - return "java".equals(terseName); + if (terseName == null) { + return false; + } + switch (terseName) { + case "cs": + case "java": + return true; + default: + return false; + } } @Override diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java index a48212290a..7a8cfd09f6 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java @@ -20,6 +20,7 @@ public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; private boolean ignoreLiteralSequences = false; + private boolean ignoreAttributes = false; /** * Sets the possible options for the C# tokenizer. @@ -27,19 +28,16 @@ public class CsTokenizer extends AntlrTokenizer { * @param properties the properties * @see #IGNORE_USINGS * @see #OPTION_IGNORE_LITERAL_SEQUENCES + * @see #IGNORE_ANNOTATIONS */ public void setProperties(Properties properties) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, Boolean.FALSE.toString())); - ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, - Boolean.FALSE.toString())); + ignoreUsings = getBooleanProperty(properties, IGNORE_USINGS); + ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES); + ignoreAttributes = getBooleanProperty(properties, IGNORE_ANNOTATIONS); } - public void setIgnoreUsings(boolean ignoreUsings) { - this.ignoreUsings = ignoreUsings; - } - - public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { - this.ignoreLiteralSequences = ignoreLiteralSequences; + private boolean getBooleanProperty(final Properties properties, final String property) { + return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString())); } @Override @@ -50,7 +48,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { - return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences); + return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences, ignoreAttributes); } /** @@ -69,15 +67,18 @@ public class CsTokenizer extends AntlrTokenizer { private final boolean ignoreUsings; private final boolean ignoreLiteralSequences; + private final boolean ignoreAttributes; private boolean discardingUsings = false; private boolean discardingNL = false; + private boolean isDiscardingAttribute = false; private AntlrToken discardingLiteralsUntil = null; private boolean discardCurrent = false; - CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { + CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { super(tokenManager); this.ignoreUsings = ignoreUsings; this.ignoreLiteralSequences = ignoreLiteralSequences; + this.ignoreAttributes = ignoreAttributes; } @Override @@ -90,6 +91,7 @@ public class CsTokenizer extends AntlrTokenizer { discardCurrent = false; skipUsingDirectives(currentToken, remainingTokens); skipLiteralSequences(currentToken, remainingTokens); + skipAttributes(currentToken); } private void skipUsingDirectives(final AntlrToken currentToken, final Iterable remainingTokens) { @@ -166,6 +168,25 @@ public class CsTokenizer extends AntlrTokenizer { discardingNL = currentToken.getKind() == CSharpLexer.NL; } + private void skipAttributes(final AntlrToken currentToken) { + if (ignoreAttributes) { + switch (currentToken.getKind()) { + case CSharpLexer.OPEN_BRACKET: + // Start of an attribute. + isDiscardingAttribute = true; + break; + case CSharpLexer.CLOSE_BRACKET: + // End of an attribute. + isDiscardingAttribute = false; + discardCurrent = true; + break; + default: + // Skip any other token. + break; + } + } + } + private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { if (ignoreLiteralSequences) { final int type = currentToken.getKind(); @@ -221,7 +242,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || isDiscardingLiterals() || discardCurrent; + return discardingUsings || discardingNL || isDiscardingAttribute || isDiscardingLiterals() || discardCurrent; } } } diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java index 63c9cd5aee..d05797d726 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java @@ -105,18 +105,33 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("csharp7And8Additions"); } + @Test + public void testAttributesAreNotIgnored() { + doTest("attributes"); + } + + @Test + public void testAttributesAreIgnored() { + doTest("attributes", "_ignored", skipAttributes()); + } + private Properties ignoreUsings() { - return properties(true, false); + return properties(true, false, false); } private Properties skipLiteralSequences() { - return properties(false, true); + return properties(false, true, false); } - private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences) { + private Properties skipAttributes() { + return properties(false, false, true); + } + + private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) { Properties properties = new Properties(); properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings)); properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences)); + properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, Boolean.toString(ignoreAttributes)); return properties; } } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs new file mode 100644 index 0000000000..811e151362 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.cs @@ -0,0 +1,42 @@ +[Serializable] +public class SampleClass +{ + // Objects of this type can be serialized. +} + +[System.Runtime.InteropServices.DllImport("user32.dll")] +extern static void SampleMethod(); + +void MethodA([In][Out] ref double x) { } +void MethodB([Out][In] ref double x) { } +void MethodC([In, Out] ref double x) { } + +[Conditional("DEBUG"), Conditional("TEST1")] +void TraceMethod() +{ + // ... +} + +[DllImport("user32.dll")] +[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)] +[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)] + +using System; +using System.Reflection; +[assembly: AssemblyTitleAttribute("Production assembly 4")] +[module: CLSCompliant(true)] + +// default: applies to method +[ValidatedContract] +int Method1() { return 0; } + +// applies to method +[method: ValidatedContract] +int Method2() { return 0; } + +// applies to parameter +int Method3([ValidatedContract] string contract) { return 0; } + +// applies to return value +[return: ValidatedContract] +int Method4() { return 0; } \ No newline at end of file diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt new file mode 100644 index 0000000000..403064af38 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes.txt @@ -0,0 +1,229 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [\[] 1 1 + [Serializable] 2 13 + [\]] 14 14 +L2 + [public] 1 6 + [class] 8 12 + [SampleClass] 14 24 +L3 + [{] 1 1 +L5 + [}] 1 1 +L7 + [\[] 1 1 + [System] 2 7 + [.] 8 8 + [Runtime] 9 15 + [.] 16 16 + [InteropServices] 17 31 + [.] 32 32 + [DllImport] 33 41 + [(] 42 42 + ["user32.dll"] 43 54 + [)] 55 55 + [\]] 56 56 +L8 + [extern] 1 6 + [static] 8 13 + [void] 15 18 + [SampleMethod] 20 31 + [(] 32 32 + [)] 33 33 + [;] 34 34 +L10 + [void] 1 4 + [MethodA] 6 12 + [(] 13 13 + [\[] 14 14 + [In] 15 16 + [\]] 17 17 + [\[] 18 18 + [Out] 19 21 + [\]] 22 22 + [ref] 24 26 + [double] 28 33 + [x] 35 35 + [)] 36 36 + [{] 38 38 + [}] 40 40 +L11 + [void] 1 4 + [MethodB] 6 12 + [(] 13 13 + [\[] 14 14 + [Out] 15 17 + [\]] 18 18 + [\[] 19 19 + [In] 20 21 + [\]] 22 22 + [ref] 24 26 + [double] 28 33 + [x] 35 35 + [)] 36 36 + [{] 38 38 + [}] 40 40 +L12 + [void] 1 4 + [MethodC] 6 12 + [(] 13 13 + [\[] 14 14 + [In] 15 16 + [,] 17 17 + [Out] 19 21 + [\]] 22 22 + [ref] 24 26 + [double] 28 33 + [x] 35 35 + [)] 36 36 + [{] 38 38 + [}] 40 40 +L14 + [\[] 1 1 + [Conditional] 2 12 + [(] 13 13 + ["DEBUG"] 14 20 + [)] 21 21 + [,] 22 22 + [Conditional] 24 34 + [(] 35 35 + ["TEST1"] 36 42 + [)] 43 43 + [\]] 44 44 +L15 + [void] 1 4 + [TraceMethod] 6 16 + [(] 17 17 + [)] 18 18 +L16 + [{] 1 1 +L18 + [}] 1 1 +L20 + [\[] 1 1 + [DllImport] 2 10 + [(] 11 11 + ["user32.dll"] 12 23 + [)] 24 24 + [\]] 25 25 +L21 + [\[] 1 1 + [DllImport] 2 10 + [(] 11 11 + ["user32.dll"] 12 23 + [,] 24 24 + [SetLastError] 26 37 + [=] 38 38 + [false] 39 43 + [,] 44 44 + [ExactSpelling] 46 58 + [=] 59 59 + [false] 60 64 + [)] 65 65 + [\]] 66 66 +L22 + [\[] 1 1 + [DllImport] 2 10 + [(] 11 11 + ["user32.dll"] 12 23 + [,] 24 24 + [ExactSpelling] 26 38 + [=] 39 39 + [false] 40 44 + [,] 45 45 + [SetLastError] 47 58 + [=] 59 59 + [false] 60 64 + [)] 65 65 + [\]] 66 66 +L24 + [using] 1 5 + [System] 7 12 + [;] 13 13 +L25 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Reflection] 14 23 + [;] 24 24 +L26 + [\[] 1 1 + [assembly] 2 9 + [:] 10 10 + [AssemblyTitleAttribute] 12 33 + [(] 34 34 + ["Production assembly 4"] 35 57 + [)] 58 58 + [\]] 59 59 +L27 + [\[] 1 1 + [module] 2 7 + [:] 8 8 + [CLSCompliant] 10 21 + [(] 22 22 + [true] 23 26 + [)] 27 27 + [\]] 28 28 +L30 + [\[] 1 1 + [ValidatedContract] 2 18 + [\]] 19 19 +L31 + [int] 1 3 + [Method1] 5 11 + [(] 12 12 + [)] 13 13 + [{] 15 15 + [return] 17 22 + [0] 24 24 + [;] 25 25 + [}] 27 27 +L34 + [\[] 1 1 + [method] 2 7 + [:] 8 8 + [ValidatedContract] 10 26 + [\]] 27 27 +L35 + [int] 1 3 + [Method2] 5 11 + [(] 12 12 + [)] 13 13 + [{] 15 15 + [return] 17 22 + [0] 24 24 + [;] 25 25 + [}] 27 27 +L38 + [int] 1 3 + [Method3] 5 11 + [(] 12 12 + [\[] 13 13 + [ValidatedContract] 14 30 + [\]] 31 31 + [string] 33 38 + [contract] 40 47 + [)] 48 48 + [{] 50 50 + [return] 52 57 + [0] 59 59 + [;] 60 60 + [}] 62 62 +L41 + [\[] 1 1 + [return] 2 7 + [:] 8 8 + [ValidatedContract] 10 26 + [\]] 27 27 +L42 + [int] 1 3 + [Method4] 5 11 + [(] 12 12 + [)] 13 13 + [{] 15 15 + [return] 17 22 + [0] 24 24 + [;] 25 25 + [}] 27 27 +EOF diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt new file mode 100644 index 0000000000..0796c4384c --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/attributes_ignored.txt @@ -0,0 +1,109 @@ + [Image] or [Truncated image[ Bcol Ecol +L2 + [public] 1 6 + [class] 8 12 + [SampleClass] 14 24 +L3 + [{] 1 1 +L5 + [}] 1 1 +L8 + [extern] 1 6 + [static] 8 13 + [void] 15 18 + [SampleMethod] 20 31 + [(] 32 32 + [)] 33 33 + [;] 34 34 +L10 + [void] 1 4 + [MethodA] 6 12 + [(] 13 13 + [ref] 24 26 + [double] 28 33 + [x] 35 35 + [)] 36 36 + [{] 38 38 + [}] 40 40 +L11 + [void] 1 4 + [MethodB] 6 12 + [(] 13 13 + [ref] 24 26 + [double] 28 33 + [x] 35 35 + [)] 36 36 + [{] 38 38 + [}] 40 40 +L12 + [void] 1 4 + [MethodC] 6 12 + [(] 13 13 + [ref] 24 26 + [double] 28 33 + [x] 35 35 + [)] 36 36 + [{] 38 38 + [}] 40 40 +L15 + [void] 1 4 + [TraceMethod] 6 16 + [(] 17 17 + [)] 18 18 +L16 + [{] 1 1 +L18 + [}] 1 1 +L24 + [using] 1 5 + [System] 7 12 + [;] 13 13 +L25 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Reflection] 14 23 + [;] 24 24 +L31 + [int] 1 3 + [Method1] 5 11 + [(] 12 12 + [)] 13 13 + [{] 15 15 + [return] 17 22 + [0] 24 24 + [;] 25 25 + [}] 27 27 +L35 + [int] 1 3 + [Method2] 5 11 + [(] 12 12 + [)] 13 13 + [{] 15 15 + [return] 17 22 + [0] 24 24 + [;] 25 25 + [}] 27 27 +L38 + [int] 1 3 + [Method3] 5 11 + [(] 12 12 + [string] 33 38 + [contract] 40 47 + [)] 48 48 + [{] 50 50 + [return] 52 57 + [0] 59 59 + [;] 60 60 + [}] 62 62 +L42 + [int] 1 3 + [Method4] 5 11 + [(] 12 12 + [)] 13 13 + [{] 15 15 + [return] 17 22 + [0] 24 24 + [;] 25 25 + [}] 27 27 +EOF