Skip to content

Commit

Permalink
[#1134] Bugfix: annotation processor should allow @Spec-annotated f…
Browse files Browse the repository at this point in the history
…ield in version provider

Closes #1134
  • Loading branch information
remkop committed Jul 30, 2020
1 parent 6731959 commit 63065d7
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 1 deletion.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Picocli follows [semantic versioning](http://semver.org/).
* [#1126] Enhancement: Make picocli trace levels case-insensitive.
* [#1128] Enhancement: `ParameterException` caused by `TypeConversionException` now have their cause exception set.
* [#1137] Bugfix: The `picocli-codegen` annotation processor causes the build to fail with a `ClassCastException` when an option has `completionCandidates` defined.
* [#1134] Bugfix: The `picocli-codegen` annotation processor should allow `@Spec`-annotated field in classes implementing `IVersionProvider`.
* [#1127] DOC: Custom ShortErrorMessageHandler manual example should use bold red for error message.
* [#1130] DOC: Clarify how to run picocli-based applications.
* [#1131] DOC: Add anchor links before section titles in user manual.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package picocli.annotation.processing.tests;

import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import org.junit.Test;

import javax.annotation.processing.Processor;

import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static picocli.annotation.processing.tests.Resources.slurp;
import static picocli.annotation.processing.tests.YamlAssert.compareCommandYamlDump;

public class Issue1134Test {
@Test
public void testIssue1134() {
Processor processor = new AnnotatedCommandSourceGeneratorProcessor();
Compilation compilation =
javac()
.withProcessors(processor)
.compile(JavaFileObjects.forResource(
"picocli/issue1134/Issue1134.java"));

assertThat(compilation).succeeded();
}

@Test
public void testIssue1134Details() {

Compilation compilation = compareCommandYamlDump(slurp("/picocli/issue1134/Issue1134.yaml"),
JavaFileObjects.forResource("picocli/issue1134/Issue1134.java"));

assertOnlySourceVersionWarning(compilation);
}

private void assertOnlySourceVersionWarning(Compilation compilation) {
assertThat(compilation).hadWarningCount(0); // #826 version warnings are now suppressed
// assertThat(compilation).hadWarningContaining("Supported source version 'RELEASE_6' from annotation processor 'picocli.annotation.processing.tests");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package picocli.issue1134;

import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.IVersionProvider;
import picocli.CommandLine.Model.CommandSpec;
import picocli.CommandLine.Option;
import picocli.CommandLine.Spec;


@Command(name = "top",
versionProvider = MyVersionProvider.class,
resourceBundle = "mybundle5")
public class Issue1134 {

@CommandLine.Option(names = "--level")
private String level;

@Spec
CommandSpec spec;
}

class MyVersionProvider implements IVersionProvider {
@Spec
CommandSpec spec;

public String[] getVersion() {
return new String[] {spec.qualifiedName() + " 1.0"};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
CommandSpec:
name: 'top'
aliases: []
userObject: picocli.issue1134.Issue1134
helpCommand: false
defaultValueProvider: null
versionProvider: VersionProviderMetaData(picocli.issue1134.MyVersionProvider)
version: []
ArgGroups: []
Options:
- names: [--level]
usageHelp: false
versionHelp: false
description: []
descriptionKey: ''
typeInfo: CompileTimeTypeInfo(java.lang.String, aux=[java.lang.String], collection=false, map=false)
arity: 1
splitRegex: ''
interactive: false
required: false
hidden: false
hideParamSyntax: false
defaultValue: 'null'
showDefaultValue: ON_DEMAND
hasInitialValue: false
initialValue: 'null'
paramLabel: '<level>'
converters: []
completionCandidates: null
getter: AnnotatedElementHolder(FIELD level in picocli.issue1134.Issue1134)
setter: AnnotatedElementHolder(FIELD level in picocli.issue1134.Issue1134)
PositionalParams: []
UnmatchedArgsBindings: []
Mixins: []
UsageMessageSpec:
width: 80
abbreviateSynopsis: false
hidden: false
showDefaultValues: false
sortOptions: true
requiredOptionMarker: ' '
headerHeading: ''
header: []
synopsisHeading: 'Usage: '
customSynopsis: []
descriptionHeading: ''
description: []
parameterListHeading: ''
optionListHeading: ''
commandListHeading: 'Commands:%n'
footerHeading: ''
footer: []
ParserSpec:
separator: '='
endOfOptionsDelimiter: '--'
expandAtFiles: true
atFileCommentChar: '#'
overwrittenOptionsAllowed: false
unmatchedArgumentsAllowed: false
unmatchedOptionsArePositionalParams: false
stopAtUnmatched: false
stopAtPositional: false
posixClusteredShortOptionsAllowed: true
aritySatisfiedByAttachedOptionParam: false
caseInsensitiveEnumValuesAllowed: false
collectErrors: false
limitSplit: false
toggleBooleanFlags: false
Subcommands: []
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVisitor;
import javax.lang.model.util.SimpleElementVisitor6;
import javax.lang.model.util.TypeKindVisitor6;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.io.PrintWriter;
Expand Down Expand Up @@ -840,7 +842,23 @@ private void connectModel(AbstractCommandSpecProcessor proc) {
logger.fine("Adding " + entry + " to commandSpec " + commandSpec1);
commandSpec1.addSpecElement(entry.getValue());
} else {
proc.error(entry.getKey(), "@Spec must be enclosed in a @Command, but was %s: %s", entry.getKey().getEnclosingElement(), entry.getKey().getEnclosingElement().getSimpleName());
Element enclosingElement = entry.getKey().getEnclosingElement();
if (enclosingElement.getKind() == ElementKind.CLASS || enclosingElement.getKind() == ENUM) {
TypeMirror typeMirror = enclosingElement.asType();
TypeElement typeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
List<? extends TypeMirror> interfaces = typeElement.getInterfaces();
boolean valid = false;
for (TypeMirror interf : interfaces) {
if (interf.toString().equals("picocli.CommandLine.IVersionProvider")) {
valid = true;
}
}
if (!valid) {
proc.error(entry.getKey(), "@Spec must be enclosed in a @Command, or in a class that implements IVersionProvider but was %s: %s", entry.getKey().getEnclosingElement(), entry.getKey().getEnclosingElement().getSimpleName());
}
} else {
proc.error(entry.getKey(), "@Spec must be enclosed in a @Command, but was %s: %s", entry.getKey().getEnclosingElement(), entry.getKey().getEnclosingElement().getSimpleName());
}
}
}
for (Map.Entry<Element, IAnnotatedElement> entry : parentCommandElements.entrySet()) {
Expand Down

0 comments on commit 63065d7

Please sign in to comment.