private void validateTarget(
            Model model,
            Shape shape,
            Shape target,
            Relationship rel,
            List<ValidationEvent> events
    ) {
        RelationshipType relType = rel.getRelationshipType();

        if (relType != RelationshipType.MIXIN && relType.getDirection() == RelationshipDirection.DIRECTED) {
            if (target.hasTrait(TraitDefinition.ID)) {
                events.add(error(shape,
                        format(
                                "Found a %s reference to trait definition `%s`. Trait definitions cannot be targeted by "
                                        + "members or referenced by shapes in any other context other than applying them as "
                                        + "traits.",
                                relType,
                                rel.getNeighborShapeId())));
                return;
            }

            // Ignoring members with the mixin trait, forbid shapes to reference mixins except as mixins.
            if (!target.isMemberShape() && target.hasTrait(MixinTrait.ID)) {
                events.add(error(shape,
                        format(
                                "Illegal %s reference to mixin `%s`; shapes marked with the mixin trait can only be "
                                        + "referenced to apply them as a mixin.",
                                relType,
                                rel.getNeighborShapeId())));
                return;
            }
        }

        validateDeprecatedTargets(shape, target, relType, events);

        switch (relType) {
            case PROPERTY:
            case MEMBER_TARGET:
                // Members and property cannot target other members, service, operation, or resource shapes.
                if (INVALID_MEMBER_TARGETS.contains(target.getType())) {
                    events.add(error(shape,
                            format(
                                    "Members cannot target %s shapes, but found %s",
                                    target.getType(),
                                    target)));
                }
                break;
            case MAP_KEY:
                target.asMemberShape().ifPresent(m -> validateMapKey(shape, m.getTarget(), model, events));
                break;
            case RESOURCE:
                if (target.getType() != ShapeType.RESOURCE) {
                    events.add(badType(shape, target, relType, ShapeType.RESOURCE));
                }
                break;
            case OPERATION:
                if (target.getType() != ShapeType.OPERATION) {
                    events.add(badType(shape, target, relType, ShapeType.OPERATION));
                }
                break;
            case INPUT:
            case OUTPUT:
                // Input/output must target structures and cannot have the error trait.
                if (target.getType() != ShapeType.STRUCTURE) {
                    events.add(badType(shape, target, relType, ShapeType.STRUCTURE));
                } else if (target.hasTrait(ErrorTrait.ID)) {
                    events.add(inputOutputWithErrorTrait(shape, target, rel.getRelationshipType()));
                }
                break;
            case ERROR:
                // Errors must target a structure with the error trait.
                if (target.getType() != ShapeType.STRUCTURE) {
                    events.add(badType(shape, target, relType, ShapeType.STRUCTURE));
                } else if (!target.hasTrait(ErrorTrait.ID)) {
                    events.add(errorNoTrait(shape, target.getId()));
                }
                break;
            case IDENTIFIER:
                validateIdentifier(shape, target, events);
                break;
            case CREATE:
            case READ:
            case UPDATE:
            case DELETE:
            case LIST:
                if (target.getType() != ShapeType.OPERATION) {
                    events.add(error(shape,
                            format(
                                    "Resource %s lifecycle operation must target an operation, but found %s",
                                    relType.toString().toLowerCase(Locale.US),
                                    target)));
                }
                break;
            case MIXIN:
                if (!target.hasTrait(MixinTrait.ID)) {
                    events.add(error(shape,
                            format(
                                    "Attempted to use %s as a mixin, but it is not marked with the mixin trait",
                                    target.getId())));
                }
                break;
            default:
                break;
        }
    }
Validates that neighbors target resolvable shapes of the correct type.
smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/TargetValidator.java