Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into natparse/allow-emhdpm…
Browse files Browse the repository at this point in the history
…-on-untyped-view

* origin/main:
  Implement parameter check for external modules (#518)
  • Loading branch information
MarkusAmshove committed Dec 29, 2024
2 parents 1ec028b + cd46f29 commit 1c75187
Show file tree
Hide file tree
Showing 42 changed files with 2,039 additions and 112 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package org.amshove.natlint.cli;

import org.amshove.natlint.cli.git.GitStatusPredicateParser;
import org.amshove.natlint.cli.predicates.DiagnosticPredicate;
import org.amshove.natlint.cli.predicates.FilePredicate;
import org.amshove.natlint.cli.predicates.GlobFilePredicate;
import org.amshove.natlint.cli.sinks.FileStatusSink;
import org.amshove.natparse.DiagnosticSeverity;
import org.amshove.natparse.IDiagnostic;
import org.amshove.natparse.natural.project.NaturalFile;
import picocli.CommandLine;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.Predicate;

@CommandLine.Command(name = "analyze", description = "Analyze the Natural project in the current working directory", mixinStandardHelpOptions = true)
@SuppressWarnings("java:S106")
Expand Down Expand Up @@ -87,15 +86,12 @@ public class AnalyzeCommand implements Callable<Integer>
}, description = "Skips analyzing with natlint", defaultValue = "false")
boolean disableLinting;

private static final List<Predicate<NaturalFile>> DEFAULT_MODULE_PREDICATES = List.of(f -> true);
private static final List<Predicate<IDiagnostic>> DEFAULT_DIAGNOSTIC_PREDICATES = List.of(d -> true);

private final List<Predicate<NaturalFile>> modulePredicates = new ArrayList<>();
private final List<Predicate<IDiagnostic>> diagnosticPredicates = new ArrayList<>();
private AnalyzerPredicates predicates;

@Override
public Integer call()
{
predicates = new AnalyzerPredicates(minimumSeverity);
configureModulePredicates();
configureDiagnosticPredicates();
configureSinkType();
Expand All @@ -108,6 +104,7 @@ public Integer call()
@SuppressWarnings("unused")
public int analyzeFromGitStatus() throws IOException
{
predicates = new AnalyzerPredicates(minimumSeverity);
configureModulePredicates();
configureDiagnosticPredicates();
configureSinkType();
Expand Down Expand Up @@ -135,7 +132,7 @@ public int analyzeFromGitStatus() throws IOException
}

var filePredicates = new GitStatusPredicateParser().parseStatusToPredicates(gitChanges);
modulePredicates.addAll(filePredicates);
filePredicates.forEach(fp -> predicates.addFilePredicate(new FilePredicate("git", fp)));

var analyzer = createAnalyzer();
var exitCode = analyzer.run();
Expand All @@ -148,38 +145,37 @@ private void configureModulePredicates()
{
qualifiedNames.stream()
.map(QualifiedModuleName::from)
.forEach(qn -> modulePredicates.add(f -> f.getFilenameWithoutExtension().equalsIgnoreCase(qn.filename) && f.getLibrary().getName().equalsIgnoreCase(qn.library)));
.forEach(qn -> predicates.addFilePredicate(new FilePredicate("file %s in library %s".formatted(qn.filename, qn.library), f -> f.getFilenameWithoutExtension().equalsIgnoreCase(qn.filename) && f.getLibrary().getName().equalsIgnoreCase(qn.library))));
}

if (relativePaths != null)
{
relativePaths.stream()
.map(p -> p.replace("./", "").replace(".\\", ""))
.map(Paths::get)
.forEach(p -> modulePredicates.add(f -> f.getProjectRelativePath().equals(p)));
.forEach(p -> predicates.addFilePredicate(new FilePredicate("modules relative to %s".formatted(p), f -> f.getProjectRelativePath().startsWith(p))));
}

if (libraries != null)
{
libraries
.forEach(l -> modulePredicates.add(f -> f.getLibrary().getName().equalsIgnoreCase(l)));
.forEach(l -> predicates.addFilePredicate(new FilePredicate("modules in library %s".formatted(l), f -> f.getLibrary().getName().equalsIgnoreCase(l))));
}

if (globs != null)
{
globs.stream()
.map(g -> FileSystems.getDefault().getPathMatcher("glob:" + g))
.forEach(gp -> modulePredicates.add(f -> gp.matches(f.getPath())));
.map(GlobFilePredicate::new)
.forEach(predicates::addFilePredicate);
}
}

private void configureDiagnosticPredicates()
{
diagnosticPredicates.add(d -> d.severity().isWorseOrEqualTo(minimumSeverity));

if (diagnosticIds != null)
{
diagnosticIds
.forEach(id -> diagnosticPredicates.add(d -> d.id().equals(id)));
.forEach(id -> predicates.addDiagnosticPredicate(new DiagnosticPredicate("id " + id, d -> d.id().equals(id))));
}
}

