1
+ use annotate_snippets:: { Annotation , AnnotationType , Renderer , Slice , Snippet , SourceAnnotation } ;
1
2
use std:: collections:: { BTreeMap , BTreeSet , HashMap } ;
2
3
use std:: ffi:: OsStr ;
3
4
use std:: path:: { Path , PathBuf } ;
4
5
use std:: rc:: Rc ;
5
6
use std:: str:: { self , FromStr } ;
6
7
8
+ use crate :: AlreadyPrintedError ;
7
9
use anyhow:: { anyhow, bail, Context as _} ;
8
10
use cargo_platform:: Platform ;
9
11
use cargo_util:: paths;
10
12
use itertools:: Itertools ;
11
13
use lazycell:: LazyCell ;
14
+ use pathdiff:: diff_paths;
12
15
use tracing:: { debug, trace} ;
13
16
use url:: Url ;
14
17
@@ -24,6 +27,7 @@ use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
24
27
use crate :: util:: errors:: { CargoResult , ManifestError } ;
25
28
use crate :: util:: interning:: InternedString ;
26
29
use crate :: util:: restricted_names;
30
+ use crate :: util:: style:: { ERROR , NOTE , WARN } ;
27
31
use crate :: util:: {
28
32
self , config:: ConfigRelativePath , validate_package_name, Config , IntoUrl , OptVersionReq ,
29
33
} ;
@@ -46,7 +50,7 @@ pub fn read_manifest(
46
50
path : & Path ,
47
51
source_id : SourceId ,
48
52
config : & Config ,
49
- ) -> Result < ( EitherManifest , Vec < PathBuf > ) , ManifestError > {
53
+ ) -> CargoResult < ( EitherManifest , Vec < PathBuf > ) > {
50
54
trace ! (
51
55
"read_manifest; path={}; source-id={}" ,
52
56
path. display( ) ,
@@ -59,15 +63,23 @@ pub fn read_manifest(
59
63
return Err ( ManifestError :: new (
60
64
anyhow:: anyhow!( "parsing `{}` requires `-Zscript`" , path. display( ) ) ,
61
65
path. into ( ) ,
62
- ) ) ;
66
+ ) ) ? ;
63
67
}
64
68
contents = embedded:: expand_manifest ( & contents, path, config)
65
69
. map_err ( |err| ManifestError :: new ( err, path. into ( ) ) ) ?;
66
70
}
67
71
68
- read_manifest_from_str ( & contents, path, embedded, source_id, config)
69
- . with_context ( || format ! ( "failed to parse manifest at `{}`" , path. display( ) ) )
70
- . map_err ( |err| ManifestError :: new ( err, path. into ( ) ) )
72
+ read_manifest_from_str ( & contents, path, embedded, source_id, config) . map_err ( |err| {
73
+ if err. is :: < AlreadyPrintedError > ( ) {
74
+ err
75
+ } else {
76
+ ManifestError :: new (
77
+ err. context ( format ! ( "failed to parse manifest at `{}`" , path. display( ) ) ) ,
78
+ path. into ( ) ,
79
+ )
80
+ . into ( )
81
+ }
82
+ } )
71
83
}
72
84
73
85
/// See also `bin/cargo/commands/run.rs`s `is_manifest_command`
@@ -97,11 +109,69 @@ fn read_manifest_from_str(
97
109
98
110
let mut unused = BTreeSet :: new ( ) ;
99
111
let deserializer = toml:: de:: Deserializer :: new ( contents) ;
100
- let manifest: manifest:: TomlManifest = serde_ignored:: deserialize ( deserializer, |path| {
112
+ let manifest: manifest:: TomlManifest = match serde_ignored:: deserialize ( deserializer, |path| {
101
113
let mut key = String :: new ( ) ;
102
114
stringify ( & mut key, & path) ;
103
115
unused. insert ( key) ;
104
- } ) ?;
116
+ } ) {
117
+ Ok ( manifest) => manifest,
118
+ Err ( e) => {
119
+ return if let Some ( span) = e. span ( ) {
120
+ let ( line, column) = translate_position ( contents. as_bytes ( ) , span. start ) ;
121
+ let lines = contents. lines ( ) ;
122
+ let line_count = lines. clone ( ) . count ( ) ;
123
+ let mut source = lines. skip ( line) . next ( ) . unwrap_or_default ( ) . to_string ( ) ;
124
+ // Add back the new line that was stripped by `.lines()`
125
+ if line_count > line + 1 {
126
+ source. push ( '\n' ) ;
127
+ // If we are trying to highlight past the end of the file, add a
128
+ // placeholder character to the end of the line.
129
+ } else if span. end >= source. len ( ) - 1 {
130
+ source. push ( '∅' ) ;
131
+ }
132
+ // Make sure we don't try to highlight past the end of the line,
133
+ // but also make sure we are highlighting at least one character
134
+ let highlight_end = ( column + ( span. end - span. start ) )
135
+ . min ( source. len ( ) )
136
+ . max ( column + 1 ) ;
137
+ // Get the path to the manifest, relative to the cwd
138
+ let manifest_path = diff_paths ( manifest_file, config. cwd ( ) )
139
+ . unwrap_or ( manifest_file. to_path_buf ( ) )
140
+ . display ( )
141
+ . to_string ( ) ;
142
+ let snippet = Snippet {
143
+ title : Some ( Annotation {
144
+ id : None ,
145
+ label : Some ( e. message ( ) ) ,
146
+ annotation_type : AnnotationType :: Error ,
147
+ } ) ,
148
+ footer : vec ! [ ] ,
149
+ slices : vec ! [ Slice {
150
+ source: & source,
151
+ line_start: line + 1 ,
152
+ origin: Some ( manifest_path. as_str( ) ) ,
153
+ annotations: vec![ SourceAnnotation {
154
+ range: ( column, highlight_end) ,
155
+ label: "" ,
156
+ annotation_type: AnnotationType :: Error ,
157
+ } ] ,
158
+ fold: false ,
159
+ } ] ,
160
+ } ;
161
+ let renderer = Renderer :: styled ( )
162
+ . error ( ERROR )
163
+ . warning ( WARN )
164
+ . info ( NOTE )
165
+ . note ( NOTE )
166
+ . help ( NOTE )
167
+ . line_no ( NOTE ) ;
168
+ writeln ! ( config. shell( ) . err( ) , "{}" , renderer. render( snippet) ) ?;
169
+ Err ( AlreadyPrintedError :: new ( e. into ( ) ) . into ( ) )
170
+ } else {
171
+ Err ( e. into ( ) )
172
+ } ;
173
+ }
174
+ } ;
105
175
let add_unused = |warnings : & mut Warnings | {
106
176
for key in unused {
107
177
warnings. add_warning ( format ! ( "unused manifest key: {}" , key) ) ;
@@ -2161,3 +2231,33 @@ impl ResolveToPath for ConfigRelativePath {
2161
2231
self . resolve_path ( c)
2162
2232
}
2163
2233
}
2234
+
2235
+ fn translate_position ( input : & [ u8 ] , index : usize ) -> ( usize , usize ) {
2236
+ if input. is_empty ( ) {
2237
+ return ( 0 , index) ;
2238
+ }
2239
+
2240
+ let safe_index = index. min ( input. len ( ) - 1 ) ;
2241
+ let column_offset = index - safe_index;
2242
+ let index = safe_index;
2243
+
2244
+ let nl = input[ 0 ..index]
2245
+ . iter ( )
2246
+ . rev ( )
2247
+ . enumerate ( )
2248
+ . find ( |( _, b) | * * b == b'\n' )
2249
+ . map ( |( nl, _) | index - nl - 1 ) ;
2250
+ let line_start = match nl {
2251
+ Some ( nl) => nl + 1 ,
2252
+ None => 0 ,
2253
+ } ;
2254
+ let line = input[ 0 ..line_start] . iter ( ) . filter ( |b| * * b == b'\n' ) . count ( ) ;
2255
+ let line = line;
2256
+
2257
+ let column = std:: str:: from_utf8 ( & input[ line_start..=index] )
2258
+ . map ( |s| s. chars ( ) . count ( ) - 1 )
2259
+ . unwrap_or_else ( |_| index - line_start) ;
2260
+ let column = column + column_offset;
2261
+
2262
+ ( line, column)
2263
+ }
0 commit comments