Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1280,40 +1280,54 @@ else if ( inheritContext.isReversed() ) {
return false;
}
}
Set<String> readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet();
String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors );

Message msg;
String[] args;

if ( targetRef.getPathProperties().isEmpty() ) {
msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE;
Element elementForMessage = mapping.getElement();
if ( elementForMessage == null ) {
elementForMessage = method.getExecutable();
}

if ( mapping.isIgnored() && mapping.getElement() == null ) {
msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED;
args = new String[] {
targetPropertyName,
resultTypeToMap.describe(),
mostSimilarProperty

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we removed the mostSimilarProperty from this error message?

@znight1020 znight1020 Jul 31, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, my main focus was on fixing the NullPointerException and ensuring a clear, specific error message for @Ignored was reported. I completely overlooked adding the "Did you mean" suggestion, my apologies for that.

Adding it would be much more consistent with the other 'Unknown property' errors. Should I create a new PR for this enhancement?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if you could create another PR and add that as well

resultTypeToMap.describe()
};
}
else {
List<String> pathProperties = new ArrayList<>( targetRef.getPathProperties() );
pathProperties.add( mostSimilarProperty );
msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE;
args = new String[] {
targetPropertyName,
resultTypeToMap.describe(),
mapping.getTargetName(),
Strings.join( pathProperties, "." )
};
Set<String> readAccessors = resultTypeToMap.getPropertyReadAccessors().keySet();
String mostSimilarProperty = Strings.getMostSimilarWord( targetPropertyName, readAccessors );

if ( targetRef.getPathProperties().isEmpty() ) {
msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_RESULTTYPE;
args = new String[] {
targetPropertyName,
resultTypeToMap.describe(),
mostSimilarProperty
};
}
else {
List<String> pathProperties = new ArrayList<>( targetRef.getPathProperties() );
pathProperties.add( mostSimilarProperty );
msg = Message.BEANMAPPING_UNKNOWN_PROPERTY_IN_TYPE;
args = new String[] {
targetPropertyName,
resultTypeToMap.describe(),
mapping.getTargetName(),
Strings.join( pathProperties, "." )
};
}
}

ctx.getMessager()
.printMessage(
mapping.getElement(),
mapping.getMirror(),
mapping.getTargetAnnotationValue(),
msg,
args
);
ctx.getMessager().printMessage(
elementForMessage,
mapping.getMirror(),
mapping.getTargetAnnotationValue(),
msg,
args
);
return true;
}
else if ( mapping.getInheritContext() != null && mapping.getInheritContext().isReversed() ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public enum Message {
BEANMAPPING_CYCLE_BETWEEN_PROPERTIES( "Cycle(s) between properties given via dependsOn(): %s." ),
BEANMAPPING_UNKNOWN_PROPERTY_IN_DEPENDS_ON( "\"%s\" is no property of the method return type." ),
BEANMAPPING_IGNORE_BY_DEFAULT_WITH_MAPPING_TARGET_THIS( "Using @BeanMapping( ignoreByDefault = true ) with @Mapping( target = \".\", ... ) is not allowed. You'll need to explicitly ignore the target properties that should be ignored instead." ),
BEANMAPPING_UNKNOWN_PROPERTY_IN_IGNORED("No property named \"%s\" exists in @Ignored for target type \"%s\""),

CONDITION_MISSING_APPLIES_TO_STRATEGY("'appliesTo' has to have at least one value in @Condition" ),
CONDITION_SOURCE_PARAMETERS_INVALID_PARAMETER("Parameter \"%s\" cannot be used with the ConditionStrategy#SOURCE_PARAMETERS. Only source and @Context parameters are allowed for conditions applicable to source parameters." ),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._3902;

import org.mapstruct.Ignored;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

/**
* Mapper for testing bug #3902.
*
* @author znight1020
*/
@Mapper
public interface ErroneousIssue3902Mapper {

ErroneousIssue3902Mapper INSTANCE = Mappers.getMapper( ErroneousIssue3902Mapper.class );

@Ignored(targets = {"name", "foo", "bar"})
ZooDto mapWithOneKnownAndMultipleUnknowns(Zoo source);

@Ignored(targets = {"name", "address", "foo"})
ZooDto mapWithMultipleKnownAndOneUnknown(Zoo source);

class Zoo {
private String name;
private String address;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}
}

class ZooDto {
private String name;
private String address;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._3902;

import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.ProcessorTest;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;

/**
* Verifies that using an unknown property in {@code @Ignored} yields a proper
* compile error instead of an internal processor error.
*
* @author znight1020
*/
@WithClasses({ErroneousIssue3902Mapper.class})
@IssueKey("3902")
public class Issue3902Test {

@ProcessorTest
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
// Test case: mapWithOneKnownAndMultipleUnknowns
@Diagnostic(
type = ErroneousIssue3902Mapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 23,
message = "No property named \"foo\" exists in @Ignored for target type " +
"\"ErroneousIssue3902Mapper.ZooDto\""
),
@Diagnostic(
type = ErroneousIssue3902Mapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 23,
message = "No property named \"bar\" exists in @Ignored for target type " +
"\"ErroneousIssue3902Mapper.ZooDto\""
),

// Test case: mapWithMultipleKnownAndOneUnknown
@Diagnostic(
type = ErroneousIssue3902Mapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 26,
message = "No property named \"foo\" exists in @Ignored for target type " +
"\"ErroneousIssue3902Mapper.ZooDto\""
)
}
)
public void shouldFailOnUnknownPropertiesInIgnored() {
}
}