diff --git a/CHANGES.md b/CHANGES.md index 38937286..76dc7903 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,12 @@ - Add `do_not_translate` to SwModuleDefinitionGrammar and SwProductDefinitionGrammar. - Add `required_by` to SwModuleDefinitionGrammar. - Add FormattingFixer to `magik-lint --apply-fixes`. +- Store exemplar and method topics at ExemplarDefinition and MethodDefinitions. +- Parse exemplar and method topics. +- Add DeprecatedTypeUsageTypedCheck check to mark deprecated exemplars. +- Add DeprecatedMethodUsageTypedCheck check to mark deprecated methods. +- Semantic tokens now mark deprecated types and methods. +- Completion provider now marks deprecated types and methods. 0.9.1 (2024-03-13) diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProvider.java index 06d1a8b1..f546a15d 100644 --- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProvider.java +++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProvider.java @@ -36,6 +36,7 @@ import nl.ramsolutions.sw.magik.parser.MagikCommentExtractor; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; +import org.eclipse.lsp4j.CompletionItemTag; import org.eclipse.lsp4j.CompletionOptions; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.ServerCapabilities; @@ -47,6 +48,7 @@ public class CompletionProvider { private static final Logger LOGGER = LoggerFactory.getLogger(CompletionProvider.class); private static final Set REMOVAL_STOP_CHARS = new HashSet<>(); + private static final String TOPIC_DEPRECATED = "deprecated"; static { REMOVAL_STOP_CHARS.add(' '); @@ -232,6 +234,9 @@ private List provideGlobalCompletion( item.setDetail(type.getFullName()); item.setDocumentation(type.getDoc()); item.setKind(CompletionItemKind.Class); + if (type.getTopics().contains(TOPIC_DEPRECATED)) { + item.setTags(List.of(CompletionItemTag.Deprecated)); + } return item; }) .forEach(items::add); @@ -292,6 +297,9 @@ private List provideMethodInvocationCompletion( item.setDetail(method.getOwner().getFullName()); item.setDocumentation(method.getDoc()); item.setKind(CompletionItemKind.Method); + if (method.getTopics().contains(TOPIC_DEPRECATED)) { + item.setTags(List.of(CompletionItemTag.Deprecated)); + } return item; }) .toList(); diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProvider.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProvider.java index bfaf7ea2..c55343e2 100644 --- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProvider.java +++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProvider.java @@ -389,6 +389,10 @@ private void buildMethodSignatureDoc( final String moduleName = Objects.requireNonNullElse(method.getModuleName(), ""); builder.append("Module: ").append(moduleName).append(SECTION_END); + // Method topics. + final String topics = method.getTopics().stream().collect(Collectors.joining(", ")); + builder.append("Topics: ").append(topics).append(SECTION_END); + // Method doc. final String methodDoc = method.getDoc(); if (methodDoc != null) { @@ -407,6 +411,10 @@ private void buildTypeSignatureDoc(final AbstractType type, final StringBuilder final String moduleName = Objects.requireNonNullElse(type.getModuleName(), ""); builder.append("Module: ").append(moduleName).append(SECTION_END); + // Topics. + final String topics = type.getTopics().stream().collect(Collectors.joining(", ")); + builder.append("Topics: ").append(topics).append(SECTION_END); + // Type doc. final String typeDoc = type.getDoc(); if (typeDoc != null) { diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticToken.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticToken.java index 2760dc44..61ba334d 100644 --- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticToken.java +++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticToken.java @@ -55,7 +55,8 @@ public Integer getTokenType() { public enum Modifier { DOCUMENTATION(0x01), READONLY(0x02), - VARIABLE_GLOBAL(0x04); + VARIABLE_GLOBAL(0x04), + DEPRECATED(0x08); private final int value; diff --git a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java index c0f7a614..7fa48ee1 100644 --- a/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java +++ b/magik-language-server/src/main/java/nl/ramsolutions/sw/magik/languageserver/semantictokens/SemanticTokenWalker.java @@ -13,13 +13,18 @@ import java.util.stream.Collectors; import nl.ramsolutions.sw.magik.MagikTypedFile; import nl.ramsolutions.sw.magik.analysis.AstWalker; +import nl.ramsolutions.sw.magik.analysis.helpers.MethodInvocationNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.PackageNodeHelper; import nl.ramsolutions.sw.magik.analysis.scope.GlobalScope; import nl.ramsolutions.sw.magik.analysis.scope.Scope; import nl.ramsolutions.sw.magik.analysis.scope.ScopeEntry; import nl.ramsolutions.sw.magik.analysis.typing.ITypeKeeper; +import nl.ramsolutions.sw.magik.analysis.typing.reasoner.LocalTypeReasonerState; +import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType; +import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult; import nl.ramsolutions.sw.magik.analysis.typing.types.MagikType; import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; +import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType; import nl.ramsolutions.sw.magik.api.MagikGrammar; import nl.ramsolutions.sw.magik.api.MagikKeyword; import nl.ramsolutions.sw.magik.api.MagikOperator; @@ -32,6 +37,7 @@ public class SemanticTokenWalker extends AstWalker { private static final String DEFAULT_PACKAGE = "user"; + private static final String TOPIC_DEPRECATED = "deprecated"; private static final List MAGIK_MODIFIER_VALUES = List.of( @@ -278,7 +284,20 @@ protected void walkPreMethodInvocation(final AstNode node) { return; } - this.addSemanticToken(identifierNode, SemanticToken.Type.METHOD); + // Test for deprecation. + final MethodInvocationNodeHelper helper = new MethodInvocationNodeHelper(node); + final AstNode receiverNode = helper.getReceiverNode(); + final LocalTypeReasonerState typeReasonerState = this.magikFile.getTypeReasonerState(); + final ExpressionResult result = typeReasonerState.getNodeType(receiverNode); + final AbstractType type = result.get(0, UndefinedType.INSTANCE); + final String methodName = helper.getMethodName(); + final Set modifiers = + type.getMethods(methodName).stream() + .anyMatch(method -> method.getTopics().contains(TOPIC_DEPRECATED)) + ? Set.of(SemanticToken.Modifier.DEPRECATED) + : Collections.emptySet(); + + this.addSemanticToken(identifierNode, SemanticToken.Type.METHOD, modifiers); } @Override @@ -353,9 +372,15 @@ private void walkPostIdentifierAtom(final AstNode node) { case GLOBAL, DYNAMIC: final TypeString typeString = TypeString.ofIdentifier(identifier, this.currentPakkage); - if (this.isKnownType(typeString)) { - this.addSemanticToken( - node, SemanticToken.Type.CLASS, Set.of(SemanticToken.Modifier.VARIABLE_GLOBAL)); + final ITypeKeeper typeKeeper = this.magikFile.getTypeKeeper(); + final AbstractType type = typeKeeper.getType(typeString); + if (type instanceof MagikType) { + final Set modifiers = + type.getTopics().contains(TOPIC_DEPRECATED) + ? Set.of( + SemanticToken.Modifier.VARIABLE_GLOBAL, SemanticToken.Modifier.DEPRECATED) + : Set.of(SemanticToken.Modifier.VARIABLE_GLOBAL); + this.addSemanticToken(node, SemanticToken.Type.CLASS, modifiers); } else { this.addSemanticToken( node, SemanticToken.Type.VARIABLE, Set.of(SemanticToken.Modifier.VARIABLE_GLOBAL)); diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProviderTest.java index ad5b6536..f3f31270 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/completion/CompletionProviderTest.java @@ -67,6 +67,7 @@ void testMethodCompletionBare() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); final Position position = new Position(1, 6); // On '.'. @@ -95,7 +96,8 @@ void testMethodCompletionSelf() { ExemplarDefinition.Sort.SLOTTED, aRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -107,6 +109,7 @@ void testMethodCompletionSelf() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); final Position position = new Position(1, 10); // On '.'. @@ -134,6 +137,7 @@ void testMethodCompletionExisting() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); final Position position = new Position(1, 8); // On 'i'. @@ -196,7 +200,8 @@ void testGlobalCompletionSlot() { ExemplarDefinition.Sort.SLOTTED, aRef, List.of(new SlotDefinition(null, code, code, null, code, aRef)), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final Position position = new Position(1, 2); // On ''. final List completions = this.getCompletions(code, definitionKeeper, position); diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/definitions/DefinitionsProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/definitions/DefinitionsProviderTest.java index 927c89b8..92fdf886 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/definitions/DefinitionsProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/definitions/DefinitionsProviderTest.java @@ -44,7 +44,8 @@ void testProvideDefinitionsFromGlobal() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final String code = "" + "_method object.method\n" + " _return rope.new()\n" + "_endmethod\n"; diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProviderTest.java index 7d1f5b91..c74970a8 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/hover/HoverProviderTest.java @@ -45,6 +45,7 @@ void testProvideHoverMethodDefinitionName() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); @@ -73,7 +74,8 @@ void testProvideHoverExemplarName() { ExemplarDefinition.Sort.SLOTTED, hoverMeTypeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final String code = "" + "_method hover_me_type.method()\n" + "_endmethod"; final Position position = new Position(0, 10); // On 'hover_me_type'. @@ -101,6 +103,7 @@ void testProvideHoverMethod() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/implementation/ImplementationProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/implementation/ImplementationProviderTest.java index c8739a52..af6e8fd5 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/implementation/ImplementationProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/implementation/ImplementationProviderTest.java @@ -39,7 +39,8 @@ void testProvideAbstractMethodImplementation() { ExemplarDefinition.Sort.SLOTTED, aRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( new Location( @@ -52,6 +53,7 @@ void testProvideAbstractMethodImplementation() { Set.of(MethodDefinition.Modifier.ABSTRACT), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); final TypeString bRef = TypeString.ofIdentifier("b", "user"); @@ -64,7 +66,8 @@ void testProvideAbstractMethodImplementation() { ExemplarDefinition.Sort.SLOTTED, bRef, Collections.emptyList(), - List.of(aRef))); + List.of(aRef), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( new Location( @@ -78,6 +81,7 @@ void testProvideAbstractMethodImplementation() { Collections.emptySet(), // Concrete. Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/inlayhint/InlayHintProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/inlayhint/InlayHintProviderTest.java index e47b9d54..f77e41d2 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/inlayhint/InlayHintProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/inlayhint/InlayHintProviderTest.java @@ -62,6 +62,7 @@ void testProvideParameterHint() { ParameterDefinition.Modifier.NONE, TypeString.UNDEFINED)), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProviderTest.java index 0d50586d..605c061d 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/references/ReferencesProviderTest.java @@ -46,6 +46,7 @@ void testProvideMethodReferenceFromMethodInvocation() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY, Collections.emptySet(), @@ -73,6 +74,7 @@ void testProvideMethodReferenceFromMethodDefintion() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY, Collections.emptySet(), @@ -100,6 +102,7 @@ void testProvideTypeReferenceFromAtom() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY, Set.of(new GlobalUsage(TypeString.SW_INTEGER, EMPTY_LOCATION)), @@ -127,6 +130,7 @@ void testProvideTypeReferenceFromMethodDefinition() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY, Set.of(new GlobalUsage(TypeString.SW_INTEGER, EMPTY_LOCATION)), diff --git a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/typehierarchy/TypeHierarchyProviderTest.java b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/typehierarchy/TypeHierarchyProviderTest.java index 9eec9b5e..653d4507 100644 --- a/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/typehierarchy/TypeHierarchyProviderTest.java +++ b/magik-language-server/src/test/java/nl/ramsolutions/sw/magik/languageserver/typehierarchy/TypeHierarchyProviderTest.java @@ -41,7 +41,8 @@ void testPrepareTypeHierarchyMethodDefinitionExemplarName() { ExemplarDefinition.Sort.SLOTTED, exemplarRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final String code = "" + "_method exemplar.method\n" + "_endmethod\n"; final Position position = new Position(0, 10); // On 'exemplar'. @@ -64,7 +65,8 @@ void testPrepareTypeHierarchyGlobal() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final String code = "" + "_method exemplar.method\n" + " rope.new()\n" + "_endmethod\n"; final Position position = new Position(1, 4); // On 'rope'. @@ -87,7 +89,8 @@ void testGetSubtypes() { ExemplarDefinition.Sort.SLOTTED, exemplarRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final TypeString subExemplarRef = TypeString.ofIdentifier("sub_exemplar", "user"); definitionKeeper.add( new ExemplarDefinition( @@ -98,7 +101,8 @@ void testGetSubtypes() { ExemplarDefinition.Sort.SLOTTED, subExemplarRef, Collections.emptyList(), - List.of(exemplarRef))); + List.of(exemplarRef), + Collections.emptySet())); final TypeHierarchyItem item = new TypeHierarchyItem( @@ -125,7 +129,8 @@ void testGetSupertypes() { ExemplarDefinition.Sort.SLOTTED, exemplarRef, Collections.emptyList(), - List.of(TypeString.ofIdentifier("slotted_format_mixin", "sw")))); + List.of(TypeString.ofIdentifier("slotted_format_mixin", "sw")), + Collections.emptySet())); final TypeHierarchyItem item = new TypeHierarchyItem( diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefaultDefinitionsAdder.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefaultDefinitionsAdder.java index 3eeda95b..36ef218b 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefaultDefinitionsAdder.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/DefaultDefinitionsAdder.java @@ -29,7 +29,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.OBJECT, TypeString.SW_OBJECT, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -39,7 +40,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_UNSET, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -49,7 +51,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_FALSE, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -59,7 +62,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_MAYBE, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -69,7 +73,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_INTEGER, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -79,7 +84,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_BIGNUM, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -89,7 +95,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_FLOAT, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -99,7 +106,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INDEXED, TypeString.SW_SYMBOL, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -109,7 +117,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_CHARACTER, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -119,7 +128,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_SW_REGEXP, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -129,7 +139,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_PROCEDURE, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -139,7 +150,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INDEXED, TypeString.SW_CHAR16_VECTOR, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -149,7 +161,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INDEXED, TypeString.SW_SIMPLE_VECTOR, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -159,7 +172,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_HEAVY_THREAD, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -169,7 +183,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_LIGHT_THREAD, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -179,7 +194,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.SLOTTED, TypeString.SW_CONDITION, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -189,7 +205,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.SLOTTED, TypeString.SW_ENUMERATION_VALUE, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -199,7 +216,8 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_INDEXED_FORMAT_MIXIN, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -209,6 +227,7 @@ public static void addDefaultDefinitions(IDefinitionKeeper definitionKeeper) { ExemplarDefinition.Sort.INTRINSIC, TypeString.SW_SLOTTED_FORMAT_MIXIN, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); } } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ExemplarDefinition.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ExemplarDefinition.java index 0c455e46..fe98bd47 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ExemplarDefinition.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/ExemplarDefinition.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Set; import nl.ramsolutions.sw.magik.Location; import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; @@ -26,6 +27,7 @@ public enum Sort { private final TypeString typeName; private final List slots; private final List parents; + private final Set topics; /** * Constructor. @@ -36,6 +38,7 @@ public enum Sort { * @param typeName Name of slotted exemplar. * @param slots Slots of slotted exemplar. * @param parents Parents of slotted exemplar. + * @param topics Topics. */ @SuppressWarnings({"checkstyle:ParameterNumber", "java:S107"}) public ExemplarDefinition( @@ -46,7 +49,8 @@ public ExemplarDefinition( final Sort sort, final TypeString typeName, final List slots, - final List parents) { + final List parents, + final Set topics) { super(location, moduleName, doc, node); if (!typeName.isSingle()) { @@ -57,6 +61,7 @@ public ExemplarDefinition( this.typeName = typeName; this.slots = List.copyOf(slots); this.parents = List.copyOf(parents); + this.topics = Set.copyOf(topics); } public List getSlots() { @@ -78,6 +83,10 @@ public List getParents() { return Collections.unmodifiableList(this.parents); } + public Set getTopics() { + return Collections.unmodifiableSet(this.topics); + } + @Override public TypeString getTypeString() { return this.typeName; @@ -107,7 +116,8 @@ public ExemplarDefinition getWithoutNode() { this.sort, this.typeName, this.slots.stream().map(SlotDefinition::getWithoutNode).toList(), - this.parents); + this.parents, + Collections.emptySet()); } @Override diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/MethodDefinition.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/MethodDefinition.java index 6d8ae1cb..4b0b6a79 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/MethodDefinition.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/MethodDefinition.java @@ -28,6 +28,7 @@ public enum Modifier { private final String methodName; private final List parameters; private final @Nullable ParameterDefinition assignmentParameter; + private final Set topics; private final ExpressionResultString returnTypes; private final ExpressionResultString loopTypes; private final Set usedGlobals; @@ -45,6 +46,7 @@ public enum Modifier { * @param modifiers Modifiers for method. * @param parameters Parameters for method. * @param assignmentParameter Assignment parameter. + * @param topics Topics. * @param doc Method doc. * @param returnTypes Return types. * @param loopTypes Loop types. @@ -60,6 +62,7 @@ public MethodDefinition( final Set modifiers, final List parameters, final @Nullable ParameterDefinition assignmentParameter, + final Set topics, final ExpressionResultString returnTypes, final ExpressionResultString loopTypes) { super(location, moduleName, doc, node); @@ -68,6 +71,7 @@ public MethodDefinition( this.modifiers = Set.copyOf(modifiers); this.parameters = List.copyOf(parameters); this.assignmentParameter = assignmentParameter; + this.topics = Set.copyOf(topics); this.returnTypes = returnTypes; this.loopTypes = loopTypes; this.usedGlobals = Collections.emptySet(); @@ -86,6 +90,7 @@ public MethodDefinition( * @param modifiers Modifiers for method. * @param parameters Parameters for method. * @param assignmentParameter Assignment parameter. + * @param topics Topics. * @param doc Method doc. * @param returnTypes Return types. * @param loopTypes Loop types. @@ -101,6 +106,7 @@ public MethodDefinition( final Set modifiers, final List parameters, final @Nullable ParameterDefinition assignmentParameter, + final Set topics, final ExpressionResultString returnTypes, final ExpressionResultString loopTypes, final Set usedGlobals, @@ -113,6 +119,7 @@ public MethodDefinition( this.modifiers = Set.copyOf(modifiers); this.parameters = List.copyOf(parameters); this.assignmentParameter = assignmentParameter; + this.topics = Set.copyOf(topics); this.returnTypes = returnTypes; this.loopTypes = loopTypes; this.usedGlobals = Collections.unmodifiableSet(usedGlobals); @@ -194,6 +201,15 @@ public ParameterDefinition getAssignmentParameter() { return this.assignmentParameter; } + /** + * Get topics. + * + * @return Topics. + */ + public Set getTopics() { + return Collections.unmodifiableSet(this.topics); + } + public Set getUsedGlobals() { return Collections.unmodifiableSet(this.usedGlobals); } @@ -227,6 +243,7 @@ public MethodDefinition getWithoutNode() { this.modifiers, this.parameters.stream().map(ParameterDefinition::getWithoutNode).toList(), this.assignmentParameter != null ? this.assignmentParameter.getWithoutNode() : null, + this.topics, this.returnTypes, this.loopTypes, this.usedGlobals, @@ -256,6 +273,7 @@ public int hashCode() { this.methodName, this.parameters, this.assignmentParameter, + this.topics, this.returnTypes, this.loopTypes); } @@ -283,6 +301,7 @@ public boolean equals(final Object obj) { && Objects.equals(this.methodName, other.methodName) && Objects.equals(this.parameters, other.parameters) && Objects.equals(this.assignmentParameter, other.assignmentParameter) + && Objects.equals(this.topics, other.topics) && Objects.equals(this.returnTypes, other.returnTypes) && Objects.equals(this.loopTypes, other.loopTypes); } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionReader.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionReader.java index ee354b38..21172f1d 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionReader.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionReader.java @@ -114,11 +114,32 @@ public MethodDefinition createInstance(final Type type) { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.UNDEFINED); } } + private static final class ExemplarDefinitionCreator + implements InstanceCreator { + + @Override + public ExemplarDefinition createInstance(final Type type) { + // This ensures `MethodDefinition.usedGlobals` etc are initialized properly, + // even if these were not set in the source JSON. + return new ExemplarDefinition( + null, + null, + null, + null, + ExemplarDefinition.Sort.UNDEFINED, + TypeString.UNDEFINED, + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet()); + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(JsonDefinitionReader.class); private final IDefinitionKeeper definitionKeeper; @@ -225,6 +246,7 @@ private Gson buildGson() { ParameterDefinition.Modifier.class, new LowerCaseEnumDeserializer()) .registerTypeAdapter(MethodDefinition.class, new MethodDefinitionCreator()) + .registerTypeAdapter(ExemplarDefinition.class, new ExemplarDefinitionCreator()) .create(); } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefEnumerationParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefEnumerationParser.java index ffa681fc..e7eec5bc 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefEnumerationParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefEnumerationParser.java @@ -4,11 +4,13 @@ import java.net.URI; import java.util.Collections; import java.util.List; +import java.util.Set; import nl.ramsolutions.sw.definitions.ModuleDefinitionScanner; import nl.ramsolutions.sw.magik.Location; import nl.ramsolutions.sw.magik.analysis.definitions.Definition; import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition; import nl.ramsolutions.sw.magik.analysis.helpers.ArgumentsNodeHelper; +import nl.ramsolutions.sw.magik.analysis.helpers.PragmaNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.ProcedureInvocationNodeHelper; import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; import nl.ramsolutions.sw.magik.api.MagikGrammar; @@ -94,6 +96,13 @@ public List parseDefinitions() { final AstNode parentNode = this.node.getParent(); final String doc = MagikCommentExtractor.extractDocComment(parentNode); + // Figure topics. + final AstNode pragmaNode = PragmaNodeHelper.getPragmaNode(node); + final Set topics = + pragmaNode != null + ? new PragmaNodeHelper(pragmaNode).getAllTopics() + : Collections.emptySet(); + final ExemplarDefinition definition = new ExemplarDefinition( location, @@ -103,7 +112,8 @@ public List parseDefinitions() { ExemplarDefinition.Sort.SLOTTED, name, Collections.emptyList(), - DefEnumerationParser.ENUM_PARENTS); + DefEnumerationParser.ENUM_PARENTS, + topics); return List.of(definition); } } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefIndexedExemplarParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefIndexedExemplarParser.java index f462f364..6b5ef8c8 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefIndexedExemplarParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefIndexedExemplarParser.java @@ -4,11 +4,13 @@ import java.net.URI; import java.util.Collections; import java.util.List; +import java.util.Set; import nl.ramsolutions.sw.definitions.ModuleDefinitionScanner; import nl.ramsolutions.sw.magik.Location; import nl.ramsolutions.sw.magik.analysis.definitions.Definition; import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition; import nl.ramsolutions.sw.magik.analysis.helpers.ArgumentsNodeHelper; +import nl.ramsolutions.sw.magik.analysis.helpers.PragmaNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.ProcedureInvocationNodeHelper; import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; import nl.ramsolutions.sw.magik.api.MagikGrammar; @@ -94,6 +96,13 @@ public List parseDefinitions() { final AstNode parentNode = this.node.getParent(); final String doc = MagikCommentExtractor.extractDocComment(parentNode); + // Figure topics. + final AstNode pragmaNode = PragmaNodeHelper.getPragmaNode(node); + final Set topics = + pragmaNode != null + ? new PragmaNodeHelper(pragmaNode).getAllTopics() + : Collections.emptySet(); + final ExemplarDefinition indexedExemplarDefinition = new ExemplarDefinition( location, @@ -103,7 +112,8 @@ public List parseDefinitions() { ExemplarDefinition.Sort.INDEXED, name, Collections.emptyList(), - parents); + parents, + topics); return List.of(indexedExemplarDefinition); } } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefMixinParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefMixinParser.java index 2829a703..008aac60 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefMixinParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefMixinParser.java @@ -4,11 +4,13 @@ import java.net.URI; import java.util.Collections; import java.util.List; +import java.util.Set; import nl.ramsolutions.sw.definitions.ModuleDefinitionScanner; import nl.ramsolutions.sw.magik.Location; import nl.ramsolutions.sw.magik.analysis.definitions.Definition; import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition; import nl.ramsolutions.sw.magik.analysis.helpers.ArgumentsNodeHelper; +import nl.ramsolutions.sw.magik.analysis.helpers.PragmaNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.ProcedureInvocationNodeHelper; import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; import nl.ramsolutions.sw.magik.api.MagikGrammar; @@ -93,6 +95,13 @@ public List parseDefinitions() { final AstNode parentNode = this.node.getParent(); final String doc = MagikCommentExtractor.extractDocComment(parentNode); + // Figure topics. + final AstNode pragmaNode = PragmaNodeHelper.getPragmaNode(node); + final Set topics = + pragmaNode != null + ? new PragmaNodeHelper(pragmaNode).getAllTopics() + : Collections.emptySet(); + final ExemplarDefinition mixinDefinition = new ExemplarDefinition( location, @@ -102,7 +111,8 @@ public List parseDefinitions() { ExemplarDefinition.Sort.INTRINSIC, name, Collections.emptyList(), - parents); + parents, + topics); return List.of(mixinDefinition); } } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefSlottedExemplarParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefSlottedExemplarParser.java index b27dc910..1bfd2b0d 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefSlottedExemplarParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefSlottedExemplarParser.java @@ -18,6 +18,7 @@ import nl.ramsolutions.sw.magik.analysis.definitions.ParameterDefinition; import nl.ramsolutions.sw.magik.analysis.definitions.SlotDefinition; import nl.ramsolutions.sw.magik.analysis.helpers.ArgumentsNodeHelper; +import nl.ramsolutions.sw.magik.analysis.helpers.PragmaNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.ProcedureInvocationNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.SimpleVectorNodeHelper; import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString; @@ -157,6 +158,13 @@ public List parseDefinitions() { // Figure doc. final String doc = MagikCommentExtractor.extractDocComment(parentNode); + // Figure topics. + final AstNode pragmaNode = PragmaNodeHelper.getPragmaNode(node); + final Set topics = + pragmaNode != null + ? new PragmaNodeHelper(pragmaNode).getAllTopics() + : Collections.emptySet(); + final ExemplarDefinition slottedExemplarDefinition = new ExemplarDefinition( location, @@ -166,7 +174,8 @@ public List parseDefinitions() { ExemplarDefinition.Sort.SLOTTED, name, slots, - parents); + parents, + topics); final List definitions = new ArrayList<>(); definitions.add(slottedExemplarDefinition); @@ -207,6 +216,7 @@ private List generateSlotMethods( getModifiers, getParameters, null, + Collections.emptySet(), new ExpressionResultString(slotTypeRef), ExpressionResultString.EMPTY); methodDefinitions.add(getMethod); @@ -228,6 +238,7 @@ private List generateSlotMethods( getModifiers, getParameters, null, + Collections.emptySet(), new ExpressionResultString(slotTypeRef), ExpressionResultString.EMPTY); methodDefinitions.add(getMethod); @@ -259,6 +270,7 @@ private List generateSlotMethods( setModifiers, setParameters, assignmentParam, + Collections.emptySet(), new ExpressionResultString(TypeString.ofParameterRef("val")), ExpressionResultString.EMPTY); methodDefinitions.add(setMethod); @@ -276,6 +288,7 @@ private List generateSlotMethods( setModifiers, setParameters, assignmentParam, + Collections.emptySet(), new ExpressionResultString(slotTypeRef), ExpressionResultString.EMPTY); methodDefinitions.add(bootMethod); diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedConstantParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedConstantParser.java index 03852dae..3cbf19ea 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedConstantParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedConstantParser.java @@ -146,6 +146,7 @@ public List parseDefinitions() { modifiers, parameters, null, + Collections.emptySet(), new ExpressionResultString(typeRef), ExpressionResultString.EMPTY); return List.of(methodDefinition); diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedVariableParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedVariableParser.java index 832fd718..16a4b17c 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedVariableParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSharedVariableParser.java @@ -161,6 +161,7 @@ private List generateVariableMethods( getModifiers, getParameters, null, + Collections.emptySet(), new ExpressionResultString(typeRef), ExpressionResultString.EMPTY); methodDefinitions.add(getMethod); @@ -192,6 +193,7 @@ private List generateVariableMethods( setModifiers, setParameters, assignmentParam, + Collections.emptySet(), new ExpressionResultString(TypeString.ofParameterRef("val")), ExpressionResultString.EMPTY); methodDefinitions.add(setMethod); @@ -209,6 +211,7 @@ private List generateVariableMethods( setModifiers, setParameters, assignmentParam, + Collections.emptySet(), new ExpressionResultString(typeRef), ExpressionResultString.EMPTY); methodDefinitions.add(bootMethod); diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSlotAccessParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSlotAccessParser.java index 40918522..f06828f2 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSlotAccessParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/DefineSlotAccessParser.java @@ -268,6 +268,7 @@ private List generateSlotMethods( getModifiers, getParameters, null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.UNDEFINED); methodDefinitions.add(getMethod); @@ -289,6 +290,7 @@ private List generateSlotMethods( getModifiers, getParameters, null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.UNDEFINED); methodDefinitions.add(getMethod); @@ -320,6 +322,7 @@ private List generateSlotMethods( setModifiers, setParameters, assignmentParam, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.UNDEFINED); methodDefinitions.add(setMethod); @@ -337,6 +340,7 @@ private List generateSlotMethods( setModifiers, setParameters, assignmentParam, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.UNDEFINED); methodDefinitions.add(bootMethod); diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/MethodDefinitionParser.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/MethodDefinitionParser.java index 0d036a00..df5b382d 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/MethodDefinitionParser.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/definitions/parsers/MethodDefinitionParser.java @@ -24,6 +24,7 @@ import nl.ramsolutions.sw.magik.analysis.definitions.SlotUsage; import nl.ramsolutions.sw.magik.analysis.helpers.MethodDefinitionNodeHelper; import nl.ramsolutions.sw.magik.analysis.helpers.ParameterNodeHelper; +import nl.ramsolutions.sw.magik.analysis.helpers.PragmaNodeHelper; import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString; import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; import nl.ramsolutions.sw.magik.api.MagikGrammar; @@ -124,6 +125,13 @@ public List parseDefinitions() { .map(String::trim) .collect(Collectors.joining("\n")); + // Figure topics. + final AstNode pragmaNode = PragmaNodeHelper.getPragmaNode(node); + final Set topics = + pragmaNode != null + ? new PragmaNodeHelper(pragmaNode).getAllTopics() + : Collections.emptySet(); + final MethodDefinitionUsageParser usageParser = new MethodDefinitionUsageParser(this.node); final Set usedGlobals = this.configuration.getMagikIndexerIndexGlobalUsages() @@ -153,6 +161,7 @@ public List parseDefinitions() { modifiers, parameters, assignmentParamter, + topics, callResult, loopResult, usedGlobals, diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/helpers/PragmaNodeHelper.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/helpers/PragmaNodeHelper.java new file mode 100644 index 00000000..50a2abda --- /dev/null +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/helpers/PragmaNodeHelper.java @@ -0,0 +1,47 @@ +package nl.ramsolutions.sw.magik.analysis.helpers; + +import com.sonar.sslr.api.AstNode; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import java.util.Set; +import java.util.stream.Collectors; +import nl.ramsolutions.sw.magik.api.MagikGrammar; + +public class PragmaNodeHelper { + + private final AstNode node; + + /** + * Constructor. + * + * @param node Node to encapsulate. + */ + public PragmaNodeHelper(final AstNode node) { + if (!node.is(MagikGrammar.PRAGMA)) { + throw new IllegalArgumentException(); + } + + this.node = node; + } + + public Set getAllTopics() { + // TODO: Does this work? I've seen `{` topics... + return this.node.getDescendants(MagikGrammar.PRAGMA_VALUE).stream() + .flatMap(valueNode -> valueNode.getDescendants(MagikGrammar.IDENTIFIER).stream()) + .map(node -> node.getTokenOriginalValue()) + .collect(Collectors.toSet()); + } + + @CheckForNull + public static AstNode getPragmaNode(final AstNode node) { + if (node.is(MagikGrammar.PRAGMA)) { + return node; + } + + final AstNode previousSibling = node.getPreviousSibling(); + if (previousSibling != null && previousSibling.is(MagikGrammar.PRAGMA)) { + return previousSibling; + } + + return null; + } +} diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/ClassInfoDefinitionReader.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/ClassInfoDefinitionReader.java index d71b55eb..fc0aba63 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/ClassInfoDefinitionReader.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/ClassInfoDefinitionReader.java @@ -349,6 +349,7 @@ private void readMethod(final String moduleName, final String line, final Buffer modifiers, parameters, assignmentParameter, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.UNDEFINED); this.definitionKeeper.add(definition); @@ -427,7 +428,8 @@ private void readSlottedClass( ExemplarDefinition.Sort.UNDEFINED, typeString, slots, - parents); + parents, + Collections.emptySet()); this.definitionKeeper.add(definition); } @@ -487,7 +489,8 @@ private void readMixin(final String moduleName, final String line, final Buffere ExemplarDefinition.Sort.INTRINSIC, typeString, Collections.emptyList(), - Collections.emptyList()); + Collections.emptyList(), + Collections.emptySet()); this.definitionKeeper.add(definition); } diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/DefinitionKeeperTypeKeeperAdapter.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/DefinitionKeeperTypeKeeperAdapter.java index 8a35bf70..5edad0e7 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/DefinitionKeeperTypeKeeperAdapter.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/DefinitionKeeperTypeKeeperAdapter.java @@ -248,6 +248,9 @@ private AbstractType createType( final MagikType magikType = new MagikType(this, location, moduleName, sort, definitionTypeString); + // Add topics. + exemplarDefinition.getTopics().forEach(magikType::addTopic); + // Add doc. final String doc = exemplarDefinition.getDoc(); magikType.setDoc(doc); @@ -420,6 +423,8 @@ private Method addMethod(final MagikType type, final MethodDefinition methodDefi methodDefinition.getReturnTypes(), methodDefinition.getLoopTypes()); + methodDefinition.getTopics().forEach(method::addTopic); + methodDefinition .getUsedGlobals() .forEach( diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/AbstractType.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/AbstractType.java index 282d80a7..b37fe287 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/AbstractType.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/AbstractType.java @@ -3,6 +3,7 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.Nullable; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -240,6 +241,10 @@ public String getModuleName() { return this.moduleName; } + public Set getTopics() { + return Collections.emptySet(); + } + /** * Get the intersection of two types. * diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikType.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikType.java index 2ee6beba..bdcca119 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikType.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikType.java @@ -43,6 +43,7 @@ public enum Sort { private final Set methods = ConcurrentHashMap.newKeySet(); private final Set parents = ConcurrentHashMap.newKeySet(); private final Map slots = new ConcurrentHashMap<>(); + private final Set topics = new HashSet<>(); private final Set genericDefinitions = ConcurrentHashMap.newKeySet(); private final ITypeKeeper typeKeeper; private Sort sort; @@ -447,6 +448,15 @@ public void removeMethod(final Method method) { this.methods.remove(method); } + @Override + public Set getTopics() { + return Collections.unmodifiableSet(this.topics); + } + + public void addTopic(final String topic) { + this.topics.add(topic); + } + @Override public String toString() { return String.format( diff --git a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/Method.java b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/Method.java index a0d2390c..27a4c6d9 100644 --- a/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/Method.java +++ b/magik-squid/src/main/java/nl/ramsolutions/sw/magik/analysis/typing/types/Method.java @@ -287,6 +287,7 @@ public String getValue() { private final ExpressionResultString callResult; private final ExpressionResultString loopbodyResult; private final String name; + private final Set topics; private final Set usedGlobals; private final Set calledMethods; private final Set usedSlots; @@ -327,6 +328,7 @@ public Method( // NOSONAR this.name = name; this.parameters = parameters; this.assignmentParameter = assignmentParameter; + this.topics = new HashSet<>(); this.doc = methodDoc; this.callResult = callResult; this.loopbodyResult = loopbodyResult; @@ -374,6 +376,14 @@ public String getName() { return this.name; } + public Set getTopics() { + return Collections.unmodifiableSet(this.topics); + } + + public void addTopic(final String topic) { + this.topics.add(topic); + } + /** * Get name of method, with parameters. * diff --git a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionWriterTest.java b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionWriterTest.java index c6234235..69d3f2e5 100644 --- a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionWriterTest.java +++ b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/definitions/io/JsonDefinitionWriterTest.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; import java.util.List; import java.util.Set; import nl.ramsolutions.sw.magik.Location; @@ -65,8 +66,9 @@ void testWriteType() throws IOException { null, ExemplarDefinition.Sort.SLOTTED, aRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); JsonDefinitionWriter.write(this.tempPath, definitionKeeper); @@ -96,6 +98,7 @@ void testWriteMethod() throws IOException { ParameterDefinition.Modifier.OPTIONAL, TypeString.UNDEFINED)), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); diff --git a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeMatcherTest.java b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeMatcherTest.java index 27057c02..64484854 100644 --- a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeMatcherTest.java +++ b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeMatcherTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Collections; import java.util.List; import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition; @@ -26,8 +27,9 @@ void testTypeEquals() { null, ExemplarDefinition.Sort.SLOTTED, typeRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type = typeKeeper.getType(typeRef); @@ -49,8 +51,9 @@ void testTypeNotEquals() { null, ExemplarDefinition.Sort.INTRINSIC, type1Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -59,8 +62,9 @@ void testTypeNotEquals() { null, ExemplarDefinition.Sort.INTRINSIC, type2Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type = typeKeeper.getType(type1Ref); @@ -83,8 +87,9 @@ void testTypeIsKindOf() { null, ExemplarDefinition.Sort.INTRINSIC, baseRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -93,8 +98,9 @@ void testTypeIsKindOf() { null, ExemplarDefinition.Sort.INTRINSIC, childRef, - List.of(), - List.of(baseRef))); + Collections.emptyList(), + List.of(baseRef), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType baseType = typeKeeper.getType(baseRef); @@ -117,8 +123,9 @@ void testTypeMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type1Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -127,8 +134,9 @@ void testTypeMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type2Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type1 = typeKeeper.getType(type1Ref); @@ -153,8 +161,9 @@ void testTypeNotMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type1Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -163,8 +172,9 @@ void testTypeNotMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type2Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -173,8 +183,9 @@ void testTypeNotMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type3Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type1 = typeKeeper.getType(type1Ref); @@ -200,8 +211,9 @@ void testCombinedTypeMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type1Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -210,8 +222,9 @@ void testCombinedTypeMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type2Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -220,8 +233,9 @@ void testCombinedTypeMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type3Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type1 = typeKeeper.getType(type1Ref); @@ -248,8 +262,9 @@ void testCombinedTypeNotMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type1Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -258,8 +273,9 @@ void testCombinedTypeNotMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type2Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -268,8 +284,9 @@ void testCombinedTypeNotMatchesCombinedType() { null, ExemplarDefinition.Sort.INTRINSIC, type3Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type1 = typeKeeper.getType(type1Ref); @@ -294,8 +311,9 @@ void testIsKindOfEquals() { null, ExemplarDefinition.Sort.INTRINSIC, typeRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type = typeKeeper.getType(typeRef); @@ -318,8 +336,9 @@ void testIsKindOfNotEquals() { null, ExemplarDefinition.Sort.INTRINSIC, type1Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -328,8 +347,9 @@ void testIsKindOfNotEquals() { null, ExemplarDefinition.Sort.INTRINSIC, type2Ref, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType type = typeKeeper.getType(type1Ref); @@ -352,8 +372,9 @@ void testIsKindOfDirectParent() { null, ExemplarDefinition.Sort.INTRINSIC, baseRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -362,8 +383,9 @@ void testIsKindOfDirectParent() { null, ExemplarDefinition.Sort.INTRINSIC, childRef, - List.of(), - List.of(baseRef))); + Collections.emptyList(), + List.of(baseRef), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType childType = typeKeeper.getType(childRef); @@ -387,8 +409,9 @@ void testIsKindOfIndirectParent() { null, ExemplarDefinition.Sort.INTRINSIC, baseRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -397,8 +420,9 @@ void testIsKindOfIndirectParent() { null, ExemplarDefinition.Sort.INTRINSIC, child1Ref, - List.of(), - List.of(baseRef))); + Collections.emptyList(), + List.of(baseRef), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -407,8 +431,9 @@ void testIsKindOfIndirectParent() { null, ExemplarDefinition.Sort.INTRINSIC, child2Ref, - List.of(), - List.of(child1Ref))); + Collections.emptyList(), + List.of(child1Ref), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final AbstractType baseType = typeKeeper.getType(baseRef); diff --git a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeReaderTest.java b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeReaderTest.java index 6de59950..af7469c5 100644 --- a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeReaderTest.java +++ b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/TypeReaderTest.java @@ -2,7 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; -import java.util.List; +import java.util.Collections; import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition; import nl.ramsolutions.sw.magik.analysis.definitions.IDefinitionKeeper; @@ -80,8 +80,9 @@ void testParseGeneric() { null, ExemplarDefinition.Sort.SLOTTED, ropeRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); final ITypeKeeper typeKeeper = new DefinitionKeeperTypeKeeperAdapter(definitionKeeper); final TypeString ropeRefWithGeneric = diff --git a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/LocalTypeReasonerTest.java b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/LocalTypeReasonerTest.java index 816139cc..6215e98d 100644 --- a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/LocalTypeReasonerTest.java +++ b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/LocalTypeReasonerTest.java @@ -81,7 +81,8 @@ void testReasonMethodReturnRope() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -93,6 +94,7 @@ void testReasonMethodReturnRope() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); @@ -217,7 +219,8 @@ void testReasonVariable() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -229,6 +232,7 @@ void testReasonVariable() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -242,6 +246,7 @@ void testReasonVariable() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); @@ -281,7 +286,8 @@ void testReasonVariable2() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -293,6 +299,7 @@ void testReasonVariable2() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -306,6 +313,7 @@ void testReasonVariable2() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); @@ -340,6 +348,7 @@ void testUnaryOperator() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); @@ -518,6 +527,7 @@ void testLoopStatement() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, new ExpressionResultString(TypeString.SW_INTEGER))); @@ -559,6 +569,7 @@ void testLoopStatementUseIterator() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, new ExpressionResultString(TypeString.SW_INTEGER))); @@ -603,6 +614,7 @@ void testLoopResultOptional() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, new ExpressionResultString(TypeString.SW_INTEGER))); @@ -727,7 +739,8 @@ void testAssignmentMethod() { ExemplarDefinition.Sort.INDEXED, propertyListRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -739,6 +752,7 @@ void testAssignmentMethod() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); @@ -779,6 +793,7 @@ void testSelf() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); @@ -812,7 +827,8 @@ void testSelfNewInit() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -824,6 +840,7 @@ void testSelfNewInit() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); @@ -1141,7 +1158,8 @@ void testSingleSuperType() { ExemplarDefinition.Sort.SLOTTED, sRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final TypeString tRef = TypeString.ofIdentifier("t", "sw"); definitionKeeper.add( new ExemplarDefinition( @@ -1152,7 +1170,8 @@ void testSingleSuperType() { ExemplarDefinition.Sort.SLOTTED, tRef, Collections.emptyList(), - List.of(sRef))); + List.of(sRef), + Collections.emptySet())); // Do analysis. final MagikTypedFile magikFile = this.createMagikFile(code, definitionKeeper); @@ -1183,7 +1202,8 @@ void testSingleNamedSuperType() { ExemplarDefinition.Sort.SLOTTED, rRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final TypeString tRef = TypeString.ofIdentifier("t", "sw"); definitionKeeper.add( @@ -1195,7 +1215,8 @@ void testSingleNamedSuperType() { ExemplarDefinition.Sort.SLOTTED, tRef, Collections.emptyList(), - List.of(rRef))); + List.of(rRef), + Collections.emptySet())); // Do analysis. final MagikTypedFile magikFile = this.createMagikFile(code, definitionKeeper); @@ -1310,7 +1331,8 @@ void testParameterReferenceUsage() { ExemplarDefinition.Sort.SLOTTED, aRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final TypeString param1Ref = TypeString.ofParameterRef("p1"); definitionKeeper.add( new MethodDefinition( @@ -1325,6 +1347,7 @@ void testParameterReferenceUsage() { new ParameterDefinition( null, null, null, null, "p1", ParameterDefinition.Modifier.NONE, param1Ref)), null, + Collections.emptySet(), new ExpressionResultString(param1Ref), ExpressionResultString.EMPTY)); @@ -1359,7 +1382,8 @@ void testParameterReferenceNested() { ExemplarDefinition.Sort.SLOTTED, aRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final TypeString param1Ref = TypeString.ofParameterRef("p1"); definitionKeeper.add( new MethodDefinition( @@ -1374,6 +1398,7 @@ void testParameterReferenceNested() { new ParameterDefinition( null, null, null, null, "p1", ParameterDefinition.Modifier.NONE, param1Ref)), null, + Collections.emptySet(), new ExpressionResultString(param1Ref), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -1389,6 +1414,7 @@ void testParameterReferenceNested() { new ParameterDefinition( null, null, null, null, "p1", ParameterDefinition.Modifier.NONE, param1Ref)), null, + Collections.emptySet(), new ExpressionResultString(param1Ref), ExpressionResultString.EMPTY)); @@ -1419,7 +1445,8 @@ void testParameterReferenceOptional() { ExemplarDefinition.Sort.SLOTTED, aRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final TypeString param1Ref = TypeString.ofParameterRef("p1"); definitionKeeper.add( new MethodDefinition( @@ -1434,6 +1461,7 @@ void testParameterReferenceOptional() { new ParameterDefinition( null, null, null, null, "p1", ParameterDefinition.Modifier.NONE, param1Ref)), null, + Collections.emptySet(), new ExpressionResultString(param1Ref), ExpressionResultString.EMPTY)); @@ -1469,7 +1497,8 @@ void testGenericMethodInvocation1() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -1481,6 +1510,7 @@ void testGenericMethodInvocation1() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -1494,6 +1524,7 @@ void testGenericMethodInvocation1() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofGenericReference( "E")), // Possibly also `sw:unset`, but for testing purposes... @@ -1531,7 +1562,8 @@ void testGenericMethodInvocation2() { ExemplarDefinition.Sort.INDEXED, propertyListRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -1543,6 +1575,7 @@ void testGenericMethodInvocation2() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -1556,6 +1589,7 @@ void testGenericMethodInvocation2() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofGenericReference( "K")), // Possibly also `sw:unset`, but for testing purposes... @@ -1571,6 +1605,7 @@ void testGenericMethodInvocation2() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofGenericReference( "E")), // Possibly also `sw:unset`, but for testing purposes... @@ -1612,7 +1647,8 @@ void testGenericMethodInvocation3() { ExemplarDefinition.Sort.SLOTTED, ropeRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -1624,6 +1660,7 @@ void testGenericMethodInvocation3() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -1637,6 +1674,7 @@ void testGenericMethodInvocation3() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofIdentifier( TypeString.SW_SIMPLE_VECTOR.getIdentifier(), @@ -1685,7 +1723,8 @@ void testGenericMethodInvocation4() { TypeString.ofGenericDefinition("K", TypeString.SW_INTEGER), TypeString.ofGenericDefinition("E", TypeString.SW_CHARACTER)), Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -1697,6 +1736,7 @@ void testGenericMethodInvocation4() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofIdentifier( TypeString.SW_SIMPLE_VECTOR.getIdentifier(), @@ -1714,6 +1754,7 @@ void testGenericMethodInvocation4() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofIdentifier( TypeString.SW_SIMPLE_VECTOR.getIdentifier(), @@ -1768,7 +1809,8 @@ void testGenericIterMethodInvocation() { ExemplarDefinition.Sort.INDEXED, propertyListRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -1780,6 +1822,7 @@ void testGenericIterMethodInvocation() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SELF), ExpressionResultString.EMPTY)); definitionKeeper.add( @@ -1793,6 +1836,7 @@ void testGenericIterMethodInvocation() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), ExpressionResultString.EMPTY, new ExpressionResultString( TypeString.ofGenericReference("K"), TypeString.ofGenericReference("E")))); @@ -1833,7 +1877,8 @@ void testGenericSlot() { ExemplarDefinition.Sort.SLOTTED, stackRef, Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -1845,6 +1890,7 @@ void testGenericSlot() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString( TypeString.ofGenericReference( "E")), // Possibly also `sw:unset`, but for testing purposes... @@ -1863,7 +1909,8 @@ void testGenericSlot() { ExemplarDefinition.Sort.SLOTTED, exemplarRef, List.of(new SlotDefinition(null, null, null, null, "stack", slotTypeRef)), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); // Do analysis. final MagikTypedFile magikFile = this.createMagikFile(code, definitionKeeper); diff --git a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/restrictions/ConditionalBodyHandlerTest.java b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/restrictions/ConditionalBodyHandlerTest.java index 85e3ed48..66b79855 100644 --- a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/restrictions/ConditionalBodyHandlerTest.java +++ b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/reasoner/restrictions/ConditionalBodyHandlerTest.java @@ -556,6 +556,7 @@ void testHandleIfMethodResult() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SW_UNSET), ExpressionResultString.EMPTY)); final MagikTypedFile magikFile = this.createMagikFile(code, definitionKeeper); @@ -593,6 +594,7 @@ void testHandleIfMethodResultBothSides() { Collections.emptySet(), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SW_UNSET), ExpressionResultString.EMPTY)); final MagikTypedFile magikFile = this.createMagikFile(code, definitionKeeper); diff --git a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikTypeTest.java b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikTypeTest.java index ced5a4fa..90976e1f 100644 --- a/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikTypeTest.java +++ b/magik-squid/src/test/java/nl/ramsolutions/sw/magik/analysis/typing/types/MagikTypeTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Collections; import java.util.List; import java.util.Set; import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; @@ -27,8 +28,9 @@ void testCreateMethodGenericWithRefs() { null, ExemplarDefinition.Sort.SLOTTED, propertyListRef, - List.of(), - List.of())); + Collections.emptyList(), + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new MethodDefinition( null, @@ -40,6 +42,7 @@ void testCreateMethodGenericWithRefs() { Set.of(), List.of(), null, + Collections.emptySet(), ExpressionResultString.EMPTY, new ExpressionResultString( TypeString.ofGenericReference("K"), TypeString.ofGenericReference("E")))); diff --git a/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/CheckList.java b/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/CheckList.java index 7c6a96a0..691471d9 100644 --- a/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/CheckList.java +++ b/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/CheckList.java @@ -6,6 +6,8 @@ import nl.ramsolutions.sw.magik.checks.MagikCheck; import nl.ramsolutions.sw.magik.checks.checks.TypeDocCheck; import nl.ramsolutions.sw.magik.typedchecks.checks.ConditionalExpressionIsFalseTypedCheck; +import nl.ramsolutions.sw.magik.typedchecks.checks.DeprecatedMethodUsageTypedCheck; +import nl.ramsolutions.sw.magik.typedchecks.checks.DeprecatedTypeUsageTypedCheck; import nl.ramsolutions.sw.magik.typedchecks.checks.GlobalExistsTypedCheck; import nl.ramsolutions.sw.magik.typedchecks.checks.MethodArgumentCountMatchesParameterCountTypedCheck; import nl.ramsolutions.sw.magik.typedchecks.checks.MethodArgumentTypeMatchesParameterTypeTypedCheck; @@ -32,6 +34,8 @@ private CheckList() {} public static List> getChecks() { return List.of( ConditionalExpressionIsFalseTypedCheck.class, + DeprecatedMethodUsageTypedCheck.class, + DeprecatedTypeUsageTypedCheck.class, GlobalExistsTypedCheck.class, MethodArgumentCountMatchesParameterCountTypedCheck.class, MethodArgumentTypeMatchesParameterTypeTypedCheck.class, diff --git a/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedMethodUsageTypedCheck.java b/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedMethodUsageTypedCheck.java new file mode 100644 index 00000000..2727eb84 --- /dev/null +++ b/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedMethodUsageTypedCheck.java @@ -0,0 +1,47 @@ +package nl.ramsolutions.sw.magik.typedchecks.checks; + +import com.sonar.sslr.api.AstNode; +import java.util.Collection; +import nl.ramsolutions.sw.magik.analysis.helpers.MethodInvocationNodeHelper; +import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType; +import nl.ramsolutions.sw.magik.analysis.typing.types.Method; +import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType; +import nl.ramsolutions.sw.magik.typedchecks.MagikTypedCheck; +import org.sonar.check.Rule; + +/** Check to test if used method is deprecaed. */ +@Rule(key = DeprecatedMethodUsageTypedCheck.CHECK_KEY) +public class DeprecatedMethodUsageTypedCheck extends MagikTypedCheck { + + @SuppressWarnings("checkstyle:JavadocVariable") + public static final String CHECK_KEY = "DeprecatedMethodUsage"; + + private static final String DEPRECATED_TOPIC = "deprecated"; + private static final String MESSAGE = "Used method '%s' is deprecated"; + + @Override + protected void walkPostMethodInvocation(final AstNode node) { + // Get type. + final AbstractType calledType = this.getTypeInvokedOn(node); + if (calledType == UndefinedType.INSTANCE) { + // Cannot give any useful information, so abort. + return; + } + + // Get method. + final MethodInvocationNodeHelper helper = new MethodInvocationNodeHelper(node); + final String methodName = helper.getMethodName(); + final Collection methods = calledType.getMethods(methodName); + + // Add issue if method is deprecated. + methods.stream() + .filter( + method -> method.getTopics().contains(DeprecatedMethodUsageTypedCheck.DEPRECATED_TOPIC)) + .forEach( + method -> { + final String fullName = calledType.getFullName() + "." + methodName; + final String message = String.format(MESSAGE, fullName); + this.addIssue(node, message); + }); + } +} diff --git a/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedTypeUsageTypedCheck.java b/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedTypeUsageTypedCheck.java new file mode 100644 index 00000000..c0a3094e --- /dev/null +++ b/magik-typed-checks/src/main/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedTypeUsageTypedCheck.java @@ -0,0 +1,46 @@ +package nl.ramsolutions.sw.magik.typedchecks.checks; + +import com.sonar.sslr.api.AstNode; +import nl.ramsolutions.sw.magik.analysis.typing.reasoner.LocalTypeReasonerState; +import nl.ramsolutions.sw.magik.analysis.typing.types.AbstractType; +import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResult; +import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; +import nl.ramsolutions.sw.magik.analysis.typing.types.UndefinedType; +import nl.ramsolutions.sw.magik.api.MagikGrammar; +import nl.ramsolutions.sw.magik.typedchecks.MagikTypedCheck; +import org.sonar.check.Rule; + +/** Check to test if used type is deprecaed. */ +@Rule(key = DeprecatedTypeUsageTypedCheck.CHECK_KEY) +public class DeprecatedTypeUsageTypedCheck extends MagikTypedCheck { + + @SuppressWarnings("checkstyle:JavadocVariable") + public static final String CHECK_KEY = "DeprecatedTypeUsage"; + + private static final String DEPRECATED_TOPIC = "deprecated"; + private static final String MESSAGE = "Used type '%s' is deprecated"; + + @Override + protected void walkPostIdentifier(final AstNode node) { + final AstNode parent = node.getParent(); + if (!parent.is(MagikGrammar.ATOM)) { + return; + } + + final LocalTypeReasonerState state = this.getTypeReasonerState(); + final ExpressionResult result = state.getNodeType(parent); + final AbstractType type = result.get(0, UndefinedType.INSTANCE); + if (type == UndefinedType.INSTANCE) { + return; + } + + if (!type.getTopics().contains(DeprecatedTypeUsageTypedCheck.DEPRECATED_TOPIC)) { + return; + } + + final TypeString typeString = type.getTypeString(); + final String typeStringStr = typeString.getFullString(); + final String message = String.format(MESSAGE, typeStringStr); + this.addIssue(node, message); + } +} diff --git a/magik-typed-checks/src/main/resources/nl/ramsolutions/sw/sonar/l10n/magik/rules/DeprecatedMethodUsage.json b/magik-typed-checks/src/main/resources/nl/ramsolutions/sw/sonar/l10n/magik/rules/DeprecatedMethodUsage.json new file mode 100644 index 00000000..06b465a5 --- /dev/null +++ b/magik-typed-checks/src/main/resources/nl/ramsolutions/sw/sonar/l10n/magik/rules/DeprecatedMethodUsage.json @@ -0,0 +1,16 @@ +{ + "title": "Deprecated method usage", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "15min" + }, + "tags": [ + "obsolete", + "typing" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "DeprecatedMethodUsage", + "sqKey": "deprecated-method-usage" + } \ No newline at end of file diff --git a/magik-typed-checks/src/main/resources/nl/ramsolutions/sw/sonar/l10n/magik/rules/DeprecatedTypeUsage.json b/magik-typed-checks/src/main/resources/nl/ramsolutions/sw/sonar/l10n/magik/rules/DeprecatedTypeUsage.json new file mode 100644 index 00000000..f7fa8603 --- /dev/null +++ b/magik-typed-checks/src/main/resources/nl/ramsolutions/sw/sonar/l10n/magik/rules/DeprecatedTypeUsage.json @@ -0,0 +1,16 @@ +{ + "title": "Deprecated type usage", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "15min" + }, + "tags": [ + "obsolete", + "typing" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "DeprecatedTypeUsage", + "sqKey": "deprecated-type-usage" + } \ No newline at end of file diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ComparedTypesDoNotMatchTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ComparedTypesDoNotMatchTest.java index ed2e599d..86188a6a 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ComparedTypesDoNotMatchTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ComparedTypesDoNotMatchTest.java @@ -98,7 +98,8 @@ void testTypeMatchable(final String code) { ExemplarDefinition.Sort.SLOTTED, TypeString.ofIdentifier("parent", "user"), Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); definitionKeeper.add( new ExemplarDefinition( null, @@ -108,7 +109,8 @@ void testTypeMatchable(final String code) { ExemplarDefinition.Sort.SLOTTED, TypeString.ofIdentifier("child", "user"), Collections.emptyList(), - List.of(TypeString.ofIdentifier("parent", "user")))); + List.of(TypeString.ofIdentifier("parent", "user")), + Collections.emptySet())); final MagikTypedCheck check = new ComparedTypesDoNotMatchTypedCheck(); final List checkResults = this.runCheck(code, definitionKeeper, check); assertThat(checkResults).isEmpty(); diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedMethodUsageTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedMethodUsageTypedCheckTest.java new file mode 100644 index 00000000..e2418e93 --- /dev/null +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedMethodUsageTypedCheckTest.java @@ -0,0 +1,64 @@ +package nl.ramsolutions.sw.magik.typedchecks.checks; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; +import nl.ramsolutions.sw.magik.analysis.definitions.IDefinitionKeeper; +import nl.ramsolutions.sw.magik.analysis.definitions.MethodDefinition; +import nl.ramsolutions.sw.magik.analysis.typing.types.ExpressionResultString; +import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; +import nl.ramsolutions.sw.magik.checks.MagikIssue; +import nl.ramsolutions.sw.magik.typedchecks.MagikTypedCheck; +import org.junit.jupiter.api.Test; + +/** Test {@link DeprecatedMethodUsageTypedCheck}. */ +public class DeprecatedMethodUsageTypedCheckTest extends MagikTypedCheckTestBase { + + private void addMethodDefinition( + final IDefinitionKeeper definitionKeeper, final String... topics) { + definitionKeeper.add( + new MethodDefinition( + null, + null, + null, + null, + TypeString.SW_OBJECT, + "m()", + EnumSet.noneOf(MethodDefinition.Modifier.class), + Collections.emptyList(), + null, + Set.of(topics), + ExpressionResultString.EMPTY, + ExpressionResultString.EMPTY)); + } + + @Test + void testMethodDeprecated() { + final IDefinitionKeeper definitionKeeper = new DefinitionKeeper(); + this.addMethodDefinition(definitionKeeper, "deprecated"); + final String code = """ + _block + object.m() + _endblock"""; + final MagikTypedCheck check = new DeprecatedMethodUsageTypedCheck(); + final List issues = this.runCheck(code, definitionKeeper, check); + assertThat(issues).hasSize(1); + } + + @Test + void testMethodNotDeprecated() { + final IDefinitionKeeper definitionKeeper = new DefinitionKeeper(); + this.addMethodDefinition(definitionKeeper, "not_deprecated"); + final String code = """ + _block + object.m() + _endblock"""; + final MagikTypedCheck check = new DeprecatedMethodUsageTypedCheck(); + final List issues = this.runCheck(code, definitionKeeper, check); + assertThat(issues).isEmpty(); + } +} diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedTypeUsageTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedTypeUsageTypedCheckTest.java new file mode 100644 index 00000000..9337984d --- /dev/null +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/DeprecatedTypeUsageTypedCheckTest.java @@ -0,0 +1,61 @@ +package nl.ramsolutions.sw.magik.typedchecks.checks; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; +import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition; +import nl.ramsolutions.sw.magik.analysis.definitions.IDefinitionKeeper; +import nl.ramsolutions.sw.magik.analysis.typing.types.TypeString; +import nl.ramsolutions.sw.magik.checks.MagikIssue; +import nl.ramsolutions.sw.magik.typedchecks.MagikTypedCheck; +import org.junit.jupiter.api.Test; + +/** Test {@link DeprecatedTypeUsageTypedCheck}. */ +public class DeprecatedTypeUsageTypedCheckTest extends MagikTypedCheckTestBase { + + private void addExemplarDefinition( + final IDefinitionKeeper definitionKeeper, final TypeString typeName, final String... topics) { + definitionKeeper.add( + new ExemplarDefinition( + null, + null, + null, + null, + ExemplarDefinition.Sort.SLOTTED, + typeName, + Collections.emptyList(), + Collections.emptyList(), + Set.of(topics))); + } + + @Test + void testTypeDeprecated() { + final IDefinitionKeeper definitionKeeper = new DefinitionKeeper(); + final TypeString typeStr = TypeString.ofIdentifier("test", "user"); + this.addExemplarDefinition(definitionKeeper, typeStr, "deprecated"); + final String code = """ + _block + user:test.m() + _endblock"""; + final MagikTypedCheck check = new DeprecatedTypeUsageTypedCheck(); + final List issues = this.runCheck(code, definitionKeeper, check); + assertThat(issues).hasSize(1); + } + + @Test + void testTypeNotDeprecated() { + final IDefinitionKeeper definitionKeeper = new DefinitionKeeper(); + final TypeString typeStr = TypeString.ofIdentifier("test", "user"); + this.addExemplarDefinition(definitionKeeper, typeStr, "not_deprecated"); + final String code = """ + _block + user:test.m() + _endblock"""; + final MagikTypedCheck check = new DeprecatedTypeUsageTypedCheck(); + final List issues = this.runCheck(code, definitionKeeper, check); + assertThat(issues).isEmpty(); + } +} diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentCountMatchesParameterCountTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentCountMatchesParameterCountTypedCheckTest.java index 9e23c19c..0810c61d 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentCountMatchesParameterCountTypedCheckTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentCountMatchesParameterCountTypedCheckTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; @@ -19,7 +20,10 @@ class MethodArgumentCountMatchesParameterCountTypedCheckTest extends MagikTypedC @Test void testMethodUnknown() { - final String code = "" + "_block\n" + " object.m()\n" + "_endblock"; + final String code = """ + _block + object.m() + _endblock"""; final IDefinitionKeeper definitionKeeper = new DefinitionKeeper(); final MagikTypedCheck check = new MethodArgumentCountMatchesParameterCountTypedCheck(); final List issues = this.runCheck(code, definitionKeeper, check); @@ -56,10 +60,14 @@ void testArgumentCountMatches() { ParameterDefinition.Modifier.NONE, TypeString.SW_OBJECT)), null, + Collections.emptySet(), ExpressionResultString.EMPTY, ExpressionResultString.EMPTY)); - final String code = "" + "_block\n" + " object.m(object, object)\n" + "_endblock"; + final String code = """ + _block + object.m(object, object) + _endblock"""; final MagikTypedCheck check = new MethodArgumentCountMatchesParameterCountTypedCheck(); final List issues = this.runCheck(code, definitionKeeper, check); assertThat(issues).isEmpty(); @@ -95,10 +103,14 @@ void testArgumentMissing() { ParameterDefinition.Modifier.NONE, TypeString.SW_OBJECT)), null, + Collections.emptySet(), ExpressionResultString.EMPTY, ExpressionResultString.EMPTY)); - final String code = "" + "_block\n" + " object.m(object)\n" + "_endblock"; + final String code = """ + _block + object.m(object) + _endblock"""; final MagikTypedCheck check = new MethodArgumentCountMatchesParameterCountTypedCheck(); final List issues = this.runCheck(code, definitionKeeper, check); assertThat(issues).hasSize(1); diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentTypeMatchesParameterTypeTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentTypeMatchesParameterTypeTypedCheckTest.java index b248a8a9..0ce613ba 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentTypeMatchesParameterTypeTypedCheckTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodArgumentTypeMatchesParameterTypeTypedCheckTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import nl.ramsolutions.sw.magik.analysis.definitions.DefinitionKeeper; @@ -39,6 +40,7 @@ private void addTestMethod(final IDefinitionKeeper definitionKeeper) { ParameterDefinition.Modifier.NONE, TypeString.SW_SYMBOL)), null, + Collections.emptySet(), ExpressionResultString.UNDEFINED, ExpressionResultString.EMPTY)); } diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodExistsTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodExistsTypedCheckTest.java index 08b004d7..2bae160d 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodExistsTypedCheckTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/MethodExistsTypedCheckTest.java @@ -41,6 +41,7 @@ void testMethodKnown() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SW_OBJECT), ExpressionResultString.EMPTY)); final MagikTypedCheck check = new MethodExistsTypedCheck(); diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ModuleRequiredForGlobalTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ModuleRequiredForGlobalTypedCheckTest.java index 585e0f88..c3da70d9 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ModuleRequiredForGlobalTypedCheckTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/ModuleRequiredForGlobalTypedCheckTest.java @@ -53,7 +53,8 @@ void testModuleIsRequired() throws IllegalArgumentException, IOException { ExemplarDefinition.Sort.SLOTTED, TypeString.ofIdentifier("rope", "sw"), Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final MagikTypedCheck check = new ModuleRequiredForGlobalTypedCheck(); final List checkResults = this.runCheck(path, definitionKeeper, check); @@ -80,7 +81,8 @@ void testModuleIsNotRequired() throws IllegalArgumentException, IOException { ExemplarDefinition.Sort.SLOTTED, TypeString.ofIdentifier("rope", "sw"), Collections.emptyList(), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final MagikTypedCheck check = new ModuleRequiredForGlobalTypedCheck(); final List checkResults = this.runCheck(path, definitionKeeper, check); diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/SlotExistsTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/SlotExistsTypedCheckTest.java index cc28e4f6..67b97977 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/SlotExistsTypedCheckTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/SlotExistsTypedCheckTest.java @@ -48,7 +48,8 @@ void testSlotKnown() { ExemplarDefinition.Sort.SLOTTED, aRef, List.of(new SlotDefinition(null, null, null, null, "slot", TypeString.UNDEFINED)), - Collections.emptyList())); + Collections.emptyList(), + Collections.emptySet())); final MagikTypedCheck check = new SlotExistsTypedCheck(); final List issues = this.runCheck(code, definitionKeeper, check); assertThat(issues).isEmpty(); diff --git a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/UndefinedMethodCallResultTypedCheckTest.java b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/UndefinedMethodCallResultTypedCheckTest.java index fdc12b95..c8f4798c 100644 --- a/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/UndefinedMethodCallResultTypedCheckTest.java +++ b/magik-typed-checks/src/test/java/nl/ramsolutions/sw/magik/typedchecks/checks/UndefinedMethodCallResultTypedCheckTest.java @@ -41,6 +41,7 @@ void testMethodInvocationDefined() { EnumSet.noneOf(MethodDefinition.Modifier.class), Collections.emptyList(), null, + Collections.emptySet(), new ExpressionResultString(TypeString.SW_OBJECT), ExpressionResultString.EMPTY)); diff --git a/type_dumper.magik b/type_dumper.magik index 2f7f6c82..aaa18e4c 100644 --- a/type_dumper.magik +++ b/type_dumper.magik @@ -15,8 +15,8 @@ $ _pragma(classify_level=restricted) _private _method method_finder.read_comment() ## Read a comment from input. - ## @return {sw:char16_vector|sw:unset} Read comment. - ## @return {sw:char16_vector|sw:unset} Method finder message. + ## @return {sw:char16_vector|sw:unset} Read comment, unset on error. + ## @return {sw:char16_vector|sw:unset} Method finder error, if any. _local msg << .input.get_line() _local line_count << msg.as_integer() _if line_count _is _unset @@ -35,11 +35,20 @@ _private _method method_finder.read_comment() _endmethod $ +_pragma(classify_level=restricted) +_private _method method_finder.read_line() + ## Read a single line from input. + ## @return {sw:char16_vector|sw:unset} Read line. + _return .input.get_line() +_endmethod +$ + _pragma(classify_level=basic, topic=type_dumper) _method method_finder.get_class_comment(class) ## Get class comment. ## @param {sw:char16_vector} class Class name, e.g., "sw:rope". - ## @return {sw:char16_vector|sw:unset} Class comment. + ## @return {sw:char16_vector|sw:unset} Class comment, unset on error. + ## @return {sw:char16_vector|sw:unset} Method finder error, if any. _local command << write_string("get_class_info comments ", class) _self.write(command, character.newline) _self.flush() @@ -47,12 +56,30 @@ _method method_finder.get_class_comment(class) _endmethod $ +_pragma(classify_level=basic, topic=type_dumper) +_method method_finder.get_class_topics(class) + ## Get class comment. + ## @param {sw:char16_vector} class Class name, e.g., "sw:rope". + ## @return {sw:simple_vector} Class topics. + _local command << write_string("get_class_info topics ", class) + _self.write(command, character.newline) + _self.flush() + _local topics << _self.read_line() + _if topics = "method finder: Invalid class" + _then + _return {} + _endif + _return topics.default("").trim_spaces().split_by(character.space) +_endmethod +$ + _pragma(classify_level=basic, topic=type_dumper) _method method_finder.get_method_comment(method, class) ## Get method comment. ## @param {sw:char16_vector} method Method name, e.g., "as_simple_vector()". ## @param {sw:char16_vector} class Class name, e.g., "sw:rope". - ## @return {sw:char16_vector|sw:unset} Method comment. + ## @return {sw:char16_vector|sw:unset} Method comment, unset on error. + ## @return {sw:char16_vector|sw:unset} Method finder error, if any. _local command << write_string("get_method_info comments ", method, " ", class) _self.write(command, character.newline) _self.flush() @@ -60,10 +87,30 @@ _method method_finder.get_method_comment(method, class) _endmethod $ +_pragma(classify_level=basic, topic=type_dumper) +_method method_finder.get_method_topics(method, class) + ## Get method comment. + ## @param {sw:char16_vector} method Method name, e.g., "as_simple_vector()". + ## @param {sw:char16_vector} class Class name, e.g., "sw:rope". + ## @return {sw:simple_vector} Method topics. + _local command << write_string("get_method_info topics ", method, " ", class) + _self.write(command, character.newline) + _self.flush() + _local topics << _self.read_line() + _if topics = "method finder: Invalid class" _orif + topics = "method finder: Invalid method" _orif + topics = "method finder: No such method found" + _then + _return {} + _endif + _return topics.default("").trim_spaces().split_by(character.space) +_endmethod +$ + _if smallworld_product.product(:sw5_java_object_wrapper) _isnt _unset _then - smallworld_product.add_product(:sw5_java_object_wrapper) - sw_module_manager.load_module(:sw5_java_object_wrapper) + smallworld_product.add_product(:sw5_java_object_wrapper) + sw_module_manager.load_module(:sw5_java_object_wrapper) _endif $ @@ -409,8 +456,8 @@ _private _method type_dumper.write_type(type_name, method_table) # Slots. instruction[:slots] << _self.build_slots(method_table) - # Pragma. - instruction[:pragma] << metadata[:topic].default({}) + # Topics. + instruction[:topics] << method_finder.get_class_topics(type_name) # Module name. instruction[:module_name] << @@ -556,9 +603,9 @@ _private _method type_dumper.write_method(type_name, method) # pass _endtry - instruction[:pragma] << + instruction[:topics] << _try - >> method.compiler_info[:topic] + >> method_finder.get_method_topics(method.name, type_name) _when error >> {} _endtry