diff --git a/language-testutils/src/test/java/de/jplag/testutils/datacollector/TestSourceIgnoredLinesCollector.java b/language-testutils/src/test/java/de/jplag/testutils/datacollector/TestSourceIgnoredLinesCollector.java index 2fff37ec1..06a67cbb7 100644 --- a/language-testutils/src/test/java/de/jplag/testutils/datacollector/TestSourceIgnoredLinesCollector.java +++ b/language-testutils/src/test/java/de/jplag/testutils/datacollector/TestSourceIgnoredLinesCollector.java @@ -25,6 +25,15 @@ public void ignoreLinesByPrefix(String prefix) { this.ignoreByCondition(line -> line.trim().startsWith(prefix)); } + /** + * Ignores lines that match the given regular expression. Whitespaces to the left and right of the line will be trimmed + * first. + * @param regex The regular expression + */ + public void ignoreLinesByRegex(String regex) { + this.ignoreByCondition(line -> line.trim().matches(regex)); + } + public void ignoreLinesByContains(String content) { this.ignoreByCondition(line -> line.contains(content)); } diff --git a/languages/csharp/pom.xml b/languages/csharp/pom.xml index 9bf5f4b66..12213357f 100644 --- a/languages/csharp/pom.xml +++ b/languages/csharp/pom.xml @@ -13,6 +13,11 @@ org.antlr antlr4-runtime + + de.jplag + language-antlr-utils + ${revision} + diff --git a/languages/csharp/src/main/java/de/jplag/csharp/CSharpLanguage.java b/languages/csharp/src/main/java/de/jplag/csharp/CSharpLanguage.java index 401727bf6..aeeb53728 100644 --- a/languages/csharp/src/main/java/de/jplag/csharp/CSharpLanguage.java +++ b/languages/csharp/src/main/java/de/jplag/csharp/CSharpLanguage.java @@ -1,29 +1,21 @@ package de.jplag.csharp; -import java.io.File; -import java.util.List; -import java.util.Set; - import org.kohsuke.MetaInfServices; -import de.jplag.ParsingException; -import de.jplag.Token; +import de.jplag.antlr.AbstractAntlrLanguage; /** * C# language with full support of C# 6 features and below. - * @author Timur Saglam */ @MetaInfServices(de.jplag.Language.class) -public class CSharpLanguage implements de.jplag.Language { +public class CSharpLanguage extends AbstractAntlrLanguage { private static final String NAME = "C# 6 Parser"; private static final String IDENTIFIER = "csharp"; private static final String[] FILE_ENDINGS = new String[] {".cs", ".CS"}; private static final int DEFAULT_MIN_TOKEN_MATCH = 8; - private final CSharpParserAdapter parser; - public CSharpLanguage() { - parser = new CSharpParserAdapter(); + super(new CSharpParserAdapter()); } @Override @@ -45,9 +37,4 @@ public String getIdentifier() { public int minimumTokenMatch() { return DEFAULT_MIN_TOKEN_MATCH; } - - @Override - public List parse(Set files) throws ParsingException { - return parser.parse(files); - } } diff --git a/languages/csharp/src/main/java/de/jplag/csharp/CSharpListener.java b/languages/csharp/src/main/java/de/jplag/csharp/CSharpListener.java index 65d04d5b7..6cc568505 100644 --- a/languages/csharp/src/main/java/de/jplag/csharp/CSharpListener.java +++ b/languages/csharp/src/main/java/de/jplag/csharp/CSharpListener.java @@ -19,7 +19,6 @@ import static de.jplag.csharp.CSharpTokenType.CONTINUE; import static de.jplag.csharp.CSharpTokenType.DELEGATE; import static de.jplag.csharp.CSharpTokenType.DESTRUCTOR; -import static de.jplag.csharp.CSharpTokenType.DO; import static de.jplag.csharp.CSharpTokenType.ENUM; import static de.jplag.csharp.CSharpTokenType.ENUMERAL; import static de.jplag.csharp.CSharpTokenType.ENUM_BEGIN; @@ -28,8 +27,6 @@ import static de.jplag.csharp.CSharpTokenType.FIELD; import static de.jplag.csharp.CSharpTokenType.FINALLY; import static de.jplag.csharp.CSharpTokenType.FIXED; -import static de.jplag.csharp.CSharpTokenType.FOR; -import static de.jplag.csharp.CSharpTokenType.FOREACH; import static de.jplag.csharp.CSharpTokenType.GOTO; import static de.jplag.csharp.CSharpTokenType.IF; import static de.jplag.csharp.CSharpTokenType.IF_BEGIN; @@ -41,9 +38,12 @@ import static de.jplag.csharp.CSharpTokenType.INVOCATION; import static de.jplag.csharp.CSharpTokenType.LOCAL_VARIABLE; import static de.jplag.csharp.CSharpTokenType.LOCK; +import static de.jplag.csharp.CSharpTokenType.LOOP_BEGIN; +import static de.jplag.csharp.CSharpTokenType.LOOP_END; import static de.jplag.csharp.CSharpTokenType.METHOD; import static de.jplag.csharp.CSharpTokenType.METHOD_BEGIN; import static de.jplag.csharp.CSharpTokenType.METHOD_END; +import static de.jplag.csharp.CSharpTokenType.NAMESPACE; import static de.jplag.csharp.CSharpTokenType.NAMESPACE_BEGIN; import static de.jplag.csharp.CSharpTokenType.NAMESPACE_END; import static de.jplag.csharp.CSharpTokenType.OBJECT_CREATION; @@ -59,25 +59,39 @@ import static de.jplag.csharp.CSharpTokenType.TRY; import static de.jplag.csharp.CSharpTokenType.UNCHECKED; import static de.jplag.csharp.CSharpTokenType.UNSAFE; -import static de.jplag.csharp.CSharpTokenType.USING; import static de.jplag.csharp.CSharpTokenType.USING_DIRECTIVE; -import static de.jplag.csharp.CSharpTokenType.WHILE; - -import org.antlr.v4.runtime.Token; - +import static de.jplag.csharp.grammar.CSharpParser.ELSE; +import static de.jplag.csharp.grammar.CSharpParser.OP_ADD_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_AND_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_COALESCING_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_DEC; +import static de.jplag.csharp.grammar.CSharpParser.OP_DIV_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_INC; +import static de.jplag.csharp.grammar.CSharpParser.OP_MOD_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_MULT_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_OR_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_SUB_ASSIGNMENT; +import static de.jplag.csharp.grammar.CSharpParser.OP_XOR_ASSIGNMENT; + +import org.antlr.v4.runtime.ParserRuleContext; + +import de.jplag.antlr.AbstractAntlrListener; import de.jplag.csharp.grammar.CSharpParser.Accessor_bodyContext; import de.jplag.csharp.grammar.CSharpParser.Accessor_declarationsContext; import de.jplag.csharp.grammar.CSharpParser.Array_initializerContext; import de.jplag.csharp.grammar.CSharpParser.Assignment_operatorContext; import de.jplag.csharp.grammar.CSharpParser.AttributeContext; +import de.jplag.csharp.grammar.CSharpParser.BodyContext; import de.jplag.csharp.grammar.CSharpParser.BreakStatementContext; -import de.jplag.csharp.grammar.CSharpParser.Catch_clausesContext; +import de.jplag.csharp.grammar.CSharpParser.CheckedExpressionContext; import de.jplag.csharp.grammar.CSharpParser.CheckedStatementContext; import de.jplag.csharp.grammar.CSharpParser.Class_bodyContext; import de.jplag.csharp.grammar.CSharpParser.Class_definitionContext; +import de.jplag.csharp.grammar.CSharpParser.Common_member_declarationContext; import de.jplag.csharp.grammar.CSharpParser.Constant_declarationContext; import de.jplag.csharp.grammar.CSharpParser.Constructor_declarationContext; import de.jplag.csharp.grammar.CSharpParser.ContinueStatementContext; +import de.jplag.csharp.grammar.CSharpParser.Conversion_operator_declaratorContext; import de.jplag.csharp.grammar.CSharpParser.Delegate_definitionContext; import de.jplag.csharp.grammar.CSharpParser.Destructor_definitionContext; import de.jplag.csharp.grammar.CSharpParser.DoStatementContext; @@ -94,425 +108,160 @@ import de.jplag.csharp.grammar.CSharpParser.IfStatementContext; import de.jplag.csharp.grammar.CSharpParser.If_bodyContext; import de.jplag.csharp.grammar.CSharpParser.Indexer_declarationContext; -import de.jplag.csharp.grammar.CSharpParser.Interface_bodyContext; import de.jplag.csharp.grammar.CSharpParser.Interface_definitionContext; +import de.jplag.csharp.grammar.CSharpParser.Local_constant_declarationContext; import de.jplag.csharp.grammar.CSharpParser.Local_variable_declarationContext; import de.jplag.csharp.grammar.CSharpParser.LockStatementContext; import de.jplag.csharp.grammar.CSharpParser.Method_bodyContext; import de.jplag.csharp.grammar.CSharpParser.Method_declarationContext; import de.jplag.csharp.grammar.CSharpParser.Method_invocationContext; import de.jplag.csharp.grammar.CSharpParser.Namespace_bodyContext; +import de.jplag.csharp.grammar.CSharpParser.Namespace_declarationContext; +import de.jplag.csharp.grammar.CSharpParser.Namespace_or_type_nameContext; import de.jplag.csharp.grammar.CSharpParser.Object_creation_expressionContext; import de.jplag.csharp.grammar.CSharpParser.Operator_declarationContext; import de.jplag.csharp.grammar.CSharpParser.Property_declarationContext; import de.jplag.csharp.grammar.CSharpParser.ReturnStatementContext; +import de.jplag.csharp.grammar.CSharpParser.Shift_expressionContext; +import de.jplag.csharp.grammar.CSharpParser.Specific_catch_clauseContext; import de.jplag.csharp.grammar.CSharpParser.Struct_bodyContext; import de.jplag.csharp.grammar.CSharpParser.Struct_definitionContext; import de.jplag.csharp.grammar.CSharpParser.SwitchStatementContext; import de.jplag.csharp.grammar.CSharpParser.Switch_sectionContext; import de.jplag.csharp.grammar.CSharpParser.ThrowStatementContext; import de.jplag.csharp.grammar.CSharpParser.TryStatementContext; +import de.jplag.csharp.grammar.CSharpParser.Type_argument_listContext; import de.jplag.csharp.grammar.CSharpParser.UncheckedExpressionContext; +import de.jplag.csharp.grammar.CSharpParser.UncheckedStatementContext; import de.jplag.csharp.grammar.CSharpParser.UnsafeStatementContext; -import de.jplag.csharp.grammar.CSharpParser.UsingStatementContext; -import de.jplag.csharp.grammar.CSharpParser.Using_directivesContext; +import de.jplag.csharp.grammar.CSharpParser.UsingAliasDirectiveContext; +import de.jplag.csharp.grammar.CSharpParser.UsingNamespaceDirectiveContext; +import de.jplag.csharp.grammar.CSharpParser.UsingStaticDirectiveContext; import de.jplag.csharp.grammar.CSharpParser.WhileStatementContext; -import de.jplag.csharp.grammar.CSharpParserBaseListener; /** - * Listener class for visiting the C# ANTLR parse tree. Transforms selected ANTLR token into JPlag tokens. - * @author Timur Saglam + * Extracts tokens for the {@link CSharpLanguage} */ -public class CSharpListener extends CSharpParserBaseListener { - - private final CSharpParserAdapter parserAdapter; - - /** - * Creates the listener. - * @param parserAdapter is the JPlag parser adapter which receives the transformed tokens. - */ - public CSharpListener(CSharpParserAdapter parserAdapter) { - this.parserAdapter = parserAdapter; - } - - /** - * Transforms an ANTLR Token into a JPlag token and transfers it to the token adapter. - * @param targetType is the type of the JPlag token to be created. - * @param token is the ANTLR token. - */ - private void transformToken(CSharpTokenType targetType, Token token) { - parserAdapter.addToken(targetType, token.getLine(), token.getCharPositionInLine() + 1, token.getText().length()); - } - - @Override - public void enterMethod_invocation(Method_invocationContext context) { - transformToken(INVOCATION, context.getStart()); - super.enterMethod_invocation(context); - } - - @Override - public void enterObject_creation_expression(Object_creation_expressionContext context) { - transformToken(OBJECT_CREATION, context.getStart()); - super.enterObject_creation_expression(context); - } - - @Override - public void enterArray_initializer(Array_initializerContext context) { - transformToken(ARRAY_CREATION, context.getStart()); - super.enterArray_initializer(context); - } - - @Override - public void enterAssignment_operator(Assignment_operatorContext context) { - transformToken(ASSIGNMENT, context.getStart()); - super.enterAssignment_operator(context); - } - - @Override - public void enterIfStatement(IfStatementContext context) { - transformToken(IF, context.getStart()); - super.enterIfStatement(context); - } - - @Override - public void enterIf_body(If_bodyContext context) { - transformToken(IF_BEGIN, context.getStart()); - super.enterIf_body(context); - } - - @Override - public void exitIf_body(If_bodyContext context) { - transformToken(IF_END, context.getStop()); - super.exitIf_body(context); - } - - @Override - public void enterSwitch_section(Switch_sectionContext context) { - transformToken(SWITCH_BEGIN, context.getStart()); - super.enterSwitch_section(context); - } - - @Override - public void exitSwitch_section(Switch_sectionContext context) { - transformToken(SWITCH_END, context.getStop()); - super.exitSwitch_section(context); - } - - @Override - public void enterSwitchStatement(SwitchStatementContext context) { - transformToken(CASE, context.getStart()); - super.enterSwitchStatement(context); - } - - @Override - public void enterDoStatement(DoStatementContext context) { - transformToken(DO, context.getStart()); - super.enterDoStatement(context); - } - - @Override - public void enterWhileStatement(WhileStatementContext context) { - transformToken(WHILE, context.getStart()); - super.enterWhileStatement(context); - } - - @Override - public void enterForStatement(ForStatementContext context) { - transformToken(FOR, context.getStart()); - super.enterForStatement(context); - } - - @Override - public void enterForeachStatement(ForeachStatementContext context) { - transformToken(FOREACH, context.getStart()); - super.enterForeachStatement(context); - } - - @Override - public void enterBreakStatement(BreakStatementContext context) { - transformToken(BREAK, context.getStart()); - super.enterBreakStatement(context); - } - - @Override - public void enterContinueStatement(ContinueStatementContext context) { - transformToken(CONTINUE, context.getStart()); - super.enterContinueStatement(context); - } - - @Override - public void enterGotoStatement(GotoStatementContext context) { - transformToken(GOTO, context.getStart()); - super.enterGotoStatement(context); - } - - @Override - public void enterReturnStatement(ReturnStatementContext context) { - transformToken(RETURN, context.getStart()); - super.enterReturnStatement(context); - } - - @Override - public void enterThrowStatement(ThrowStatementContext context) { - transformToken(THROW, context.getStart()); - super.enterThrowStatement(context); - } - - @Override - public void enterCheckedStatement(CheckedStatementContext context) { - transformToken(CHECKED, context.getStart()); - super.enterCheckedStatement(context); - } - - @Override - public void enterUncheckedExpression(UncheckedExpressionContext context) { - transformToken(UNCHECKED, context.getStart()); - super.enterUncheckedExpression(context); - } - - @Override - public void enterLockStatement(LockStatementContext context) { - transformToken(LOCK, context.getStart()); - super.enterLockStatement(context); - } - - @Override - public void enterUsingStatement(UsingStatementContext context) { - transformToken(USING, context.getStart()); - super.enterUsingStatement(context); - } - - @Override - public void enterTryStatement(TryStatementContext context) { - transformToken(TRY, context.getStart()); - super.enterTryStatement(context); - } - - @Override - public void enterCatch_clauses(Catch_clausesContext context) { - transformToken(CATCH, context.getStart()); - super.enterCatch_clauses(context); - } - - @Override - public void enterFinally_clause(Finally_clauseContext context) { - transformToken(FINALLY, context.getStart()); - super.enterFinally_clause(context); - } - - @Override - public void enterNamespace_body(Namespace_bodyContext context) { - transformToken(NAMESPACE_BEGIN, context.getStart()); - super.enterNamespace_body(context); - } - - @Override - public void exitNamespace_body(Namespace_bodyContext context) { - transformToken(NAMESPACE_END, context.getStop()); - super.exitNamespace_body(context); - } - - @Override - public void enterUsing_directives(Using_directivesContext context) { - transformToken(USING_DIRECTIVE, context.getStart()); - super.enterUsing_directives(context); - } - - @Override - public void enterClass_definition(Class_definitionContext context) { - transformToken(CLASS, context.getStart()); - super.enterClass_definition(context); - } - - @Override - public void enterClass_body(Class_bodyContext context) { - transformToken(CLASS_BEGIN, context.getStart()); - super.enterClass_body(context); - } - - @Override - public void exitClass_body(Class_bodyContext context) { - transformToken(CLASS_END, context.getStop()); - super.exitClass_body(context); - } - - @Override - public void enterMethod_declaration(Method_declarationContext context) { - transformToken(METHOD, context.getStart()); - super.enterMethod_declaration(context); - } - - @Override - public void enterMethod_body(Method_bodyContext context) { - transformToken(METHOD_BEGIN, context.getStart()); - super.enterMethod_body(context); - } - - @Override - public void exitMethod_body(Method_bodyContext context) { - transformToken(METHOD_END, context.getStop()); - super.exitMethod_body(context); - } - - @Override - public void enterProperty_declaration(Property_declarationContext context) { - transformToken(PROPERTY, context.getStart()); - super.enterProperty_declaration(context); - } - - @Override - public void enterEvent_declaration(Event_declarationContext context) { - transformToken(EVENT, context.getStart()); - super.enterEvent_declaration(context); - } - - @Override - public void enterIndexer_declaration(Indexer_declarationContext context) { - transformToken(INDEXER, context.getStart()); - super.enterIndexer_declaration(context); - } - - @Override - public void enterOperator_declaration(Operator_declarationContext context) { - transformToken(OPERATOR, context.getStart()); - super.enterOperator_declaration(context); - } - - @Override - public void enterConstructor_declaration(Constructor_declarationContext context) { - transformToken(CONSTRUCTOR, context.getStart()); - super.enterConstructor_declaration(context); - } - - @Override - public void enterDestructor_definition(Destructor_definitionContext context) { - transformToken(DESTRUCTOR, context.getStart()); - super.enterDestructor_definition(context); - } - - @Override - public void enterStruct_definition(Struct_definitionContext context) { - transformToken(STRUCT, context.getStart()); - super.enterStruct_definition(context); - } - - @Override - public void enterStruct_body(Struct_bodyContext context) { - transformToken(STRUCT_BEGIN, context.getStart()); - super.enterStruct_body(context); - } - - @Override - public void exitStruct_body(Struct_bodyContext context) { - transformToken(STRUCT_END, context.getStop()); - super.exitStruct_body(context); - } - - @Override - public void enterInterface_definition(Interface_definitionContext context) { - transformToken(INTERFACE, context.getStart()); - super.enterInterface_definition(context); - } - - @Override - public void enterInterface_body(Interface_bodyContext context) { - transformToken(INTERFACE_BEGIN, context.getStart()); - super.enterInterface_body(context); - } - - @Override - public void exitInterface_body(Interface_bodyContext context) { - transformToken(INTERFACE_END, context.getStart()); - super.exitInterface_body(context); - } - - @Override - public void enterEnum_definition(Enum_definitionContext context) { - transformToken(ENUM, context.getStart()); - super.enterEnum_definition(context); - } - - @Override - public void enterEnum_body(Enum_bodyContext context) { - transformToken(ENUM_BEGIN, context.getStart()); - super.enterEnum_body(context); - } - - @Override - public void exitEnum_body(Enum_bodyContext context) { - transformToken(ENUM_END, context.getStop()); - super.exitEnum_body(context); - } - - @Override - public void enterEnum_member_declaration(Enum_member_declarationContext context) { - transformToken(ENUMERAL, context.getStart()); - super.enterEnum_member_declaration(context); - } - - @Override - public void enterAttribute(AttributeContext context) { - transformToken(ATTRIBUTE, context.getStart()); - super.enterAttribute(context); - } - - @Override - public void enterDelegate_definition(Delegate_definitionContext context) { - transformToken(DELEGATE, context.getStart()); - super.enterDelegate_definition(context); - } - - @Override - public void enterUnsafeStatement(UnsafeStatementContext context) { - transformToken(UNSAFE, context.getStart()); - super.enterUnsafeStatement(context); - } - - @Override - public void enterFixedStatement(FixedStatementContext context) { - transformToken(FIXED, context.getStart()); - super.enterFixedStatement(context); - } - - @Override - public void enterAccessor_declarations(Accessor_declarationsContext context) { - transformToken(ACCESSORS_BEGIN, context.getStart()); - super.enterAccessor_declarations(context); - } - - @Override - public void exitAccessor_declarations(Accessor_declarationsContext context) { - transformToken(ACCESSORS_END, context.getStart()); - super.enterAccessor_declarations(context); - } - - @Override - public void enterAccessor_body(Accessor_bodyContext context) { - transformToken(ACCESSOR_BEGIN, context.getStart()); - super.enterAccessor_body(context); - } - - @Override - public void exitAccessor_body(Accessor_bodyContext context) { - transformToken(ACCESSOR_END, context.getStart()); - super.exitAccessor_body(context); - } - - @Override - public void enterConstant_declaration(Constant_declarationContext context) { - transformToken(CONSTANT, context.getStart()); - super.enterConstant_declaration(context); - } - - @Override - public void enterField_declaration(Field_declarationContext context) { - transformToken(FIELD, context.getStart()); - super.enterField_declaration(context); +public class CSharpListener extends AbstractAntlrListener { + public CSharpListener() { + visit(UsingNamespaceDirectiveContext.class).map(USING_DIRECTIVE); + visit(UsingAliasDirectiveContext.class, this::isAliasUsageAlsoImport).map(USING_DIRECTIVE); + visit(UsingStaticDirectiveContext.class).map(USING_DIRECTIVE); + + visit(Method_invocationContext.class).map(INVOCATION); + visit(Object_creation_expressionContext.class).map(OBJECT_CREATION); + visit(Array_initializerContext.class).map(ARRAY_CREATION); + visit(Assignment_operatorContext.class).map(ASSIGNMENT); + visit(IfStatementContext.class).map(IF); + visit(ELSE).map(IF); + visit(If_bodyContext.class).map(IF_BEGIN, IF_END); + visit(Switch_sectionContext.class).map(SWITCH_BEGIN, SWITCH_END); + visit(SwitchStatementContext.class).map(CASE); + + visit(DoStatementContext.class).map(LOOP_BEGIN, LOOP_END); + visit(WhileStatementContext.class).map(LOOP_BEGIN, LOOP_END); + visit(ForStatementContext.class).map(LOOP_BEGIN, LOOP_END); + visit(ForeachStatementContext.class).map(LOOP_BEGIN, LOOP_END); + + visit(BreakStatementContext.class).map(BREAK); + visit(ContinueStatementContext.class).map(CONTINUE); + visit(GotoStatementContext.class).map(GOTO); + + visit(ReturnStatementContext.class).map(RETURN); + visit(ThrowStatementContext.class).map(THROW); + + visit(CheckedStatementContext.class).map(CHECKED); + visit(CheckedExpressionContext.class).map(CHECKED); + visit(UncheckedStatementContext.class).map(UNCHECKED); + visit(UncheckedExpressionContext.class).map(UNCHECKED); + + visit(LockStatementContext.class).map(LOCK); + + visit(TryStatementContext.class).map(TRY); + visit(Specific_catch_clauseContext.class).map(CATCH); + visit(Finally_clauseContext.class).map(FINALLY); + + visit(Namespace_declarationContext.class).map(NAMESPACE); + visit(Namespace_bodyContext.class).map(NAMESPACE_BEGIN, NAMESPACE_END); + visit(Class_definitionContext.class).map(CLASS); + visit(Class_bodyContext.class, this::isClassBody).map(CLASS_BEGIN, CLASS_END); + visit(Method_declarationContext.class).map(METHOD); + visit(Method_bodyContext.class).map(METHOD_BEGIN, METHOD_END); + visit(Property_declarationContext.class).map(PROPERTY); + visit(Event_declarationContext.class).map(EVENT); + visit(Indexer_declarationContext.class).map(INDEXER); + visit(Operator_declarationContext.class).map(OPERATOR); + visit(BodyContext.class, this::isOperatorBody).map(METHOD_BEGIN, METHOD_END); + visit(Conversion_operator_declaratorContext.class).map(OPERATOR); + visit(BodyContext.class, this::isConversionOperatorBody).map(METHOD_BEGIN, METHOD_END); + visit(Constructor_declarationContext.class).map(CONSTRUCTOR); + visit(BodyContext.class, this::isConstructorBody).map(METHOD_BEGIN, METHOD_END); + visit(Destructor_definitionContext.class).map(DESTRUCTOR); + visit(Struct_definitionContext.class).map(STRUCT); + visit(Struct_bodyContext.class).map(STRUCT_BEGIN, STRUCT_END); + visit(Interface_definitionContext.class).map(INTERFACE); + visit(Class_bodyContext.class, this::isInterfaceBody).map(INTERFACE_BEGIN, INTERFACE_END); + visit(Enum_definitionContext.class).map(ENUM); + visit(Enum_bodyContext.class).map(ENUM_BEGIN, ENUM_END); + visit(Enum_member_declarationContext.class).map(ENUMERAL); + visit(AttributeContext.class).map(ATTRIBUTE); + visit(Delegate_definitionContext.class).map(DELEGATE); + visit(UnsafeStatementContext.class).map(UNSAFE); + visit(FixedStatementContext.class).map(FIXED); + visit(Accessor_declarationsContext.class).map(ACCESSORS_BEGIN, ACCESSORS_END); + visit(Accessor_bodyContext.class).map(ACCESSOR_BEGIN, ACCESSOR_END); + visit(Constant_declarationContext.class).map(CONSTANT); + visit(Field_declarationContext.class).map(FIELD); + visit(Local_variable_declarationContext.class).map(LOCAL_VARIABLE); + visit(Local_constant_declarationContext.class).map(LOCAL_VARIABLE); + + registerUnaryOperatorVisitors(); + } + + private void registerUnaryOperatorVisitors() { + visit(OP_DEC).map(ASSIGNMENT); + visit(OP_INC).map(ASSIGNMENT); + + visit(Shift_expressionContext.class, expr -> expr.additive_expression().size() == 2).map(ASSIGNMENT); + + visit(OP_ADD_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_SUB_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_AND_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_DIV_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_MOD_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_OR_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_XOR_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_MULT_ASSIGNMENT).map(ASSIGNMENT); + visit(OP_COALESCING_ASSIGNMENT).map(ASSIGNMENT); + } + + private boolean isClassBody(ParserRuleContext context) { + return hasAncestor(context, Class_definitionContext.class, Interface_definitionContext.class); + } + + private boolean isInterfaceBody(ParserRuleContext context) { + return hasAncestor(context, Interface_definitionContext.class, Class_definitionContext.class); + } + + private boolean isConstructorBody(BodyContext context) { + return context.parent instanceof Constructor_declarationContext; + } + + private boolean isOperatorBody(BodyContext context) { + return context.parent instanceof Operator_declarationContext; + } + + private boolean isConversionOperatorBody(BodyContext context) { + if (!(context.parent instanceof Common_member_declarationContext parent)) { + return false; + } + + return parent.conversion_operator_declarator() != null; } - @Override - public void enterLocal_variable_declaration(Local_variable_declarationContext context) { - transformToken(LOCAL_VARIABLE, context.getStart()); - super.enterLocal_variable_declaration(context); + private boolean isAliasUsageAlsoImport(UsingAliasDirectiveContext context) { + Namespace_or_type_nameContext namespace = getDescendant(context, Namespace_or_type_nameContext.class); + + return namespace != null && (!namespace.DOT().isEmpty() && getDescendant(namespace, Type_argument_listContext.class) == null); } } diff --git a/languages/csharp/src/main/java/de/jplag/csharp/CSharpParserAdapter.java b/languages/csharp/src/main/java/de/jplag/csharp/CSharpParserAdapter.java index 3b2b0d8a2..7d0b8951d 100644 --- a/languages/csharp/src/main/java/de/jplag/csharp/CSharpParserAdapter.java +++ b/languages/csharp/src/main/java/de/jplag/csharp/CSharpParserAdapter.java @@ -1,80 +1,37 @@ package de.jplag.csharp; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.tree.ParseTree; -import org.antlr.v4.runtime.tree.ParseTreeWalker; -import de.jplag.AbstractParser; -import de.jplag.ParsingException; -import de.jplag.Token; -import de.jplag.TokenType; +import de.jplag.antlr.AbstractAntlrListener; +import de.jplag.antlr.AbstractAntlrParserAdapter; import de.jplag.csharp.grammar.CSharpLexer; import de.jplag.csharp.grammar.CSharpParser; -import de.jplag.util.FileUtils; /** * Parser adapter for the ANTLR 4 CSharp Parser and Lexer. It receives file to parse and passes them to the ANTLR * pipeline. Then it walks the produced parse tree and creates JPlag token with the {@link CSharpListener}. - * @author Timur Saglam */ -public class CSharpParserAdapter extends AbstractParser { - private List tokens; - private File currentFile; - - /** - * Creates the parser adapter. - */ - public CSharpParserAdapter() { - super(); +public class CSharpParserAdapter extends AbstractAntlrParserAdapter { + @Override + protected Lexer createLexer(CharStream input) { + return new CSharpLexer(input); } - /** - * Parses all tokens from a set of files. - * @param files is the set of files. - * @return the list of parsed tokens. - */ - public List parse(Set files) throws ParsingException { - tokens = new ArrayList<>(); - for (File file : files) { - parseFile(file); - tokens.add(Token.fileEnd(file)); - } - return tokens; + @Override + protected CSharpParser createParser(CommonTokenStream tokenStream) { + return new CSharpParser(tokenStream); } - private void parseFile(File file) throws ParsingException { - try (BufferedReader reader = FileUtils.openFileReader(file)) { - currentFile = file; - - // create a lexer, a parser and a buffer between them. - CSharpLexer lexer = new CSharpLexer(CharStreams.fromReader(reader)); - CommonTokenStream tokens = new CommonTokenStream(lexer); - CSharpParser parser = new CSharpParser(tokens); - - // Create a tree walker and the entry context defined by the parser grammar - ParserRuleContext entryContext = parser.compilation_unit(); - ParseTreeWalker treeWalker = new ParseTreeWalker(); - - // Walk over the parse tree: - for (int i = 0; i < entryContext.getChildCount(); i++) { - ParseTree parseTree = entryContext.getChild(i); - treeWalker.walk(new CSharpListener(this), parseTree); - } - } catch (IOException exception) { - throw new ParsingException(file, exception.getMessage(), exception); - } + @Override + protected ParserRuleContext getEntryContext(CSharpParser parser) { + return parser.compilation_unit(); } - /* package-private */ void addToken(TokenType type, int line, int column, int length) { - tokens.add(new Token(type, currentFile, line, column, length)); + @Override + protected AbstractAntlrListener getListener() { + return new CSharpListener(); } } diff --git a/languages/csharp/src/main/java/de/jplag/csharp/CSharpTokenType.java b/languages/csharp/src/main/java/de/jplag/csharp/CSharpTokenType.java index f8d6a4347..3895ed55e 100644 --- a/languages/csharp/src/main/java/de/jplag/csharp/CSharpTokenType.java +++ b/languages/csharp/src/main/java/de/jplag/csharp/CSharpTokenType.java @@ -17,10 +17,6 @@ public enum CSharpTokenType implements TokenType { SWITCH_BEGIN("SWITCH{"), SWITCH_END("}SWITCH"), CASE("CASE"), - DO("DO"), - WHILE("WHILE"), - FOR("FOR"), - FOREACH("FOREACH"), BREAK("BREAK"), CONTINUE("CONTINUE"), GOTO("GOTO"), @@ -29,10 +25,10 @@ public enum CSharpTokenType implements TokenType { CHECKED("CHECKED"), UNCHECKED("UNCHECKED"), LOCK("LOCK"), - USING("USING"), TRY("TRY"), CATCH("CATCH"), FINALLY("FINALLY"), + NAMESPACE("NAMESPACE"), NAMESPACE_BEGIN("NAMESPACE{"), NAMESPACE_END("}NAMESPACE"), USING_DIRECTIVE("USING"), @@ -67,7 +63,10 @@ public enum CSharpTokenType implements TokenType { ACCESSORS_BEGIN("ACCESSORS{"), ACCESSORS_END("}ACCESSORS"), ACCESSOR_BEGIN("ACCESSOR{"), - ACCESSOR_END("}ACCESSOR"); + ACCESSOR_END("}ACCESSOR"), + + LOOP_BEGIN("LOOP{"), + LOOP_END("}LOOP"); private final String description; diff --git a/languages/csharp/src/test/java/de/jplag/csharp/CSharpTest.java b/languages/csharp/src/test/java/de/jplag/csharp/CSharpTest.java new file mode 100644 index 000000000..2acbe4d98 --- /dev/null +++ b/languages/csharp/src/test/java/de/jplag/csharp/CSharpTest.java @@ -0,0 +1,56 @@ +package de.jplag.csharp; + +import static de.jplag.csharp.CSharpTokenType.ACCESSORS_BEGIN; +import static de.jplag.csharp.CSharpTokenType.ACCESSORS_END; +import static de.jplag.csharp.CSharpTokenType.ACCESSOR_BEGIN; +import static de.jplag.csharp.CSharpTokenType.ACCESSOR_END; +import static de.jplag.csharp.CSharpTokenType.ASSIGNMENT; +import static de.jplag.csharp.CSharpTokenType.CLASS; +import static de.jplag.csharp.CSharpTokenType.CLASS_BEGIN; +import static de.jplag.csharp.CSharpTokenType.CLASS_END; +import static de.jplag.csharp.CSharpTokenType.CONSTRUCTOR; +import static de.jplag.csharp.CSharpTokenType.FIELD; +import static de.jplag.csharp.CSharpTokenType.IF; +import static de.jplag.csharp.CSharpTokenType.IF_BEGIN; +import static de.jplag.csharp.CSharpTokenType.IF_END; +import static de.jplag.csharp.CSharpTokenType.INVOCATION; +import static de.jplag.csharp.CSharpTokenType.LOCAL_VARIABLE; +import static de.jplag.csharp.CSharpTokenType.METHOD; +import static de.jplag.csharp.CSharpTokenType.METHOD_BEGIN; +import static de.jplag.csharp.CSharpTokenType.METHOD_END; +import static de.jplag.csharp.CSharpTokenType.PROPERTY; +import static de.jplag.csharp.CSharpTokenType.RETURN; + +import de.jplag.testutils.LanguageModuleTest; +import de.jplag.testutils.datacollector.TestDataCollector; +import de.jplag.testutils.datacollector.TestSourceIgnoredLinesCollector; + +public class CSharpTest extends LanguageModuleTest { + public CSharpTest() { + super(new CSharpLanguage(), CSharpTokenType.class); + } + + @Override + protected void collectTestData(TestDataCollector collector) { + collector.testFile("TestClass.cs").testSourceCoverage().testTokenSequence(CLASS, CLASS_BEGIN, FIELD, CONSTRUCTOR, METHOD_BEGIN, + LOCAL_VARIABLE, METHOD_END, METHOD, METHOD_BEGIN, IF, IF_BEGIN, INVOCATION, IF_END, IF, IF_BEGIN, INVOCATION, IF_END, METHOD_END, + PROPERTY, ACCESSORS_BEGIN, ACCESSOR_BEGIN, ACCESSOR_END, ACCESSOR_BEGIN, ACCESSOR_END, ACCESSORS_END, FIELD, PROPERTY, + ACCESSORS_BEGIN, ACCESSOR_BEGIN, RETURN, ACCESSOR_END, ACCESSOR_BEGIN, ASSIGNMENT, ACCESSOR_END, ACCESSORS_END, CLASS_END); + + collector.testFile("AllInOneNoPreprocessor.cs").testSourceCoverage().testTokenCoverage(); + } + + @Override + protected void configureIgnoredLines(TestSourceIgnoredLinesCollector collector) { + collector.ignoreMultipleLines("/*", "*/"); + collector.ignoreLinesByPrefix("//"); + collector.ignoreLinesByRegex(".*//test-ignore"); + + // Using (import) as alias + collector.ignoreLinesByRegex("using.*=.*<.*>.*;"); + collector.ignoreLinesByRegex("using.*=[^.]+;"); + + collector.ignoreLinesByPrefix("extern"); + collector.ignoreByCondition(line -> line.trim().matches("[a-zA-Z0-9]+:.*")); + } +} diff --git a/languages/csharp/src/test/java/de/jplag/csharp/MinimalCSharpTest.java b/languages/csharp/src/test/java/de/jplag/csharp/MinimalCSharpTest.java deleted file mode 100644 index e4f710ab0..000000000 --- a/languages/csharp/src/test/java/de/jplag/csharp/MinimalCSharpTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package de.jplag.csharp; - -import static de.jplag.csharp.CSharpTokenType.ACCESSORS_BEGIN; -import static de.jplag.csharp.CSharpTokenType.ACCESSORS_END; -import static de.jplag.csharp.CSharpTokenType.ACCESSOR_BEGIN; -import static de.jplag.csharp.CSharpTokenType.ACCESSOR_END; -import static de.jplag.csharp.CSharpTokenType.ASSIGNMENT; -import static de.jplag.csharp.CSharpTokenType.CLASS; -import static de.jplag.csharp.CSharpTokenType.CLASS_BEGIN; -import static de.jplag.csharp.CSharpTokenType.CLASS_END; -import static de.jplag.csharp.CSharpTokenType.CONSTRUCTOR; -import static de.jplag.csharp.CSharpTokenType.FIELD; -import static de.jplag.csharp.CSharpTokenType.IF; -import static de.jplag.csharp.CSharpTokenType.IF_BEGIN; -import static de.jplag.csharp.CSharpTokenType.IF_END; -import static de.jplag.csharp.CSharpTokenType.INVOCATION; -import static de.jplag.csharp.CSharpTokenType.LOCAL_VARIABLE; -import static de.jplag.csharp.CSharpTokenType.METHOD; -import static de.jplag.csharp.CSharpTokenType.METHOD_BEGIN; -import static de.jplag.csharp.CSharpTokenType.METHOD_END; -import static de.jplag.csharp.CSharpTokenType.PROPERTY; -import static de.jplag.csharp.CSharpTokenType.RETURN; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.File; -import java.nio.file.Path; -import java.util.List; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.jplag.ParsingException; -import de.jplag.SharedTokenType; -import de.jplag.Token; -import de.jplag.TokenPrinter; -import de.jplag.TokenType; - -class MinimalCSharpTest { - private final Logger logger = LoggerFactory.getLogger(MinimalCSharpTest.class); - - private static final Path BASE_PATH = Path.of("src", "test", "resources", "de", "jplag", "csharp"); - private static final String TEST_SUBJECT = "TestClass.cs"; - - private de.jplag.Language language; - private File baseDirectory; - - @BeforeEach - public void setUp() { - language = new CSharpLanguage(); - baseDirectory = BASE_PATH.toFile(); - assertTrue(baseDirectory.exists(), "Could not find base directory!"); - } - - @Test - void testParsingTestClass() throws ParsingException { - List expectedToken = List.of(CLASS, CLASS_BEGIN, FIELD, CONSTRUCTOR, LOCAL_VARIABLE, METHOD, METHOD_BEGIN, IF, IF_BEGIN, - INVOCATION, IF_END, IF_BEGIN, INVOCATION, IF_END, METHOD_END, PROPERTY, ACCESSORS_BEGIN, ACCESSOR_BEGIN, ACCESSOR_END, ACCESSOR_BEGIN, - ACCESSOR_END, ACCESSORS_END, FIELD, PROPERTY, ACCESSORS_BEGIN, ACCESSOR_BEGIN, RETURN, ACCESSOR_END, ACCESSOR_BEGIN, ASSIGNMENT, - ACCESSOR_END, ACCESSORS_END, CLASS_END, SharedTokenType.FILE_END); - - // Parse test input - List result = language.parse(Set.of(new File(baseDirectory, TEST_SUBJECT))); - logger.info(TokenPrinter.printTokens(result, baseDirectory)); - - // Compare parsed tokens: - assertEquals(expectedToken.size(), result.size()); - List actualToken = result.stream().map(Token::getType).toList(); - assertEquals(expectedToken, actualToken); - } - -} diff --git a/languages/csharp/src/test/resources/de/jplag/csharp/AllInOneNoPreprocessor.cs b/languages/csharp/src/test/resources/de/jplag/csharp/AllInOneNoPreprocessor.cs new file mode 100644 index 000000000..b819b136b --- /dev/null +++ b/languages/csharp/src/test/resources/de/jplag/csharp/AllInOneNoPreprocessor.cs @@ -0,0 +1,1050 @@ +//From https://github.com/antlr/grammars-v4/tree/master/csharp/examples + +extern alias Foo; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using M = System.Math; + +using ConsoleApplication2.Test; + +/**/ +/* the previous comment is an empty delimited comment and not a document comment */ +/** this is a document comment */ +// this one is a single line comment + +using X = int1; +using Y = ABC.X; + +using static System.Math; +using static System.DayOfWeek; +using static System.Linq.Enumerable; + +[assembly: System.Copyright(@"(C)""2009")] +[module: System.Copyright("\n\t\u0123(C) \"2009" + "\u0123")] + +class TopLevelType : IDisposable +{ + void IDisposable.Dispose() { } +} + +namespace My +{ + using A.B; + + interface CoContra { } + delegate void CoContra2<[System.Obsolete()] out T, in K> () where T : struct; + + public unsafe partial class A : C, I + { + [DllImport("kernel32", SetLastError = true)] + static extern bool CreateDirectory(string name, SecurityAttribute sa); + + private const int global = int.MinValue - 1; + + static A() + { + } + + [method: Obsolete] + public A([param: Obsolete] int foo) : base(1) { + L: { //test-ignore + int i = sizeof(int); + ++i; + var s1 = $"x {1 , -2 :d}"; + var s2 = $@"x {1 , -2 :d}"; + } //test-ignore + + + Console.WriteLine(export.iefSupplied.command); + + const int? local = int.MaxValue; + const Guid? local0 = new Guid(r.ToString()); + + var привет = local; + var мир = local; + int local3 = 0, local4 = 1; + local3 = local4 = 1; + var local5 = null as Action ?? null; + var local6 = local5 is Action; + + var u = 1u; + var U = 1U; + long hex = 0xBADC0DE, Hex = 0XDEADBEEF, l = -1L, L = 1L, l2 = 2l; + ulong ul = 1ul, Ul = 1Ul, uL = 1uL, UL = 1UL, lu = 1lu, Lu = 1Lu, lU = 1lU, LU = 1LU; + int minInt32Value = -2147483648; + int minInt64Value = -9223372036854775808L; + + bool @bool; + byte @byte; + char @char = 'c', \u0066 = '\u0066', hexchar = '\x0130', hexchar2 = (char)0xBAD; + string \U00000065 = "\U00000065"; + decimal @decimal = 1.44M; + @decimal = 1.2m; + dynamic @dynamic; + double @double = M.PI; + @double = 1d; + @double = 1D; + @double = -1.2e3; + float @float = 1.2f; + @float = 1.44F; + int @int = local ?? -1; + long @long; + object @object; + sbyte @sbyte; + short @short; + string @string = @"""/*"; + uint @uint; + ulong @ulong; + ushort @ushort; + + dynamic dynamic = local5; + var add = 0; + var alias = 0; + var arglist = 0; + var ascending = 0; + var async = 0; + var await = 0; + var by = 0; + var descending = 0; + var dynamic = 0; + var equals = 0; + var from = 0; + var get = 0; + var group = 0; + var into = 0; + var join = 0; + var let = 0; + var nameof = 0; + var on = 0; + var orderby = 0; + var partial = 0; + var remove = 0; + var select = 0; + var set = 0; + var var = 0; + var when = 0; + var where = 0; + var yield = 0; + var __ = 0; + where = yield = 0; + + if (i > 0) + { + return; + } + else if (i == 0) + { + throw new Exception(); + } + var o1 = new MyObject(); + var o2 = new MyObject(var); + var o3 = new MyObject { A = i }; + var o4 = new MyObject(@dynamic) + { A = 0, B = 0, C = 0 };//test-ignore + var o5 = new { A = 0 }; + var dictionaryInitializer = new Dictionary + { //test-ignore + {1, ""}, //test-ignore + {2, "a"} //test-ignore + };//test-ignore + float[] a = new float[] + { 0f, 1.1f };//test-ignore + int[, ,] cube = { { { 111, 112, }, { 121, 122 } }, { { 211, 212 }, { 221, 222 } } }; + int[][] jagged = { { 111 }, { 121, 122 } }; + int[][,] arr = new int[5][,]; // as opposed to new int[][5,5] + arr[0] = new int[5,5]; // as opposed to arr[0,0] = new int[5]; + arr[0][0,0] = 47; + int[] arrayTypeInference = new[] { 0, 1, }; + switch (3) { } + switch (i) + {//test-ignore + case 0: case 1: + {//test-ignore + goto case 2; + }//test-ignore + case 2 + 3: + {//test-ignore + goto default; + break; + }//test-ignore + default: + {//test-ignore + return; + }//test-ignore + }//test-ignore + while (i < 10) { + ++i; + if (true) continue; + break; + } + do { + ++i; + if (true) continue; + break; + } while (i < 10); + for (int j = 0; j < 100; ++j) { + for(;;) { + for (int i = 0, j = 0; i < length; i++, j++) { } + if (true) continue; + break; + } + } + label: + goto label; + label2: ; + foreach (var i in Items()) { + if (i == 7) + return; + else + continue; + } + checked + {//test-ignore + checked(++i); + }//test-ignore + unchecked + {//test-ignore + unchecked(++i); + }//test-ignore + lock (sync) + process(); + using (var v = BeginScope()) + using (A a = new A()) + using (A a = new A(), b = new A()) + using (BeginScope()) + return; + yield return this.items[3];//test-ignore + yield break; //test-ignore + fixed (int* p = stackalloc int[100], q = &y) + { //test-ignore + *intref = 1; + } //test-ignore + fixed (int* p = stackalloc int[100]) + { //test-ignore + *intref = 1; + } //test-ignore + unsafe + { //test-ignore + int* p = null; + } //test-ignore + try + { //test-ignore + throw null; + } //test-ignore + catch (System.AccessViolationException av) + { //test-ignore + throw av; + } //test-ignore + catch (Exception) + { //test-ignore + throw; + } //test-ignore + finally + { //test-ignore + try { } catch { } + } //test-ignore + var anonymous = + { + A = 1, + B = 2, + C = 3, + }; //test-ignore + var query = from c in customers + let d = c//test-ignore + where d != null//test-ignore + join c1 in customers on c1.GetHashCode() equals c.GetHashCode() + join c1 in customers on c1.GetHashCode() equals c.GetHashCode() into e + group c by c.Country//test-ignore + into g //test-ignore + orderby g.Count() ascending + orderby g.Key descending //test-ignore + select new { Country = g.Key, CustCount = g.Count() }; + query = from c in customers + select c into d//test-ignore + select d;//test-ignore + } + ~A() + { //test-ignore + } //test-ignore + private readonly int f1; + [Obsolete] + [NonExisting] + [Foo::NonExisting(var, 5)] + [CLSCompliant(false)] + [Obsolete, System.NonSerialized, NonSerialized, CLSCompliant(true || false & true)] + private volatile int f2; + [return: Obsolete] + [method: Obsolete] + public void Handler(object value) + { + } + public int m(T t) where T : class, new() + { + base.m(t); + return 1; + } + public string P + { //test-ignore + get + { + return "A"; + } + set; + } //test-ignore + public abstract string P + { //test-ignore + get; + } //test-ignore + public abstract int this[int index] + { //test-ignore + protected internal get; + internal protected set; + } //test-ignore + [method: Obsolete] + [field: Obsolete] + [event: Obsolete] + public readonly event Event E; + [event: Test] + public event Action E1 + { //test-ignore + [Obsolete] + add { value = value; } + [Obsolete] + [return: Obsolete] + remove { E += Handler; E -= Handler; } + } //test-ignore + public static A operator +(A first, A second) + { + Delegate handler = new Delegate(Handler); + return first.Add(second); + } + [method: Obsolete] + [return: Obsolete] + public static bool operator true(A a) + { + return true; + } + public static bool operator false(A a) + { + return false; + } + class C + { + } + } + public struct S : I + { + public S() + { + } + private int f1; + [Obsolete("Use Script instead", error: false)] + private volatile int f2; + public abstract int m(T t) where T : struct + { + return 1; + } + public string P + { //test-ignore + get + { + int value = 0; + return "A"; + } + set; + } //test-ignore + public abstract string P + { //test-ignore + get; + } //test-ignore + public abstract int this[int index] + { //test-ignore + get; + internal protected set; + } //test-ignore + public event Event E; + public static A operator +(A first, A second) + { + return first.Add(second); + } + fixed int field[10];//test-ignore + class C + { + } + } + public interface I + { + void A(int value); + string Value + { //test-ignore + get; + set; + } //test-ignore + unsafe void UpdateSignatureByHashingContent([In]byte* buffer, int size); + } + [type: Flags] + public enum E + { + A, + B = A, + C = 2 + A, + D, + } + + public delegate void Delegate(object P); + namespace Test + { + using System; + using System.Collections; + public class Список + { + public static IEnumerable Power(int number, int exponent) + { + Список Список = new Список(); + Список.Main(); + int counter = (0 + 0); + int אתר = 0; + while (++counter++ < --exponent--) { + result = result * number + +number+++++number; + yield return result;//test-ignore + } + } + static void Main() + { + foreach (int i in Power(2, 8)) + { //test-ignore + Console.Write("{0} ", i); + } + } + async void Wait() + { + await System.Threading.Tasks.Task.Delay(0); + } + void AsyncAnonymous() // C # 5 feature + { + var task = Task.Factory.StartNew(async () => + { //test-ignore + return await new WebClient().DownloadStringTaskAsync("http://example.com"); + }); //test-ignore + } + } + } +} + +namespace ConsoleApplication1 +{ + namespace RecursiveGenericBaseType + { + class A : B, A> where T : A + { + protected virtual A M() { } + protected abstract B, A> N() { } + static B, A> O() { } + } + + sealed class B : A> + { + protected override A M() { } + protected sealed override B, A> N() { } + new static A O() { } + } + } + + namespace Boo + { + public class Bar where T : IComparable + { + public T f; + public class Foo : IEnumerable + { + public void Method(K k, T t, U u) + where K : IList, IList, IList //test-ignore + where V : IList //test-ignore + { + A a; + M(A(5)); + } + }; + }; + }; + + class Test + { + void Bar3() + { + var x = new Boo.Bar.Foo(); + x.Method(" ", 5, new object()); + + var q = from i in new int[] { 1, 2, 3, 4 } + where i > 5//test-ignore + select i;//test-ignore + } + + public static implicit operator Test(string s) + { + return new ConsoleApplication1.Test(); + } + public static explicit operator Test(string s = "") + { + return new Test(); + } + + public int foo = 5; + void Bar2() + { + foo = 6; + this.Foo = 5.GetType(); Test t = "sss"; + } + + public event EventHandler MyEvent = delegate { }; + + void Blah() + { + int i = 5; + int? j = 6; + + Expression> e = () => i; + Expression> e2 = b => () => { return; }; + Func f = async delegate (bool a) + { //test-ignore + return await !a; + }; //test-ignore + Func f2 = (a, b) => 0; + f2 = (int a, int b) => 1; + Action a = Blah; + f2 = () => {}; + f2 = () => {;}; + } + + delegate Recursive Recursive(Recursive r); + delegate Recursive Recursive(Recursive r); + + public Type Foo { + [Obsolete("Name", error = false)] + get//test-ignore + { + var result = typeof(IEnumerable); + var t = typeof(int?) == typeof(Nullable); + t = typeof(IEnumerable); + return typeof(IEnumerable<>); + } + set//test-ignore + { + var t = typeof(System.Int32); + t.ToString(); + t = value; + } + }//test-ignore + + public void Constants() + { + int i = 1 + 2 + 3 + 5; + global::System.String s = "a" + (System.String)"a" + "a" + "a" + "a" + "A"; + } + + public void ConstructedType() + { + List i = null; + int c = i.Count; + } + } +} + +namespace Comments.XmlComments.UndocumentedKeywords +{ + /// + /// Whatever + /// + /// + /// // + /// /* */ + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + class /*///*/C + { + void M(T t, U u) + { + // comment + /* *** / */ + /* // + */ + /*s*///comment + // /***/ + /*s*/int /*s*/intValue = 0; + intValue = intValue /*s*/+ 1; + string strValue = /*s*/"hello"; + /*s*/MyClass c = new MyClass(); + string verbatimStr = /*s*/@"\\\\"; + } + } + + //General Test F. Type a very long class name, verify colorization happens correctly only upto the correct size (118324) + class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/*Scen8*/{ } + + class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX22/*Scen9*/{ } + + class yield + { + void Foo(__arglist) + { + C c = null; + c.M(5, default(U)); + TypedReference tr = __makeref(c); + Type t = __reftype(tr); + int j = __refvalue(tr, int); + Params(a: t, b: t); + Params(ref c, out c); + } + void Params(ref dynamic a, out dynamic b, params dynamic[] c) {} + void Params(out dynamic a = 2, ref dynamic c = default(dynamic), params dynamic[][] c) {} + + public override string ToString() { return base.ToString(); } + + public partial void OnError(); + + public partial void method() + { + int?[] a = new int?[5];/*[] bug*/ // YES [] + int[] var = { 1, 2, 3, 4, 5 };/*,;*/ + int i = a[i];/*[]*/ + Foo f = new Foo();/*<> ()*/ + f.method();/*().*/ + i = i + i - i * i / i % i & i | i ^ i;/*+ - * / % & | ^*/ + bool b = true & false | true ^ false;/*& | ^*/ + b = !b;/*!*/ + i = ~i;/*~i*/ + b = i < i && i > i;/*< && >*/ + int? ii = 5;/*? bug*/ // NO ? + int f = true ? 1 : 0;/*? :*/ // YES : + i++;/*++*/ + i--;/*--*/ + b = true && false || true;/*&& ||*/ + i << 5;/*<<*/ + i >> 5;/*>>*/ + b = i == i && i != i && i <= i && i >= i;/*= == && != <= >=*/ + i += 5.0;/*+=*/ + i -= i;/*-=*/ + i *= i;/**=*/ + i /= i;/*/=*/ + i %= i;/*%=*/ + i &= i;/*&=*/ + i |= i;/*|=*/ + i ^= i;/*^=*/ + i <<= i;/*<<=*/ + i >>= i;/*>>=*/ + object s = x => x + 1;/*=>*/ + double d = .3; + Point point; + unsafe + {//test-ignore + Point* p = &point;/** &*/ + p->x = 10;/*->*/ + }//test-ignore + IO::BinaryReader br = null; + x[i: 1] = 3; + x[i: 1, j: 5] = "str"; + } + + struct Point { public int X; public int Y; public void ThisAccess() { this = this; } } + } + + // From here:https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6 + class CSharp6Features + { + // Initializers for auto-properties + public string First { get; set; } = "Jane"; + public string Last { get; set; } = "Doe"; + + // Getter-only auto-properties + public string Third { get; } = "Jane"; + public string Fourth { get; } = "Doe"; + + // Expression bodies on method-like members + public Point Move(int dx, int dy) => new Point(x + dx, y + dy); + public static Complex operator +(Complex a, Complex b) => a.Add(b); + public static implicit operator string(Person p) => p.First + " " + p.Last; + public void Print() => Console.WriteLine(First + " " + Last); + + // Expression bodies on property-like function members + public string Name => First + " " + Last; + public int this[long id] => id; + + async void Test() + { + // Using static + WriteLine(Sqrt(3*3 + 4*4)); + WriteLine(Friday - Monday); + var range = Range(5, 17); // Ok: not extension + var even = range.Where(i => i % 2 == 0); // Ok + + // Null-conditional operators + int? length = customers?.Length; // null if customers is null + Customer first = customers?[0]; // null if customers is null + int length = customers?.Length ?? 0; // 0 if customers is null + int? first = customers?[0]?.Orders?.Count(); + PropertyChanged?.Invoke(this, args); + + // String interpolation + string s = $"{p.Name, 20} is {p.Age:D3} year{{s}} old #"; + s = $"{p.Name} is \"{p.Age} year{(p.Age == 1 ? "" : "s")} old"; + s = $"{(p.Age == 2 ? $"{new Person { } }" : "")}"; + s = $@"\{p.Name} + ""\"; //test-ignore + s = $"Color [ R={func(b: 3):#0.##}, G={G:#0.##}, B={B:#0.##}, A={A:#0.##} ]"; + + // nameof expressions + if (x == null) + throw new ArgumentNullException(nameof(x)); + WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode" + + // Index initializers + var numbers = new Dictionary { + [7] = "seven", //test-ignore + [9] = "nine", //test-ignore + [13] = "thirteen" //test-ignore + }; //test-ignore + + // Exception filters + try {} + catch (MyException e) when (myfilter(e)) + { } //test-ignore + + // Await in catch and finally blocks + Resource res = null; + try + { //test-ignore + res = await Resource.OpenAsync(); // You could do this. + } //test-ignore + catch(ResourceException e) + { //test-ignore + await Resource.LogAsync(res, e); // Now you can do this … + } //test-ignore + finally + { //test-ignore + if (res != null) + await res.CloseAsync(); // … and this. + } //test-ignore + } + } +} + +class CSharp70 +{ + void PatternMatching(string arg, int b) + { + switch (arg) + { //test-ignore + case "A" when b > 50: + case "B" when b < 50: //test-ignore + default: + break; + } //test-ignore + + (A D, E H) = e; + + if (x?.y?.z is Type value2) + { + // code using value + } + + if (expr is Type v) { Hello(); } + } + + public static async Task LocalFunctions(string[] args) + { + string Hello2(int i) //test-ignore + { //test-ignore + return args[i]; + } //test-ignore + + async Task Hello(T i) => await Task.FromResult(args[i]); + await Hello(1); + } + + public static void OutVar(string[] args) + { + int.TryParse(Hello(1), out var item); + int.TryParse(Hello(1), out int item); + } + + public void ThrowExpression() + { + var result = nullableResult ?? throw new NullReferenceException(); + } + + public void BinaryLiterals() + { + int nineteen = 0b10011; + } + + public void DigitSeparators() + { + int bin = 0b1001_1010_0001_0100; + int hex = 0x1b_a0_44_fe; + int dec = 33_554_432; + int weird = 1_2__3___4____5_____6______7_______8________9; + double real = 1_000.111_1e-1_000; + } +} + +class CSharp71 +{ + void DefaultWithoutTypeName(string content = default) + { + DefaultWithoutTypeName(default); + } + + void TupleRecognize(int a, (int, int) b, (int, int, int)? c) + { + var result = list.Select(c => (c.f1, f3: c.f2)).Where(t => t.f2 == 1); + } +} + +class CSharp72 +{ + readonly struct ReadonlyRef1 + { + Func s = (in int x) => x; + ref TValue this[in TKey index] => null; + public static Vector3 operator+(in Vector3 x, in Vector3 y) => null; + + static readonly ref Vector3 M1_Trace() + { + // OK + ref readonly var r1 = ref M1(); + + // Not valid. Need an LValue + ref readonly Vector3 r2 = ref default(Vector3); + + // Not valid. r1 is readonly. + Mutate(ref r1); + + // OK. + Print(in r1); + + // OK. + return ref r1; + } + } + + ref struct ReadonlyRef2 + { + ref readonly Guid Test(in Vector3 v1, in Vector3 v2) + { + // not OK!! + v1 = default(Vector3); + + // not OK!! + v1.X = 0; + + // not OK!! + foo(ref v1.X); + + return ref (arr != null ? ref arr[0]: ref otherArr[0]); + + Span span = stackalloc int[1]; + + // OK + return new Vector3(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); + } + + ref T Choice(bool condition, ref T consequence, ref T alternative) + { + if (condition) + { + return ref consequence; + } + else + { + return ref alternative; + } + } + } + + public void DoSomething(bool isEmployed, string personName, int personAge) { } + + public void NonTrailingNamedArguments() + { + DoSomething(isEmployed:true, name, age); // currently CS1738, but would become legal + DoSomething(true, personName:name, age); // currently CS1738, but would become legal + DoSomething(name, isEmployed:true, age); // remains illegal + DoSomething(name, age, isEmployed:true); // remains illegal + DoSomething(true, personAge:age, personName:name); // already legal + } + + public void ConditionalRef() + { + ref var r = ref (arr != null ? ref arr[0]: ref otherArr[0]); + } + + public void LeadingSeparator() + { + var res = 0 + + 123 // permitted in C# 1.0 and later //test-ignore + + 1_2_3 // permitted in C# 7.0 and later //test-ignore + + 0x1_2_3 // permitted in C# 7.0 and later //test-ignore + + 0b101 // binary literals added in C# 7.0 //test-ignore + + 0b1_0_1 // permitted in C# 7.0 and later //test-ignore + + // in C# 7.2, _ is permitted after the `0x` or `0b` + + 0x_1_2 // permitted in C# 7.2 and later //test-ignore + + 0b_1_0_1 // permitted in C# 7.2 and later //test-ignore + ; //test-ignore + } +} + +class CSharp73 +{ + void Blittable(T value) where T : unmanaged + { //test-ignore + var unmanaged = 666; + } //test-ignore + + unsafe struct IndexingMovableFixed + { + public fixed int myFixedField[10]; //test-ignore + } + + static IndexingMovableFixed s; + + public unsafe void IndexingMovableFixedFields() + { + int* ptr = s.myFixedField; + int t = s.myFixedField[5]; + } + + public void PatternBasedFixed() + { + fixed(byte* ptr = byteArray) + { //test-ignore + // ptr is a native pointer to the first element of the array + // byteArray is protected from being moved/collected by the GC for the duration of this block + } //test-ignore + } + + public void StackallocArrayInitializer() + { + Span a = stackalloc int[3]; // currently allowed + Span a = stackalloc int[3] { 1, 2, 3 }; + Span a = stackalloc int[] { 1, 2, 3 }; + Span a = stackalloc[] { 1, 2, 3 }; + } + + public void TupleEquality() + { + (int, (int, int)) t1, t2; + var res = t1 == (1, (2, 3)); + } +} + +namespace CSharp80 +{ + class CSharp80ExceptInterfaceDefaultImplement + { + void ReferenceNullable() + { + var? x = E; + x!.ToString(); + string? wtf = null; + int?[]? hello; + } + + void Patterns() + { + if (o is string { Length: 5 } s) Do(); + + return lang.CountOfTokens switch + { //test-ignore + 1 => 100, //test-ignore + 2 => 200, //test-ignore + _ => throw new global::System.Exception() + }; //test-ignore + + var newState = (GetState(), action, hasKey) switch + { //test-ignore + (DoorState.Closed, Action.Open, _) => DoorState.Opened, //test-ignore + (DoorState.Opened, Action.Close, _) => DoorState.Closed, //test-ignore + (DoorState.Closed, Action.Lock, true) => DoorState.Locked, //test-ignore + (DoorState.Locked, Action.Unlock, true) => DoorState.Closed, //test-ignore + (var state, _, _) => state //test-ignore + }; //test-ignore + } + + async Task AsyncStreams() + { + await foreach (var item in asyncEnumerables) + { //test-ignore + } + } + + void Ranges() + { + var thirdItem = list[2]; // list[2] + var lastItem = list[^1]; // list[Index.CreateFromEnd(1)] + var multiDimensional = list[3, ^2]; // list[3, Index.CreateFromEnd(2)] + + var slice1 = list[2..^3]; // list[Range.Create(2, Index.CreateFromEnd(3))] + var slice2 = list[..^3]; // list[Range.ToEnd(Index.CreateFromEnd(3))] + var slice3 = list[2..]; // list[Range.FromStart(2)] + var slice4 = list[..]; // list[Range.All] + var multiDimensional = list[1..2, ..]; // list[Range.Create(1, 2), Range.All] + } + + void UsingDeclarators() + { + using var item = new FileStream("./.f"); + fixed char* ch = "hell"; + item.Dispose(); // no! + } + + void StaticLocalFunction() + { + static unsafe void Func1() {} //test-ignore + static unsafe void Func1() {} //test-ignore + async static void Func2() {} //test-ignore + static async void Func2() {} //test-ignore + } + + void NullCoalescingAssignment() + { + var item = a ??= b ??= c ??= d ??= throw new Exception(); + } + + public readonly float Hello() + { + return 0.1f; + } + } + + interface IA + { + void M() { WriteLine("IA.M"); } + } + + interface IA + { + void M() { WriteLine("IA.M"); } + } + + interface IB : IA + { + override void IA.M() { WriteLine("IB.M"); } // explicitly named + } + + interface IC : IA + { + override void M() { WriteLine("IC.M"); } // implicitly named + } +} diff --git a/languages/csharp/src/test/resources/de/jplag/csharp/TestClass.cs b/languages/csharp/src/test/resources/de/jplag/csharp/TestClass.cs index 4188cd3cb..3b6a4bd14 100644 --- a/languages/csharp/src/test/resources/de/jplag/csharp/TestClass.cs +++ b/languages/csharp/src/test/resources/de/jplag/csharp/TestClass.cs @@ -15,8 +15,7 @@ public void MyMethod(int parameter1, string parameter2) if(parameter1 == 0) { Console.WriteLine("Second parameter {0}", parameter2); } else { - Console.WriteLine("First Parameter {0}, second parameter {1}", - parameter1, parameter2); + Console.WriteLine("First Parameter {0}, second parameter {1}", parameter1, parameter2); } } @@ -26,8 +25,8 @@ public void MyMethod(int parameter1, string parameter2) private int myPropertyVar; public int MyProperty - { + {//test-ignore get { return myPropertyVar; } set { myPropertyVar = value; } - } + }//test-ignore } \ No newline at end of file