use crate::common::{create_psr4_workspace, create_test_backend};
use tower_lsp::LanguageServer;
use tower_lsp::lsp_types::*;
#[tokio::test]
async fn test_define_value_no_trailing_paren() {
let backend = create_test_backend();
let uri = "file:///define_value.php";
let content = "<?php\ndefine('REPORT_ALL', 255);\n";
backend.update_ast(uri, content);
let dmap = backend.global_defines().read();
let info = dmap
.get("REPORT_ALL")
.expect("REPORT_ALL should be in global_defines");
assert_eq!(
info.value.as_deref(),
Some("255"),
"value should be '255', not '255)' or '255);'"
);
}
#[tokio::test]
async fn test_const_value_no_trailing_semicolon() {
let backend = create_test_backend();
let uri = "file:///const_value.php";
let content = "<?php\nconst LIMIT = 100;\n";
backend.update_ast(uri, content);
let dmap = backend.global_defines().read();
let info = dmap
.get("LIMIT")
.expect("LIMIT should be in global_defines");
assert_eq!(
info.value.as_deref(),
Some("100"),
"value should be '100', not '100;'"
);
}
#[tokio::test]
async fn test_define_string_value() {
let backend = create_test_backend();
let uri = "file:///define_str.php";
let content = "<?php\ndefine('APP_NAME', 'PHPantom');\n";
backend.update_ast(uri, content);
let dmap = backend.global_defines().read();
let info = dmap
.get("APP_NAME")
.expect("APP_NAME should be in global_defines");
assert_eq!(
info.value.as_deref(),
Some("'PHPantom'"),
"string value should include quotes but no trailing paren"
);
}
#[tokio::test]
async fn test_define_no_value() {
let backend = create_test_backend();
let uri = "file:///define_noval.php";
let content = "<?php\ndefine('NO_VAL');\n";
backend.update_ast(uri, content);
let dmap = backend.global_defines().read();
if let Some(info) = dmap.get("NO_VAL") {
assert_eq!(
info.value, None,
"define with no second arg should have no value"
);
}
}
#[tokio::test]
async fn test_define_value_multi_constant_stub_file() {
let backend = create_test_backend();
let uri = "file:///mysqli_constants.php";
let content = concat!(
"<?php\n",
"define('MYSQLI_REPORT_ALL', 255);\n",
"/**\n",
" * Turns reporting off.\n",
" */\n",
"define('MYSQLI_REPORT_OFF', 0);\n",
);
backend.update_ast(uri, content);
let dmap = backend.global_defines().read();
let all = dmap
.get("MYSQLI_REPORT_ALL")
.expect("MYSQLI_REPORT_ALL should exist");
assert_eq!(
all.value.as_deref(),
Some("255"),
"value should be '255', not '255)'"
);
let off = dmap
.get("MYSQLI_REPORT_OFF")
.expect("MYSQLI_REPORT_OFF should exist");
assert_eq!(off.value.as_deref(), Some("0"), "value should be '0'");
}
#[tokio::test]
async fn test_const_declaration_value_clean() {
let backend = create_test_backend();
let uri = "file:///consts.php";
let content = "<?php\nconst LIMIT = 100;\nconst NAME = 'hello';\n";
backend.update_ast(uri, content);
let dmap = backend.global_defines().read();
let limit = dmap.get("LIMIT").expect("LIMIT should exist");
assert_eq!(limit.value.as_deref(), Some("100"));
let name = dmap.get("NAME").expect("NAME should exist");
assert_eq!(name.value.as_deref(), Some("'hello'"));
}
#[tokio::test]
async fn test_goto_definition_constant_same_file() {
let backend = create_test_backend();
let uri = Url::parse("file:///constants.php").unwrap();
let text = concat!(
"<?php\n", "define('APP_VERSION', '1.0.0');\n", "define('APP_NAME', 'PHPantom');\n", "\n", "echo APP_VERSION;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 4,
character: 7,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve constant APP_VERSION to its define() call"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 1,
"APP_VERSION is defined on line 1"
);
assert_eq!(
location.range.start.character, 0,
"define() starts at column 0"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_same_file_second_constant() {
let backend = create_test_backend();
let uri = Url::parse("file:///constants2.php").unwrap();
let text = concat!(
"<?php\n", "define('APP_VERSION', '1.0.0');\n", "define('APP_NAME', 'PHPantom');\n", "\n", "echo APP_NAME;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 4,
character: 7,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve constant APP_NAME to its define() call"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 2,
"APP_NAME is defined on line 2"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_double_quoted() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_dq.php").unwrap();
let text = concat!(
"<?php\n", "define(\"DB_HOST\", 'localhost');\n", "\n", "echo DB_HOST;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 3,
character: 7,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve double-quoted define constant"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(location.range.start.line, 1, "DB_HOST is defined on line 1");
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_indented_define() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_indent.php").unwrap();
let text = concat!(
"<?php\n", "if (!defined('DEBUG_MODE')) {\n", " define('DEBUG_MODE', true);\n", "}\n", "\n", "echo DEBUG_MODE;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 5,
character: 7,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(result.is_some(), "Should resolve indented define constant");
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 2,
"DEBUG_MODE is defined on line 2"
);
assert_eq!(
location.range.start.character, 4,
"define() is indented by 4 spaces"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_cross_file() {
let (backend, _dir) = create_psr4_workspace(
r#"{ "autoload": { "psr-4": { "App\\": "src/" } } }"#,
&[
(
"src/constants.php",
concat!(
"<?php\n", "define('MAX_RETRIES', 3);\n", "define('DEFAULT_TIMEOUT', 30);\n", ),
),
(
"src/Service.php",
concat!(
"<?php\n", "namespace App;\n", "class Service {\n", " public function getTimeout(): int {\n", " return DEFAULT_TIMEOUT;\n", " }\n", "}\n", ),
),
],
);
let constants_path = _dir.path().join("src/constants.php");
let constants_uri = Url::from_file_path(&constants_path).unwrap();
let constants_text = std::fs::read_to_string(&constants_path).unwrap();
backend
.did_open(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: constants_uri.clone(),
language_id: "php".to_string(),
version: 1,
text: constants_text,
},
})
.await;
let service_path = _dir.path().join("src/Service.php");
let service_uri = Url::from_file_path(&service_path).unwrap();
let service_text = std::fs::read_to_string(&service_path).unwrap();
backend
.did_open(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: service_uri.clone(),
language_id: "php".to_string(),
version: 1,
text: service_text,
},
})
.await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier {
uri: service_uri.clone(),
},
position: Position {
line: 4,
character: 18,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve DEFAULT_TIMEOUT to its define() in constants.php"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, constants_uri, "Should jump to constants.php");
assert_eq!(
location.range.start.line, 2,
"DEFAULT_TIMEOUT is defined on line 2 of constants.php"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_cross_file_first_constant() {
let (backend, _dir) = create_psr4_workspace(
r#"{ "autoload": { "psr-4": { "App\\": "src/" } } }"#,
&[
(
"src/constants.php",
concat!(
"<?php\n", "define('MAX_RETRIES', 3);\n", "define('DEFAULT_TIMEOUT', 30);\n", ),
),
(
"src/Worker.php",
concat!(
"<?php\n", "namespace App;\n", "class Worker {\n", " public function run(): void {\n", " for ($i = 0; $i < MAX_RETRIES; $i++) {}\n", " }\n", "}\n", ),
),
],
);
let constants_path = _dir.path().join("src/constants.php");
let constants_uri = Url::from_file_path(&constants_path).unwrap();
let constants_text = std::fs::read_to_string(&constants_path).unwrap();
backend
.did_open(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: constants_uri.clone(),
language_id: "php".to_string(),
version: 1,
text: constants_text,
},
})
.await;
let worker_path = _dir.path().join("src/Worker.php");
let worker_uri = Url::from_file_path(&worker_path).unwrap();
let worker_text = std::fs::read_to_string(&worker_path).unwrap();
backend
.did_open(DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: worker_uri.clone(),
language_id: "php".to_string(),
version: 1,
text: worker_text,
},
})
.await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier {
uri: worker_uri.clone(),
},
position: Position {
line: 4,
character: 35,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve MAX_RETRIES to its define() in constants.php"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, constants_uri, "Should jump to constants.php");
assert_eq!(
location.range.start.line, 1,
"MAX_RETRIES is defined on line 1 of constants.php"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_in_function_arg() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_arg.php").unwrap();
let text = concat!(
"<?php\n", "define('LOG_LEVEL', 'info');\n", "\n", "function setLevel(string $level): void {}\n", "setLevel(LOG_LEVEL);\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 4,
character: 12,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve LOG_LEVEL used as function argument"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 1,
"LOG_LEVEL is defined on line 1"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_inside_class() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_class.php").unwrap();
let text = concat!(
"<?php\n", "define('BASE_URL', 'https://example.com');\n", "\n", "class ApiClient {\n", " public function getUrl(): string {\n", " return BASE_URL . '/api';\n", " }\n", "}\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 5,
character: 18,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve BASE_URL used inside a class method"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 1,
"BASE_URL is defined on line 1"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_unknown_returns_none() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_unknown.php").unwrap();
let text = concat!(
"<?php\n", "echo UNKNOWN_CONSTANT;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 1,
character: 10,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_none(),
"Unknown constant should not resolve to any definition"
);
}
#[tokio::test]
async fn test_goto_definition_constant_in_array_index() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_array.php").unwrap();
let text = concat!(
"<?php\n", "define('KEY_NAME', 'name');\n", "\n", "$data = ['name' => 'Alice'];\n", "echo $data[KEY_NAME];\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 4,
character: 14,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve KEY_NAME used as array index"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 1,
"KEY_NAME is defined on line 1"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_multiple_defines() {
let backend = create_test_backend();
let uri = Url::parse("file:///multi_const.php").unwrap();
let text = concat!(
"<?php\n", "define('FIRST', 1);\n", "define('SECOND', 2);\n", "define('THIRD', 3);\n", "\n", "echo THIRD;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 5,
character: 7,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve THIRD to its specific define() call"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 3,
"THIRD is defined on line 3, not line 1 or 2"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_spaces_in_define() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_spaces.php").unwrap();
let text = concat!(
"<?php\n", "define( 'SPACED_CONST', 42 );\n", "\n", "echo SPACED_CONST;\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 3,
character: 7,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve define() with spaces after paren"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 1,
"SPACED_CONST is defined on line 1"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_in_comparison() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_cmp.php").unwrap();
let text = concat!(
"<?php\n", "define('STATUS_ACTIVE', 1);\n", "define('STATUS_INACTIVE', 0);\n", "\n", "function isActive(int $status): bool {\n", " return $status === STATUS_ACTIVE;\n", "}\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 5,
character: 27,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"Should resolve STATUS_ACTIVE used in comparison"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 1,
"STATUS_ACTIVE is defined on line 1"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_inside_class_method() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_in_method.php").unwrap();
let text = concat!(
"<?php\n", "class Demo {\n", " public function run(): void {\n", " define('APP_VERSION', '1.0.0');\n", " echo APP_VERSION;\n", " }\n", "}\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 4,
character: 15,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"GTD on APP_VERSION usage inside class method should jump to define() call"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 3,
"define('APP_VERSION', ...) is on line 3"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}
#[tokio::test]
async fn test_goto_definition_constant_inside_namespace_class_method() {
let backend = create_test_backend();
let uri = Url::parse("file:///const_ns_method.php").unwrap();
let text = concat!(
"<?php\n", "namespace Demo {\n", " class GtdDemo {\n", " public function demo(): void {\n", " define('APP_VERSION', '1.0.0');\n", " echo APP_VERSION;\n", " }\n", " }\n", "}\n", );
let open_params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: uri.clone(),
language_id: "php".to_string(),
version: 1,
text: text.to_string(),
},
};
backend.did_open(open_params).await;
let params = GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams {
text_document: TextDocumentIdentifier { uri: uri.clone() },
position: Position {
line: 5,
character: 20,
},
},
work_done_progress_params: WorkDoneProgressParams::default(),
partial_result_params: PartialResultParams::default(),
};
let result = backend.goto_definition(params).await.unwrap();
assert!(
result.is_some(),
"GTD on APP_VERSION usage in namespace class method should jump to define() call"
);
match result.unwrap() {
GotoDefinitionResponse::Scalar(location) => {
assert_eq!(location.uri, uri);
assert_eq!(
location.range.start.line, 4,
"define('APP_VERSION', ...) is on line 4"
);
}
other => panic!("Expected Scalar location, got: {:?}", other),
}
}