Skip to content

Commit

Permalink
Merge tag '0.9.1' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenLooman committed Mar 15, 2024
2 parents 0ba3865 + 7c47d63 commit dda6628
Show file tree
Hide file tree
Showing 24 changed files with 362 additions and 134 deletions.
9 changes: 9 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
- Add UnsafeEvaluateInvocationCheck to test for unsafe `unsafe_evaluate()` method calls.
- Add SwChar16VectorEvaluateInvocationCheck to test for `sw:char16_vector.evaluate()` method calls.

0.9.1 (2024-03-13)

- Fix actually not indexing large files in magik-language-server.
- Fix `sonar-magik-plugin` not setting property `sonar.lang.patterns.magik`.
- Fix printing order of issues in `magik-lint`.
- Fix `module.def` parser not parsing partial test entries.
- Fix `sonar-magik-plugin` crashing on syntax error token during Copy/Paste Detection-phase.
- Fix reasoning errors where types were regarded as combined, when it was actually singular.

0.9.0 (2024-02-26)

- Support configurable libs dirs.
Expand Down
2 changes: 2 additions & 0 deletions magik-lint/lint-demo.magik
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ _method object.abc
## Test method.
## @return {sw:integer}
_local x << 10
_local y << 20
_local z << 30
_return 10
_endmethod
$
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public void run(final Collection<Path> paths) throws IOException, ReflectiveOper
.map(this::runChecksOnFile)
.flatMap(List::stream)
.sorted((issue0, issue1) -> locationCompare.compare(issue0.location(), issue1.location()))
.sequential()
.limit(maxInfractions)
.forEach(this.reporter::reportIssue);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,14 @@ public static LexerlessGrammar create() {
.rule(TEST_ENTRY)
.is(
builder.firstOf(
builder.sequence("name", WHITESPACE, IDENTIFIER),
builder.sequence("framework", WHITESPACE, IDENTIFIER),
builder.sequence("topics", WHITESPACE, IDENTIFIER_LIST),
builder.sequence("args", WHITESPACE, REST_OF_LINE),
builder.sequence("description", WHITESPACE, REST_OF_LINE),
builder.sequence("label", WHITESPACE, IDENTIFIER),
builder.sequence("topic", WHITESPACE, IDENTIFIER),
builder.sequence("arg", WHITESPACE, IDENTIFIER)));
builder.sequence("name", builder.optional(WHITESPACE, IDENTIFIER)),
builder.sequence("framework", builder.optional(WHITESPACE, IDENTIFIER)),
builder.sequence("topics", builder.optional(WHITESPACE, IDENTIFIER_LIST)),
builder.sequence("args", builder.optional(WHITESPACE, REST_OF_LINE)),
builder.sequence("description", builder.optional(WHITESPACE, REST_OF_LINE)),
builder.sequence("label", builder.optional(WHITESPACE, IDENTIFIER)),
builder.sequence("topic", builder.optional(WHITESPACE, IDENTIFIER)),
builder.sequence("arg", builder.optional(WHITESPACE, IDENTIFIER))));
builder.rule(FREE_LINES).is(builder.zeroOrMore(FREE_LINE));
builder.rule(FREE_LINE).is(builder.regexp("(?!end).*[\r\n]+"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ public int compare(final Location location0, final Location location1) {
return uri0.compareTo(uri1);
}

// compare start of range
// Compare start position of range.
final Range range0 = validLocation0.getRange();
Objects.requireNonNull(range0);
final Range range1 = validLocation1.getRange();
Objects.requireNonNull(range0);
Objects.requireNonNull(range1);

final Position position0 = range0.getStartPosition();
final Position position1 = range1.getStartPosition();
return position0.compareTo(position1);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package nl.ramsolutions.sw.magik.analysis.typing.indexer;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import nl.ramsolutions.sw.magik.MagikFile;
import nl.ramsolutions.sw.magik.analysis.MagikAnalysisConfiguration;
import nl.ramsolutions.sw.magik.analysis.definitions.BinaryOperatorDefinition;
import nl.ramsolutions.sw.magik.analysis.definitions.ConditionDefinition;
import nl.ramsolutions.sw.magik.analysis.definitions.Definition;
import nl.ramsolutions.sw.magik.analysis.definitions.ExemplarDefinition;
import nl.ramsolutions.sw.magik.analysis.definitions.GlobalDefinition;
import nl.ramsolutions.sw.magik.analysis.definitions.IDefinitionKeeper;
import nl.ramsolutions.sw.magik.analysis.definitions.MethodDefinition;
import nl.ramsolutions.sw.magik.analysis.definitions.PackageDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Magik file indexer. */
public class MagikIndexer {

private static final Logger LOGGER = LoggerFactory.getLogger(MagikIndexer.class);
private static final long MAX_SIZE = 1024L * 1024L * 10L; // 10 MB

private final IDefinitionKeeper definitionKeeper;
private final Map<Path, Set<PackageDefinition>> indexedPackages = new HashMap<>();
private final Map<Path, Set<ExemplarDefinition>> indexedTypes = new HashMap<>();
private final Map<Path, Set<MethodDefinition>> indexedMethods = new HashMap<>();
private final Map<Path, Set<GlobalDefinition>> indexedGlobals = new HashMap<>();
private final Map<Path, Set<BinaryOperatorDefinition>> indexedBinaryOperators = new HashMap<>();
private final Map<Path, Set<ConditionDefinition>> indexedConditions = new HashMap<>();

public MagikIndexer(final IDefinitionKeeper definitionKeeper) {
this.definitionKeeper = definitionKeeper;
}

/**
* Index all magik file(s).
*
* @param analysisConfiguration Analysis configuration.
* @param paths Paths to index.
* @throws IOException -
*/
public void indexPaths(
final MagikAnalysisConfiguration analysisConfiguration, final Stream<Path> paths)
throws IOException {
paths
.filter(path -> path.toString().toLowerCase().endsWith(".magik"))
.forEach(path -> this.indexPathCreated(analysisConfiguration, path));
}

/**
* Index a single magik file when it is created (or first read).
*
* @param path Path to magik file.
*/
@SuppressWarnings("checkstyle:IllegalCatch")
public void indexPathCreated(
final MagikAnalysisConfiguration analysisConfiguration, final Path path) {
LOGGER.debug("Scanning created file: {}", path);

try {
this.scrubDefinitions(path);
this.readDefinitions(analysisConfiguration, path);
} catch (final Exception exception) {
LOGGER.error("Error indexing created file: " + path, exception);
}
}

/**
* Index a single magik file when it is changed.
*
* @param path Path to magik file.
*/
@SuppressWarnings("checkstyle:IllegalCatch")
public void indexPathChanged(
final MagikAnalysisConfiguration analysisConfiguration, final Path path) {
LOGGER.debug("Scanning changed file: {}", path);

try {
this.scrubDefinitions(path);
this.readDefinitions(analysisConfiguration, path);
} catch (final Exception exception) {
LOGGER.error("Error indexing changed file: " + path, exception);
}
}

/**
* Un-index a single magik file when it is deleted.
*
* @param path Path to magik file.
*/
@SuppressWarnings("checkstyle:IllegalCatch")
public void indexPathDeleted(final Path path) {
LOGGER.debug("Scanning deleted file: {}", path);

try {
this.scrubDefinitions(path);
} catch (final Exception exception) {
LOGGER.error("Error indexing deleted file: " + path, exception);
}
}

private void handleDefinition(final MagikFile magikFile, final Definition rawDefinition) {
// Strip off AstNode, we don't want to store this.
final Definition definition = rawDefinition.getWithoutNode();

final Path path = Path.of(magikFile.getUri());
if (definition instanceof PackageDefinition) {
final PackageDefinition packageDefinition = (PackageDefinition) definition;
this.definitionKeeper.add(packageDefinition);

final Set<PackageDefinition> defs =
this.indexedPackages.computeIfAbsent(path, k -> new HashSet<>());
defs.add(packageDefinition);
} else if (definition instanceof ExemplarDefinition) {
final ExemplarDefinition exemplarDefinition = (ExemplarDefinition) definition;
this.definitionKeeper.add(exemplarDefinition);

final Set<ExemplarDefinition> defs =
this.indexedTypes.computeIfAbsent(path, k -> new HashSet<>());
defs.add(exemplarDefinition);
} else if (definition instanceof MethodDefinition) {
final MethodDefinition methodDefinition = (MethodDefinition) definition;
this.definitionKeeper.add(methodDefinition);

final Set<MethodDefinition> defs =
this.indexedMethods.computeIfAbsent(path, k -> new HashSet<>());
defs.add(methodDefinition);
} else if (definition instanceof GlobalDefinition) {
final GlobalDefinition globalDefinition = (GlobalDefinition) definition;
this.definitionKeeper.add(globalDefinition);

final Set<GlobalDefinition> defs =
this.indexedGlobals.computeIfAbsent(path, k -> new HashSet<>());
defs.add(globalDefinition);
} else if (definition instanceof BinaryOperatorDefinition) {
final BinaryOperatorDefinition binaryOperatorDefinition =
(BinaryOperatorDefinition) definition;
this.definitionKeeper.add(binaryOperatorDefinition);

final Set<BinaryOperatorDefinition> defs =
this.indexedBinaryOperators.computeIfAbsent(path, k -> new HashSet<>());
defs.add(binaryOperatorDefinition);
} else if (definition instanceof ConditionDefinition) {
final ConditionDefinition conditionDefinition = (ConditionDefinition) definition;
this.definitionKeeper.add(conditionDefinition);

final Set<ConditionDefinition> defs =
this.indexedConditions.computeIfAbsent(path, k -> new HashSet<>());
defs.add(conditionDefinition);
}
}

/**
* Read definitions from path.
*
* @param path Path to magik file.
*/
private void readDefinitions(
final MagikAnalysisConfiguration analysisConfiguration, final Path path) {
this.indexedMethods.put(path, new HashSet<>());
this.indexedGlobals.put(path, new HashSet<>());
this.indexedBinaryOperators.put(path, new HashSet<>());
this.indexedPackages.put(path, new HashSet<>());
this.indexedTypes.put(path, new HashSet<>());
this.indexedConditions.put(path, new HashSet<>());

try {
final long size = Files.size(path);
if (size > MagikIndexer.MAX_SIZE) {
LOGGER.warn(
"Ignoring file: {}, due to size: {}, max size: {}", path, size, MagikIndexer.MAX_SIZE);
return;
}

final MagikFile magikFile = new MagikFile(analysisConfiguration, path);
magikFile
.getDefinitions()
.forEach(definition -> this.handleDefinition(magikFile, definition));
} catch (final IOException exception) {
LOGGER.error(exception.getMessage(), exception);
}
}

/**
* Scrub definitions.
*
* @param path Path to magik file.
*/
private void scrubDefinitions(final Path path) {
this.indexedMethods
.getOrDefault(path, Collections.emptySet())
.forEach(this.definitionKeeper::remove);
this.indexedMethods.remove(path);

this.indexedGlobals
.getOrDefault(path, Collections.emptySet())
.forEach(this.definitionKeeper::remove);
this.indexedGlobals.remove(path);

this.indexedBinaryOperators
.getOrDefault(path, Collections.emptySet())
.forEach(this.definitionKeeper::remove);
this.indexedBinaryOperators.remove(path);

this.indexedTypes.getOrDefault(path, Collections.emptySet()).stream()
.forEach(this.definitionKeeper::remove);
this.indexedTypes.remove(path);

this.indexedPackages.getOrDefault(path, Collections.emptySet()).stream()
.forEach(this.definitionKeeper::remove);
this.indexedPackages.remove(path);

this.indexedConditions
.getOrDefault(path, Collections.emptySet())
.forEach(this.definitionKeeper::remove);
this.indexedConditions.remove(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ void handleBinaryExpression(final AstNode node) {
final AbstractType rightType = rightResult.get(0, unsetType);
final TypeString rightRef = rightType.getTypeString();
switch (operatorStr.toLowerCase()) {
case "_is", "_isnt":
case "_is":
case "_isnt":
result = new ExpressionResult(falseType);
break;

case "_andif", "_orif":
case "_andif":
case "_orif":
// Returns RHS if LHS is true.
final CombinedType combinedType = new CombinedType(falseType, rightType);
final AbstractType combinedType = CombinedType.combine(falseType, rightType);
result = new ExpressionResult(combinedType);
break;

Expand Down Expand Up @@ -140,13 +142,15 @@ void handleAugmentedAssignmentExpression(final AstNode node) {
final TypeString rightRef = rightType.getTypeString();
final ExpressionResult result;
switch (operatorStr.toLowerCase()) {
case "_is", "_isnt":
case "_is":
case "_isnt":
result = new ExpressionResult(falseType);
break;

case "_andif", "_orif":
case "_andif":
case "_orif":
// Returns RHS if LHS is true.
final CombinedType combinedType = new CombinedType(falseType, rightType);
final AbstractType combinedType = CombinedType.combine(falseType, rightType);
result = new ExpressionResult(combinedType);
break;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void handleParameter(final AstNode node) {
final AbstractType type = this.typeReader.parseTypeString(parameterTypeString);
if (helper.isOptionalParameter()) {
final AbstractType unsetType = this.typeKeeper.getType(TypeString.SW_UNSET);
final AbstractType optionalType = new CombinedType(type, unsetType);
final AbstractType optionalType = CombinedType.combine(type, unsetType);
result = new ExpressionResult(optionalType);
} else {
result = new ExpressionResult(type);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nl.ramsolutions.sw.magik.analysis.typing.types;

import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
Expand All @@ -20,25 +19,18 @@ public class CombinedType extends AbstractType {
/**
* Constructor.
*
* @param types Combined types.
*/
public CombinedType(final AbstractType... types) {
this(Arrays.asList(types));
}

/**
* Constructor.
* <p>Use method {@link #combine(AbstractType...)} create a new instance of this type.
*
* @param types Combined types.
*/
public CombinedType(final Collection<AbstractType> types) {
private CombinedType(final Collection<AbstractType> types) {
super(null, null);
this.types =
types.stream()
.flatMap(
type ->
type instanceof CombinedType combinedType
? combinedType.getTypes().stream()
type instanceof CombinedType
? ((CombinedType) type).getTypes().stream()
: Stream.of(type))
.collect(Collectors.toUnmodifiableSet());
}
Expand Down Expand Up @@ -177,6 +169,10 @@ public AbstractType substituteType(final AbstractType from, final AbstractType t
return this;
}

if (substitutedTypes.size() == 1) {
return substitutedTypes.iterator().next();
}

return new CombinedType(substitutedTypes);
}

Expand Down
Loading

0 comments on commit dda6628

Please sign in to comment.