diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index 1bf2954c18..0580732bb4 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -163,7 +163,7 @@ exactly identical. {% include custom/cli_option_row.html options="--ignore-literals" description="Ignore literal values such as numbers and strings when comparing text. By default, literals are not ignored." - languages="Java" + languages="Java, C++" %} {% include custom/cli_option_row.html options="--ignore-literal-sequences" description="Ignore sequences of literals such as list initializers. @@ -173,7 +173,7 @@ exactly identical. {% include custom/cli_option_row.html options="--ignore-identifiers" description="Ignore names of classes, methods, variables, constants, etc. when comparing text. By default, identifier names are not ignored." - languages="Java" + languages="Java, C++" %} {% include custom/cli_option_row.html options="--ignore-annotations" description="Ignore language annotations (Java) or attributes (C#) when comparing text. diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java index 520fc7327c..5312f8abaf 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/CppLanguageModule.java @@ -47,6 +47,8 @@ public class CppLanguageModule extends CpdOnlyLanguageModuleBase { LanguagePropertyBundle bundle = super.newPropertyBundle(); bundle.definePropertyDescriptor(CpdLanguageProperties.CPD_IGNORE_LITERAL_SEQUENCES); bundle.definePropertyDescriptor(CpdLanguageProperties.CPD_IGNORE_LITERAL_AND_IDENTIFIER_SEQUENCES); + bundle.definePropertyDescriptor(CpdLanguageProperties.CPD_ANONYMIZE_IDENTIFIERS); + bundle.definePropertyDescriptor(CpdLanguageProperties.CPD_ANONYMIZE_LITERALS); bundle.definePropertyDescriptor(CPD_SKIP_BLOCKS); return bundle; } diff --git a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexer.java b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexer.java index 5d0e423a51..69287747ba 100644 --- a/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexer.java +++ b/pmd-cpp/src/main/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexer.java @@ -9,8 +9,9 @@ import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.cpd.CpdLanguageProperties; -import net.sourceforge.pmd.cpd.impl.CpdLexerBase; +import net.sourceforge.pmd.cpd.TokenFactory; import net.sourceforge.pmd.cpd.impl.JavaCCTokenFilter; +import net.sourceforge.pmd.cpd.impl.JavaccCpdLexer; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.TokenManager; import net.sourceforge.pmd.lang.ast.impl.javacc.CharStream; @@ -26,17 +27,21 @@ import net.sourceforge.pmd.lang.document.TextDocument; * *

Note: This class has been called CPPTokenizer in PMD 6

. */ -public class CppCpdLexer extends CpdLexerBase { +public class CppCpdLexer extends JavaccCpdLexer { private boolean skipBlocks; private Pattern skipBlocksStart; private Pattern skipBlocksEnd; private final boolean ignoreIdentifierAndLiteralSeqences; private final boolean ignoreLiteralSequences; + private final boolean ignoreLiterals; + private final boolean ignoreIdentifiers; public CppCpdLexer(LanguagePropertyBundle cppProperties) { ignoreLiteralSequences = cppProperties.getProperty(CpdLanguageProperties.CPD_IGNORE_LITERAL_SEQUENCES); ignoreIdentifierAndLiteralSeqences = cppProperties.getProperty(CpdLanguageProperties.CPD_IGNORE_LITERAL_AND_IDENTIFIER_SEQUENCES); + ignoreLiterals = cppProperties.getProperty(CpdLanguageProperties.CPD_ANONYMIZE_LITERALS); + ignoreIdentifiers = cppProperties.getProperty(CpdLanguageProperties.CPD_ANONYMIZE_IDENTIFIERS); String skipBlocksPattern = cppProperties.getProperty(CppLanguageModule.CPD_SKIP_BLOCKS); if (StringUtils.isNotBlank(skipBlocksPattern)) { skipBlocks = true; @@ -73,6 +78,23 @@ public class CppCpdLexer extends CpdLexerBase { return new CppTokenFilter(tokenManager, ignoreLiteralSequences, ignoreIdentifierAndLiteralSeqences); } + @Override + protected void processToken(TokenFactory tokenEntries, JavaccToken currentToken) { + int kind = currentToken.getKind(); + String image = currentToken.getImage(); + + boolean isLiteral = kind == CppTokenKinds.STRING || kind == CppTokenKinds.RSTRING || kind == CppTokenKinds.CHARACTER || kind == CppTokenKinds.DECIMAL_INT_LITERAL || kind == CppTokenKinds.HEXADECIMAL_INT_LITERAL || kind == CppTokenKinds.OCTAL_INT_LITERAL || kind == CppTokenKinds.FLOAT_LITERAL || kind == CppTokenKinds.BINARY_INT_LITERAL || kind == CppTokenKinds.ZERO; + if (ignoreLiterals && isLiteral) { + image = CppTokenKinds.describe(kind); + } + + if (ignoreIdentifiers && (kind == CppTokenKinds.ID)) { + image = CppTokenKinds.describe(kind); + } + + tokenEntries.recordToken(image, currentToken.getReportLocation()); + } + private static class CppTokenFilter extends JavaCCTokenFilter { private final boolean ignoreLiteralSequences; diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexerTest.java index 805bb9a86d..90bfe90a32 100644 --- a/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexerTest.java +++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/lang/cpp/cpd/CppCpdLexerTest.java @@ -59,6 +59,16 @@ class CppCpdLexerTest extends CpdTextComparisonTest { doTest("specialComments"); } + @Test + void testIgnoreLiterals() { + doTest("ignoreLiterals", "", ignoreLiterals()); + } + + @Test + void testIgnoreIdents() { + doTest("ignoreIdents", "", ignoreIdents()); + } + @Test void testMultiLineMacros() { doTest("multilineMacros"); @@ -142,7 +152,7 @@ class CppCpdLexerTest extends CpdTextComparisonTest { } private static LanguagePropertyConfig skipBlocks(String skipPattern) { - return properties(true, skipPattern, false, false); + return properties(true, skipPattern, false, false, false, false); } private static LanguagePropertyConfig skipBlocks() { @@ -150,22 +160,31 @@ class CppCpdLexerTest extends CpdTextComparisonTest { } private static LanguagePropertyConfig dontSkipBlocks() { - return properties(false, null, false, false); + return properties(false, null, false, false, false, false); } private static LanguagePropertyConfig skipLiteralSequences() { - return properties(false, null, true, false); + return properties(false, null, true, false, false, false); } private static LanguagePropertyConfig skipIdentifierAndLiteralsSequences() { - return properties(false, null, true, true); + return properties(false, null, true, true, false, false); } private static LanguagePropertyConfig skipIdentifierSequences() { - return properties(false, null, false, true); + return properties(false, null, false, true, false, false); } - private static LanguagePropertyConfig properties(boolean skipBlocks, String skipPattern, boolean skipLiteralSequences, boolean skipSequences) { + private static LanguagePropertyConfig ignoreIdents() { + return properties(false, null, false, false, false, true); + } + + private static LanguagePropertyConfig ignoreLiterals() { + return properties(false, null, false, false, true, false); + } + + + private static LanguagePropertyConfig properties(boolean skipBlocks, String skipPattern, boolean skipLiteralSequences, boolean skipSequences, boolean ignoreLiterals, boolean ignoreIdents) { return properties -> { if (!skipBlocks) { properties.setProperty(CppLanguageModule.CPD_SKIP_BLOCKS, ""); @@ -174,6 +193,8 @@ class CppCpdLexerTest extends CpdTextComparisonTest { } properties.setProperty(CpdLanguageProperties.CPD_IGNORE_LITERAL_SEQUENCES, skipLiteralSequences); properties.setProperty(CpdLanguageProperties.CPD_IGNORE_LITERAL_AND_IDENTIFIER_SEQUENCES, skipSequences); + properties.setProperty(CpdLanguageProperties.CPD_ANONYMIZE_LITERALS, ignoreLiterals); + properties.setProperty(CpdLanguageProperties.CPD_ANONYMIZE_IDENTIFIERS, ignoreIdents); }; } } diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreIdents.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreIdents.cpp new file mode 100644 index 0000000000..91473e3e67 --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreIdents.cpp @@ -0,0 +1,6 @@ +class Test { + void f(int a, float b) { + auto c = a + b; + int d = 6; + } +} \ No newline at end of file diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreIdents.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreIdents.txt new file mode 100644 index 0000000000..b564fde247 --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreIdents.txt @@ -0,0 +1,35 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [class] 1 6 + [] 7 11 + [{] 12 13 +L2 + [void] 2 6 + [] 7 8 + [(] 8 9 + [int] 9 12 + [] 13 14 + [,] 14 15 + [float] 16 21 + [] 22 23 + [)] 23 24 + [{] 25 26 +L3 + [auto] 3 7 + [] 8 9 + [=] 10 11 + [] 12 13 + [+] 14 15 + [] 16 17 + [;] 17 18 +L4 + [int] 3 6 + [] 7 8 + [=] 9 10 + [6] 11 12 + [;] 12 13 +L5 + [}] 2 3 +L6 + [}] 1 2 +EOF diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreLiterals.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreLiterals.cpp new file mode 100644 index 0000000000..cbae7336ba --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreLiterals.cpp @@ -0,0 +1,43 @@ + void main() { + char x = L'a'; // wide chars + x = '\0x05'; // hex + // x = L''; // empty character is an error + + print("\ oMedia"); // whitespace escape + + + // char prefixes + char16_t c = u'\u00F6'; + wchar_t b = L'\xFFEF'; + char a = '\x30'; + char32_t d = U'\U0010FFFF'; + + // string prefixes + char A[] = "Hello\x0A"; + wchar_t B[] = L"Hell\xF6\x0A"; + char16_t C[] = u"Hell\u00F6"; + char32_t D[] = U"Hell\U000000F6\U0010FFFF"; + auto E[] = u8"\u00F6\U0010FFFF"; + + + + char* rawString = R"( + [Sinks.1] + Destination=Console + AutoFlush=true + Format="[%TimeStamp%] %ThreadId% %QueryIdHigh% %QueryIdLow% %LoggerFile%:%Line% (%Severity%) - %Message%" + Filter="%Severity% >= WRN" + )"; + + + + // digit separators + auto integer_literal = 1'000''000; + auto floating_point_literal = 0.000'015'3; + auto hex_literal = 0x0F00'abcd'6f3d; + auto silly_example = 1'0'0'000'00; + + // boolean literals + int b1 = 0B001101; // C++ 14 binary literal + int b2 = 0b000001; // C++ 14 binary literal +} \ No newline at end of file diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreLiterals.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreLiterals.txt new file mode 100644 index 0000000000..ef31240e44 --- /dev/null +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/ignoreLiterals.txt @@ -0,0 +1,135 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [void] 2 6 + [main] 7 11 + [(] 11 12 + [)] 12 13 + [{] 14 15 +L2 + [char] 5 9 + [x] 10 11 + [=] 12 13 + [] 14 18 + [;] 18 19 +L3 + [x] 5 6 + [=] 7 8 + [] 9 16 + [;] 16 17 +L6 + [print] 5 10 + [(] 10 11 + [] 11 24 + [)] 24 25 + [;] 25 26 +L10 + [char16_t] 5 13 + [c] 14 15 + [=] 16 17 + [] 18 27 + [;] 27 28 +L11 + [wchar_t] 5 12 + [b] 13 14 + [=] 15 16 + [] 17 26 + [;] 26 27 +L12 + [char] 5 9 + [a] 10 11 + [=] 12 13 + [] 15 21 + [;] 21 22 +L13 + [char32_t] 5 13 + [d] 14 15 + [=] 16 17 + [] 18 31 + [;] 31 32 +L16 + [char] 5 9 + [A] 10 11 + [\[] 11 12 + [\]] 12 13 + [=] 14 15 + [] 16 27 + [;] 27 28 +L17 + [wchar_t] 5 12 + [B] 13 14 + [\[] 14 15 + [\]] 15 16 + [=] 17 18 + [] 19 34 + [;] 34 35 +L18 + [char16_t] 5 13 + [C] 14 15 + [\[] 15 16 + [\]] 16 17 + [=] 18 19 + [] 20 33 + [;] 33 34 +L19 + [char32_t] 5 13 + [D] 14 15 + [\[] 15 16 + [\]] 16 17 + [=] 18 19 + [] 20 47 + [;] 47 48 +L20 + [auto] 5 9 + [E] 10 11 + [\[] 11 12 + [\]] 12 13 + [=] 14 15 + [] 16 36 + [;] 36 37 +L24 + [char] 5 9 + [*] 9 10 + [rawString] 11 20 + [=] 21 22 + [] 23 7 +L30 + [;] 7 8 +L35 + [auto] 5 9 + [integer_literal] 10 25 + [=] 26 27 + [] 28 38 + [;] 38 39 +L36 + [auto] 5 9 + [floating_point_literal] 10 32 + [=] 33 34 + [] 35 46 + [;] 46 47 +L37 + [auto] 5 9 + [hex_literal] 10 21 + [=] 22 23 + [] 24 40 + [;] 40 41 +L38 + [auto] 5 9 + [silly_example] 10 23 + [=] 24 25 + [] 26 38 + [;] 38 39 +L41 + [int] 5 8 + [b1] 9 11 + [=] 12 13 + [] 14 22 + [;] 22 23 +L42 + [int] 5 8 + [b2] 9 11 + [=] 12 13 + [] 14 22 + [;] 22 23 +L43 + [}] 1 2 +EOF