// Copyright (c) 2026, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of dart.developer;

/// Holds the source code location where an object was created, when its class
/// was annotated with `@pragma('track-creation-locations')`.
///
/// When a class definition is annotated with `@pragma('track-creation-locations')`,
/// the Dart compiler injects the call-site location into any invocation of that
/// class's or any subclass's constructors and stores it in the created object.
///
/// The location of such an object can be read by calling [CreationLocation.of]
/// with the object as the argument.
///
/// ## Example
///
/// ```dart
/// import 'dart:developer';
///
/// // Marks this and any subclass to have their constructor call-sites tracked.
/// @pragma('track-creation-locations')
/// class TargetClass {
///   TargetClass();
/// }
///
/// void main() {
///   // The source-code location of this constructor call is injected into the object.
///   final instance = TargetClass();
///
///   final location = CreationLocation.of(instance);
///   print(location); // Will print the current file path, line 11, column 20
/// }
/// ```
///
/// ## Limitations
///
/// The compiler transformation relies on injecting a named parameter into the
/// target class's constructors. Since Dart semantics do not permit a function
/// to have both optional positional parameters and named parameters simultaneously,
/// this transformation **will silently skip** any constructor that declares optional
/// positional parameters. Calling [CreationLocation.of] on an object whose
/// constructor was skipped, will return `null`.
final class CreationLocation {
  /// Returns the creation location of [object].
  ///
  /// The provided object must be an instance of a class annotated with
  /// `@pragma('track-creation-locations')`.
  static CreationLocation? of(Object? object) {
    if (object is _HasCreationLocation) {
      return object._location;
    }
    return null;
  }

  const CreationLocation._({
    required this.file,
    required this.line,
    required this.column,
    this.name,
  });

  /// File path of the location.
  final String file;

  /// 1-based line number.
  final int line;

  /// 1-based column number.
  final int column;

  /// Optional name of the parameter or function at this location.
  final String? name;

  /// JSON representation of this location.
  Map<String, Object?> toJsonMap() {
    return <String, Object?>{
      'file': file,
      'line': line,
      'column': column,
      'name': name,
    };
  }

  @override
  String toString() => <String>[?name, file, '$line', '$column'].join(':');
}

/// Interface for classes that track the source code location their
/// constructor was called from.
abstract interface class _HasCreationLocation {
  /// The location where the constructor was called.
  CreationLocation? _location;
}
