diff --git a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties index 5f4b76794e0..7cd7677c36a 100644 --- a/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties +++ b/codan/org.eclipse.cdt.codan.checkers/OSGI-INF/l10n/bundle.properties @@ -149,3 +149,8 @@ checker.name.GotoStatementChecker = Goto statement in source files checker problem.name.GotoStatementProblem = Goto statement used problem.messagePattern.GotoStatementProblem = Code that uses goto statements is harder to understand than alternative constructions problem.description.GotoStatementProblem = This rule will flag goto statements in source files + +checker.name.CopyrightChecker = Copyright in source code checker +problem.name.CopyrightProblem = Lack of copyright information +problem.messagePattern.CopyrightProblem = Copyright information missing +problem.description.CopyrightProblem = This rule will flag files without copyright information diff --git a/codan/org.eclipse.cdt.codan.checkers/plugin.xml b/codan/org.eclipse.cdt.codan.checkers/plugin.xml index d1d46d6eef8..539f7a67ea3 100644 --- a/codan/org.eclipse.cdt.codan.checkers/plugin.xml +++ b/codan/org.eclipse.cdt.codan.checkers/plugin.xml @@ -484,5 +484,20 @@ name="%problem.name.GotoStatementProblem"> + + + + diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java index 1a1543e9fb4..9f8b3afef34 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.java @@ -39,6 +39,8 @@ public class CheckersMessages extends NLS { public static String UnusedSymbolInFileScopeChecker_CharacterSequence; public static String UnusedSymbolInFileScopeChecker_Exceptions; + public static String Copyright_regex; + static { NLS.initializeMessages(CheckersMessages.class.getName(), CheckersMessages.class); } diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties index 56ecf256a67..36347f40118 100644 --- a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CheckersMessages.properties @@ -30,3 +30,5 @@ SuspiciousSemicolonChecker_ParamElse=Do not report an error after 'if' when 'els ProblemBindingChecker_Candidates=Candidates are: UnusedSymbolInFileScopeChecker_CharacterSequence=Identifier character sequence: UnusedSymbolInFileScopeChecker_Exceptions=Exceptions (identifier character sequence) + +Copyright_regex=Regex to search for copyright information \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CopyrightChecker.java b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CopyrightChecker.java new file mode 100644 index 00000000000..7e3e6426e51 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.checkers/src/org/eclipse/cdt/codan/internal/checkers/CopyrightChecker.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2019 Marco Stornelli + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + *******************************************************************************/ +package org.eclipse.cdt.codan.internal.checkers; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.eclipse.cdt.codan.checkers.CodanCheckersActivator; +import org.eclipse.cdt.codan.core.cxx.model.AbstractIndexAstChecker; +import org.eclipse.cdt.codan.core.model.IProblem; +import org.eclipse.cdt.codan.core.model.IProblemLocation; +import org.eclipse.cdt.codan.core.model.IProblemLocationFactory; +import org.eclipse.cdt.codan.core.model.IProblemWorkingCopy; +import org.eclipse.cdt.core.dom.ast.IASTComment; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; + +/** + * Checker for lack of copyright information + */ +public class CopyrightChecker extends AbstractIndexAstChecker { + public static final String ERR_ID = "org.eclipse.cdt.codan.internal.checkers.CopyrightProblem"; //$NON-NLS-1$ + public static final String PARAM_REGEX = "regex"; //$NON-NLS-1$ + private static final String DEF_REGEX = ".*Copyright.*"; //$NON-NLS-1$ + private Pattern fPattern; + private StringBuilder builder = new StringBuilder(); + private int lastOffset; + private boolean multiLine = false; + + /** + * Internal result + * Not found: we didn't find a proper comment + * Found: we found a comment at line 1 but without match, we can stop the search + * Found match: comment at line 1 with matching regex + */ + private enum Result { + NOT_FOUND, FOUND, FOUND_MATCH; + } + + @Override + public boolean runInEditor() { + return false; + } + + @Override + public void processAst(IASTTranslationUnit ast) { + multiLine = false; + lastOffset = 0; + builder.setLength(0); + String regex = getRegex(); + if (regex == null || regex.isEmpty()) + regex = DEF_REGEX; + try { + fPattern = Pattern.compile(regex, Pattern.MULTILINE | Pattern.DOTALL); + } catch (PatternSyntaxException e) { + CodanCheckersActivator.log(e); + return; + } + IASTComment[] comments = ast.getComments(); + if (comments == null) { + setProblem(); + return; + } + Result found = Result.NOT_FOUND; + for (IASTComment comment : comments) { + found = processComment(comment); + if (found == Result.FOUND || found == Result.FOUND_MATCH) + break; + } + if (found == Result.NOT_FOUND && multiLine) { + String c = builder.toString(); + if (!fPattern.matcher(c).matches()) { + setProblem(); + } + } else if (found != Result.FOUND_MATCH) + setProblem(); + } + + @Override + public void initPreferences(IProblemWorkingCopy problem) { + super.initPreferences(problem); + addPreference(problem, PARAM_REGEX, CheckersMessages.Copyright_regex, DEF_REGEX); + } + + public String getRegex() { + final IProblem pt = getProblemById(ERR_ID, getFile()); + return (String) getPreference(pt, PARAM_REGEX); + } + + protected Result processComment(IASTComment comment) { + if (comment.isPartOfTranslationUnitFile()) { + IASTNodeLocation nodeLocation = comment.getFileLocation(); + if (nodeLocation == null) { + return Result.NOT_FOUND; + } + String c = comment.getRawSignature(); + int currentOffset = nodeLocation.getNodeOffset(); + if (!comment.isBlockComment() && c.startsWith("//") //$NON-NLS-1$ + && (currentOffset == lastOffset + 1 || (!multiLine && currentOffset == 0))) { + builder.append(c); + builder.append("\n"); //$NON-NLS-1$ + lastOffset = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); + multiLine = true; + return Result.NOT_FOUND; + } + if (multiLine) { + c = builder.toString(); + } else if (nodeLocation.getNodeOffset() != 0) { + return Result.NOT_FOUND; + } + if (fPattern.matcher(c).matches()) + return Result.FOUND_MATCH; + return Result.FOUND; + } + return Result.NOT_FOUND; + } + + private void setProblem() { + IProblemLocationFactory locFactory = getRuntime().getProblemLocationFactory(); + IProblemLocation p = locFactory.createProblemLocation(getFile(), 1); + reportProblem(ERR_ID, p); + } +} \ No newline at end of file diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CopyrightCheckerTest.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CopyrightCheckerTest.java new file mode 100644 index 00000000000..e76744dab46 --- /dev/null +++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/internal/checkers/CopyrightCheckerTest.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2019 Marco Stornelli + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.codan.core.internal.checkers; + +import org.eclipse.cdt.codan.core.tests.CheckerTestCase; +import org.eclipse.cdt.codan.internal.checkers.CopyrightChecker; + +/** + * Test for {@link CopyrightChecker} class + */ +public class CopyrightCheckerTest extends CheckerTestCase { + + public static final String ERR_ID = CopyrightChecker.ERR_ID; + + @Override + public void setUp() throws Exception { + super.setUp(); + enableProblems(ERR_ID); + } + + @Override + public boolean isCpp() { + return true; + } + + //int main() {return 0;} + public void testWithoutCopyright() throws Exception { + loadCodeAndRun(getAboveComment()); + checkErrorLine(1, ERR_ID); + } + + ////Copyright 2019 + //int main() {return 0;} + public void testWithCopyright() throws Exception { + loadCodeAndRun(getAboveComment()); + checkNoErrorsOfKind(ERR_ID); + } + + ////============================================================================ + //// Name : test.cpp + //// Author : Blah + //// Version : 1.0 + //// Copyright : Your copyright notice + //// Description : Hello World in C++, Ansi-style + ////============================================================================ + // + //int main() {return 0;} + public void testWithCopyrightMultiline() throws Exception { + loadCodeAndRun(getAboveComment()); + checkNoErrorsOfKind(ERR_ID); + } + + ////============================================================================ + //// Name : test.cpp + //// Author : Blah + //// Version : 1.0 + //// Description : Hello World in C++, Ansi-style + ////============================================================================ + // + //int main() {return 0;} + public void testWithoutCopyrightMultiline() throws Exception { + loadCodeAndRun(getAboveComment()); + checkErrorLine(1, ERR_ID); + } + + //// Just another comment here + // + ////============================================================================ + //// Name : test.cpp + //// Author : Blah + //// Version : 1.0 + //// Copyright : Your copyright notice + //// Description : Hello World in C++, Ansi-style + ////============================================================================ + // + //int main() {return 0;} + public void testWithCopyrightMultilineNoHeader() throws Exception { + loadCodeAndRun(getAboveComment()); + checkErrorLine(1, ERR_ID); + } + + ////============================================================================ + //// Name : test.cpp + //// Author : Blah + //// Version : 1.0 + //// Copyright : Your copyright notice + //// Description : Hello World in C++, Ansi-style + ////============================================================================ + // + //// Just another comment here + //int main() {return 0;} + public void testWithCopyrightMultilinePostComment() throws Exception { + loadCodeAndRun(getAboveComment()); + checkNoErrorsOfKind(ERR_ID); + } + + ///**************************************************************************** + // * Name : test.cpp + // * Author : Blah + // * Version : 1.0 + // * Copyright : Your copyright notice + // * Description : Hello World in C++, Ansi-style + // ****************************************************************************/ + // + //int main() {return 0;} + public void testWithCopyrightBlock() throws Exception { + loadCodeAndRun(getAboveComment()); + checkNoErrorsOfKind(ERR_ID); + } + + //// Just another comment here + ///**************************************************************************** + // * Name : test.cpp + // * Author : Blah + // * Version : 1.0 + // * Copyright : Your copyright notice + // * Description : Hello World in C++, Ansi-style + // ****************************************************************************/ + // + //int main() {return 0;} + public void testWithCopyrightBlockNoHeader() throws Exception { + loadCodeAndRun(getAboveComment()); + checkErrorLine(1, ERR_ID); + } +} diff --git a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java index 2deb3a5030d..0d174e1ff87 100644 --- a/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java +++ b/codan/org.eclipse.cdt.codan.core.tests/src/org/eclipse/cdt/codan/core/tests/AutomatedIntegrationSuite.java @@ -23,6 +23,7 @@ import org.eclipse.cdt.codan.core.internal.checkers.CatchByReferenceTest; import org.eclipse.cdt.codan.core.internal.checkers.ClassMembersInitializationCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.CommentCheckerLineTests; import org.eclipse.cdt.codan.core.internal.checkers.CommentCheckerNestedTests; +import org.eclipse.cdt.codan.core.internal.checkers.CopyrightCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.DecltypeAutoCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.FormatStringCheckerTest; import org.eclipse.cdt.codan.core.internal.checkers.GotoStatementCheckerTest; @@ -86,6 +87,7 @@ public class AutomatedIntegrationSuite extends TestSuite { suite.addTestSuite(CommentCheckerLineTests.class); suite.addTestSuite(CommentCheckerNestedTests.class); suite.addTestSuite(GotoStatementCheckerTest.class); + suite.addTestSuite(CopyrightCheckerTest.class); // framework suite.addTest(CodanFastTestSuite.suite()); // quick fixes