Expand All @@ -200,8 +196,7 @@ private CliAnalyzer createAnalyzer()
theWorkingDirectory,
sinkType.createSink(theWorkingDirectory),
fileStatusMode ? FileStatusSink.create() : FileStatusSink.dummy(),
modulePredicates.isEmpty() ? DEFAULT_MODULE_PREDICATES : modulePredicates,
diagnosticPredicates.isEmpty() ? DEFAULT_DIAGNOSTIC_PREDICATES : diagnosticPredicates,
predicates,
disableLinting
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.amshove.natlint.cli;

import org.amshove.natlint.cli.predicates.DiagnosticPredicate;
import org.amshove.natlint.cli.predicates.IFilePredicate;
import org.amshove.natparse.DiagnosticSeverity;
import org.amshove.natparse.IDiagnostic;
import org.amshove.natparse.natural.project.NaturalFile;

import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("java:S106")
public class AnalyzerPredicates
{
private final List<IFilePredicate> filePredicates = new ArrayList<>();
private final List<DiagnosticPredicate> diagnosticPredicates = new ArrayList<>();
private final DiagnosticSeverity minimumSeverity;

public AnalyzerPredicates(DiagnosticSeverity minimumSeverity)
{
this.minimumSeverity = minimumSeverity;
}

void addFilePredicate(IFilePredicate predicate)
{
filePredicates.add(predicate);
}

void addDiagnosticPredicate(DiagnosticPredicate predicate)
{
diagnosticPredicates.add(predicate);
}

public boolean shouldAnalyzeFile(NaturalFile file)
{
return filePredicates.isEmpty() || filePredicates.stream().anyMatch(p -> p.predicate().test(file));
}

public boolean shouldPrintDiagnostic(IDiagnostic diagnostic)
{
if (!diagnostic.severity().isWorseOrEqualTo(minimumSeverity))
{
return false;
}

return diagnosticPredicates.isEmpty() || diagnosticPredicates.stream().anyMatch(p -> p.predicate().test(diagnostic));
}

public void printSettings()
{
var hasPredicates = minimumSeverity != DiagnosticSeverity.INFO
|| !filePredicates.isEmpty()
|| !diagnosticPredicates.isEmpty();

if (!hasPredicates)
{
return;
}

if (!filePredicates.isEmpty())
{
System.out.println("Only the following files will be analyzed:");
filePredicates.forEach(fp -> System.out.println("- " + fp.description()));
}

System.out.println("Only the following diagnostics will be printed:");
System.out.printf("- minimum severity %s%n", minimumSeverity);
diagnosticPredicates.forEach(dp -> System.out.println("- " + dp.description()));

System.out.println();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,20 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;

public class CliAnalyzer
{
private final List<Predicate<NaturalFile>> filePredicates;
private final List<Predicate<IDiagnostic>> diagnosticPredicates;
private final ActualFilesystem filesystem;
private final IDiagnosticSink diagnosticSink;
private final boolean disableLinting;
private Path workingDirectory;
private final AnalyzerPredicates predicates;
private final FileStatusSink fileStatusSink;

public CliAnalyzer(Path workingDirectory, IDiagnosticSink sink, FileStatusSink fileStatusSink, List<Predicate<NaturalFile>> filePredicates, List<Predicate<IDiagnostic>> diagnosticPredicates, boolean disableLinting)
public CliAnalyzer(Path workingDirectory, IDiagnosticSink sink, FileStatusSink fileStatusSink, AnalyzerPredicates predicates, boolean disableLinting)
{
this.workingDirectory = workingDirectory;
this.filePredicates = filePredicates;
this.diagnosticPredicates = diagnosticPredicates;
this.predicates = predicates;
filesystem = new ActualFilesystem();
diagnosticSink = sink;
this.fileStatusSink = fileStatusSink;
Expand Down Expand Up @@ -88,6 +85,8 @@ public int run()
editorconfigPath.toFile().exists() ? ".editorconfig picked up" : ""
);

predicates.printSettings();

return analyze(projectFile.get());
}

Expand Down Expand Up @@ -123,7 +122,8 @@ private int analyze(Path projectFilePath)
fileStatusSink.printError(file.getPath(), MessageType.INDEX_EXCEPTION, file.getInitException());
return;
}
if (filePredicates.stream().noneMatch(p -> p.test(file)))

if (!predicates.shouldAnalyzeFile(file))
{
fileStatusSink.printStatus(file.getPath(), MessageType.FILE_EXCLUDED);
return;
Expand Down Expand Up @@ -223,7 +223,7 @@ private int analyze(Path projectFilePath)

private List<? extends IDiagnostic> filterDiagnostics(ReadOnlyList<? extends IDiagnostic> diagnostics)
{
return diagnostics.stream().filter(d -> diagnosticPredicates.stream().allMatch(p -> p.test(d))).toList();
return diagnostics.stream().filter(predicates::shouldPrintDiagnostic).toList();
}

private TokenList lex(NaturalFile file, ArrayList<IDiagnostic> allDiagnosticsInFile)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.amshove.natlint.cli.predicates;

import org.amshove.natparse.IDiagnostic;

import java.util.function.Predicate;

public record DiagnosticPredicate(String description, Predicate<IDiagnostic> predicate)
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.amshove.natlint.cli.predicates;

import org.amshove.natparse.natural.project.NaturalFile;

import java.util.function.Predicate;

public record FilePredicate(String description, Predicate<NaturalFile> predicate) implements IFilePredicate
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.amshove.natlint.cli.predicates;

import org.amshove.natparse.natural.project.NaturalFile;

import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.util.function.Predicate;

public class GlobFilePredicate implements IFilePredicate
{
private final String glob;
private final PathMatcher matcher;

public GlobFilePredicate(String glob)
{
this.glob = glob;
matcher = FileSystems.getDefault().getPathMatcher("glob:" + glob);
}

@Override
public String description()
{
return "glob: " + glob;
}

@Override
public Predicate<NaturalFile> predicate()
{
return f -> matcher.matches(f.getPath());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.amshove.natlint.cli.predicates;

import org.amshove.natparse.natural.project.NaturalFile;

import java.util.function.Predicate;

public interface IFilePredicate
{
String description();

Predicate<NaturalFile> predicate();
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ public static FileEdit addPrototype(LanguageServerFile inFile, IFunction calledF
var insertion = rangeFinder.findInsertionPositionForStatementAtStart(inFile);

var defineDataBlock = "";
var parameter = calledFunction.defineData().parameterInOrder();
var parameter = calledFunction.defineData().declaredParameterInOrder();
if (!parameter.isEmpty())
{
defineDataBlock = """
%n DEFINE DATA
%s
END-DEFINE""".formatted(
calledFunction.defineData().parameterInOrder().stream().map(p ->
calledFunction.defineData().declaredParameterInOrder().stream().map(p ->
{
if (p instanceof IUsingNode using)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ private String functionParameterListAsSnippet(LanguageServerFile function)

var builder = new StringBuilder();
var index = 1;
for (var parameter : hasDefineData.defineData().parameterInOrder())
for (var parameter : hasDefineData.defineData().declaredParameterInOrder())
{
if (index > 1)
{
Expand All @@ -690,7 +690,7 @@ private String externalModuleParameterListAsSnippet(LanguageServerFile module)

var builder = new StringBuilder();
var index = 1;
for (var parameter : hasDefineData.defineData().parameterInOrder())
for (var parameter : hasDefineData.defineData().declaredParameterInOrder())
{
builder.append(" ");
var parameterName = parameter instanceof IUsingNode using ? using.target().symbolName() : ((IVariableNode) parameter).name();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ private void addModuleParameter(IMarkupContentBuilder contentBuilder, INaturalMo
contentBuilder.appendSection("Parameter", nested ->
{
var parameterBlock = new StringBuilder();
for (var parameterDefinition : hasDefineData.defineData().parameterInOrder())
for (var parameterDefinition : hasDefineData.defineData().declaredParameterInOrder())
{
if (parameterDefinition instanceof IUsingNode using)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private SignatureHelp provideSignatureForStatement(IModuleReferencingNode module
signatureInformation.setDocumentation(MarkupContentBuilderFactory.newBuilder().appendCode(moduleDocumentation).build());
}

hasDefineData.defineData().parameterInOrder().stream()
hasDefineData.defineData().declaredParameterInOrder().stream()
.map(pi -> mapToParameterInformation(calledModule, pi))
.forEach(p -> signatureInformation.getParameters().add(p));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ public interface IFilesystem
Optional<Path> findFile(String name, Path root);

Stream<String> peekFile(Path path);

static String filenameWithoutExtension(Path path)
{
var split = path.getFileName().toString().split("\\.");
return split[0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ default boolean isUpperUnbound()
* <a href=
* "https://documentation.softwareag.com/natural/nat912win/sm/defineda_array.htm#Variable_Arrays_in_a_Parameter_Data_Area">Documentation</a>
*/
default boolean isUpperVariable()
{
return upperBound() == VARIABLE_BOUND;
}
boolean isUpperVariable();

default int occurerences()
default int occurrences()
{
if (isLowerUnbound() || isUpperUnbound())
{
Expand Down
Loading

0 comments on commit 1c75187

Please sign in to comment.